【ArchSummit架构师峰会】探讨数据与人工智能相互驱动的关系>>> 了解详情
写点什么

我是如何把 5 万行 C++ 代码移植到 Go 的?

  • 2019-04-13
  • 本文字数:2795 字

    阅读完需:约 9 分钟

我是如何把5万行C++代码移植到Go的?

Go 语言的创始人之一 Rob Pike 曾表示,他希望 Go 能够被 C++程序员所接受,但结果差强人意。最近,在作者就职的 HFT 公司里,一个团队成功地把一些对速度不太敏感的基础设施代码从 Python 移植到了 Go,这也促使他们决定尝试用 Go 对复杂冗余的 C++服务端程序进行重构,这些代码有 5W 行之多,并且对吞吐量有一定的要求。

这个服务端程序使用了跟公司核心交易软件相同的技术和库,不同地是交易软件对系统的延迟更敏感,几乎每一微秒都很重要,而 C++服务端并不需要这种程度的性能。

因此,使用 Go 自带的调度程序完全可以满足要求,没有必要使用交易系统实现的超优化 C++框架,虽然损失了一些性能但获得了更好的可维护性。需要一提的是,本文作者负责了整个代码的重写工作。

前言

从商业角度来看,这个项目是成功的:重写工作提前完成;性能在可接受的范围之内;并且整体代码量不超过 1W 行(代码量的剧减主要是因为重写团队删除了一些过时的或者不需要的特性)。但从开发者的角度来看,作者认为结果并不是最优的。Go 并不支持参数多态,作者因此使用了两到三倍的代码来实现类似功能。其中一部分是为了保障类型安全:Go 强制开发者在类型修饰和类型安全之间做出取舍,作者选择了一个比较均衡的实现。总的来说,如果需要一般的类型安全,那么相对少的代码就可以实现,而如果需要更好的类型安全,则需要更多的代码。


接下来让我们对比一下 Go 语言的优缺点。

优点

Emacs 开发平台

借助自动完成、跳转到定义、保存时的错误检查、智能重构和 GoTest 集成等插件,Emacs 成为了 Go 语言环境下最好的 IDE 工具。另外,它也可以很方便地通过 Elisp 进行定制和扩展。如果你本人恰好是 Emacs 的爱好者,这绝对是一个大大的加分项。

Goroutines(协程)

Go 实现了基于消息传递的并发,作者认为这是最简单的并发形式,使用也超级方便。通过将 GOMAXPROCS 设置为 1,Go 还允许开发者通过使用与并发代码完全相同的方式来编写并行/异步代码。与其它提供内置轻量级线程调度器的语言 Erlang/Elixir 和 Haskell 相比:前者缺乏静态类型,后者在实际开发中很少被管理人员采用。

没有继承

在很多情况下,基于继承的 OO(面向对象)是一种反模式,这些冗余和模糊的代码几乎没有什么好处,Go 则直接取消了这类代码。这有可能也是 Rob Pike 等人设计 Go 的初衷:谷歌内部有一大堆类似于企业版本 Fizzbuzz 的 Java/C++代码,他们希望能从这些代码中彻底解放出来。也就是说,尽管在旧的 C++服务端遗留代码中使用继承是合理的,但最好还是使用更现代的风格来重写代码,而且重写过程也并不复杂。

更好的可读性

Go 代码更易于阅读和理解。相比之下,很多 C++代码需要几个小时才能完全理解。Go 本身也促使开发者编写可读的代码:这种语言完全避免了下面这种自做聪明的情形


“嘿,这篇论文(基本上没人读得懂)中的>8=3 运算符可以让我节省 10 行代码,我最好把它写进代码里,我的同事也不难理解这行代码,因为它的意思已经在类型签名中很清楚地表达出来了(反正我是没看懂):(PrimMonad W, PoshFunctor Y, ReichsLens S) => W Y S((I -> W) -> Y) -> G -> Bool "。

简单而规范的语法

当我们需要将一个封闭函数的名称添加到每个日志字符串的开头时,如果使用 Emacs,一个简单的 regexp find-replace(正则表达式)命令就可以实现,而对于更复杂的语言则需要使用解析器。不论是通过 Emacs 宏或者是 Go 模板,简单的语法可以更容易地生成代码。


