对于通过的函数的评估比在朱莉亚中的直接评估慢.有解决方法吗?

Til*_*ill 4 julia

假设我有许多不同的非常简单的函数,称为f1,f2,....我想将所有f函数存储在fStruct中,并将其中一个f函数传递给例如g,当我在我的代码中需要它时.但是当我将函数f传递给g时,函数g()的求值要慢得多.有解决方法吗?我丑陋的解决方案是使用一个整体函数,它通过if-else语句选择正确的f()函数.下面是慢速计算的最小示例.

using BenchmarkTools
struct fFunction
    f1
    f2
end
f() = return 1
fStruct = fFunction(f, f)
g = fStruct.f1
@btime f() --> 0.001 ns (0 allocations: 0 bytes)
@btime g() --> 9.591 ns (0 allocations: 0 bytes)
Run Code Online (Sandbox Code Playgroud)

EDIT1:

我还可以问一下,为什么函数g比较慢,或者如何在下面的最小例子中使它像f一样快

using BenchmarkTools
f() = return 1
func = "f"
g = eval(Meta.parse(func))
f == g -->true
@btime f() --> 0.001 ns (0 allocations: 0 bytes)
@btime g() --> 11.907 ns (0 allocations: 0 bytes)
Run Code Online (Sandbox Code Playgroud)

EDIT2:

谢谢您的回答.我用解决方案更新帖子.

using BenchmarkTools
f() = return 1
function g(x)
   h = f
   h()
end
const g2 = f
@btime f()
@btime g(f)
@btime g2()
Run Code Online (Sandbox Code Playgroud)

f,g和g2给你相同的速度.

struct fFunctionAmbigiousType{F}
     f1::F
     f2::F
end
struct fFunctionDeclaredType{F}
     f1::F
     f2::F
end
fStructAmbigiousType = fFunctionAmbigiousType(f, f)
fStructDeclaredType = fFunctionDeclaredType(f, f)
fTuple = (f1 = f, f2 = f)
@btime $fStructAmbigiousType.f1
@btime $fStructDeclaredType.f1
@btime $fTuple.f1
fStructAmbigiousTypeFunctionPassed = fStructAmbigiousType.f1
fStructDeclaredTypeFunctionPassed = fStructDeclaredType.f1
fTupleFunctionPassed = fTuple.f1
@btime $fStructAmbigiousTypeFunctionPassed() 
@btime $fStructDeclaredTypeFunctionPassed() 
@btime $fTupleFunctionPassed() 
Run Code Online (Sandbox Code Playgroud)

fFunctionAmbigiousType,fFunctionDeclaredType和fTuple为您提供相同的速度.向结构声明函数的类型不会改变任何东西.朱莉娅在两种情况下都理解{f}的类型.如果经常应用函数,则可以使用参数结构或参数化的NamedTuple,但当然速度较慢.如果您经常应用f函数,则应首先将其传递给g或类似的东西,以避免每次都进行索引.

亲切的问候,直到

Bog*_*ski 6

你的问题有几个问题.

如何做基准测试

实际上在你的代码中,两个函数同样快.问题是g不在const全球范围内引入惩罚.要看到这个声明g为const或$g@btime调用中使用,看看没有区别:

julia> using BenchmarkTools

julia> struct fFunction
           f1
           f2
       end

julia> f() = return 1
f (generic function with 1 method)

julia> fStruct = fFunction(f, f)
fFunction(f, f)

julia> const g = fStruct.f1
f (generic function with 1 method)

julia> @btime f()
  0.001 ns (0 allocations: 0 bytes)
1

julia> @btime g()
  0.001 ns (0 allocations: 0 bytes)
1
Run Code Online (Sandbox Code Playgroud)

julia> using BenchmarkTools

julia> struct fFunction
           f1
           f2
       end

julia> f() = return 1
f (generic function with 1 method)

julia> fStruct = fFunction(f, f)
fFunction(f, f)

julia> g = fStruct.f1
f (generic function with 1 method)

julia> @btime f()
  0.001 ns (0 allocations: 0 bytes)
1

julia> @btime $g()
  0.001 ns (0 allocations: 0 bytes)
1
Run Code Online (Sandbox Code Playgroud)

如何分析您的代码

但是,这种等价是人为的,因为您gfStruct全局范围中提取,因此在调用之前对其进行求值@btime.更合适的测试是:

julia> using BenchmarkTools

julia> struct fFunction
           f1
           f2
       end

julia> f() = return 1
f (generic function with 1 method)

julia> fStruct = fFunction(f, f)
fFunction(f, f)

julia> test1() = f()
test1 (generic function with 1 method)

julia> test2(fStruct) = fStruct.f1()
test2 (generic function with 1 method)

julia> @btime test1()
  0.001 ns (0 allocations: 0 bytes)
1

julia> @btime test2($fStruct)
  14.462 ns (0 allocations: 0 bytes)
1

julia> @code_warntype test1()
Body::Int64
1 1 ?     return 1                                                                                                                   ?

julia> @code_warntype test2(fStruct)
Body::Any
1 1 ? %1 = (Base.getfield)(fStruct, :f1)::Any                                                                           ?? getproperty
  ?   %2 = (%1)()::Any                                                                                                  ?
  ???      return %2
Run Code Online (Sandbox Code Playgroud)

并且您看到使用fFunctionstruct不高效,因为它f1f2字段具有抽象类型(Any具体).

如何编写一个包含函数的高效结构

使用带参数的a Tuple,a NamedTuple或struct,因为它们都提供类型信息.元组将被简单地定义为(f,f),a NamedTuple将是(f1=f, f2=f).最复杂的情​​况是参数结构,我在这里向您展示(代码为Tuple,NamedTuple甚至更简单):

julia> using BenchmarkTools

julia> struct fFunction{F1,F2}
           f1::F1
           f2::F2
       end

julia> f() = return 1
f (generic function with 1 method)

julia> fStruct = fFunction(f, f)
fFunction{typeof(f),typeof(f)}(f, f)

julia> test1() = f()
test1 (generic function with 1 method)

julia> test2(fStruct) = fStruct.f1()
test2 (generic function with 1 method)

julia> @btime test1()
  0.001 ns (0 allocations: 0 bytes)
1

julia> @btime test2($fStruct)
  1.866 ns (0 allocations: 0 bytes)
1

julia> @code_warntype test1()
Body::Int64
1 1 ?     return 1                                                                                                                   ?

julia> @code_warntype test2(fStruct)
Body::Int64
1 1 ?     (Base.getfield)(fStruct, :f1)                                                                                 ?? getproperty
  ???     return 1
Run Code Online (Sandbox Code Playgroud)

您可以看到使用fFunction定义为参数类型几乎没有开销(您支付的唯一成本是字段提取).

如果问题不明确请告诉我,我可以详细说明.