追捕,我如何移植50,000行代码?,璟

Go 言语的创始人之一 Rob Pike 曾表明,他萧条期望 Go 能够被 C++ 程序员所承受,但成果差强人意。最近,在作者上任的 HFT 公司里,一个团队成功地把一些对速度不太灵敏的基础设施代码从 Python 移植到了 Go,这也促进他们决议尝试用 Go 对杂乱冗余的 C++ 效劳端程序进行重构,这些代码有 5W 行之多,并且对吞吐量有必定的要求。这个效劳端程序运用了跟公司中心买卖软件相同的技能和库,不同地是买卖软件对体系的推迟更灵敏,简直每一微秒都很重要,而 C++ 效劳端并不需求这种程度的功用。因而,运用 Go 自带的调度程序彻底能够满足要求,没有追捕,我怎么移植50,000行代码?,璟必要运用买卖体系完结的超优化 C++ 结构,虽然丢失了一些功用但获得了更好的可保护性。需求一提葬花吟的是,本追捕,我怎么移植50,000行代码?,璟文作者担任了整个代码的重写作业。

前语

从商业视点来看,这个项目是成功的:重写作业提前完结;功用在可承受的规模之内在线翻译器;并且全体代码量不超越 1W 行(代码量的剧减首要是因为重写团队删除了一些过期的或许不需求的特性)。但从开发者的视点来看,作者以为成果并不是最优的。Go 并不支撑参数多态,作者因而运用了两到三倍的代码来完结相似功用。其间一部分是为了保证类型安全:Go 强制开发者在类型润饰和类型安全之间做出取顾十八娘全文阅览免费舍,作者挑选了一个比较均衡的追捕,我怎么移植50,000行代码?,璟完结。总的来说,假如需求一般的类型安全,那么相对少的代码就能够完结,而假如需求更好的类型安全,则需求更多的代码。

接下来让咱们比照一下 Go 言语的优缺陷。

长处

Emacs 开发渠道

凭借主动完结、跳转到界说、保薄存时的过错检查、智能重构和 GoTest 集成等插件,Emacs 成为了 Go 言语环境下最好的 IDE 东西。别的,它也能够很便利地经过 Elisp 进行定制和扩展。假如你自己恰好是 Ema喜欢影院cs 的爱好者,这肯定是一个大大的加分项。

Goroutines(协程)

Go 完结了根据音讯传递的并发,作者以为这是最简略的并发方法,运用也超级便利。经过将 GOMAXPROCS 设置为 1,Go 还答应开发者轿子雪山经过运用与并发代码彻底相同的方法来编写并行 / 异步代码。与其它供给内置轻量级线程调度器的言语 Erlang/Elixir 和 Haskell 比较:前者缺少静态类型,后者在实践开发中很少被管理人员选用。

没有承继

在许多情况下,根据承继的 OO(面向对象)是一种反形式,这些冗余和含糊的代码简直没有什么优点,Go 则直接取消了这类代码。这有或许也是 Rob 追捕,我怎么移植50,000行代码?,璟Pike 等人规划 Go 的初衷:谷歌内部有一大堆相似于企业版别 Fizzbuzz 的 Java/C++ 代码,他们期望能从这些代码中彻底解放出来。也就是说,虽然在旧的 C++ 效劳端留传代码中运用承继是合理的,但最好仍是运用更现代的风格来重写代码,并且重写进程也并不杂乱。

更好的可读性

Go 代码更易于阅览和了解。比较之下,许多 C++ 代码需求几个小时才干彻底了解。Go 自身也促进开发者编写可读的代码:这种言语彻底避免了下面这种自做聪明的景象:

“嘿,这篇论文(基本上没人读得懂)中的 >8=3 运算符能够让我节约 10 行代码,我最好把它写进代码追捕,我怎么移植50,000行代码?,璟里,我的搭档也不难了解这行代码,因为它的意思现已在类型签名中很清楚地表达出来了(横竖我是没看懂):(PrimMonad W, PoshFunctor Y, ReichsLens S) => W Y S((I -> W) -> Y) -> G -> Bool "。

简略而规范的语法

当咱们需求将一个关闭函数的称号添加到每个日志字符串的开始时,假如运用 Emacs,一个简略的regexp find-replace(正则表达式)指令就能够完结,而关于更杂乱的言语则需求运用解析器。不论是经过 Emacs 宏或许是 Go 模板,简略的语法能够更简单地生成代码。

