Compilation 如何获得最小的ocamlopt编译本机二进制文件?

Compilation 如何获得最小的ocamlopt编译本机二进制文件?,compilation,size,ocaml,executable,minimum,Compilation,Size,Ocaml,Executable,Minimum,我很惊讶地看到,即使是一个简单的程序,如: 打印字符串“Hello world!\n”; 当通过ocamlopt静态编译为本机代码并使用一些非常激进的选项(使用musl)时,在我的系统上仍然大约190KB $ ocamlopt.opt -compact -verbose -o helloworld \ -ccopt -static \ -ccopt -s \ -ccopt -ffunction-sections \ -ccopt -fdata-sections

我很惊讶地看到,即使是一个简单的程序,如:

打印字符串“Hello world!\n”;
当通过
ocamlopt
静态编译为本机代码并使用一些非常激进的选项(使用
musl
)时,在我的系统上仍然大约190KB

$ ocamlopt.opt -compact -verbose -o helloworld \
    -ccopt -static \
    -ccopt -s \
    -ccopt -ffunction-sections \
    -ccopt -fdata-sections \
    -ccopt -Wl \
    -ccopt -gc-sections \
    -ccopt -fno-stack-protector \
    helloworld.ml && { ./helloworld ; du -h helloworld; }
+ as -o 'helloworld.o' '/tmp/camlasm759655.s'
+ as -o '/tmp/camlstartupfc4271.o' '/tmp/camlstartup5a7610.s'
+ musl-gcc -Os -o 'helloworld'   '-L/home/vaab/.opam/4.02.3+musl+static/lib/ocaml' -static -s -ffunction-sections -fdata-sections -Wl -gc-sections -fno-stack-protector '/tmp/camlstartupfc4271.o' '/home/vaab/.opam/4.02.3+musl+static/lib/ocaml/std_exit.o' 'helloworld.o' '/home/vaab/.opam/4.02.3+musl+static/lib/ocaml/stdlib.a' '/home/vaab/.opam/4.02.3+musl+static/lib/ocaml/libasmrun.a' -static  -lm 
Hello world !
196K    helloworld
如何从ocamlopt获取最小的二进制文件?

对于像今天这样的简单程序(iot、android、alpine VM…)来说,
190KB的大小太大了,与简单的C程序(大约6KB,或者直接对ASM进行编码并调整以获得一个大约150B的工作二进制文件)相比就差了。我天真地认为,我可以简单地抛弃
C
,编写简单的静态程序来完成一些琐碎的事情,在编译之后,我会得到一些简单的汇编代码,这些代码的大小与等效的C程序相比不会太大。可能吗

我认为我理解的:

当删除gcc的
-s
以获得关于二进制文件中剩余内容的一些提示时,我可以注意到许多
ocaml
符号,我还读到了
ocamlrun
的一些环境变量。这就好像
ocamlopt
所称的“本机编译”是关于将
ocamlrun
和程序的非本机
字节码
打包到一个文件中并使其可执行。不完全是我所期望的。我显然错过了一些重要的观点。但如果是这样的话,我会很感兴趣为什么它不像我预期的那样

其他编译为本机代码的语言也有同样的问题:给一些天真的用户(如我自己)留下大致相同的问题:

  • 去:
  • 锈蚀:

我也用Haskell进行了测试,在没有调整的情况下,所有语言编译器都在为“hello world”程序制作700KB以上的二进制文件(调整前Ocaml也是如此)。

你的问题非常广泛,我不确定它是否适合Stackoverflow的格式。它值得彻底检查

在当今的限制条件下(iot、android、alpine VM…),对于一个简单的程序来说,190KB的大小太大了,与简单的C程序相比就差了(大约6KB,或者直接编写ASM并调整内容以获得一个大约150B的工作二进制文件)

首先,这不是一个公平的比较。如今,已编译的C二进制文件是一个远非独立二进制文件的工件。它应该更像是框架中的插件。因此,如果您想计算一个给定二进制文件实际使用的字节数,那么我们将计算加载程序、shell、libc库以及整个linux或windows内核的大小,它们构成了应用程序的运行时

与Java或Common Lisp不同,OCaml对公共C运行时非常友好,并尝试重用其大部分功能。但是OCaml仍然有自己的运行时,其中最大(也是最重要的部分)是垃圾收集器。运行时间不是非常大(约30 KLOC),但仍然会增加重量。由于OCaml使用静态链接,每个OCaml程序都会有一个副本

因此,C二进制文件有一个显著的优势,因为它们通常在C运行时已经可用的系统中运行(因此,它通常被排除在等式之外)。但是,有些系统根本没有C运行时,只有OCaml运行时,请参见示例。在这样的系统中,OCaml二进制文件更受欢迎。另一个例子是该项目,在该项目中(调整了编译器和运行时之后),他们设法将OCaml运行时和程序安装到64Kb的闪存中(阅读文章,它对二进制大小非常有见解)

如何从ocamlopt获取最小的二进制文件

当确实需要最小化大小时,使用Mirage Unikernels或实现您自己的运行时。对于一般情况,请使用
strip
upx
。(例如,使用
upx--best
I可以将示例的二进制大小减少到50K,而无需更多技巧)。如果性能没有那么重要,那么可以使用字节码,字节码通常比机器码小。因此,您将支付一次(大约200k的运行时间),并为每个程序支付几个字节(例如,为您的helloworld支付200字节)

另外,不要创建许多小型二进制文件,而是创建一个二进制文件。在您的特定示例中,helloworld编译单元的大小是字节码中的200字节和机器码中的700字节。其余50k为启动线束,应仅包括一次。此外,由于OCaml在运行时支持动态链接,您可以轻松创建一个加载程序,在需要时加载模块。在这种情况下,二进制文件将变得非常小(数百字节)

这就好像ocamlopt所谓的“本机编译”是关于将ocamlrun和程序的非本机字节码打包到一个文件中并使其可执行。不完全是我所期望的。我显然错过了一些重要的观点。但如果是这样的话,我会很感兴趣为什么它不像我预期的那样

不,不,这是完全错误的。本机编译是指将程序编译为机器代码,无论是x86、ARM还是其他版本。运行时是用C编写的,编译成机器代码,并且还链接。OCaml标准库主要是用OCaml编写的,也编译为机器代码,并且还链接到二进制文件中(只有那些使用的模块,OCaml静态链接是非常有效的,前提是程序可以很好地拆分为模块(编译单元)


关于
OCAMLRUNPARAM
环境变量,它只是一个参数化运行时行为的环境变量,主要是垃圾收集器的参数

虽然它没有回答这个问题,但我相信下面的工作可能会让潜在的观众感兴趣