Emacs+Go==参数多态:我们可以使用 Emacs 宏来加速生成 Go 所需要的"复制粘贴",而且,如果函数编写正确,那我们也可以用 regex 命令来更新所有的"复制粘贴"函数。这样,我们就可以很容易地更新 fooInt、fooFloat 和 fooDouble 等函数,对比支持参数多态的语言对 foo 函数的更新,整个过程没有什么太大区别。这样做的缺点是,虽然 Emacs 宏和 regex 命令可以编写和修改 Go 代码,但它仍然不如真正的多态实现那样简洁和易读;而且对于不熟悉 regex 以及可扩展编辑器(Emacs)的人来说,维护同样也不容易。

有效的内置模板

通过 Go 的文本/模板包,我们可以很容易地生成新代码。它还允许开发者在生成代码时使用 IO:例如,有一个同某些特定服务交互的库,它通过 XML Schema 生成。如果能够用不同的函数来生成不同的数据类型,那么就可以保证代码的类型安全。


在 C++中,IO 不能在编译时执行,因此不能使用上述模式来生成代码。允许编译时使用 IO 的语言有:


  • F#,通过 TypeProviders 实现。

  • Idris,也使有 TypeProviders。

  • Lisp,可以在宏中执行 IO。

  • Haskell,它有一个编译期运行的函数 IO -> Q。

  • D,编译时可以使用“import”来读取文件。

  • Nimrod,有特殊的函数实现。

  • Elixir 或 Erlang,可以通过宏执行任意的 IO。

  • Rust,可以使用函数 libsyntax 在编译时执行任意的计算和 IO。

缺点

斯德哥尔摩综合征

前面已经提到,在允许使用 IO 的特性上,使用模板生成 Go 代码要比用 C++元编程好得多,而 C++元编程在这里显然是多余的,因为完全可以用另外一种可以支持 IO 的程序语言来生成代码。

没有实现参数多态

尽管很多人认为这在实践中并不是一个问题,但在这里,它是一个很严重的问题。如果把新的 Go 代码再移植回 C++的话,考虑到 C++的函数多态和类型多态,代码量可能会减少到目前的一半,并且具有更好的类型安全。如果用 Haskell 重写的话,代码量会更少,而使用 Clojure 的话,代码量有可能控制到 1000 行以内,当然这些代码可能很难被调试或维护。

牺牲了类型安全

针对服务器处理的各种 protobuffer messages(协议缓冲消息),我们使用了扩展属性的方式,作者最初打算为每一种消息设置一种扩展属性,这样 FooExtensionAttribute 就不能用在 Bar 函数上。Go 并没有实现参数多态和泛型,这意味着将会产生大量的重复代码,所以最终只使用了一种 ExtensionAttribute,并且类型系统也没有检查它是否用于扩展合适的消息。

二进制文件太大

如果使用代码来生成类型安全的 API,并确保每种数据类型都有明确的类型访问器和诸如此类的东西,则很容易生成超过 10W 行的 Go 代码以及 30MB 以上的二进制文件,编译时间也会更长。在这种情况下,一般会超过 10 秒。当然,这不是一个很严重的问题,因为我们可以把代码编译成静态库,这只需要一次,之后就可以通过静态链接来访问了。

内核兼容性有待提高

很多时候由于各种无奈的原因,需要把代码部署到一个旧内核上。而且,如果这个内核不支持最新的 Go 版本,就不得不换到一个旧的、很慢的 Go 版本,这多少有些令人沮丧。

结语

Go 语言是一把双刃剑:它禁止一切复杂的抽象,不管是优秀的抽象亦或是很差的抽象。如果你和你的同事正在使用很糟糕的抽象,那切换到不能使用抽象的 Go 语言自然很好,反之亦然。当然这也要取决于判断抽象好坏的标准。


查看英文原文


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



2019-04-13 14:0016274

评论 3 条评论