Emacs+是非帝国Go== 参数多态:咱们能够运用 E赵圣桑macs 宏来加快生成 Go 所需求的 " 复制粘贴 ",并且,假如函数编写正确,那咱们也能够用regex指令来更新全部的 " 复制粘贴 " 函数。这样,咱们就能够很简单地更新 fooInt、fooFloat 和 fooDouble 等函数,比照支撑参数多态的言语对 foo 函数的更新,整个进程没有什么太大差异。这样做的缺陷是,虽然 Emacs 宏和regex指令能够编写和修正 Go 代码,但它依然不如真实的多态完结那样简练和易读;并且追捕,我怎么移植50,000行代码?,璟关于不熟悉 regex 以及可扩展编辑器(Emacs)的人来说,保护相同也不简单。

有用的内置模板

经过 Go 的文本 / 模板包,咱们能够很简单地生成新代码。它还答应开发者在生成代码时运用 IO:例如,有一个同某些特定效劳交互的库,它经过 XML Schema 生成。假如能够用不同的函数来生成不同的数据情歌类型,那么就能够保证代码的类型安全。

在 C++ 中,IO 不能在编译时履行,因而不能运用上述形式来生成代码。答应编译时运用 IO 的言语有:

  • F#,经过 TypeProviders 完结。
  • Idris,也使有 TypeProviders。
  • Lisp,能够在宏中履行 IO。
  • Haskell,它有一个编译期运转的函数 IO -> Q。
  • D追捕,我怎么移植50,000行代码?,璟,编译伟人网络时能够运用“import”来读取文件。
  • Nimrod,有特别的函数完结。
  • Elixir 或 Erlang,能够经过宏履行恣意的 IO。
  • Rust,能够运用函数libsyntax在编译时履行恣意的核算和 IO。

缺陷

斯德哥尔摩综合征

前面现已说到,在答应运用 IO 的特性上,运用模板生成 Go 代码要比用 C++ 元编程好得多,而 C++ 元编程在这儿显然是剩余的,因为彻底能够用别的一种能够支撑 IO 的程序言语来生成代码。

没有完结参数多态

虽然许多人以为这超级在实践中并不是一个问题,但在这儿,它是一个很严重的问题。假如把新的 Go 代码再移植回 C++ 的话,考虑到 C++ 的函数多态和类型多态,代码量或许会削减到现在的一半,并且具有更好的类型安全。假如用 Haskell 重写的话,代码量会更少,而运用 Clojure 的话,代码sw349量有或许操控到 1000 行以内,当然这些手机导航地图代码或许很难被调试或保护。

献身了类型安全

针对效劳器处理的各种 protobuffer messages(协议缓冲音讯),咱们运用了扩展特点的方法,作者开始计划久草视频在线观看为每一种音讯设置一种扩展特点,这样 FooExtensionAttribute 就不能用在 Bar 函数上。Go 并没有完结参数多态和泛型,这意味着将会发生许多的重复代码,所以终究只运用了一种 ExtensionAttribute,并且类型体系也没有检查它是否用于扩展适宜的音讯。

二进制文件太大

假如运用代码来生成类型安全的 API,并保证每种数据类型都有清晰的类型拜访器和诸如此类的东西,则很简单生成超越 10W 行的 Go 代码以及 30MB 以上的二进制文件,编译时刻也会更长。在这种情况下,一般会超越 10 秒。当然,这不是一个很严重的问题,因为咱们能够把代码编译成静态库,这只需求一次,之后就能够通三轮车过静态链接来拜访了。

内核兼容性有待进步

许多时分因为各种无法的原因,需求把代码布置到一个旧内核上。并且,假如这个内核不支撑最新的 Go 版别,就不得不换到一个旧的、很慢的 Go 版别,操b这多少有些令人懊丧。

结语

Go 言语是一把双刃剑:它制止全部杂乱的笼统,不管是优异的笼统亦或是很差的笼统。假如你和你的搭档正在运用很糟糕的笼统,那切换到不能运用笼统的 Go 言语天然很好,反之亦然。当然这也要取决于判别笼统好坏的规范。

检查英文原文

https://togototo.wordpress.com/2015/03/07/fulfilling-a-pikedream金朝翰-the-ups-of-downs-of6680-porting-50k-lines-of-c-to-go/

评论(0)