【问题标题】:Optimally passing dimensions of fixed size array in julia在 Julia 中优化传递固定大小数组的维度
【发布时间】:2016-07-18 19:22:56
【问题描述】:

我想编写一个以矩阵为输入的函数。这是复杂项目中频繁的低级调用,因此使该函数尽可能快地具有潜在的严重性能影响。因为速度对我来说非常重要,所以我使用FixedSizeArrays 中的类型,因为我知道这将节省内存使用量。但我经常知道输入矩阵的某些属性,但我不确定我是否在优化使用它。

这是一个简单的例子。想象一下我想让以下功能尽可能快:

using FixedSizeArrays

function foo( input::Mat )
# NB: Mat is the FixedSizeArrays matrix type
  return 2 * input
end

显然,这是一个微不足道的例子,但这不是重点。关键是我对矩阵input 的维度有所了解:它总是只有两列,我总是可以在运行时指定行数。这似乎是可以传递给编译器以使我的代码更快的信息。我可以将它作为定义input 大小的参数传递吗?这是一个不起作用的示例,但应该让您对我正在尝试做的事情有所了解。

function bar( int::N, thismat::Mat{N,2,Float64} )
  return 2 * thismat
end

我可以做这样的事情吗?如果可以的话,这还能用吗?也许 FixedSizeArrays 已经做了所有可以做的事情。谢谢你的想法!

【问题讨论】:

    标签: performance julia fixed-size-types


    【解决方案1】:

    固定大小的数组已经专门用于大小。当行数 N 在您的情况下可以变化时,这些数组不适合。您注意到的任何性能问题都可能是由于过度专业化

    让我更具体一点。

    Julia 编译器能够通过对参数类型的积极专业化来实现零成本抽象。所以一般来说(也就是说,在所有情况下,除了一些特殊化太昂贵或被显式禁用的情况),如果使用两个不同的类型签名调用函数,则将编译该函数的两个版本。

    由于Mat 的大小是其类型的一部分,这意味着将为Mat 的每个可能大小编译一个版本。所以你寻求的专业已经完成了。

    然而,专业化并不是免费的。有两个相关的费用:

    • 第一次在特定签名上调用函数时,将分配内存并且必须运行编译器。
    • 当将无法推断类型的参数传递给函数时,存在“类型不稳定”,需要动态调度。动态调度涉及运行时查找。

    因此,如果您的矩阵大小为(2, N),其中N 变化且在编译时未知,则将产生动态调度 的性能成本。这种性能成本可以通过使用函数屏障技术来限制:对于每个类型不稳定的调用,我们只会产生一次这种成本,因此限制此类调用的数量可以提高性能。

    但更能提高性能的是完全避免这种动态调度。可以构造一个数组类型,只对类型中的列数进行编码,并在运行时将行数作为一个字段。也就是说,您的性能问题可能是由于过度专业化造成的,您需要创建类型以减少专业化的数量。

    找到正确的平衡点对于从应用程序中尽可能多地发挥性能至关重要。专注于数组的大小实际上很少有用——例如,即使是 C 和 C++ 代码也倾向于将数组大小作为运行时参数传递,而不是专注于特定的数组大小。这不是那么贵。在更多情况下,FixedSizeArrays.jl 不会提高性能,反而会伤害它。当然,在某些情况下它会有所帮助,但您的情况可能不是其中之一。


    就您而言,为了获得最佳性能,我怀疑这样的类型会最快:

    immutable TwoColumnMatrix{T, BaseType} <: AbstractArray{T, 2}
        height::Int
        base::BaseType
    end
    
    function TwoColumnMatrix(A::Matrix)
        size(A, 2) == 2 || throw(ArgumentError("must be two columns"))
        TwoColumnMatrix{eltype(A), typeof(A)}(size(A, 1), A)
    end
    
    Base.@propagate_inbounds function getindex(M::TwoColumnMatrix, n::Int)
        M.base[n]
    end
    
    size(M::TwoColumnMatrix) = (M.height, 2)
    

    您可能需要定义其他方法以获得最佳性能,并且一如既往地进行基准测试。包装器的开销可能不值得编译器了解尺寸。

    【讨论】:

    • @squipbar 我对这个例子有了一些想法。有一个额外的指针取消引用和分支不好(一点也不好)。查看新的,它避免了这些麻烦;不过,我也没有对此进行基准测试。
    • @squipbar 如果您还没有看过,请观看 Tim Holy 关于数组和迭代的演示视频:youtube.com/watch?v=fl0g9tHeghA
    • 我总是从你的回答中学到很多东西,即使我不是问这个问题的人!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-08-27
    • 2022-09-27
    • 2023-04-03
    • 2013-07-15
    • 2022-01-16
    • 2013-01-24
    相关资源
    最近更新 更多