Julia的BigInts似乎很慢

时间:2016-05-12 17:20:42

标签: python performance julia bigint

我对Julia印象非常深刻,因为它在处理器密集的Euler Project问题上跑得比D快。 #303如果有人有兴趣。

奇怪的是,朱莉娅的BigInts似乎有多缓慢。奇怪,因为我看了他们的表现相当不错。

以下是使用Euler递推公式计算15k分区数的Julia程序。

function eu78()
  lim = 15000
  ps = zeros(BigInt, lim)

  function p(n)  #applies Euler recurrence formula
    if n < 0
      return BigInt(0)
    elseif n == 0
      return BigInt(1)
    elseif ps[n] > 0
      return ps[n]
    end
    s = BigInt(0)
    f = BigInt(-1)
    for k = 1 : n
      f *= -1
      t1 = (k * (3k - 1)) ÷ BigInt(2)
      t2 = (k * (3k + 1)) ÷ 2
      s += f * (p(n - t1) + p(n - t2))
    end
    ps[n] = s
  end

  for i = 1 : lim
    p(i)
  end
  println(ps[lim])
end

eu78()

以惊人的3分43秒跑步,以产生132位数的答案。

使用pypy运行的等效Python代码只需要8秒钟。

我做错了什么?

3 个答案:

答案 0 :(得分:7)

目前,Julia的BigInts相当缓慢。这是因为每个操作分配一个新的BigInt,而不是Python,其中所有整数都是BigInts,因此他们花了相当多的时间来确保基本操作很快。 Python实际上使用混合方法,其中小整数值以内联方式表示,并且仅当值变得太大时才表示为BigInts。在Julia中绝对可以做到这一点,但是还没有人实现这一点 - 部分原因是标准Int值是机器整数,因此BigInts的性能并不重要。 BigInt性能的真正提升将来自于将Julia的BigInt类型与GC集成,并允许小的BigInt值被分配堆栈 - 并且存在于寄存器中。但是,我们还没到那里。

答案 1 :(得分:3)

以下版本在我的机器上使用Julia 0.4:

在12秒内运行
const lim = 6*10^4
const ps = zeros(Int64, lim)
ps[1] = 1

function p(n)  #applies Euler recurrence formula for the number of partitions
    n < 0 && return 0
    n == 0 && return 1

    ps[n] > 0 && return ps[n]

    s, f = 0, -1

    for k = 1:n
      f *= -1
      t1 = (k * (3k - 1)) ÷ 2
      t2 = (k * (3k + 1)) ÷ 2
      s += f * (p(n - t1) + p(n - t2))
    end

    siz = 10^9
    ps[n] = mod(s, siz)
end

function eu78(lim=6*10^4)

    for i = 10:lim
        a = p(i)
        if mod(a, 1000000) == 0
            return (i, a)
        end
    end
end

@time eu78(10)  # warm-up

@time eu78(6*10^4)

答案 2 :(得分:0)

感谢Stefan的快速反应。

我觉得自从朱莉娅版本没有bigint仍然比pypy慢几个数量级时还有其他事情发生。

所以这不是一个答案,而是一个不同的问题。

欧拉项目的问题是找到第一个数字,其数量总计为百万分之一。所以我们需要超过6位有效数字,因此可以使用机器整数来完成。

以下是Julia中的这个版本,它在2分44秒内完成。

function eu78()
  const lim = 6 * 10 ^ 4
  ps = zeros(Int64, lim)
  ps[1] = 1
  const siz = 10 ^ 9

  function p(n)  #applies Euler recurrence formula for the number of partitions
    if n < 0
      return 0
    elseif n == 0
      return 1
    elseif ps[n] > 0
      return ps[n]
    end
    s, f = 0, -1
    for k = 1 : n
      f *= -1
      t1 = (k * (3k - 1)) ÷ 2
      t2 = (k * (3k + 1)) ÷ 2
      s += f * (p(n - t1) + p(n - t2))
    end
    ps[n] = mod(s, siz)
  end

 for i = 10 : lim
   a = p(i)
   if mod(a, 1000000) == 0
     println(i,'\n', a)
     break
   end
 end
end

eu78()

(顺便说一下,谢谢你们在Julia中使用%来给出余数而不是更常用的模数。:)这花了我整整一个晚上试图让它返回答案,任何%$#%&amp;!而不是一无所获。)

在pypy中运行的python版本在41秒内完成,四分之一的时间。 (公平地说,在python2中运行,需要13分48秒。)

那么问题是什么?第20行的双递归?我怎样才能加快速度?并不是说读这篇文章的人都会关心,但Project Euler中有一分钟的程序执行规则。