函数作为where关键字的另一个函数的输入效果

问题描述 投票:0回答:1

我想了解这个例子中发生了什么:

在此实施中:

using LinearAlgebra
using StaticArrays, BenchmarkTools

function foo(u,p)
    a,b,c,d = p;
    x,y = u;
    return SA_F64[a*x - b*x*y, -c*y + d*x*y];
end

function bar(f, u, h, p) 
    return SVector{2,Float64}(u + h*f(u,p));
end

function baz(f, u, func, p)
    n_p = 100000;
    h = 1.0/n_p;
    out = zeros(n_p, 2);
    for i in 1:n_p
        u = func(f, u, h, p);
        out[i,:]=u
    end
    return out;
end


u = SA_F64[1., 2.];
p = SA_F64[1.5, 1.0, 3.0, 1.0];

@btime baz(foo, u, bar, p)

我得到的执行时间为

6.033 ms (399492 allocations: 12.20 MiB)
。这需要太多内存来计算。

我发现一个实现在做:

function baz(f::T, u, func, p) where T
    n_p = 100000;
    h = 1.0/n_p;
    out = zeros(n_p, 2);
    for i in 1:n_p
        u = func(f, u, h, p);
        out[i,:]=u
    end
    return out;
end

最终结果完全相同,但计算时间为:

1.000 ms (2 allocations: 1.53 MiB)
。这太不可思议了!

规范

baz(f::T, u, func, p) where T
做了什么来获得这种性能改进?

julia
1个回答
0
投票

这与 Julia 性能提示有关,标题为“注意 Julia 何时避免专业化

作为一种启发式方法,Julia 在三种特定情况下避免自动专门化参数类型参数:

Type
Function
Vararg
。当参数在方法中使用时,Julia 总是会专门化,但如果参数只是传递给另一个函数,则不会。

从根本上来说,Julia 的性能优势来自于推断类型并在此基础上专门化代码:当我们调用

sum(50:5:500000)
时,Julia 推断参数是
StepRange
,并且编译特定于范围参数的特定
sum
代码并运行。这会产生专门针对这种类型的非常快的机器代码。

但是,如上所述,如果

Function
参数没有在代码中直接使用(即调用),则不会发生这种特化 - 这里就是这种情况,因为
f
被传递给
baz
,但没有在那里使用,而是传递给
bar
。因此,Julia 在运行时才将
f
视为某些
Function
,而不是特定的
foo
函数。上面链接的页面继续说:

这通常不会影响运行时的性能,并且可以提高编译器性能。如果您发现它确实对您的情况在运行时产生性能影响,您可以通过向方法声明添加类型参数来触发专门化。

因此,添加类型参数

T
是向编译器发出的一个信号,告诉编译器“要专门研究这里的特定函数,而不是将其视为泛型
Function
”。

© www.soinside.com 2019 - 2024. All rights reserved.