Golang Go语言中我怎么觉得Java的JNI比CGO要好呢?

发布于 1周前 作者 zlyuanteng 来自 Go语言

null
Golang Go语言中我怎么觉得Java的JNI比CGO要好呢?

48 回复

那你总得给个理由吧

更多关于Golang Go语言中我怎么觉得Java的JNI比CGO要好呢?的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


比方说我用 go 调用 C++,我不想用 pkg-config ,我甚至想要把 C++ 的 .so 嵌入到我最后生成的 executable binary 里,有没有一种方式?别说用 Docker 容器什么的。

P/Invoke 请求一战

so 是动态库啊,一般是不能嵌入的, 虽然有一些魔改的 trick 能从内存中加载动态库,但那绝对不是正常做法。
正常来说你想要静态链接那就去构建静态链接库。

动态库是单独的文件,你需要的是静态库,本质就是 cgo 绑定 C 。
。。但是这些和垃圾回收有什么关系呢?

你的感觉没错.

go 的 cgo, 本来就是个半残. 不建议使用. (早期 uber 等大量依赖 cgo 的, 应该都苦不堪言)

用到 cgo 的场景, 其实应该换语言了. 用 rust/c/c++ 配合 FFI 更合适. (微服务场景, 都是 RPC 抹平 业务单元差异)

不要被单一语言锁死. 适合谁, 谁上.

多学一门语言, 拓宽解决问题的思路.

最坏的就是 all-in-one 思想, 东施效颦, 各种捉急.

#9 一定要用 go 调 c++的话,最佳实践是什么?

公司同样的业务,封装底下自研的时序库的动态链接库,
早期 java 调用 jni ,后面改 jna ,
我后面又用 go 去弄 cgo ,
最后我又改成了 python 的 ctypes ,python 最终使用的上层用协程加 ctypes 接口处用多进程
这些全是我一个人弄的,其中整个体验下来,cgo 的体验是最差的,当你想用协程去调 cgo 时,结果可想而知。




1. 如果是 Server 端, 可以通过 微服务 RPC(如 gRPC) / Restful API / Websoket 方式来调用. 避免侵入性. 把 C++ 改造成一个独立的服务. 暴露 RPC/Rest API 给 Go 调用. 即可以绕过 cgo.
2. 如果是 client 端, 如果用 Go 写 Client, 这本身已经是邪路. 那只能 cgo. 当然, 如果是 Desktop 桌面版的 app. 也可以同上, 把 桌面版 app 改造成 client/server 模型, 中间变成 proxy, 依然可以走 RPC/Rest API 方式调用.

请问 FFI 是什么?搜索不到。



github.com/hhstore/blog/issues/242
github.com/hhstore/blog/issues/355

可以参考我这 2 篇博客, 关于 FFI 的内容.

https://en.wikipedia.org/wiki/Foreign_function_interface


FFI 是通用的跨语言调用规范. 主流语言基本都兼容 C ABI.

所以, 都可以通过 C ABI 方式来跨语言通信.

所以到底 cgo 有啥问题? 能具体描述一下吗?

JNI 是 Java Native Interface ,不是垃圾回收

于是现在 uber 换 zig 做 cgo 后端了 233333

CGO 确实体验式最差的,并且尼玛死活没计划改…

不过你说的这个问题倒不是什么比较大的问题,想内嵌不过是 dynamic library loader 的问题…有很多第三方库可以用的 https://github.com/rainycape/dl




https://cloud.tencent.com/developer/article/1650525
https://dave.cheney.net/2016/01/18/cgo-is-not-go
https://relistan.com/cgo-when-and-when-not-to-use-it


看这些讨论吧.

除了极端场景, 只有 c/c++ 的库, 需要 binding 给 go 使用. 才用 cgo. 而这个代价, 可能非常高.

即使要用, 请隔离在小服务内. 然后 RPC 暴露给其他服务. 不要污染整个大项目.

这是坨💩. 谁用谁知道.

C++ 基本上没用过,只能先用 CGO ,回头再用 C++ 写服务了。

embed 打包到二进制里

喔,看叉屁了

谢谢, 这些我都知道的. 但我感觉还是说不通 cgo 是半残啊, 你有没有跟 jni 或者 python 调 c 的这些语言对比资料. 我学习一下其它语言在调 c 这块比 go 好在哪

