问题

我想知道Julia中的不可变类型和性能.

  1. 在这种情况下,复合类型不可变改进功能?文档说

    它们在某些情况下更有效.类型如复杂示例 上面可以有效地包装到数组中,在某些情况下 编译器能够完全避免分配不可变对象。

    我真的不明白第二部分。

  2. 有没有使复合类型不可变减少性能的情况(除了需要通过引用更改字段的情况外)?我认为一个例子可能是当不可变类型的对象被反复用作参数时,因为

    具有不可变类型的对象通过复制(在赋值语句和函数调用中)传递,而可变类型通过引用传递。

    但是,我在一个简单的例子中找不到任何区别:

     abstract MyType
    
    type MyType1 <: MyType
        v::Vector{Int}
    end
    
    immutable MyType2 <: MyType
        v::Vector{Int}
    end
    
    
    g(x::MyType) = sum(x.v)
    
    function f(x::MyType)
        a = zero(Int)
        for i in 1:10_000
            a += g(x)
        end
        return a
    end
    
    x = fill(one(Int), 10_000)
    x1 = MyType1(x)
    @time f(x1)
    # elapsed time: 0.030698826 seconds (96 bytes allocated)
    x2 = MyType2(x)
    @time f(x2)
    # elapsed time: 0.031835494 seconds (96 bytes allocated)
     

    那么为什么f比不可变类型慢?是否有使用不可变类型使代码慢的情况?

  最佳答案

当不可变类型小并且完全由直接数据组成时,它们特别快,没有引用(指针)到heap分配的对象.例如,一个由两个Ints组成的不可变类型可能存储在寄存器中,根本不存在于内存中.

知道值不会更改也有助于我们优化代码.例如,您在循环中访问x.v,而且由于x.v将始终引用相同的向量,我们可以在循环之外提升负载,而不是在每次迭代中重新加载.但是,您是否从中获得任何好处取决于该负载是否占用了循环中的很大一部分时间.

在实践中,immutables缓慢代码是罕见的,但有两种情况可能会发生.首先,如果您有一个大型的不可变类型(比如100 Int s)并做类似排序一个数组的事情,您需要在很多地方移动它们,那么额外的复制可能比指向带引用的对象慢.第二,不可变对象通常最初没有在堆上分配.如果您需要存储对一个堆的引用(例如在Any数组中),我们需要将对象移动到堆中.从那里编译器通常不足以重复使用对象的 -heap-aping,因此它可能会在一个可变的前端分配更快.

  相同标签的其他问题

julia