我是在一个代码库# TODO this seems very clumsy to duplicate the loop code like this?中发现的,所以我想我可以尝试一下,作为一个学习练习。我想得到一些关于我的想法的反馈,特别是:
[1,1], octaves=10) Old: 0.000013 seconds (63 allocations: 1.062 KiB)New: 0.000008 seconds (41 allocations: 1.500 KiB)谢谢!
原功能:
# coords is [x] or [x, y] or [x, y, z] or [x, y, z, w]
function _octaves(coords::Array{T, 1} where T <: Real;
octaves::Int = 1,
persistence=1.0)
total = 0.0
frequency = 1.0
amplitude = 1.0
maxval = 0.0
# TODO this seems very clumsy to duplicate the loop code like this?
l = length(coords)
if l == 1
for i in 1:octaves
total += simplexnoise(coords[1] * frequency) * amplitude
maxval += amplitude
amplitude *= persistence
frequency *= 2
end
elseif l == 2
for i in 1:octaves
total += simplexnoise(coords[1] * frequency, coords[2] * frequency) * amplitude
maxval += amplitude
amplitude *= persistence
frequency *= 2
end
elseif l == 3
for i in 1:octaves
total += simplexnoise(coords[1] * frequency, coords[2] * frequency, coords[3] * frequency) * amplitude
maxval += amplitude
amplitude *= persistence
frequency *= 2
end
elseif l == 4
for i in 1:octaves
total += simplexnoise(coords[1] * frequency, coords[2] * frequency, coords[3] * frequency, coords[4] * frequency) * amplitude
maxval += amplitude
amplitude *= persistence
frequency *= 2
end
end
return total / maxval
end我的重构:
function _octaves(coords::Array{T, 1} where T <: Real;
octaves::Int = 1,
persistence=1.0)
total = 0.0
frequency = 1.0
amplitude = 1.0
maxval = 0.0
for _ in 1:octaves
inputs = coords .* frequency
total += simplexnoise(inputs...) * amplitude
maxval += amplitude
amplitude *= persistence
frequency *= 2
end
return total / maxval
end发布于 2020-05-27 07:59:41
原则上,这是非常空泛的。循环重构正是我应该做的。
以下是其他一些建议:
const Coords{T} = NTuple{N, T} where N
function _octaves(coords::Coords{T}; octaves::Int=1, persistence::T=one(T)) where {T<:Real}
if octaves < 1
# prevent zero division
throw(DomainError(octaves, "`octaves` must be at least 1!"))
end
total = zero(T)
maxval = zero(T)
for octave in 1:octaves
p = octave - 1
frequency = 2 ^ p
amplitude = persistence ^ p
maxval += amplitude
scaled_coords = coords .* frequency
total += simplexnoise(scaled_coords...) * amplitude
end
return total / maxval
endone和zero来确保类型的稳定性(这是一个非常重要的概念,如果您不知道这个概念,就应该阅读它!)frequency的计算非常简单,因为它是一个整数幂为2。amplitude可能会做一些不必要的工作;我认为这样做更清楚,不值得进行微优化,但是您必须在用例中测试这一点。coords的类型选择实际上取决于代码的其余部分。如果这只用于有界维的坐标,我非常希望像上面所写的那样使用元组。如果您一直使用向量(这看起来有点像信号处理?),那么使用Coords{T} = Vector{T}就更容易了。
虽然不是问题的一部分,但让simplenoise处理可迭代的而不是varargs参数可能更好。即使是simplenoise(frequency, coords),scaled_coords的分配也会被删除。
如果求和有任何可能溢出,请查看Base.widen。另外,您可能希望确保octaves < 8 * sizeof(Int)或类似的东西,并在开始时将其添加到检查中。
如果这确实是需要优化的某些代码的热点部分,并且在许多调用中octaves是常量,则有可能编写一个@generated函数来完全展开循环,但是除非它是关键的,否则它是不值得的。
至于基准测试的区别:首先,您应该使用BenchmarkTools.jl,而不是@time。然后,我还没有度量自己,但这肯定是由于您构建的中间inputs数组。
https://codereview.stackexchange.com/questions/241969
复制相似问题