2015-11-13 60 views
7

版本:从朱莉娅V0.4起(我用0.5.0-dev的+ 433(2015年9月29日15:39 UTC))上演节目 - 杰克Bolewski的讲话

参考Jake Bolewski: Staged programming in Julia

问题:看杰克斯Bolewski的关于StaticVec演讲结束后我没赶上例子背后的理念与length功能。

julia> type StaticVec{T,N} 
     vals::Vector{T} 
     end 

julia> StaticVec(T,vals...) = StaticVec{T,length(vals)}([vals...]) 
StaticVec{T,N} 

julia> v= StaticVec(Float64,1,2,3) 
StaticVec{Float64,3}([1.0,2.0,3.0]) 

非上演length

julia> function Base.length{T,N}(v::StaticVec{T,N}) 
     N 
     end 
length (generic function with 58 methods) 

julia> code_llvm(length, (StaticVec{Float64,3},)) 

define i64 @julia_length_21889(%jl_value_t*) { 
top: 
    ret i64 3 
} 

,并上演length版本

julia> @generated function Base.length{T,N}(v::StaticVec{T,N}) 
     :(N) 
     end 
length (generic function with 58 methods) 

julia> code_llvm(length, (StaticVec{Float64,3},)) 

define i64 @julia_length_21888(%jl_value_t*) { 
top: 
    ret i64 3 
} 

给出相同的LLVM代码。

我想我明白舞台编程背后的想法,但在这个特殊的例子中,我不明白演讲者的意图。任何人都可以向我解释吗?

回答

11

这个例子可能不是最好的选择,因为正如你指出的那样,它根本不需要生成函数。 Jiahao Chen最近写了一个blog post,这个例子用一个生成的函数来进行高效的数据平滑。下面是码给他的类似位,通过剥离在前面M循环迭代和背面,从而避免在主循环体​​分支甚至进一步提高了效率:

immutable SavitzkyGolayFilter{M,N} end 

wrapL(i, n) = ifelse(1 ≤ i, i, i + n) 
wrapR(i, n) = ifelse(i ≤ n, i, i - n) 

@generated function smooth!{M,N}(
    ::Type{SavitzkyGolayFilter{M,N}}, 
    data::AbstractVector, 
    smoothed::AbstractVector, 
) 
    # compute filter coefficients from the Jacobian 
    J = Float64[(i-M-1)^(j-1) for i = 1:2M+1, j = 1:N+1] 
    e₁ = [1; zeros(N)] 
    C = J' \ e₁ 

    # generate code to evaluate filter on data matrix 
    pre = :(for i = 1:$M end) 
    main = :(for i = $(M+1):n-$M end) 
    post = :(for i = n-$(M-1):n end) 
    for loop in (pre, main, post) 
     body = loop.args[2].args 
     push!(body, :(x = $(C[M+1]) * data[i])) 
     for j = reverse(1:M) 
      idx = loop !== pre ? :(i-$j) : :(wrapL(i-$j,n)) 
      push!(body, :(x += $(C[M+1-j]) * data[$idx])) 
     end 
     for j = 1:M 
      idx = loop !== post ? :(i+$j) : :(wrapR(i+$j,n)) 
      push!(body, :(x += $(C[M+1+j]) * data[$idx])) 
     end 
     push!(body, :(smoothed[i] = x)) 
    end 
    quote 
     n = length(data) 
     n == length(smoothed) || throw(DimensionMismatch()) 
     @inbounds $pre; @inbounds $main; @inbounds $post 
     return smoothed 
    end 
end 

smooth{S<:SavitzkyGolayFilter,T}(::Type{S}, data::AbstractVector{T}) = 
    smooth!(S, data, Vector{typeof(1.0*one(T))}(length(data))) 

smooth(SavitzkyGolayFilter{3,4}, rand(1000))生成的代码,例如,是以下内容:

n = length(data) 
n == length(smoothed) || throw(DimensionMismatch()) 
@inbounds for i = 1:3 
    x = 0.5670995670995674 * data[i] 
    x += 0.02164502164502159 * data[wrapL(i - 3, n)] 
    x += -0.1298701298701297 * data[wrapL(i - 2, n)] 
    x += 0.32467532467532445 * data[wrapL(i - 1, n)] 
    x += 0.32467532467532473 * data[i + 1] 
    x += -0.12987012987013022 * data[i + 2] 
    x += 0.021645021645021724 * data[i + 3] 
    smoothed[i] = x 
end 
@inbounds for i = 4:n-3 
    x = 0.5670995670995674 * data[i] 
    x += 0.02164502164502159 * data[i - 3] 
    x += -0.1298701298701297 * data[i - 2] 
    x += 0.32467532467532445 * data[i - 1] 
    x += 0.32467532467532473 * data[i + 1] 
    x += -0.12987012987013022 * data[i + 2] 
    x += 0.021645021645021724 * data[i + 3] 
    smoothed[i] = x 
end 
@inbounds for i = n-2:n 
    x = 0.5670995670995674 * data[i] 
    x += 0.02164502164502159 * data[i - 3] 
    x += -0.1298701298701297 * data[i - 2] 
    x += 0.32467532467532445 * data[i - 1] 
    x += 0.32467532467532473 * data[wrapR(i + 1, n)] 
    x += -0.12987012987013022 * data[wrapR(i + 2, n)] 
    x += 0.021645021645021724 * data[wrapR(i + 3, n)] 
    smoothed[i] = x 
end 
return smoothed 

正如您可能想象的那样,这会生成非常高效的机器码。我希望能稍微清理生成函数的概念。

+0

谢谢Stefan。虽然你的例子非常精细,我更喜欢文档中的例子,但是你的答案已经证实了我的预测。 –