这个帖子的目的不是对比不同编程语言开发效率的高低。
这个帖子实际上讨论的是以下内容:
软件工程中生产力消耗的占比
语言层面如何改善生产力的利用率
Go 有何特殊之处
以代码行数来衡量程序员的产出是非常可笑的指标,同理,以代码复杂度、语法糖支持来评价开发效率也很离谱。
如果一定要找一个量化指标来评价开发效率,那就是相同开发需求下所消耗的总人力。
实际上,从理解业务需求开始,到构思实现方式和架构占据了写代码这个过程中绝大部分时间。
如果考虑团队协作的一般场景,沟通成本又占据了团队人力消耗的一半(个人估计)。
也就是说,纯代码层面的效率提升即便优化到极致,对于整个开发成本的改善贡献可能远低于 10%。
能看代码不求人才是最节省沟通成本的方式。
这里的反例就是互联网与工程化兴起之前的各种语言,需要通过外部工具、团队规则才能保证有效协作的各种脚本语言。
开发者讨厌屎山的深层原因还是代码不好读、不好改。
影响可读性的因素很多,这里简单列举几个:
在没有 lsp 辅助的情况下,快速定位到方法的实现,即代码结构组织
隐式控制流,在某些地方隐藏着初始化 init 逻辑,或者有非直接可见的构造、析构方法
人脑的栈空间是非常有限的,间接、隐藏信息越多,理解成本越高。
往小的方面说,直接复制其他代码引入项目算是复用。往大的方面说,包引入机制也是复用。
这里的重点是语言的官方仓库和工具链系统来一致化、规范化这个过程。
缺少官方包管理、没有官方工具链的各种语言都深受其苦。
面向对象的思想就是某个阶段编程语言带来的巨大进步。
在工程化成为现代语言标配的今天,组合替代继承也是巨大进步。
关于这一点我在其他帖子里有零散的论述,这里就不展开了。
我觉得这个部分是误解最多的,大多数的讨论都没有关注到真正的重点。
Go 最重要的贡献之一是基于 chan 的思维模型:Share memory by communication。
日常反复被强调的 goroutine 其实不重要,很多语言也可以有样学样。
通过 chan/goroutine 的结合,初学者即便不理解 race/signal/thread/shared memory 等等概念,一样可以快速、准确地写出并发逻辑。
没有魔法就是编程语言对开发者最大的尊重。
Debug 浪费的生产力往往会远超写代码的节省。
在网络编程的时代,原生 tls 才是真正的跨平台。
技术上跨平台的核心是交叉编译,工程上,保证一条命令构建全平台、全指令集,最后的障碍就是原生 tls 。
这里节省的是构建系统,一旦有了外部依赖,语言自身的工具链就不够用了。
Go 从非常早期就是手搓编译器,而不是转换成 IL 交给通用编译器。缺点是各种语言特性、语法糖都会影响编译器的编写,优点是编译很快。
人类的多线程能力很差,现实任务频繁上下文切换会极大降低工作效率。
如果你曾经被 webpack 构建或者 rust 编译支配,你会理解快速编译、即时反馈的意义。
PS
这个帖子实际价值也就最后一个章节,还没有展开说。权当抛砖引玉,欢迎斧正。特地放到 Go 节点也是不想让话题沦为口水争论。
1
pursuer 2 天前
要说编译速度快,那脚本不是全秒了。
按照现在的发展情况看,我怀疑如果 TC39 不开摆,除了 C/C++和 Rust ,其他语言生态最终都会被绑定了浏览器的 JS/TS 占据。即使 JS 是一个槽点很多的语言。JS 的拖累还有 NPM 和 Node 的 Native 扩展。 |
2
ninjashixuan 2 天前
我比较喜欢的一个点是 go 对文件、网络 io 的抽象,写起来很舒服。
|
3
kuanat OP @pursuer #1
编译速度说的是和编译类型的语言比,而且即使和 vm+jit 类型相比也是快的。 JS 的整个生态都建立浏览器之上,能力的边界取决于浏览器实现了多少对操作系统的封装。 如果一定要拿出数据作证的话,js 的份额一直就是在浏览器这个笼子里,别的进不来它也出不去。 |
4
kuanat OP @ninjashixuan #2
这里可能涉及到编程语言的设计哲学,即要在多大程度上暴露或者封装操作系统的抽象。 在 go 看来,内核 fd 是没有抽象过的 Reader Writer Closer 有用的,这种用接口的实现方式是对“万物皆文件”非常好的诠释。 同理,socket 相关的抽象也是一样。我个人认为这些都是有意的设计,最初就把跨平台作为目标之一,官方库就对如何封装如何抽象,实现哪些功能做了规划。 所以很多人说 go 的设计定位是 better c ,但我认为这不是重点,语言层面做个 better c 不难,其他语言一样做得很好。像 c 一样把操作系统底层完全暴露出来是没用的,go 优于 c 同时抢占了部分 c 的市场的原因是它解决了 c 工程化的很多问题。 |
5
zealot0630 2 天前 via Android
不同语言在不同应用场景下能发挥的功能差距非常大,抛开应用场景谈效率毫无意义。
没有最好的语言,只有最适合的语言。 |
6
tonyjia87 2 天前
好奇的地方,资本对 go 的发力领域是否还有持续投入。 毕竟 java 太多沉淀,rust 太多声音,typescript 太多表现欲。
|
7
pursuer 2 天前
@kuanat 如果真的只在浏览器范围的话,但现在个人 PC 已经成 Electron 宇宙了,移动端也是各种浏览器套壳,如果浏览器开放 Raw socket 和文件系统权限,加上 ServiceWorker 后台运行。感觉现在的工具类应用相当数量都会被 Web 化。
作为特性,只和特定类型语言比没有意义。快速验证相关现在就是 python/js 这种脚本用的最多。 |
9
uSy62nMkdH 2 天前
“相同开发需求下所消耗的总人力” —— 这是一个伪命题没有办法求证
|
10
kuanat OP @pursuer #7
这个讨论有点偏题了,我想表达的是浏览器相对于整个软件开发领域占比是有限的,这个数据无法支撑你得出“除 c 和 rust 之外被 js 占据”这样的结论。 我把这个帖子发在 go 而不是程序员节点,目的也是真正讨论工程化和生产力的问题。隔壁帖子的争论 xx 语言的 yy 不好,是建立在真的要把 xx 语言用在所有场景这样一个隐含前提下的。然而现实的人类并不会拿一把锤子就把万物当作钉子。 所以我不认为某种语言一统天下是个好事,这不符合我的技术哲学认知。 |
11
lesismal 1 天前 1
> Go 最重要的贡献之一是基于 chan 的思维模型:Share memory by communication 。
> 日常反复被强调的 goroutine 其实不重要,很多语言也可以有样学样。 chan 挺好, 但本质上相当于个阻塞队列, 即使没有 golang, pipe/cond_t+mutex/sem 之类的传统的进程间通信/线程同步的这些也都能实现类似的阻塞队列组件. 但多进程多线程和这些 syscall 要么阻塞线程要么异步. Share memory by communication 更像是从 erlang/actor 拿来的, 但 golang 整个语言本身没有像 erlang 那样纯 actor. actor 是个太监模型, actor 之父是为了他们自家电信业务造的 erlang, 电信的那种每个 erlang 进程处理一个连接, 设备进行管理功能也不算复杂不需要连接之间有复杂的交互, 这种场景用 erlang 的进程通信比较简单. 但纯 actor 并不适合于复杂的业务, 例如连接之间有复杂的交互. 而且不管哪种 actor, 让一些即使很简单的交互, 或者一些用普通的逻辑处理比较简单的交互, 也都强制必须用通信的方式, 都是需要数据拷贝/调度或者切栈上下文之类的, 这些都带来了额外的性能损失. 性能损失这一点, chan 也是类似的, 简单的有性能需要的功能, 用 chan 未必见得划算. runtime 基础之上: goroutine 让大家绝大多数时候能写同步代码, 这个解决了传统高并发高性能的 c/c++/java netty/nodejs 等语言的 callback 逻辑不直观和 callback hell 的问题. 传统的进程间通信/线程同步 syscall 封装组件, 即使实现同步组件, 但阻塞的是进程/线程, 所以不能用 goroutine 与这些 syscall 结合来让大家写同步代码, 所以需要 chan 这种基于 runtime 的轻量阻塞队列实现. golang 标准库提供了个基于 runtime 的 cond_t 也可以自己封装下实现 chan 或者类似的组件, 但 chan 已经足够方便了, 我暂时想不到有需要自己搞这个的需要. 从这些角度讲, 我觉得 goroutine 仍然是最重要的; chan 很好, 但是次之, 因为也有很多场景是不需要甚至不适合用 chan 的. |
12
kuanat OP @lesismal #11
你说得非常对,我想了一下,还是 goroutine 更重要一些。 如果拿 js 来做对比的话,很明显 js 努力的方向一直是用同步的方式写异步逻辑,经过了 promise 到 async/await 的迭代,但 go 这边就很符合直觉。 另外性能在绝大部分场景不那么重要,可读性、效率和性能之间找个平衡点更重要。现在不是单体应用硬怼 c10k 的时代了。 |
13
tonyjia87 1 天前
@kuanat 相信未来会很多公司尝试 go ,共建社区。 现在看比较知名的产物 docker ,kubernetes ,甚至核心成员写的 ollama ,知名的 hugo ,https://github.com/topics/go 前十似乎都是在搞隔离,交付。似乎 infrastructure 才是发力点,可能不准确但是占据多。
|
14
lesismal 1 天前
> 如果拿 js 来做对比的话,很明显 js 努力的方向一直是用同步的方式写异步逻辑,经过了 promise 到 async/await 的迭代,但 go 这边就很符合直觉。
对对, 非常同意. promise 对于很多人都是个坑, 因为看着是顺序的实际上不是本次 eventloop, 所以遇到 for 循环里的 promise 或者 promise 后面的逻辑, 其实都是违反时序直觉的, 很多新手遇到了 bug 都仍然难搞懂这个. async/await 虽然提供了, 但也是难于理解难于使用, 比 goroutine 自动档差远了 |
15
phoulx 1 天前
#2 #4 毕竟 Go 承接自 Plan 9 ,真正「一切皆文件」的系统
|