首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >循环逻辑重构及其对分配的影响

循环逻辑重构及其对分配的影响
EN

Code Review用户
提问于 2020-05-08 21:06:03
回答 1查看 52关注 0票数 2

我是在一个代码库# TODO this seems very clumsy to duplicate the loop code like this?中发现的,所以我想我可以尝试一下,作为一个学习练习。我想得到一些关于我的想法的反馈,特别是:

  1. 这是正确的习惯朱莉娅,还是接近?还是我只是在用朱莉娅写Python?
  2. 如何解释这两种方法在分配上的差异?(使用的参数:[1,1], octaves=10)
    • Old: 0.000013 seconds (63 allocations: 1.062 KiB)
    • New: 0.000008 seconds (41 allocations: 1.500 KiB)

谢谢!

原功能:

代码语言:javascript
复制
# 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

我的重构:

代码语言:javascript
复制
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
EN

回答 1

Code Review用户

回答已采纳

发布于 2020-05-27 07:59:41

原则上,这是非常空泛的。循环重构正是我应该做的。

以下是其他一些建议:

代码语言:javascript
复制
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
end
  1. 使用onezero来确保类型的稳定性(这是一个非常重要的概念,如果您不知道这个概念,就应该阅读它!)
  2. 用明确的力量代替一些中间的更新。对于编译器来说,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数组。

票数 1
EN
页面原文内容由Code Review提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://codereview.stackexchange.com/questions/241969

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档