我写了一个小基准来比较Python、Ruby、JavaScript和C的不同解释器/编译器的性能。正如预期的那样,结果表明(优化的)C打败了脚本语言,但是它的系数非常高。
结果是:
sven@jet:~/tmp/js$ time node bla.js # * JavaScript with node *
0
real 0m1.222s
user 0m1.190s
sys 0m0.015s
sven@jet:~/tmp/js$ time ruby foo.rb # * Ruby *
0
real 0m52.428s
user 0m52.395s
sys 0m0.028s
sven@jet:~/tmp/js$ time python blub.py # * Python with CPython *
0
real 1m16.480s
user 1m16.371s
sys 0m0.080s
sven@jet:~/tmp/js$ time pypy blub.py # * Python with PyPy *
0
real 0m4.707s
user 0m4.579s
sys 0m0.028s
sven@jet:~/tmp/js$ time ./cpp_non_optimized 1000 1000000 # * C++ with -O0 (gcc) *
0
real 0m1.702s
user 0m1.699s
sys 0m0.002s
sven@jet:~/tmp/js$ time ./cpp_optimized 1000 1000000 # * C++ with -O3 (gcc) *
0
real 0m0.003s # (!!!) <---------------------------------- WHY?
user 0m0.002s
sys 0m0.002s
我想知道是否有人能解释为什么优化的C代码比其他代码快三个数量级以上。
C基准测试使用命令行参数以防止在编译时预计算结果。
下面,我放置了不同语言基准测试的源代码,它们应该在语义上是等价的。此外,我提供了优化的C编译器输出的汇编代码(使用gcc)。当查看优化的汇编时,编译器似乎将基准测试中的两个循环合并为一个,但是,仍然有一个循环!
JavaScript:
var s = 0;
var outer = 1000;
var inner = 1000000;
for (var i = 0; i < outer; ++i) {
for (var j = 0; j < inner; ++j) {
++s;
}
s -= inner;
}
console.log(s);
蟒蛇:
s = 0
outer = 1000
inner = 1000000
for _ in xrange(outer):
for _ in xrange(inner):
s += 1
s -= inner
print s
红宝石:
s = 0
outer = 1000
inner = 1000000
outer_end = outer - 1
inner_end = inner - 1
for i in 0..outer_end
for j in 0..inner_end
s = s + 1
end
s = s - inner
end
puts s
C :
#include <iostream>
#include <cstdlib>
#include <cstdint>
int main(int argc, char* argv[]) {
uint32_t s = 0;
uint32_t outer = atoi(argv[1]);
uint32_t inner = atoi(argv[2]);
for (uint32_t i = 0; i < outer; ++i) {
for (uint32_t j = 0; j < inner; ++j)
++s;
s -= inner;
}
std::cout << s << std::endl;
return 0;
}
程序集(使用gcc-S-O3-std=C 0x编译上述C代码时):
.file "bar.cpp"
.section .text.startup,"ax",@progbits
.p2align 4,,15
.globl main
.type main, @function
main:
.LFB1266:
.cfi_startproc
pushq %r12
.cfi_def_cfa_offset 16
.cfi_offset 12, -16
movl $10, %edx
movq %rsi, %r12
pushq %rbp
.cfi_def_cfa_offset 24
.cfi_offset 6, -24
pushq %rbx
.cfi_def_cfa_offset 32
.cfi_offset 3, -32
movq 8(%rsi), %rdi
xorl %esi, %esi
call strtol
movq 16(%r12), %rdi
movq %rax, %rbp
xorl %esi, %esi
movl $10, %edx
call strtol
testl %ebp, %ebp
je .L6
movl %ebp, %ebx
xorl %eax, %eax
xorl %edx, %edx
.p2align 4,,10
.p2align 3
.L3: # <--- Here is the loop
addl $1, %eax # <---
cmpl %eax, %ebx # <---
ja .L3 # <---
.L2:
movl %edx, %esi
movl $_ZSt4cout, %edi
call _ZNSo9_M_insertImEERSoT_
movq %rax, %rdi
call _ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_
popq %rbx
.cfi_remember_state
.cfi_def_cfa_offset 24
popq %rbp
.cfi_def_cfa_offset 16
xorl %eax, %eax
popq %r12
.cfi_def_cfa_offset 8
ret
.L6:
.cfi_restore_state
xorl %edx, %edx
jmp .L2
.cfi_endproc
.LFE1266:
.size main, .-main
.p2align 4,,15
.type _GLOBAL__sub_I_main, @function
_GLOBAL__sub_I_main:
.LFB1420:
.cfi_startproc
subq $8, %rsp
.cfi_def_cfa_offset 16
movl $_ZStL8__ioinit, %edi
call _ZNSt8ios_base4InitC1Ev
movl $__dso_handle, %edx
movl $_ZStL8__ioinit, %esi
movl $_ZNSt8ios_base4InitD1Ev, %edi
addq $8, %rsp
.cfi_def_cfa_offset 8
jmp __cxa_atexit
.cfi_endproc
.LFE1420:
.size _GLOBAL__sub_I_main, .-_GLOBAL__sub_I_main
.section .init_array,"aw"
.align 8
.quad _GLOBAL__sub_I_main
.local _ZStL8__ioinit
.comm _ZStL8__ioinit,1,1
.hidden __dso_handle
.ident "GCC: (Ubuntu 4.8.2-19ubuntu1) 4.8.2"
.section .note.GNU-stack,"",@progbits
有没有办法在优化JIT编译的代码后缓存它,或者它是否必须在每次运行程序时重新优化代码?
如果我用Python编写,我会尝试减小代码的大小,以获得代码正在做什么的“开销”视图。就像尝试写这个(更容易阅读IMO):
for i in range(outer):
innerS = sum(1 for _ in xrange(inner))
s += innerS
s -= innerS
或者甚至s=sum(内部-内部用于xrange(外部)中的_)
我没有对程序集做完整的分析,但是看起来它做了内部循环的循环展开,并且计算出它和inner的减法一起是一个nop。
程序集似乎只执行外部循环,该循环仅递增计数器,直到到达外部。它甚至可以优化它,但似乎没有这样做。
优化器已经计算出内部循环和随后的一行是无效的,并消除了它。不幸的是,它也没能消除外环。
请注意,node.js示例比未优化的 C 示例更快,这表明 V8(节点的 JIT 编译器)已成功消除至少一个循环。但是,它的优化有一些开销,因为(像任何JIT编译器一样)它必须平衡优化和配置文件引导的重新优化的机会与这样做的成本。
现在我正在学习C++我做了我的第一个小程序加法计算器,我写两个数字,它把它们相加。你能告诉我为什么我的输出是0吗? 输出:
我需要帮助我的错误就像 代码:
在这个打印从1到10000000的所有数字、Haskell版本和C版本的简单程序中,为什么Haskell版本如此缓慢,以及哪些命令有助于学习如何提高Haskell程序的性能? 下面是一份报告,包含重现我激动人心的事件所需的所有细节,制作报告时会打印出来源,包括Makefile的来源:
我正在学习使用stl向量,这是奇怪的,这个程序不能工作。这有什么问题?如果我想用Vector实现同样的功能,应该怎么做呢?
我试图制作一个基本的C程序来读取一个文件,但由于某种原因,当我使用运行它时<代码>/Test1 Test1。txt我得到“错误:'s'可能在未初始化的情况下用于此函数”。
问题内容: 为了在工作中进行演示,我想比较NodeJS和C的性能。这是我写的: Node.js(for.js): 我使用GCC编译for.c并运行它: 结果: 然后我在NodeJS中尝试了它: 结果: 在运行了无数次之后,我发现无论如何它都是成立的。如果我将for.c切换double为long在循环中使用a而不是a ,则C花费的时间甚至更长! 不是试图发动火焰战争,但是为什么执行相同操作的Node