简体   繁体   English

鉴于这段代码,为什么python版本比C语言慢100倍?

[英]Given this same piece of code, why is the python version 100x+ slower than C?

I'm doing project euler Q14. 我正在做项目euler Q14。

Which starting number, under one million, produces the longest collatz chain? 哪个起始编号低于一百万,产生最长的collat​​z链?

I was very surprised to see someone who could get a result in 0.7sec. 看到有人能在0.7秒内获得成绩,我感到非常惊讶。 More surprised when I see it is merely a naive brute force solution. 当我看到它只是一个天真的蛮力解决方案时更加惊讶。

I was skeptical as I spent so much time to optimize my python version, getting the runtime down to about a minute. 我怀疑是因为我花了很多时间来优化我的python版本,将运行时间降低到大约一分钟。 So I ran the code myself... OP wasn't lying. 所以我自己运行代码...... OP没有说谎。

I translated the same code to Python, it failed to terminate after 5 minutes. 我将相同的代码翻译成Python,5分钟后无法终止。

What gives? 是什么赋予了?

C version : http://codepad.org/VD9QJDkt 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 version: http://codepad.org/ZloPyEcz 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)

In short - it's not slower, it's just stuck. 简而言之 - 它并不慢,它只是卡住了。

The while loop in your python version is an infinite loop - your indentation doesn't include changing j so you'll never exit it. 你的python版本中的while循环是一个无限循环 - 你的缩进不包括改变j所以你永远不会退出它。 The fact that your program didn't just "take longer" but actually got completely stuck should have been a hint (don't feel bad though, I once waited 3 days before getting convinced about a similar scenario). 你的程序不仅“需要更长时间”而且实际上完全卡住的事实应该是一个提示(虽然我不会感觉很糟糕,我曾经等过3天才能确信类似的情况)。

That's one thing, fixing that would make the program stop, but with incorrect results - that's because the outer for loop is also missing the indentation - you want to run the check for each number in the range. 这是一回事,修复会使程序停止,但结果不正确 - 这是因为外部for循环也缺少缩进 - 你想对范围内的每个数字运行检查。

Fixed code: 固定代码:

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)

Gives - 给 -

(837799, 525)
34.4885718822

while the c version gives - 而c版本给出 -

longest: 837799 (525)
It took 0.600000 seconds

There, now everything makes sense again, python is indeed slower and we can get to the real question :) 在那里,现在一切都有意义,python确实更慢,我们可以得到真正的问题:)

On a side note though - this is far from being optimized, as you may repeat numbers you already visited. 但是请注意 - 这远非优化,因为您可能会重复已经访问过的数字。 A better algorithm here would have been storing the results for these numbers in some handy lookup table. 这里一个更好的算法就是将这些数字的结果存储在一些方便的查找表中。


Now, regarding the fundamental question here (which is relevant even after fixing the code as you can see) - execution time across languages is a tricky domain, even if you truly perform the same algorithm in your code, the actual implementation is affected by compiler or interpreter behavior - Python is interpreted and therefore your code has to perform through another layer of code that manages your program, while C just runs natively. 现在,关于这里的基本问题(即使在你修改代码之后也是相关的) - 跨语言的执行时间是一个棘手的领域,即使你在代码中真正执行相同的算法,实际的实现也会受到编译器的影响或解释器行为 - Python被解释,因此您的代码必须通过管理程序的另一层代码执行,而C只是本机运行。 This opens up potential language features (and maybe some optimizations), and it would depend on benchmarking each application to see how well it works, but it's probably safe to say that on most workloads you'll observe that python (or other interpreted languages) behaves 10-100x slower due to this overhead. 这开启了潜在的语言功能(可能还有一些优化),它将依赖于对每个应用程序进行基准测试以了解它的工作情况,但可以肯定地说,在大多数工作负载上,您将观察到python(或其他解释语言)由于这种开销,行为慢了10-100倍。

Furthermore - having compiled the c code in advance allows your compiler to produce a far better optimized code. 此外 - 预先编译c代码允许编译器生成更好的优化代码。 It's possible to use JITting on python to mitigate that (and reduce the interpreter overhead a bit), but it's not available on all python implementations (at least not "pure" ones) 可以在python上使用JITting来缓解这种情况(并稍微减少解释器开销),但它并不适用于所有python实现(至少不是“纯”实现)

See also the discussion at - Why are Python Programs often slower than the Equivalent Program Written in C or C++? 另请参阅讨论 - 为什么Python程序通常比用C或C ++编写的等效程序慢?

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM