【问题标题】:Given this same piece of code, why is the python version 100x+ slower than C?给定同样的代码,为什么 python 版本比 C 慢 100x+?
【发布时间】:2013-12-01 15:55:49
【问题描述】:

我正在做项目 euler Q14。

100 万以下的哪个起始数字产生最长的 collat​​z 链?

看到有人能在 0.7 秒内得到结果,我感到非常惊讶。当我看到它只是一个幼稚的蛮力解决方案时,我更加惊讶。

我很怀疑,因为我花了很多时间来优化我的 python 版本,将运行时间缩短到大约一分钟。所以我自己运行代码...... OP 没有撒谎。

我将相同的代码翻译成 Python,5 分钟后无法终止。

什么给了?

C 版:http://codepad.org/VD9QJDkt

#include <stdio.h>
#include <time.h>

int main(int argc, char **argv)
{
  int longest = 0;
  int terms = 0;
  int i;
  unsigned long j;
  clock_t begin, end;
  double time_spent;

  begin = clock();

  for (i = 1; i <= 1000000; i++)
  {
    j = i;
    int this_terms = 1;

    while (j != 1)
    {
      this_terms++;

      if (this_terms > terms)
      {
        terms = this_terms;
        longest = i;
      }

      if (j % 2 == 0)
      {
        j = j / 2;
      }
      else
      {
        j = 3 * j + 1;
      }
    }
  }

  printf("longest: %d (%d)\n", longest, terms);

  end = clock();
  time_spent = (double)(end - begin) / CLOCKS_PER_SEC;
  printf("It took %f seconds\n", time_spent);
  return 0;
}

Python 版本:http://codepad.org/ZloPyEcz

import time

def iterative_brute_force(n):
    longest = 0
    terms = 0
    for i in range(1, n + 1):
        j = i
        this_terms = 1

    while j != 1:
        this_terms += 1
        if this_terms > terms:
            terms = this_terms
            longest = i

    if j % 2 == 0:
        j = j / 2
    else:
        j = 3 * j + 1

    return longest, terms

t0 = time.time()
print(iterative_brute_force(10 ** 6))
t1 = time.time()
total = t1-t0
print(total)

【问题讨论】:

  • 你认为 python 比 c 运行得更快吗?另外,我认为问题出在您的python算法中..
  • 你应该看到this
  • @aIKid 我不认为令人惊讶的是它更慢,但它需要 这么多(至少 300 倍)更多时间。两个程序的算法相同,所以这不是问题。
  • 我不是专家,但您确定您的 while 循环符合您的预期吗?
  • @Leeor 你发现了。现在将其扩展为一个答案,并点赞:-)

标签: python c performance code-translation


【解决方案1】:

简而言之 - 它并不慢,只是卡住了。

python 版本中的 while 循环是一个无限循环 - 您的缩进不包括更改 j,因此您永远不会退出它。您的程序不仅“花费更长的时间”而且实际上完全卡住了这一事实应该是一个暗示(不过不要难过,我曾经等了 3 天才确信类似的情况)。

这是一回事,修复它会使程序停止,但结果不正确 - 这是因为外部 for 循环也缺少缩进 - 你想对范围内的每个数字运行检查。

固定代码:

import time

def iterative_brute_force(n):
    longest = 0
    terms = 0
    for i in range(1, n + 1):
        j = i
        this_terms = 1

        while j != 1:
            this_terms += 1
            if this_terms > terms:
                terms = this_terms
                longest = i

            if j % 2 == 0:
                j = j / 2
            else:
                j = 3 * j + 1

    return longest, terms

t0 = time.time()
print(iterative_brute_force(10 ** 6))
t1 = time.time()
total = t1-t0
print(total)

给 -

(837799, 525)
34.4885718822

而 c 版本给出 -

longest: 837799 (525)
It took 0.600000 seconds

好了,现在一切都说得通了,python 确实慢了,我们可以找到真正的问题了 :)

不过,附带说明一下 - 这远未优化,因为您可能会重复您已经访问过的数字。这里更好的算法是将这些数字的结果存储在一些方便的查找表中。


现在,关于这里的基本问题(即使在修复代码之后也相关) - 跨语言的执行时间是一个棘手的领域,即使您真的在代码中执行相同的算法,实际的实现是受编译器或解释器行为的影响 - Python 被解释,因此您的代码必须通过管理您的程序的另一层代码执行,而 C 只是本机运行。这开启了潜在的语言特性(可能还有一些优化),它取决于对每个应用程序进行基准测试以查看它的工作情况,但可以肯定地说,在大多数工作负载上,您会观察到 Python(或其他解释语言)由于这种开销,行为速度要慢 10-100 倍。

此外 - 提前编译 c 代码可以让您的编译器生成更好的优化代码。可以在 python 上使用 JITting 来缓解这种情况(并稍微减少解释器开销),但在所有 python 实现(至少不是“纯”实现)上都是 not available

另见讨论 - Why are Python Programs often slower than the Equivalent Program Written in C or C++?

【讨论】:

    猜你喜欢
    • 2020-08-29
    • 1970-01-01
    • 2021-05-30
    • 2014-08-14
    • 1970-01-01
    • 2011-03-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多