V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
The Go Programming Language
http://golang.org/
Go Playground
Go Projects
Revel Web Framework
thewiredguy
V2EX  ›  Go 编程语言

我怎么觉得 Java 的 JNI 比 Go 的 CGO 要好呢?

  •  
  •   thewiredguy · 2022-06-12 23:45:57 +08:00 · 6740 次点击
    这是一个创建于 676 天前的主题,其中的信息可能已经有所发展或是发生改变。
    47 条回复    2022-06-14 21:49:59 +08:00
    learningman
        1
    learningman  
       2022-06-12 23:49:59 +08:00 via Android
    那你总得给个理由吧
    thewiredguy
        2
    thewiredguy  
    OP
       2022-06-13 00:03:19 +08:00
    thewiredguy
        3
    thewiredguy  
    OP
       2022-06-13 00:06:33 +08:00
    比方说我用 go 调用 C++,我不想用 pkg-config ,我甚至想要把 C++ 的 .so 嵌入到我最后生成的 executable binary 里,有没有一种方式?别说用 Docker 容器什么的。
    SMGdcAt4kPPQ
        4
    SMGdcAt4kPPQ  
       2022-06-13 00:25:56 +08:00
    P/Invoke 请求一战
    Buges
        5
    Buges  
       2022-06-13 00:36:25 +08:00 via Android
    so 是动态库啊,一般是不能嵌入的, 虽然有一些魔改的 trick 能从内存中加载动态库,但那绝对不是正常做法。
    正常来说你想要静态链接那就去构建静态链接库。
    lysS
        6
    lysS  
       2022-06-13 00:42:21 +08:00
    动态库是单独的文件,你需要的是静态库,本质就是 cgo 绑定 C 。
    。。但是这些和垃圾回收有什么关系呢?
    SMGdcAt4kPPQ
        7
    SMGdcAt4kPPQ  
       2022-06-13 00:49:29 +08:00
    Suddoo
        8
    Suddoo  
       2022-06-13 01:49:53 +08:00
    可以关注一下 巴拿马项目 https://github.com/openjdk/panama-foreign
    akaHenry
        9
    akaHenry  
       2022-06-13 02:47:57 +08:00   ❤️ 10
    你的感觉没错.

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

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

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

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

    最坏的就是 all-in-one 思想, 东施效颦, 各种捉急.
    nmap
        10
    nmap  
       2022-06-13 09:39:31 +08:00
    @orzglory #9 一定要用 go 调 c++的话,最佳实践是什么?
    Kasumi20
        11
    Kasumi20  
       2022-06-13 10:21:28 +08:00
    都不如 Rust
    cccjh
        12
    cccjh  
       2022-06-13 10:22:52 +08:00
    公司同样的业务,封装底下自研的时序库的动态链接库,
    早期 java 调用 jni ,后面改 jna ,
    我后面又用 go 去弄 cgo ,
    最后我又改成了 python 的 ctypes ,python 最终使用的上层用协程加 ctypes 接口处用多进程
    这些全是我一个人弄的,其中整个体验下来,cgo 的体验是最差的,当你想用协程去调 cgo 时,结果可想而知。
    akaHenry
        13
    akaHenry  
       2022-06-13 10:24:36 +08:00   ❤️ 1
    @nmap


    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 方式调用.
    cxytz01
        14
    cxytz01  
       2022-06-13 10:31:14 +08:00
    @orzglory 请问 FFI 是什么?搜索不到。
    akaHenry
        15
    akaHenry  
       2022-06-13 10:34:55 +08:00   ❤️ 2
    @cxytz01

    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 方式来跨语言通信.
    hhaobao
        16
    hhaobao  
       2022-06-13 10:50:29 +08:00
    所以到底 cgo 有啥问题? 能具体描述一下吗?
    @orzglory
    @cccjh
    thewiredguy
        17
    thewiredguy  
    OP
       2022-06-13 11:00:47 +08:00
    @lysS JNI 是 Java Native Interface ,不是垃圾回收
    janxin
        18
    janxin  
       2022-06-13 11:01:02 +08:00
    @orzglory 于是现在 uber 换 zig 做 cgo 后端了 233333

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

    不过你说的这个问题倒不是什么比较大的问题,想内嵌不过是 dynamic library loader 的问题...有很多第三方库可以用的 https://github.com/rainycape/dl
    akaHenry
        19
    akaHenry  
       2022-06-13 11:01:16 +08:00
    @hhaobao


    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 暴露给其他服务. 不要污染整个大项目.

    这是坨💩. 谁用谁知道.
    thewiredguy
        20
    thewiredguy  
    OP
       2022-06-13 11:06:12 +08:00
    C++ 基本上没用过,只能先用 CGO ,回头再用 C++ 写服务了。
    DefoliationM
        21
    DefoliationM  
       2022-06-13 11:20:25 +08:00
    @thewiredguy embed 打包到二进制里
    lysS
        22
    lysS  
       2022-06-13 11:41:51 +08:00
    @thewiredguy 喔,看叉屁了
    hhaobao
        23
    hhaobao  
       2022-06-13 11:42:09 +08:00
    @orzglory 谢谢, 这些我都知道的. 但我感觉还是说不通 cgo 是半残啊, 你有没有跟 jni 或者 python 调 c 的这些语言对比资料. 我学习一下其它语言在调 c 这块比 go 好在哪
    icexin
        24
    icexin  
       2022-06-13 11:43:45 +08:00   ❤️ 10
    跨语言调用 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++也付出了很多代价,到现在还在打补丁。 软件工程没有银弹,作为工程师最主要的工作就是熟悉各个模块的脾性,对症下药,挑选最合适项目的方案,而不是强行套方案,最终不符合预期的时候骂骂咧咧。
    zhuangzhuang1988
        25
    zhuangzhuang1988  
       2022-06-13 11:44:24 +08:00
    @ComputerIdiot 都不如.net 系, 就是关注度太低了
    lysS
        26
    lysS  
       2022-06-13 11:56:01 +08:00
    有头文件的时候 cgo 还行吧,中间可以自动生成 go 的类型、函数。而且 FFI 本来就属于是程序间的 api ,两个语言“揉得太碎”,肯定性能和代码可扩展性会受损
    Nugine0
        27
    Nugine0  
       2022-06-13 12:18:33 +08:00   ❤️ 2
    @icexin Rust/C++/Zig 都可以无缝调用 C 语言,都有无栈协程,手动(半自动?)管理内存,允许声明与 C 一致的内存布局。相比 C++,Rust/Zig 的包管理和交叉编译更方便。你说的这些问题在系统级编程语言中都有完善的解决方案。
    Buges
        28
    Buges  
       2022-06-13 12:28:10 +08:00 via Android
    @icexin cgo 我见到最多的诟病是开销问题,调个系统 API 还好,如果是 hotpath 那开销非常明显。
    jni/cpython 之类的开销如何我倒是不清楚。
    还有交叉编译可以用 zig 作为 CC 使用,能让 go/rust 在交叉编译含 c 代码的库时开箱即用。
    Srar
        29
    Srar  
       2022-06-13 13:42:50 +08:00 via iPhone
    上面各位提到的 cgo 开销问题可以用魔法缓解下

    https://github.com/petermattis/fastcgo
    7v9TEc53
        30
    7v9TEc53  
       2022-06-13 14:00:20 +08:00
    @icexin 24# 说的非常对,还有问题的建议细品层主的回复并应用到其他语言上进行对比
    swulling
        31
    swulling  
       2022-06-13 14:04:43 +08:00
    有条件还是建议隔离出 C/C++服务,单独提供 RPC 供调用,如果是本地调用可以用 Unix domain socket 。这样后续维护起来也简单。
    THESDZ
        32
    THESDZ  
       2022-06-13 14:46:38 +08:00
    建议 rpc ,担心性能可以考虑 grpc
    https://github.com/hashicorp/go-plugin
    fgodt
        33
    fgodt  
       2022-06-13 15:40:39 +08:00
    但凡用 cgo 写过实际大项目的 都应该不会再选 cgo 了
    akaHenry
        34
    akaHenry  
       2022-06-13 22:12:14 +08:00
    @hhaobao

    我贴给你的 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 是半残, 就是尽量不要用.

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

    非喜欢抬杠的, 你喜欢, 你就多用, 每个项目都用.
    akaHenry
        35
    akaHenry  
       2022-06-13 22:15:58 +08:00
    https://github.com/trustwallet/wallet-core

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

    core lib 层跨平台场景, 选项只有 rust/c/c++. 不要走邪路.
    akaHenry
        36
    akaHenry  
       2022-06-13 22:31:54 +08:00
    我上面贴的 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 的. 那.... 你开心就好.
    akaHenry
        37
    akaHenry  
       2022-06-13 22:35:22 +08:00
    至于 Python 的 FFI + C ABI 非常成熟. (毕竟 Python 性能优化靠 C)

    社区实践了几百年了. Python 社区基础库, 基本都是 C 实现.
    我是懒得贴了. 没有讨论必要.
    Cbdy
        38
    Cbdy  
       2022-06-13 22:50:57 +08:00 via Android
    jni 确实还行
    hhaobao
        39
    hhaobao  
       2022-06-14 01:01:36 +08:00
    @orzglory 我觉得你误会了吧, 我只是认真请教, 关于 cgo 我用很久了, 既然你说 cgo 半残是指尽量不要用的意思, 那我就明白了, 我只是以为你能给出一些其它资料能更深入分析的.

    看来 V2EX 上还是不能随便请教问题, 只要请教就被当杠精.
    hhaobao
        40
    hhaobao  
       2022-06-14 01:06:54 +08:00
    @orzglory 另外, 我觉得在描述一样技术时, 把它说成"半残", 这么严重的词, 然后你意思仅是尽量不要用, 我还以为你能补齐它的另一半. 有点意思, 你们都是大牛, 我们都是扛精.
    akaHenry
        41
    akaHenry  
       2022-06-14 02:53:04 +08:00
    @hhaobao 我说的钢筋, 不是说你.

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

    看你这个反应.

    我觉得我贴的内容多余. 免回.
    akaHenry
        42
    akaHenry  
       2022-06-14 03:04:31 +08:00   ❤️ 1
    > 挑选最合适项目的方案,而不是强行套方案,最终不符合预期的时候骂骂咧咧。

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

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

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

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

    我本来懒得喷.

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

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

    喜欢用 cgo, 你就多用啊. 我又不是写给你的.
    masterclock
        43
    masterclock  
       2022-06-14 08:27:37 +08:00
    其他不说,cgo 把东西写在注释里,注释成了会影响程序本身的东西,那还是注释吗?为什么不设计一些专门的结构来处理?就为了让语言 “干净”?
    cheng6563
        44
    cheng6563  
       2022-06-14 09:19:16 +08:00
    @masterclock 为了编译速度,一个符号都不想加,你敢信。之前设计泛型时一度考虑继续用括号()表示泛型
    7v9TEc53
        45
    7v9TEc53  
       2022-06-14 10:20:24 +08:00
    抛开业务场景谈方案好坏就是耍流氓
    xsen
        46
    xsen  
       2022-06-14 18:17:54 +08:00
    @cccjh #12 cgo 体验还算好,你是实践做法有些问题
    不要业务代码直接调用 cgo ,做个 cgo-binding pkg 隔离
    zinwalin
        47
    zinwalin  
       2022-06-14 21:49:59 +08:00
    JNI 太繁琐了。
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   我们的愿景   ·   实用小工具   ·   2695 人在线   最高记录 6543   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 45ms · UTC 15:02 · PVG 23:02 · LAX 08:02 · JFK 11:02
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.