跨语言调用 c 模块有其自身的复杂度在,不可能简单,也注定不是平凡的事情,任何语言都要面对。
主要有以下几个问题:
1. 包管理的差异,c 没有官方的包管理,所以库的管理完全看系统,或者团队的约定工具,如 cmake 等。go 其实可以跟 c 一起源码发布成一个 go 的包,见 https://github.com/mattn/go-sqlite3
2. 交叉编译,新的语言基本上都有很方便的交叉编译方案,甚至不需要(脚本语言)。而 c 的交叉编译工具链其实很复杂,搞过嵌入式或者搞过路由器的都知道我在说啥。
3. 数据类型的差异。这部分主要是内存布局的差异带来的。比如一个 c 的 struct 不一定对应着高级语言里面的一个 class 。go 在这一点上其实做的很好了,go 的一个 struct 基本上对应一个 c 的 struct 。指针的概念也跟几乎一致,除了算数运算。所以 cgo 几乎可以不加修改的直接引用 c 的头文件,以及传递 go struct 作为参数给 c 函数。
4. 内存管理的差异。c 是手动管理的内存,所有内存都要自己显式分配和释放。新的语言基本上都有自己的内存管理方案,即 GC 。这就带来了语言之间内存传递的问题,c 分配的内存不会被 go tracing ,go 的内存也不能被 c 手动释放,其他语言类似。
5. 并发模型的差异。这部分基本上是语言自带协程的独特问题。因为 go 里面的协程有自己的栈,而 c 基本上可以认为是操作系统的线程栈。这两者之间的差异会带来诸如 TLS 和栈切换的问题。

所以综合看来,除了第 5 点是 go 特有的,其他语言几乎都有类似的问题。go 引入 cgo 主要是为了解决复用已有的大量 c 库的遗产,不是和 c 无缝调用,混合编程,目前也就 c++做到了和 c 无缝衔接,但为了这个能力 c++也付出了很多代价,到现在还在打补丁。 软件工程没有银弹,作为工程师最主要的工作就是熟悉各个模块的脾性,对症下药,挑选最合适项目的方案,而不是强行套方案,最终不符合预期的时候骂骂咧咧。

都不如.net 系, 就是关注度太低了

有头文件的时候 cgo 还行吧,中间可以自动生成 go 的类型、函数。而且 FFI 本来就属于是程序间的 api ,两个语言“揉得太碎”,肯定性能和代码可扩展性会受损

Rust/C++/Zig 都可以无缝调用 C 语言,都有无栈协程,手动(半自动?)管理内存,允许声明与 C 一致的内存布局。相比 C++,Rust/Zig 的包管理和交叉编译更方便。你说的这些问题在系统级编程语言中都有完善的解决方案。

cgo 我见到最多的诟病是开销问题,调个系统 API 还好,如果是 hotpath 那开销非常明显。
jni/cpython 之类的开销如何我倒是不清楚。
还有交叉编译可以用 zig 作为 CC 使用,能让 go/rust 在交叉编译含 c 代码的库时开箱即用。

上面各位提到的 cgo 开销问题可以用魔法缓解下

https://github.com/petermattis/fastcgo

24# 说的非常对,还有问题的建议细品层主的回复并应用到其他语言上进行对比

有条件还是建议隔离出 C/C++服务,单独提供 RPC 供调用,如果是本地调用可以用 Unix domain socket 。这样后续维护起来也简单。

建议 rpc ,担心性能可以考虑 grpc
https://github.com/hashicorp/go-plugin

但凡用 cgo 写过实际大项目的 都应该不会再选 cgo 了



我贴给你的 FFI 相关博客, 就是 rust 和 dart 通过 C ABI 跨语言通信啊? 怀疑你有没有认真看?

至于 Python 调用 C. 这完全不用讨论.
Python 社区, 大量的包, 都是 C 实现, 然后 binding 给 Python 使用.
这是 Python 性能优化的基本手段.

> 关于 FFI / C ABI 跨语言通信. 是双向的. ( C ABI 是桥)

上面很多讨论, 都假定是 Go 通过 cgo 来调用 C 代码. (注意, 还有反过来的场景)

rust/c/c++ 的 FFI 都是双向通信能力优秀.

说 CGo 是半残, 就是尽量不要用.

没有什么方案设计, 是非它不行的, 缺它不可.
没有什么方案设计, 是非它不行的, 缺它不可.
没有什么方案设计, 是非它不行的, 缺它不可.

非喜欢抬杠的, 你喜欢, 你就多用, 每个项目都用.

https://github.com/trustwallet/wallet-core

这个 c++ 项目, 实现了跨语言 binding 到多个平台 iOS, Android(通过 jni).

core lib 层跨平台场景, 选项只有 rust/c/c++. 不要走邪路.

我上面贴的 2 篇博客, 都不看的?

github.com/hhstore/blog/issues/242
github.com/hhstore/blog/issues/355


> rust + ffi + flutter 实践案例:

