【问题标题】:How can I speedup this Julia code?如何加速这个 Julia 代码?
【发布时间】:2017-04-05 17:06:18
【问题描述】:

代码实现了 Pollard rho() 函数的示例,用于查找正整数 n 的因子。我检查了 Julia "Primes" 包中的一些代码,这些代码快速运行以试图加速 pollard_rho() 函数,但都无济于事。代码应在大约 100 毫秒到 30 秒(Erlang、Haskell、Mercury、SWI Prolog)内执行 n = 1524157897241274137,但在 JuliaBox、IJulia 和 Julia REPL 上大约需要 3 到 4 分钟。我怎样才能让它快速运行?

pollard_rho(1524157897241274137) = 1234567891

__precompile__()
module Pollard

export pollard_rho

function pollard_rho{T<:Integer}(n::T)
    f(x::T, r::T, n) = rem(((x ^ T(2)) + r), n)
    r::T = 7; x::T = 2; y::T = 11; y1::T = 11; z::T = 1
    while z == 1
        x  = f(x, r, n)
        y1 = f(y, r, n)
        y  = f(y1, r, n)
        z  = gcd(n, abs(x - y))
    end
    z >= n ? "error" : z
end

end # module

【问题讨论】:

  • 您可以在不同的线程上调用x = f(x, r, n)y1 = f(y, r, n)。另外,rx 不是 T 类型是否有原因?
  • 谢谢。我在定义 f 时声明了 r 和 x 的类型,并且在我重复输入局部变量时收到了警告。至少,这是我对警告所报告内容的理解。
  • 看起来都输入得很好。分析时的问题完全是由于gcd 函数:它占用了我大约 85% 的时间。也许 Julia 在 Base 中的 gcd 需要一些工作。你知道其他语言使用什么算法吗?这是 Julia 的:github.com/JuliaLang/julia/blob/master/base/intfuncs.jl#L3
  • 是的,我用其他语言编写了我使用的那些。它们是此处使用的命令式代码的尾递归版本。 Julia 的“factor()”在一秒钟内运行 n 的值。我上面的代码在大约 200 秒内运行它。有些东西很不对劲,但不知道是什么。感谢您反馈代码输入正确。
  • 你可能想在 Julia 的 Github 上打开一个问题。 github.com/JuliaLang/julia/issues

标签: performance julia number-theory


【解决方案1】:

这里有很多类型不稳定的问题。

  1. 不返回字符串"error" 或结果;而是显式调用error()

  2. 正如 Chris 所说,xr 应该被注释为 T 类型,否则它们将不稳定。

溢出似乎也存在潜在问题。一种解决方案是在截断回类型 T 之前扩大平方步骤。

function pollard_rho{T<:Integer}(n::T)
    f(x::T, r::T, n) = rem(Base.widemul(x, x) + r, n) % T
    r::T = 7; x::T = 2; y::T = 11; y1::T = 11; z::T = 1
    while z == 1
        x  = f(x, r, n)
        y1 = f(y, r, n)
        y  = f(y1, r, n)
        z  = gcd(n, abs(x - y))
    end
    z >= n ? error() : z
end

进行这些更改后,该函数将以您预期的速度运行。

julia> @btime pollard_rho(1524157897241274137)
  4.128 ms (0 allocations: 0 bytes)
1234567891

要查找这些类型不稳定的问题,请使用@code_warntype 宏。

【讨论】:

  • 我认为返回的不稳定性在这里无关紧要,因为没有使用返回?
  • 是的,如果这是供最终用户直接从 REPL 使用,这并不重要。但如果其他更高级别的函数调用pollard_rho,这将很重要。
  • 当我将您的代码 Base.widemul(x,x) 更改回 x*x 时,计算时间增加到 42 秒。分别查看@code_native 这两个,它们都简化为imulq 操作。为什么widemul 的影响仍然如此之大?
  • 如果没有widemul,你会溢出Int64,并且算法错误地迭代了179216149次。正确的计算只迭代 16752 次。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-03-16
  • 2014-01-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-01-28
相关资源
最近更新 更多