Julia 抽象类型数组构造JIT性能
在我的一个应用程序中,我必须在数组中存储不同子类型的元素,JIT的性能让我大吃一惊。 下面是一个简单的例子Julia 抽象类型数组构造JIT性能,julia,Julia,在我的一个应用程序中,我必须在数组中存储不同子类型的元素,JIT的性能让我大吃一惊。 下面是一个简单的例子 abstract A immutable B <: A end immutable C <: A end b = B() c = C() @time getindex(A, b, b) @time getindex(A, b, c) @time getindex(A, c, c) @time getindex(A, c, b) @time getindex(A, b, c,
abstract A
immutable B <: A end
immutable C <: A end
b = B()
c = C()
@time getindex(A, b, b)
@time getindex(A, b, c)
@time getindex(A, c, c)
@time getindex(A, c, b)
@time getindex(A, b, c, b)
@time getindex(A, b, c, c);
0.007756 seconds (6.03 k allocations: 276.426 KB)
0.007878 seconds (5.01 k allocations: 223.087 KB)
0.005175 seconds (2.44 k allocations: 128.773 KB)
0.004276 seconds (2.42 k allocations: 127.546 KB)
0.004107 seconds (2.45 k allocations: 129.983 KB)
0.004090 seconds (2.45 k allocations: 129.983 KB)
在我的应用程序中,我面临许多不同的子类型:每个元素的类型都是NTuple{N,a}
,其中N
可以更改。因此,最终应用程序被困在JIT中
最好的办法是什么?我能想到的唯一方法是创建一个包装器,比如说W
,然后在进入数组之前将我的所有元素装箱到W
。因此编译器只编译数组函数一次
immutable W
value::NTuple
end
多亏了@Matt B。在重载了他的getindex
之后
c = C()
@time getindex(A, b, b)
@time getindex(A, b, c)
@time getindex(A, c, c)
@time getindex(A, c, b)
@time getindex(A, b, c, b)
@time getindex(A, b, c, c);
0.008493 seconds (6.43 k allocations: 289.646 KB)
0.000867 seconds (463 allocations: 19.012 KB)
0.000005 seconds (5 allocations: 240 bytes)
0.000003 seconds (5 allocations: 240 bytes)
0.004035 seconds (2.37 k allocations: 122.535 KB)
0.000003 seconds (5 allocations: 256 bytes)
而且,我意识到tuple的JIT实际上是相当有效的
@time tuple(1,2)
@time tuple(b, b)
@time tuple(b, c)
@time tuple(c, c)
@time tuple(c, b)
@time tuple(b, c, b)
@time tuple(b, c, c);
@time tuple(b, b)
@time tuple(b, c)
@time tuple(c, c)
@time tuple(c, b)
@time tuple(b, c, b)
@time tuple(b, c, c);
0.000004 seconds (149 allocations: 10.183 KB)
0.000011 seconds (7 allocations: 336 bytes)
0.000008 seconds (7 allocations: 336 bytes)
0.000007 seconds (7 allocations: 336 bytes)
0.000007 seconds (7 allocations: 336 bytes)
0.000005 seconds (7 allocations: 352 bytes)
0.000004 seconds (7 allocations: 352 bytes)
0.000003 seconds (5 allocations: 192 bytes)
0.000004 seconds (5 allocations: 192 bytes)
0.000002 seconds (5 allocations: 192 bytes)
0.000002 seconds (5 allocations: 192 bytes)
0.000002 seconds (5 allocations: 192 bytes)
0.000002 seconds (5 allocations: 192 bytes)
这里的JIT启发式可能在基本库中进行更好的调优。虽然Julia默认为参数类型的唯一排列生成专门化方法,但有几个转义图案填充可用于减少专门化的数量:
- 使用
而不是f(T::Type)
。两者都是类型良好的,并且通过推理表现良好,但前者只能为所有类型生成一个方法f{T}(::Type{T})
- 使用未记录的所有大写字母
标志,而不是g(::ANY)
。它在语义上是相同的,但是g(::ANY)
将阻止该参数的专门化ANY
function Base.getindex{T<:A}(::Type{T}, vals::ANY...)
a = Array(T,length(vals))
@inbounds for i = 1:length(vals)
a[i] = vals[i]
end
return a
end
function Base.getindex{T这里的JIT启发式可能在基本库中得到更好的调整。虽然Julia默认为参数类型的唯一排列生成专门化方法,但有一些转义图案填充可用于减少专门化的数量:
- 使用
f(T::Type)
而不是f{T}(::Type{T})
。两者都是类型良好的,并且通过推理表现良好,但前者只为所有类型生成一个方法
- 使用未记录的all caps
g(::ANY)
标志,而不是g(::ANY)
。它在语义上是相同的,但是ANY
将阻止该参数的专门化
在这种情况下,您可能希望专门化类型,而不是值:
function Base.getindex{T<:A}(::Type{T}, vals::ANY...)
a = Array(T,length(vals))
@inbounds for i = 1:length(vals)
a[i] = vals[i]
end
return a
end
function Base.getindex{t如果我想编写一个函数test(vals::ANY…)
,是否也可以断言vals…
必须是A
类型?否,据我所知,不在方法签名内。不过,您可以在方法体中这样做。在这种情况下,分配给T
类型数组的元素将尝试转换任何非A
值。如果没有定义转换。如果要编写函数测试(VAL::ANY…)
,是否也可以断言vals…
必须是A
类型?否,据我所知,不在方法签名内。不过,您可以在方法体中这样做。在这种情况下,分配给T
类型数组的元素将尝试转换任何非A
值。如果没有定义转换。