- https://github.com/AppFlowy-IO/AppFlowy
- 这个项目, UI 层使用 flutter, core lib 层使用 rust.
- 是非常现代的设计方案.


> FFI 库( rust + dart):

https://github.com/fzyzcjy/flutter_rust_bridge
https://github.com/rust-lang/rust-bindgen
https://github.com/google/autocxx
https://github.com/rust-qt/ritual
https://github.com/eqrion/cbindgen
https://github.com/getditto/safer_ffi


https://github.com/shekohex/flutterust
https://github.com/sunshine-protocol/dart-bindgen
https://github.com/brickpop/flutter-rust-ffi
https://github.com/TimNN/cargo-lipo
https://github.com/bbqsrc/cargo-ndk

https://github.com/dart-lang/ffigen


跨平台场景, 要注意其他语言的 FFI 基础设施建设(易用性 /成熟度). Go 完全没有可比性.

现在贴内容, 都要投喂到这种程度, 才看吗?
如果看了我上面 2 篇博客. 还要坚持用 cgo 的. 那… 你开心就好.

至于 Python 的 FFI + C ABI 非常成熟. (毕竟 Python 性能优化靠 C)

社区实践了几百年了. Python 社区基础库, 基本都是 C 实现.
我是懒得贴了. 没有讨论必要.

jni 确实还行

我觉得你误会了吧, 我只是认真请教, 关于 cgo 我用很久了, 既然你说 cgo 半残是指尽量不要用的意思, 那我就明白了, 我只是以为你能给出一些其它资料能更深入分析的.

看来 V2EX 上还是不能随便请教问题, 只要请教就被当杠精.

另外, 我觉得在描述一样技术时, 把它说成"半残", 这么严重的词, 然后你意思仅是尽量不要用, 我还以为你能补齐它的另一半. 有点意思, 你们都是大牛, 我们都是扛精.

我说的钢筋, 不是说你.

只是回复你上面的疑问, 顺便提了一下, 没有开新回复.

看你这个反应.

我觉得我贴的内容多余. 免回.

> 挑选最合适项目的方案,而不是强行套方案,最终不符合预期的时候骂骂咧咧。

上面某楼, 讲这种没卵用的废话.

1. 你没有使用过一个方案. 怎么知道好不好, 合适不合适? 不实践, 就知道是不是强行套方案? 你开天眼了? 云玩家?
2. 使用过, 很臭. 不能说? 不能排雷? 让更多人避坑?
3. 说它臭, 就是骂骂咧咧? 没有银弹, 就你懂?
4. 合着, 实践过, 被坑过. 再推荐给别人踩坑, 是什么形态?

这种看似"理中客", 最好笑.

你用, 我推荐.
我用, 我不用.

我本来懒得喷.

看还有人看不懂上下文. 明确一下吧.

而且, 我列了大量资料, 不去看. 不去对比各种语言的方案进展.

喜欢用 cgo, 你就多用啊. 我又不是写给你的.

其他不说,cgo 把东西写在注释里,注释成了会影响程序本身的东西,那还是注释吗?为什么不设计一些专门的结构来处理?就为了让语言 “干净”?

为了编译速度,一个符号都不想加,你敢信。之前设计泛型时一度考虑继续用括号()表示泛型

抛开业务场景谈方案好坏就是耍流氓

#12 cgo 体验还算好,你是实践做法有些问题
不要业务代码直接调用 cgo ,做个 cgo-binding pkg 隔离

JNI 太繁琐了。

作为IT领域GO语言方面的专家,对于您提出的关于Java的JNI与Golang的CGO的比较,我有以下观点:

JNI(Java Native Interface)是Java提供的一种编程框架,它允许Java代码与其他语言(如C、C++)编写的应用程序或库进行交互。JNI的优势在于其能够直接调用本地代码,这在处理计算密集型任务或需要直接操作硬件时尤为高效。此外,JNI还允许Java程序访问底层操作系统的功能,实现与平台特定的特性或优化的互操作。

而CGO是Golang提供的一个工具,它使得Golang程序能够调用C语言代码。CGO在简化跨语言调用和代码复用方面具有一定优势,但相对于JNI来说,其应用场景和性能优化可能稍逊一筹。

然而,需要强调的是,JNI和CGO各有其适用场景和优势。在选择使用哪种技术时,应充分考虑具体项目的需求、团队的熟悉度以及特定的应用场景。

总的来说,Java的JNI和Golang的CGO都是强大的工具,它们在不同的应用场景下都能发挥重要作用。因此,无法一概而论地说哪个更好,而是需要根据实际情况进行选择。

回到顶部