Scala中的curried,partial applied和'normal'函数之间的性能特征是什么?

jos*_*iah 7 performance benchmarking scala currying partial-application

我在这里看一看:Scala currying与部分应用的函数,但那里的答案更多地回答了Scala中currying,partial application和normal函数之间的功能和语义差异.

我有兴趣了解这些可用于功能的不同技术之间是否有任何性能考虑因素,即......

如果我们使用普通函数的性能作为基础:

def add3(a: Int, b: Int, c: Int) = a + b + c
add3(1, 2, 3)
Run Code Online (Sandbox Code Playgroud)

然后比较:

// First
(add3 _).curried(1)(2)(3)

// Second
val add2 = add3(1, _: Int, _: Int)
val add1 = add2(2, _: Int)
add1(3)

// Third
def add3(a: Int)(b: Int)(c: Int) = a + b + c
add3(1)(2)(3)
Run Code Online (Sandbox Code Playgroud)

如果我发现一些性能不佳的代码(无论是在速度还是内存使用方面),我可能想要注意的是什么?我看到在所述代码段中发生了很多currying或部分应用程序?

例如,在Haskell中,我会查看正在生成多少个thunk并在其中闲逛.我认为Scala使用类似的方法来传递部分应用和curried函数,而Scala如何处理这些事情的细节将是很有价值的.

Aiv*_*ean 5

我使用scala-to-java工具将您的代码段转换为java,结果如下:

  1. 简单的函数调用被转换为类似的函数调用:

    def add3(a: Int, b: Int, c: Int) = a + b + c
    add3(1, 2, 3)
    
    Run Code Online (Sandbox Code Playgroud)

    结果:

    public final class _$$anon$1 {
        private int add3(final int a, final int b, final int c) {
            return a + b + c;
        }
    
        {
            this.add3(1, 2, 3);
        }
    }
    
    Run Code Online (Sandbox Code Playgroud)
  2. "第一个"片段:(add3 _).curried(1)(2)(3)基本上被转化为:

    ((Function3<Integer, Object, Object, Object>)new _$$anon$1$$anonfun._$$anon$1$$anonfun$1(this)).curried().apply(BoxesRunTime.boxToInteger(1)).apply(BoxesRunTime.boxToInteger(2)).apply$mcII$sp(3);
    
    Run Code Online (Sandbox Code Playgroud)

    我省略了样板.这里发生的是创建包装器函数类add3,然后curried调用该类的方法,然后apply在前一个应用程序的结果上调用三次.你可以查看curried 这里的来源.它做什么,它只返回几个高阶函数(返回函数的函数).

    因此,与"case 0"相比,为单个curried调用创建了几个额外的函数包装器.

  3. "第二":

    // Second
    val add2 = add3(1, _: Int, _: Int)
    val add1 = add2(2, _: Int)
    add1(3)
    
    Run Code Online (Sandbox Code Playgroud)

    我不会提供整个页面的转换输出.如果需要,请在此处查看.基本上是发生了什么,add2并且add1使用具有相应数量的参数的单个方法生成功能性包装类.所以,当你打电话时add1(3),它会调用add2哪个电话add3.生成的包装器实际上是单例,因此开销限于几个函数调用.

  4. 第三:

    def add3(a: Int)(b: Int)(c: Int) = a + b + c
    add3(1)(2)(3)
    
    Run Code Online (Sandbox Code Playgroud)

    再次转换为简单的函数调用:

    public final class _$$anon$1 {
        private int add3(final int a, final int b, final int c) {
            return a + b + c;
        }
    
        {
            this.add3(1, 2, 3);
        }
    }
    
    Run Code Online (Sandbox Code Playgroud)

    但是如果你试图以咖喱的方式使用它,例如,像这样val p = add3(1) _,将另外生成一个功能包装类.