【问题标题】:Exponent calculation speed指数计算速度
【发布时间】:2016-07-01 18:50:19
【问题描述】:

我目前正在测试 Julia(我使用过 Matlab)

在matlab中N^3的计算速度比NxNxN慢。 N^2 和 NxN 不会发生这种情况。他们使用不同的算法来计算高阶指数,因为他们更喜欢准确性而不是速度。

我认为 Julia 也会做同样的事情。

我想问是否有办法强制 Julia 使用乘法而不是默认算法来计算 N 的指数,至少对于立方指数而言。

前段时间我在matlab上做了一些测试。我把那段代码翻译给了朱莉娅。

代码链接: http://pastebin.com/bbeukhTc (我不能在这里上传所有的链接:()

Matlab 2014 上的脚本结果:

指数1

经过的时间是 68.293793 秒。 (最小的17.7倍)

指数2

经过的时间是 24.236218 秒。 (最小的 6.3 倍)

指数3

经过的时间是 3.853348 秒。

Julia 0.46 上的脚本结果:

指数1

18.423204 秒(8.22 k 分配:372.563 KB)(最小的 51.6 倍)

指数2

13.746904 秒(9.02 k 分配:407.332 KB)(最小的 38.5 倍)

指数3

0.356875 秒(10.01 k 分配:450.441 KB)

在我的测试中,julia 比 Matlab 快,但我使用的是相对较旧的版本。我无法测试其他版本。

【问题讨论】:

    标签: algorithm performance julia


    【解决方案1】:

    查看 Julia 的源代码:

    julia/base/math.jl:

    ^(x::Float64, y::Integer) =
        box(Float64, powi_llvm(unbox(Float64,x), unbox(Int32,Int32(y))))
    ^(x::Float32, y::Integer) =
        box(Float32, powi_llvm(unbox(Float32,x), unbox(Int32,Int32(y))))
    

    julia/base/fastmath.jl:

    pow_fast{T<:FloatTypes}(x::T, y::Integer) = pow_fast(x, Int32(y))
    pow_fast{T<:FloatTypes}(x::T, y::Int32) =
        box(T, Base.powi_llvm(unbox(T,x), unbox(Int32,y)))
    

    我们可以看到 Julia 使用了 powi_llvm

    检查llvm's source code

    define double @powi(double %F, i32 %power) {
    ; CHECK: powi:
    ; CHECK: bl __powidf2
            %result = call double @llvm.powi.f64(double %F, i32 %power)
        ret double %result
    }
    

    现在,__powidf2 是这里有趣的功能:

    COMPILER_RT_ABI double
    __powidf2(double a, si_int b)
    {
        const int recip = b < 0;
        double r = 1;
        while (1)
        {
            if (b & 1)
                r *= a;
            b /= 2;
            if (b == 0)
                break;
            a *= a;
        }
        return recip ? 1/r : r;
    }
    

    示例 1:给定 a = 2; b = 7:

     -              r          =   1
     - iteration 1: r = 1 *  2 =   2; b = (int)(7/2) = 3; a = 2 * 2 =  4
     - iteration 2: r = 2 *  4 =   8; b = (int)(3/2) = 1; a = 4 * 4 = 16
     - iteration 3: r = 8 * 16 = 128; 
    

    示例 2:给定 a = 2; b = 8:

     -              r           =   1
     - iteration 1: r           =   1; b = (int)(8/2) = 4; a =  2 *  2 =   4
     - iteration 2: r           =   1; b = (int)(4/2) = 2; a =  4 *  4 =  16
     - iteration 3: r           =   1; b = (int)(2/2) = 1; a = 16 * 16 = 256
     - iteration 4: r = 1 * 256 = 256; b = (int)(1/2) = 0;
    

    整数幂总是以序列乘法的形式实现。这就是为什么 N^3 比 N^2 慢的原因。

    jl_powi_llvm(在 fastmath.jl 中调用。“jl_”由宏扩展连接)另一方面,将指数转换为浮点并调用 pow()。 C源代码:

    JL_DLLEXPORT jl_value_t *jl_powi_llvm(jl_value_t *a, jl_value_t *b)
    {
        jl_value_t *ty = jl_typeof(a);
        if (!jl_is_bitstype(ty))
            jl_error("powi_llvm: a is not a bitstype");
        if (!jl_is_bitstype(jl_typeof(b)) || jl_datatype_size(jl_typeof(b)) != 4)
            jl_error("powi_llvm: b is not a 32-bit bitstype");
        jl_value_t *newv = newstruct((jl_datatype_t*)ty);
        void *pa = jl_data_ptr(a), *pr = jl_data_ptr(newv);
        int sz = jl_datatype_size(ty);
        switch (sz) {
        /* choose the right size c-type operation */
        case 4:
            *(float*)pr = powf(*(float*)pa, (float)jl_unbox_int32(b));
            break;
        case 8:
            *(double*)pr = pow(*(double*)pa, (double)jl_unbox_int32(b));
            break;
        default:
            jl_error("powi_llvm: runtime floating point intrinsics are not implemented for bit sizes other than 32 and 64");
        }
        return newv;
    }
    

    【讨论】:

    • 感谢您的回复。关于 math.jl 和 fastmath.jl 的信息是我要找的。​​span>
    • @fastmath 实际上调用了pow_fast,这是一个通用函数,它在正确的类型上调用相同的powi_llvm,就像没有@fastmath 一样。当从 REPL 调用 @fastmath 宏时,会出现一些类型推断混淆。
    • 另外,pow_fast 调用 jl_powi_llvm - 那么您能否将 jl_ 添加到 powi_llvm 的最后一个链接。并感谢您为我澄清了一些事情的好答案(我正在使用 0.5)。
    • @DanGetz:谢谢丹
    【解决方案2】:

    Lior 的回答非常好。这是您提出的问题的解决方案:是的,有一种方法可以强制使用乘法,但会以准确性为代价。这是@fastmath 宏:

    julia> @benchmark 1.1 ^ 3
    BenchmarkTools.Trial: 
      samples:          10000
      evals/sample:     999
      time tolerance:   5.00%
      memory tolerance: 1.00%
      memory estimate:  16.00 bytes
      allocs estimate:  1
      minimum time:     13.00 ns (0.00% GC)
      median time:      14.00 ns (0.00% GC)
      mean time:        15.74 ns (6.14% GC)
      maximum time:     1.85 μs (98.16% GC)
    
    julia> @benchmark @fastmath 1.1 ^ 3
    BenchmarkTools.Trial: 
      samples:          10000
      evals/sample:     1000
      time tolerance:   5.00%
      memory tolerance: 1.00%
      memory estimate:  0.00 bytes
      allocs estimate:  0
      minimum time:     2.00 ns (0.00% GC)
      median time:      3.00 ns (0.00% GC)
      mean time:        2.59 ns (0.00% GC)
      maximum time:     20.00 ns (0.00% GC)
    

    请注意,使用@fastmath,性能要好得多。

    【讨论】:

    • 感谢@fastmath 宏。我将看看它在我需要分析的数据上的表现,看看准确性的成本是否不高。我不知道我是否可以关闭帖子,但我得到的回复已经足够了
    • 注意第一个基准有分配。所以它们没有明显的可比性。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-04-20
    • 2011-09-28
    • 2021-07-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多