已知纯函数可促进并行化。 函数式编程在本质上适合于并行执行是什么呢?
Javac之类的编译器是否足够聪明,可以检测方法何时是纯函数?人们总是可以实现实现诸如Function之类的功能接口的类,但会产生副作用。
NullPointerException
s。对于典型的Java应用程序,基于此进行优化的好处可能也很小。
已知纯函数可促进并行化。 函数式编程在本质上适合于并行执行是什么呢?
Javac之类的编译器是否足够聪明,可以检测方法何时是纯函数?人们总是可以实现实现诸如Function之类的功能接口的类,但会产生副作用。
NullPointerException
s。对于典型的Java应用程序,基于此进行优化的好处可能也很小。
Answers:
是诸如Javac这样的编译器,它们足够聪明,可以检测方法何时是纯函数。
这不是“足够聪明”的问题。这称为纯度分析,在一般情况下证明是不可能的:它等效于解决停止问题。
当然,现在,优化器一直在执行无法证明的事情,“在一般情况下无法证明”并不意味着它永远不会起作用,而仅意味着它不能在所有情况下都能起作用。因此,实际上有算法来检查一个函数是否纯净,结果常常是“我不知道”,这意味着出于安全性和正确性的考虑,您需要假设该特定功能可能不纯净。
即使在它确实起作用的情况下,算法也是复杂且昂贵的。
因此,这就是问题1:它仅适用于特殊情况。
问题2:图书馆。为了使函数成为纯函数,它只能调用纯函数(而那些函数只能调用纯函数,依此类推)。Javac显然只知道Java,并且只知道它可以看到的代码。因此,如果您的函数在另一个编译单元中调用一个函数,则您将无法知道它是否是纯函数。如果它调用用另一种语言编写的函数,您将不会知道。如果它在库中甚至尚未安装的函数中调用函数,您将不知道。等等。
仅当您进行整个程序分析时,整个程序都用相同的语言编写,并且一次全部编译时,这才起作用。您不能使用任何库。
问题3:安排时间。一旦弄清了哪些部分是纯的,您仍然必须将它们安排在单独的线程中。或不。启动和停止线程非常昂贵(尤其是在Java中)。即使保留线程池并且不启动或停止线程池,线程上下文切换也很昂贵。您需要确保计算的运行时间明显长于调度和上下文切换所需的时间,否则您将失去性能而不是获得性能。
正如您现在可能猜到的那样,在一般情况下,弄清楚计算将花费多长时间是不可能的(我们甚至无法弄清楚是否将花费有限的时间,更不用说要花费多少时间了),即使在特殊情况。
除了:Javac和优化。请注意,大多数Javac实现实际上并没有执行很多优化。例如,Oracle的javac实现依赖底层执行引擎进行优化。这导致了另一组问题:例如,javac认为某个特定的函数是纯函数并且足够昂贵,因此它将其编译为在其他线程上执行。然后,平台的优化器(例如,HotSpot C2 JIT编译器)出现并优化整个功能。现在,您有一个空线程不执行任何操作。或者,再次想像一下,javac决定在另一个线程上调度函数,而平台优化器可以 完全优化它,除非它无法跨线程边界执行内联,因此现在可以不必要地执行可以完全优化的功能。
因此,只有在一个编译器一次性完成大部分优化的情况下,这样做才有意义,以便编译器了解并可以利用不同级别的所有不同优化以及它们之间的相互作用。
请注意,例如,HotSpot C2 JIT编译器实际上会执行一些自动矢量化,这也是自动并行化的一种形式。
definition
,使用不同definition
的purity
可能是模糊的
StringBuilder
)都是无稽之谈,因此我将其忽略,仅假设OP编写javac但表示Hotspot。您的问题2是反对在Javac中进行任何优化的一个很好的理由。
投票赞成的答案未能说明一件事。线程之间的同步通信非常昂贵。如果该函数能够以每秒数百万次调用的速度执行,那么实际上并行化它而不是保持原样会对您造成更大的伤害。
不幸的是,使用带有原子变量的繁忙循环的同步线程间通信的最快形式是能量效率低的。如果必须使用条件变量来节省能量,则线程间通信的性能会受到影响。
因此,编译器不仅需要确定函数是否是纯函数,还需要估计函数的执行时间以查看并行化是否是净赢。同样,它需要在使用原子变量或条件变量的繁忙循环之间进行选择。这将需要在后台创建线程。
如果动态创建线程,它甚至比使用条件变量还要慢。因此,编译器将需要设置一定数量的已在运行的线程。
因此,您的问题的答案是否定的,编译器不够“智能”,无法自动并行化纯函数,尤其是在Java世界中。他们很聪明,没有自动将它们并行化!