为什么一个包含100亿次迭代的大型for循环在Python中运行的时间要比在C中运行的时间长得多?
我目前正在比较Python3和C中的两个循环计算。对于Python,我有:为什么一个包含100亿次迭代的大型for循环在Python中运行的时间要比在C中运行的时间长得多?,python,c,python-3.x,Python,C,Python 3.x,我目前正在比较Python3和C中的两个循环计算。对于Python,我有: # Python3 t1 = time.process_time() a = 100234555 b = 22333335 c = 341500 for i in range(1, 10000000001): a = a - (b % 2) b = b - (c % 2) print("Sum is", a+b) t2 = time.process_time() print(t2-t1, "Seconds
# Python3
t1 = time.process_time()
a = 100234555
b = 22333335
c = 341500
for i in range(1, 10000000001):
a = a - (b % 2)
b = b - (c % 2)
print("Sum is", a+b)
t2 = time.process_time()
print(t2-t1, "Seconds")
然后在C中,我做同样的事情:
#include <stdio.h>
int main() {
long long a = 100234555;
long long b = 22333335;
long long c = 341500;
for(long long i = 1; i <= 10000000000; i++){
a = a - (b % 2);
b = b - (c % 2);
}
printf("Sum is %lld\n", a+b);
return 0;
}
#包括
int main(){
长a=100234555;
长b=22333335;
长c=341500;
对于(long long i=1;i答案非常简单。Python是解释语言。所有指令都由解释程序(执行脚本的特殊程序)执行。它比编译为本机机器代码的C代码慢得多。部分原因是Python字节代码由程序而不是CPU直接执行,但大部分开销是由对象模型而不是由整数的不变性导致的内存分配和释放造成的翻译能力
现在的情况是,您的C代码可以更改数字的值,但在Python中,数字是不可变的,这意味着它们不会更改。这意味着,当您进行求和时,Python必须为每个新值创建一个新的int
对象,然后在不再使用后销毁旧的int
对象。这使得计算速度大大降低而不仅仅是修改单个内存值
还有一种可能性是你的C编译器很聪明,原因是通过一系列优化,它可以完全删除你的for
循环,结果将是相同的——就像循环实际运行一样。我希望代码运行速度比你的示例中的情况快得多,但它可以做到那个
Python没有这样的智能编译器。它不能做这样伟大和聪明的事情;它只是没有设计来优化代码,因为在动态类型语言中很难做到如此可靠(尽管Python是强类型的,这在某种程度上是可能的。正如dmuir所注意到的,如果编译器正确地传播一些常量,代码可以大大简化。例如:clang-O1
将C代码编译成以下形式(cf):
gcc-O1
产生基本相似的代码
由于这归结为对printf
的一次调用,解释似乎是:
- Python编译器在优化代码方面不如C编译器聪明
<> LI>编译C 12行代码需要很长时间。3秒的硬件设置太长了!在我的DIKY笔记本上只需要0.15秒就可以编译和运行所有优化的代码。你编译为C++ +?
在禁用优化的情况下测试C版本(-O0
)会产生以下输出:
$ time (clang -O0 -o loop10g loop10g.c && ./loop10g)
Sum is -9877432110
real 4m15.352s
user 3m47.232s
sys 0m3.252s
使用未优化的C语言仍然比Python快得多:255秒vs:>3500秒
Python代码是用字节码和带有动态类型值的堆栈来解释的:10到20是一个典型的减速系数。此外,对于大值,整数算法会自动切换到bignum模式,这里可能就是这种情况,尽管代价应该更高。这不是答案。这是它的一部分,但是st的开销是由整数的不变性引起的内存分配和释放引起的,这是由对象模型引起的,而不是解释性引起的。@WIZWIZZ4是答案在Python上运行相同的简单计算指令比在C上慢,但没有达到观察到的程度。系数1000只能解释为by编译器优化了大部分迭代。OP没有说明使用了什么优化,但在我看来,聪明的优化者可以完全删除循环——因为c%2是0,b永远不会改变,因为b%2是1,循环的效果是从a中减去循环计数。没有任何东西可以阻止解释器进行静态分析和使用它有意思的是,如果用bitwise&1替换%怎么办?同样慢吗?因为C是用本机代码编译的,这些本机代码针对您当前的体系结构进行了大量优化,并直接在硬件上运行,而Python是一种解释语言,它不做任何事情。@skyboyer注意用替换%2
e> &1
在C中创建了一个不同的功能。%2
导致1,0,-1
&1
导致1,0
@chux,谢谢,我不知道。当它返回-1时,你能给我一个例子吗?只是好奇,起初无法找到try@skyboyerprintf(“%d\n”,-1%2)
请参见。这意味着。这只是实现细节。这是真的吗?使用对象类型而不是纯整数值,并且没有机制在对象无用时优化对象??为什么?@R..Python不是为速度而设计的。如果你想在Python中使用可变整数,你可以将其作为扩展添加,但这会很混乱对于大多数程序员来说,由于Python的默认引用模型,如果它在默认情况下是这样工作的,那么这对大多数程序员来说都是一个挑战。这不是“想要可变整数”的问题.没有人希望这样。这是一个想要没有对象或对象生存期关联的纯值的问题,因为这是一个巨大的低效率层,没有语义差异。@R..我希望看到这样做。如果你在GitHub上使用Python并尝试实现它,我会帮助你。这是作弊吗?;)@Stargateur:不是真的,这个测试很愚蠢。相反,试着用经典的递归定义计算fib(50)
。识别Fibonacci函数并将递归转换为迭代代码将是欺骗IMHO。弄清楚程序员试图做什么并更快地完成它不是“欺骗”。这是优化。即使它用于哑斐波那契代码。顺便说一句,CPython始终使用bignum表示,好吧。bignum支腿是30位(在ALU至少为64位的机器上),对单支腿数字的操作也进行了一定程度的优化。此外,还有一个小整数缓存,以避免过多的整数
$ time (clang -O0 -o loop10g loop10g.c && ./loop10g)
Sum is -9877432110
real 4m15.352s
user 3m47.232s
sys 0m3.252s