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
环境变量,它只是一个参数化运行时行为的环境变量,主要是垃圾收集器的参数 虽然它没有回答这个问题,但我相信下面的工作可能会让潜在的观众感兴趣