发布
用户头像
我心中的编程语言尝鲜指数:
第一梯队:Rust(系统编程),Julia(服务器端开发及数据领域),Dart(移动开发)
第二梯队:C++1x(系统编程),Go(服务器端开发),Python(服务端开发及数据领域),Kotlin(移动开发),Swift(移动开发)
第三梯队:C/传统C++(系统编程),Java(服务器端开发及Android开发),PHP(服务器开发)
2019-04-22 12:21
回复
用户头像
试试Rust吧,效果会更好!
2019-04-21 14:21
回复
用户头像
希望哪一天,go能支持泛型和多态
2019-04-15 11:08
回复
没有更多了
发现更多内容

NGINX 和 HAProxy:基于公有云标准环境的用户体验测试对比

NGINX开源社区

nginx 公有云 HAProxy

创业做共享电动车!如何找厂家合作

共享电单车厂家

共享电动车厂家 共享电单车厂商 共享电单车投放 共享电动车创业项目

小程序的安全架构解析

Onegun

小程序 安全 小程序容器 小程序架构

关于华为MetaERP,我说几句

SAP虾客

oracle 华为 SAP metaerp

用Leangoo领歌Scrum敏捷开发工具管理产品路线图?

顿顿顿

Scrum 敏捷开发 敏捷项目管理 scrum敏捷工具

阿里巴巴宣布加入Linux Foundation AI&Data基金会,捐赠首个开源项目DeepRec

阿里云大数据AI技术

人工智能 大数据

PAG动效框架源码笔记 (一)概览

olinone

ios Android; 特效

给大家推荐一款CRMEB_Java新零售社交电商系统

CRMEB

从缓存的本质说起,说服技术大佬用Redis

华为云开发者联盟

数据库 后端 华为云 华为云开发者联盟 企业号 5 月 PK 榜

MySQL百万数据深度分页优化思路分析

做梦都在改BUG

Java MySQL 数据库

GPT:低代码的终局性机遇

小博

人工智能 低代码 PaaS GPT

云服务器管理用什么软件好?行云管家好用吗?

行云管家

云计算 云服务器 云资源 云管理

如何使用apache的ab压力测试小工具传参数

北桥苏

ab测试 A/B 测试

复杂推理:大型语言模型的"北极星"能力

OneFlow

2023淘宝天猫金婴奖公布,小度学习机获年度超级新品奖!

科技热闻

上海国家会计学院刘勤:事项法会计支撑企业更好地应对不确定性

用友BIP

AI会对我们日常生活带来什么影响?这些技术会改变我们学习阅读工作方式吗?| 社区征文

汀丶人工智能

人工智能 ChatGPT 人工智能ChatGPT 吗? 三周年征文

重磅来袭!豆瓣评分9.2,万人血书的多线程与高并发v2.0版本

做梦都在改BUG

Java 多线程 高并发

SET智能合约量化系统app开发案例

薇電13242772558

合约量化 量化交易

ShareSDK 创建应用

MobTech袤博科技

来这公司一年碰到的问题比我过去10年都多

艾小仙

Java kafka spring spring-boot

顶礼膜拜!阿里内部出品,全网首发Spring Security项目实战搭建

做梦都在改BUG

Java spring spring security Spring Security OAuth

SpringBoot 结合 Liquibase 实现数据库变更管理

做梦都在改BUG

Java spring Liquibase

共识算法之Raft算法模拟数

TiAmo

算法 共识算法 模拟数

定了!AIRIOT新品发布会,6月6日北京见。

AIRIOT

物联网 发布会 产品发布会 直播预约

利用Appuploader上架IPA步骤

雪奈椰子

如何解决注册并发问题并提高QPS

北桥苏

队列 并发 Redis 核心技术与实战

ChatGPT会对我们日常生活带来什么影响?这些技术会改变我们学习阅读工作方式吗?| 社区征文

汀丶人工智能

人工智能 ChatGPT 人工智能ChatGPT 吗? 三周年征文

如何更好的分析潜在人脉?聊聊华为云图引擎GES的Cypher子查询

华为云开发者联盟

人工智能 华为云 华为云开发者联盟 企业号 5 月 PK 榜

百人研发团队百亿销售规模的技术架构实践分享

车江毅

大模型竞争白热化 这家广州的科技公司却开辟了新战场

科技热闻

我是如何把5万行C++代码移植到Go的?_编程语言_logicchains_InfoQ精选文章