OCaml编译速度和functors

3
在OCaml中使用functors本质上是在编译时运行代码(在这方面,我认为它们比Java泛型更接近C++模板)。
因此我的问题是:编译器在编译之前是否执行任何优化,或者它直接开始生成代码,或者它尝试在此之前执行任何优化?
这个问题更加实际。很多时候,我使用Map.Make或类似的functor来为给定类型生成map/hashtable等。当我在多个模块中使用它时,我开始感到担忧,因为我认为编译器会多次执行相同的操作(尤其是从脚本语言背景下来看,编译速度成为一个问题)。
那么我需要担心吗?如果我在多个模块中执行Map.Make(MyModule),编译器能否说“嘿,我刚刚用这个类型编译了这个functor,我可能不需要再次执行它”?
是的,我知道我可以有一个单独的utils模块并在其中运行所有functors,但我通常会避免utils-like-kitchen-sink modules。
1个回答

4
你想知道编译器是否能够说“嗨,我刚刚用这个类型编译了这个函数对象,我可能不需要再次编译它吗?”。C ++编译器通常编译模板的方式是这样的。相比之下,ocamlc和ocamlopt的编译方案产生不需要复制的通用代码。OCaml函数对象Map.Make只编译一次,适用于所有可能应用它的模块。当你从Map.Make(String)和Map.Make(Float)调用iter时,执行的是相同的代码。或者当你将Map.Make应用于String的两个不同应用程序时运行iter时,也是如此。

哦,好的,看起来我完全误解了模块系统……但是我非常困惑。 OCaml中的浮点数和整数没有装箱,对吧?同样的代码怎么可能在两个不同的未装箱类型上运行?实际上,在类型擦除的情况下即使是装箱类型,这也是如何做到的呢? - George Karpenkov
@cheshire OCaml中的浮点数是装箱的。只有浮点数组包含未装箱的浮点数。OCaml中的整数与指针大小相同,并且它们的最低有效位设置为将它们与指针区分开来。因此,这两种不同的int大小使得OCaml代码比Java代码更不可移植,而且这两种大小分别为31位和63位。 - Pascal Cuoq
1
谢谢!我回去继续阅读了...由于某种原因,我认为OCaml的美妙之处在于它在编译时知道哪个函数使用哪个类型,因此可以为非装箱值生成非常快的代码。 - George Karpenkov
我也很困惑OCaml如何能在性能上击败Java(通常情况下),因为在Java中,从“float”切换到“Float”会导致性能大幅下降,而OCaml始终使用装箱值。 - George Karpenkov
1
@cheshire 在Java中有很多与性能相悖的地方。但是作为一个喜欢阅读OCaml生成的汇编代码的人(如果你想亲自看到通用代码,我建议使用这种方法),OCaml也会进行许多不必要的浮点数装箱/拆箱,而编译器实现者只是开始消除最明显的问题。无论如何,请注意,OCaml浮点数之所以被装箱,是有原因的,而Java通过在堆栈上放置未装箱的浮点数所获得的好处,必须付出更复杂或保守的垃圾收集器的代价,这可能会导致整体性能损失更大。 - Pascal Cuoq

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接