C ++与Java? 为什么ICC生成比VC更慢的代码?
以下是C ++中的简单循环。 计时器使用QueryPerformanceCounter()并且非常准确。 我发现Java需要60%的时间C ++采取,这不可能?! 我在这里做错了什么? 即使严格的别名(这不包括在代码中)根本没有帮助…
long long var = 0; std::array<int, 1024> arr; int* arrPtr = arr.data(); CHighPrecisionTimer timer; for(int i = 0; i < 1024; i++) arrPtr[i] = i; timer.Start(); for(int i = 0; i < 1024 * 1024 * 10; i++){ for(int x = 0; x < 1024; x++){ var += arrPtr[x]; } } timer.Stop(); printf("Unrestricted: %lld us, Value = %lld\n", (Int64)timer.GetElapsed().GetMicros(), var);
这个C ++运行大约9.5秒。 我正在使用英特尔编译器12.1与主处理器优化(特别是我的),一切最大。 所以这是英特尔编译器最好的! 自动并行化有趣的消耗70%的CPU而不是25%,但没有得到更快的工作;)…
现在我使用下面的Java代码进行比较:
long var = 0; int[] arr = new int[1024]; for(int i = 0; i < 1024; i++) arr[i] = i; for(int i = 0; i < 1024 * 1024; i++){ for(int x = 0; x < 1024; x++){ var += arr[x]; } } long nanos = System.nanoTime(); for(int i = 0; i < 1024 * 1024 * 10; i++){ for(int x = 0; x < 1024; x++){ var += arr[x]; } } nanos = (System.nanoTime() - nanos) / 1000; System.out.print("Value: " + var + ", Time: " + nanos);
Java代码通过积极的优化和服务器虚拟机(不debugging)进行调用。 它在我的机器上运行了大约7秒钟(只使用一个线程)。
这是英特尔编译器的失败,还是我又太愚蠢了?
[编辑]:好吧,现在inheritance人的事情…似乎更像是一个在英特尔编译器^^错误。 [请注意,我正在使用Intel Quadcore Q6600,这是相当古老的。 Intel Compiler可能会在最近的CPU上performance得更好,比如Core i7]
Intel x86 (without vectorization): 3 seconds MSVC x64: 5 seconds Java x86/x64 (Oracle Java 7): 7 seconds Intel x64 (with vectorization): 9.5 seconds Intel x86 (with vectorization): 9.5 seconds Intel x64 (without vectorization): 12 seconds MSVC x86: 15 seconds (uhh)
[编辑]:另一个不错的案例;)。 考虑以下简单的lambdaexpression式
#include <stdio.h> #include <tchar.h> #include <Windows.h> #include <vector> #include <boost/function.hpp> #include <boost/lambda/bind.hpp> #include <boost/typeof/typeof.hpp> template<class TValue> struct ArrayList { private: std::vector<TValue> m_Entries; public: template<class TCallback> void Foreach(TCallback inCallback) { for(int i = 0, size = m_Entries.size(); i < size; i++) { inCallback(i); } } void Add(TValue inValue) { m_Entries.push_back(inValue); } }; int _tmain(int argc, _TCHAR* argv[]) { auto t = [&]() {}; ArrayList<int> arr; int res = 0; for(int i = 0; i < 100; i++) { arr.Add(i); } long long freq, t1, t2; QueryPerformanceFrequency((LARGE_INTEGER*)&freq); QueryPerformanceCounter((LARGE_INTEGER*)&t1); for(int i = 0; i < 1000 * 1000 * 10; i++) { arr.Foreach([&](int v) { res += i; }); } QueryPerformanceCounter((LARGE_INTEGER*)&t2); printf("Time: %lld\n", ((t2-t1) * 1000000) / freq); if(res == 4950) return -1; return 0; }
英特尔编译器再次闪耀:
MSVC x86/x64: 12 milli seconds Intel x86/x64: 1 second
嗯? 好吧,我想慢90倍不是一件坏事…
我不太确定这是否适用: 好吧,根据对这个线程的回答:intel编译器是已知的(我也知道这一点,但我只是没有想到,他们可以放弃对他们的处理器的支持)拥有处理器的性能可怕,不像编译器那样“知道”,比如AMD处理器,甚至可能是像我的英特尔处理器这样的过时的处理器……所以,如果有一个最近使用英特尔处理器的人可以试试这个,那就太好了。
这是Intel编译器的x64输出:
std::array<int, 1024> arr; int* arrPtr = arr.data(); QueryPerformanceFrequency((LARGE_INTEGER*)&freq); 000000013F05101D lea rcx,[freq] 000000013F051022 call qword ptr [__imp_QueryPerformanceFrequency (13F052000h)] for(int i = 0; i < 1024; i++) arrPtr[i] = i; 000000013F051028 mov eax,4 000000013F05102D movd xmm0,eax 000000013F051031 xor eax,eax 000000013F051033 pshufd xmm1,xmm0,0 000000013F051038 movdqa xmm0,xmmword ptr [__xi_z+28h (13F0521A0h)] 000000013F051040 movdqa xmmword ptr arr[rax*4],xmm0 000000013F051046 paddd xmm0,xmm1 000000013F05104A movdqa xmmword ptr [rsp+rax*4+60h],xmm0 000000013F051050 paddd xmm0,xmm1 000000013F051054 movdqa xmmword ptr [rsp+rax*4+70h],xmm0 000000013F05105A paddd xmm0,xmm1 000000013F05105E movdqa xmmword ptr [rsp+rax*4+80h],xmm0 000000013F051067 add rax,10h 000000013F05106B paddd xmm0,xmm1 000000013F05106F cmp rax,400h 000000013F051075 jb wmain+40h (13F051040h) QueryPerformanceCounter((LARGE_INTEGER*)&t1); 000000013F051077 lea rcx,[t1] 000000013F05107C call qword ptr [__imp_QueryPerformanceCounter (13F052008h)] var += arrPtr[x]; 000000013F051082 movdqa xmm1,xmmword ptr [__xi_z+38h (13F0521B0h)] for(int i = 0; i < 1024 * 1024 * 10; i++){ 000000013F05108A xor eax,eax var += arrPtr[x]; 000000013F05108C movdqa xmm0,xmmword ptr [__xi_z+48h (13F0521C0h)] long long var = 0, freq, t1, t2; 000000013F051094 pxor xmm6,xmm6 for(int x = 0; x < 1024; x++){ 000000013F051098 xor r8d,r8d var += arrPtr[x]; 000000013F05109B lea rdx,[arr] 000000013F0510A0 xor ecx,ecx 000000013F0510A2 movq xmm2,mmword ptr arr[rcx] for(int x = 0; x < 1024; x++){ 000000013F0510A8 add r8,8 var += arrPtr[x]; 000000013F0510AC punpckldq xmm2,xmm2 for(int x = 0; x < 1024; x++){ 000000013F0510B0 add rcx,20h var += arrPtr[x]; 000000013F0510B4 movdqa xmm3,xmm2 000000013F0510B8 pand xmm2,xmm0 000000013F0510BC movq xmm4,mmword ptr [rdx+8] 000000013F0510C1 psrad xmm3,1Fh 000000013F0510C6 punpckldq xmm4,xmm4 000000013F0510CA pand xmm3,xmm1 000000013F0510CE por xmm3,xmm2 000000013F0510D2 movdqa xmm5,xmm4 000000013F0510D6 movq xmm2,mmword ptr [rdx+10h] 000000013F0510DB psrad xmm5,1Fh 000000013F0510E0 punpckldq xmm2,xmm2 000000013F0510E4 pand xmm5,xmm1 000000013F0510E8 paddq xmm6,xmm3 000000013F0510EC pand xmm4,xmm0 000000013F0510F0 movdqa xmm3,xmm2 000000013F0510F4 por xmm5,xmm4 000000013F0510F8 psrad xmm3,1Fh 000000013F0510FD movq xmm4,mmword ptr [rdx+18h] 000000013F051102 pand xmm3,xmm1 000000013F051106 punpckldq xmm4,xmm4 000000013F05110A pand xmm2,xmm0 000000013F05110E por xmm3,xmm2 000000013F051112 movdqa xmm2,xmm4 000000013F051116 paddq xmm6,xmm5 000000013F05111A psrad xmm2,1Fh 000000013F05111F pand xmm4,xmm0 000000013F051123 pand xmm2,xmm1 for(int x = 0; x < 1024; x++){ 000000013F051127 add rdx,20h var += arrPtr[x]; 000000013F05112B paddq xmm6,xmm3 000000013F05112F por xmm2,xmm4 for(int x = 0; x < 1024; x++){ 000000013F051133 cmp r8,400h var += arrPtr[x]; 000000013F05113A paddq xmm6,xmm2 for(int x = 0; x < 1024; x++){ 000000013F05113E jb wmain+0A2h (13F0510A2h) for(int i = 0; i < 1024 * 1024 * 10; i++){ 000000013F051144 inc eax 000000013F051146 cmp eax,0A00000h 000000013F05114B jb wmain+98h (13F051098h) } } QueryPerformanceCounter((LARGE_INTEGER*)&t2); 000000013F051151 lea rcx,[t2] 000000013F051156 call qword ptr [__imp_QueryPerformanceCounter (13F052008h)] printf("Unrestricted: %lld ms, Value = %lld\n", ((t2-t1)*1000/freq), var); 000000013F05115C mov r9,qword ptr [t2] long long var = 0, freq, t1, t2; 000000013F051161 movdqa xmm0,xmm6 printf("Unrestricted: %lld ms, Value = %lld\n", ((t2-t1)*1000/freq), var); 000000013F051165 sub r9,qword ptr [t1] 000000013F05116A lea rcx,[string "Unrestricted: %lld ms, Value = %"... (13F0521D0h)] 000000013F051171 imul rax,r9,3E8h 000000013F051178 cqo 000000013F05117A mov r10,qword ptr [freq] 000000013F05117F idiv rax,r10 long long var = 0, freq, t1, t2; 000000013F051182 psrldq xmm0,8 printf("Unrestricted: %lld ms, Value = %lld\n", ((t2-t1)*1000/freq), var); 000000013F051187 mov rdx,rax long long var = 0, freq, t1, t2; 000000013F05118A paddq xmm6,xmm0 000000013F05118E movd r8,xmm6 printf("Unrestricted: %lld ms, Value = %lld\n", ((t2-t1)*1000/freq), var); 000000013F051193 call qword ptr [__imp_printf (13F052108h)]
而这一个是MSVC x64版本的组装:
int _tmain(int argc, _TCHAR* argv[]) { 000000013FF61000 push rbx 000000013FF61002 mov eax,1050h 000000013FF61007 call __chkstk (13FF61950h) 000000013FF6100C sub rsp,rax 000000013FF6100F mov rax,qword ptr [__security_cookie (13FF63000h)] 000000013FF61016 xor rax,rsp 000000013FF61019 mov qword ptr [rsp+1040h],rax long long var = 0, freq, t1, t2; std::array<int, 1024> arr; int* arrPtr = arr.data(); QueryPerformanceFrequency((LARGE_INTEGER*)&freq); 000000013FF61021 lea rcx,[rsp+28h] 000000013FF61026 xor ebx,ebx 000000013FF61028 call qword ptr [__imp_QueryPerformanceFrequency (13FF62000h)] for(int i = 0; i < 1024; i++) arrPtr[i] = i; 000000013FF6102E xor r11d,r11d 000000013FF61031 lea rax,[rsp+40h] 000000013FF61036 mov dword ptr [rax],r11d 000000013FF61039 inc r11d 000000013FF6103C add rax,4 000000013FF61040 cmp r11d,400h 000000013FF61047 jl wmain+36h (13FF61036h) QueryPerformanceCounter((LARGE_INTEGER*)&t1); 000000013FF61049 lea rcx,[rsp+20h] 000000013FF6104E call qword ptr [__imp_QueryPerformanceCounter (13FF62008h)] 000000013FF61054 mov r11d,0A00000h 000000013FF6105A nop word ptr [rax+rax] for(int i = 0; i < 1024 * 1024 * 10; i++){ for(int x = 0; x < 1024; x++){ 000000013FF61060 xor edx,edx 000000013FF61062 xor r8d,r8d 000000013FF61065 lea rcx,[rsp+48h] 000000013FF6106A xor r9d,r9d 000000013FF6106D mov r10d,100h 000000013FF61073 nop word ptr [rax+rax] var += arrPtr[x]; 000000013FF61080 movsxd rax,dword ptr [rcx-8] 000000013FF61084 add rcx,10h 000000013FF61088 add rbx,rax 000000013FF6108B movsxd rax,dword ptr [rcx-14h] 000000013FF6108F add r9,rax 000000013FF61092 movsxd rax,dword ptr [rcx-10h] 000000013FF61096 add r8,rax 000000013FF61099 movsxd rax,dword ptr [rcx-0Ch] 000000013FF6109D add rdx,rax 000000013FF610A0 dec r10 000000013FF610A3 jne wmain+80h (13FF61080h) for(int i = 0; i < 1024 * 1024 * 10; i++){ for(int x = 0; x < 1024; x++){ 000000013FF610A5 lea rax,[rdx+r8] 000000013FF610A9 add rax,r9 000000013FF610AC add rbx,rax 000000013FF610AF dec r11 000000013FF610B2 jne wmain+60h (13FF61060h) } } QueryPerformanceCounter((LARGE_INTEGER*)&t2); 000000013FF610B4 lea rcx,[rsp+30h] 000000013FF610B9 call qword ptr [__imp_QueryPerformanceCounter (13FF62008h)] printf("Unrestricted: %lld ms, Value = %lld\n", ((t2-t1)*1000/freq), var); 000000013FF610BF mov rax,qword ptr [rsp+30h] 000000013FF610C4 lea rcx,[string "Unrestricted: %lld ms, Value = %"... (13FF621B0h)] 000000013FF610CB sub rax,qword ptr [rsp+20h] 000000013FF610D0 mov r8,rbx 000000013FF610D3 imul rax,rax,3E8h 000000013FF610DA cqo 000000013FF610DC idiv rax,qword ptr [rsp+28h] 000000013FF610E1 mov rdx,rax 000000013FF610E4 call qword ptr [__imp_printf (13FF62138h)] return 0; 000000013FF610EA xor eax,eax
英特尔编译器configuration无vector化,64位,最高优化(这是惊人的慢,12秒):
000000013FC0102F lea rcx,[freq] double var = 0; long long freq, t1, t2; 000000013FC01034 xorps xmm6,xmm6 std::array<double, 1024> arr; double* arrPtr = arr.data(); QueryPerformanceFrequency((LARGE_INTEGER*)&freq); 000000013FC01037 call qword ptr [__imp_QueryPerformanceFrequency (13FC02000h)] for(int i = 0; i < 1024; i++) arrPtr[i] = i; 000000013FC0103D mov eax,2 000000013FC01042 mov rdx,100000000h 000000013FC0104C movd xmm0,eax 000000013FC01050 xor eax,eax 000000013FC01052 pshufd xmm1,xmm0,0 000000013FC01057 movd xmm0,rdx 000000013FC0105C nop dword ptr [rax] 000000013FC01060 cvtdq2pd xmm2,xmm0 000000013FC01064 paddd xmm0,xmm1 000000013FC01068 cvtdq2pd xmm3,xmm0 000000013FC0106C paddd xmm0,xmm1 000000013FC01070 cvtdq2pd xmm4,xmm0 000000013FC01074 paddd xmm0,xmm1 000000013FC01078 cvtdq2pd xmm5,xmm0 000000013FC0107C movaps xmmword ptr arr[rax*8],xmm2 000000013FC01081 paddd xmm0,xmm1 000000013FC01085 movaps xmmword ptr [rsp+rax*8+60h],xmm3 000000013FC0108A movaps xmmword ptr [rsp+rax*8+70h],xmm4 000000013FC0108F movaps xmmword ptr [rsp+rax*8+80h],xmm5 000000013FC01097 add rax,8 000000013FC0109B cmp rax,400h 000000013FC010A1 jb wmain+60h (13FC01060h) QueryPerformanceCounter((LARGE_INTEGER*)&t1); 000000013FC010A3 lea rcx,[t1] 000000013FC010A8 call qword ptr [__imp_QueryPerformanceCounter (13FC02008h)] for(int i = 0; i < 1024 * 1024 * 10; i++){ 000000013FC010AE xor eax,eax for(int x = 0; x < 1024; x++){ 000000013FC010B0 xor edx,edx var += arrPtr[x]; 000000013FC010B2 lea ecx,[rdx+rdx] for(int x = 0; x < 1024; x++){ 000000013FC010B5 inc edx for(int x = 0; x < 1024; x++){ 000000013FC010B7 cmp edx,200h var += arrPtr[x]; 000000013FC010BD addsd xmm6,mmword ptr arr[rcx*8] 000000013FC010C3 addsd xmm6,mmword ptr [rsp+rcx*8+58h] for(int x = 0; x < 1024; x++){ 000000013FC010C9 jb wmain+0B2h (13FC010B2h) for(int i = 0; i < 1024 * 1024 * 10; i++){ 000000013FC010CB inc eax 000000013FC010CD cmp eax,0A00000h 000000013FC010D2 jb wmain+0B0h (13FC010B0h) } } QueryPerformanceCounter((LARGE_INTEGER*)&t2); 000000013FC010D4 lea rcx,[t2] 000000013FC010D9 call qword ptr [__imp_QueryPerformanceCounter (13FC02008h)]
英特尔编译器没有vector化,32位和最高优化(现在明显是赢家,运行时间大约3秒,组装看起来好多了):
00B81088 lea eax,[t1] 00B8108C push eax 00B8108D call dword ptr [__imp__QueryPerformanceCounter@4 (0B82004h)] 00B81093 xor eax,eax 00B81095 pxor xmm0,xmm0 00B81099 movaps xmm1,xmm0 for(int x = 0; x < 1024; x++){ 00B8109C xor edx,edx var += arrPtr[x]; 00B8109E addpd xmm0,xmmword ptr arr[edx*8] 00B810A4 addpd xmm1,xmmword ptr [esp+edx*8+40h] 00B810AA addpd xmm0,xmmword ptr [esp+edx*8+50h] 00B810B0 addpd xmm1,xmmword ptr [esp+edx*8+60h] for(int x = 0; x < 1024; x++){ 00B810B6 add edx,8 00B810B9 cmp edx,400h 00B810BF jb wmain+9Eh (0B8109Eh) for(int i = 0; i < 1024 * 1024 * 10; i++){ 00B810C1 inc eax 00B810C2 cmp eax,0A00000h 00B810C7 jb wmain+9Ch (0B8109Ch) double var = 0; long long freq, t1, t2; 00B810C9 addpd xmm0,xmm1 } } QueryPerformanceCounter((LARGE_INTEGER*)&t2); 00B810CD lea eax,[t2] 00B810D1 push eax 00B810D2 movaps xmmword ptr [esp+4],xmm0 00B810D7 call dword ptr [__imp__QueryPerformanceCounter@4 (0B82004h)] 00B810DD movaps xmm0,xmmword ptr [esp]
tl; dr:你在这里看到的似乎是ICC在向量化循环时失败的尝试 。
我们从MSVC x64开始:
这是关键的循环:
$LL3@main: movsxd rax, DWORD PTR [rdx-4] movsxd rcx, DWORD PTR [rdx-8] add rdx, 16 add r10, rax movsxd rax, DWORD PTR [rdx-16] add rbx, rcx add r9, rax movsxd rax, DWORD PTR [rdx-12] add r8, rax dec r11 jne SHORT $LL3@main
你在这里看到的是由编译器展开的标准循环。 MSVC展开为4次迭代,并将var
variables分为四个寄存器: r10
, rbx
, r9
和r8
。 然后在循环结束时,将这4个寄存器汇总在一起。
这里是四个重新组合的地方:
lea rax, QWORD PTR [r8+r9] add rax, r10 add rbx, rax dec rdi jne SHORT $LL6@main
请注意,MSVC目前不执行自动vector化。
现在让我们来看看你的ICC输出的一部分:
000000013F0510A2 movq xmm2,mmword ptr arr[rcx] 000000013F0510A8 add r8,8 000000013F0510AC punpckldq xmm2,xmm2 000000013F0510B0 add rcx,20h 000000013F0510B4 movdqa xmm3,xmm2 000000013F0510B8 pand xmm2,xmm0 000000013F0510BC movq xmm4,mmword ptr [rdx+8] 000000013F0510C1 psrad xmm3,1Fh 000000013F0510C6 punpckldq xmm4,xmm4 000000013F0510CA pand xmm3,xmm1 000000013F0510CE por xmm3,xmm2 000000013F0510D2 movdqa xmm5,xmm4 000000013F0510D6 movq xmm2,mmword ptr [rdx+10h] 000000013F0510DB psrad xmm5,1Fh 000000013F0510E0 punpckldq xmm2,xmm2 000000013F0510E4 pand xmm5,xmm1 000000013F0510E8 paddq xmm6,xmm3 ...
你在这里看到的是ICC试图向这个循环进行vector化。 这与MSVC所做的(分成多个和)类似,但是使用SSE寄存器而不是每个寄存器两个和。
但事实certificate,vector化的开销恰好超过了vector化的好处。
如果我们一步一步地走这些指令,我们可以看到ICC如何对其进行vector化:
// Load two ints using a 64-bit load. {x, y, 0, 0} movq xmm2,mmword ptr arr[rcx] // Shuffle the data into this form. punpckldq xmm2,xmm2 xmm2 = {x, x, y, y} movdqa xmm3,xmm2 xmm3 = {x, x, y, y} // Mask out index 1 and 3. pand xmm2,xmm0 xmm2 = {x, 0, y, 0} // Arithmetic right-shift to copy sign-bit across the word. psrad xmm3,1Fh xmm3 = {sign(x), sign(x), sign(y), sign(y)} // Mask out index 0 and 2. pand xmm3,xmm1 xmm3 = {0, sign(x), 0, sign(y)} // Combine to get sign-extended values. por xmm3,xmm2 xmm3 = {x, sign(x), y, sign(y)} xmm3 = {x, y} // Add to accumulator... paddq xmm6,xmm3
所以它正在做一些非常混乱的解包只是vector化。 混乱来自于需要使用SSE指令将32位整数签名扩展到64位。
SSE4.1实际上为此提供了PMOVSXDQ
指令。 但是目标机器不支持SSE4.1,或者ICC在这种情况下不够智能。
但重要的是:
英特尔编译器试图向量化循环。 但是,添加的开销似乎大大超过了vector化的好处。 因此,为什么它更慢。
编辑:更新OP的结果:
- ICC x64没有vector化
- ICC x86与vector化
你把数据types改成了double
。 所以现在是浮点。 不再有那些困扰整数版本的丑陋的符号填充转换。
但是,由于您禁用了x64版本的vector化,显然它变慢了。
ICC x86vector化:
00B8109E addpd xmm0,xmmword ptr arr[edx*8] 00B810A4 addpd xmm1,xmmword ptr [esp+edx*8+40h] 00B810AA addpd xmm0,xmmword ptr [esp+edx*8+50h] 00B810B0 addpd xmm1,xmmword ptr [esp+edx*8+60h] 00B810B6 add edx,8 00B810B9 cmp edx,400h 00B810BF jb wmain+9Eh (0B8109Eh)
这里不多 – 标准向量化+ 4倍循环展开。
没有vector化的ICC x64:
000000013FC010B2 lea ecx,[rdx+rdx] 000000013FC010B5 inc edx 000000013FC010B7 cmp edx,200h 000000013FC010BD addsd xmm6,mmword ptr arr[rcx*8] 000000013FC010C3 addsd xmm6,mmword ptr [rsp+rcx*8+58h] 000000013FC010C9 jb wmain+0B2h (13FC010B2h)
没有vector化+只有2个循环展开。
在所有情况下,禁用vector化将会损害浮点运算的性能。
这个例子很简单,不同的语言不应该有所作为,愚蠢到不能certificate什么。 循环可以被编译器优化成一个简单的赋值,或者运行迭代的整个次数,或者一些迭代可能被展开…我不知道你为什么决定写这个testing程序,但是它没有testing任何关于语言的东西,因为一旦逻辑优化被执行,它就归结为完全相同的程序集。
此外,关于英特尔编译器的性能,它将在很大程度上取决于确切的硬件和编译器版本。 编译器生成不同版本的代码,并且倾向于为AMD处理器生成可怕的代码。 即使对于英特尔,如果它不能识别具体的处理器,它会回落到一个安全的低速模式。
当你消除了不可能的东西时,无论如何,不pipe不可能,都必须是真相。
你一方面掌握了一些数据,另一方面假设(C ++总是比Java快)。 当数据告诉你时,为什么要求人们certificate你的假设呢?
如果你想从JVM获得程序集来比较运行的东西,那么命令行选项是'-XX:+ PrintOptoAssembly',但是你需要下载一个debug jvm来完成。 看看大会至less会告诉你为什么一个比另一个快。
为了logging,我在我的盒子(x86_64 linux)上运行了两个代码,C ++用std::array
,一个普通的int[1024]
并且为了完整性也用long
来代替int
。 Java(open-jdk 1.6)的主频为3.8s,C ++(int)为3.37s,C ++(long)为3.9s。 我的编译器是g ++ 4.5.1。 也许只是英特尔的编译器不如想象的那么好。
我认为Java编译器实现了JITC(即时编译,或者一些更新的技术)来处理本机编译器的速度,并且可以推断出你的数组不会改变,因此可以对内部循环应用常量折叠。
我怀疑罪魁祸首是简单的循环展开。 更换
var += arrPtr[x];
同
var += arrPtr[x++]; var += arrPtr[x++]; var += arrPtr[x++]; var += arrPtr[x];
并观察C ++版本的运行速度。
我看到你正在运行以下循环
for(int i = 0; i < 1024 * 1024; i++){ for(int x = 0; x < 1024; x++){ var += arr[x]; } }
在Java代码中两次; 而一旦在c + +代码; 这可能会导致caching热身,从而使Java代码最终执行速度比C ++更快。