我想测量PC可以递增计数器N
倍的速度(例如N = 10^9
)。
我尝试了以下代码:
using namespace std
auto start = chrono::steady_clock::now();
for (int i = 0; i < N; ++i)
{
}
auto end = chrono::steady_clock::now();
但是,编译器足够聪明,只需设置i = N即可,无论N的值如何,我都能得到start==end
。
如何更改代码以测量增量速度? (在循环中添加昂贵的操作会控制运行时,并且无法确保测量结果正确。)
我使用Windows 10和Visual Studio 15.9.7。
有点动机:我的代码在N = 10 ^ 9的情况下大约需要2秒。我想知道是否还有剩余的“肉”可用于进一步优化(例如,它可能会下降到1秒?还是循环本身需要更多时间?)
答案 0 :(得分:4)
这个问题在C或C ++中实际上没有任何意义。编译器旨在生成满足您的源代码定义的约束的最快代码。在您的问题中,您没有定义编译器必须执行循环的约束。由于循环无效,因此优化程序将其删除。
Gabriel Staple's answer可能是您最明智地回答问题的方法,但它也不是正确的,因为它定义了太多限制,限制了编译器实现最佳代码的自由。易失性经常迫使编译器在每次修改变量时将结果写回内存。
例如此代码:
void foo(int N) {
for (volatile int i = 0; i < N; ++i)
{
}
}
成为此程序集(在我尝试过的x64编译器上):
mov DWORD PTR [rsp-4], 0
mov eax, DWORD PTR [rsp-4]
cmp edi, eax
jle .L1
.L3:
mov eax, DWORD PTR [rsp-4] # Read i from mem
add eax, 1 # i++
mov DWORD PTR [rsp-4], eax # Write i to mem
mov eax, DWORD PTR [rsp-4] # Read it back again before
# evaluating the loop condition.
cmp eax, edi # Is i < N?
jl .L3 # Jump back to L3 if not.
.L1:
听起来您的真实问题更像是多快:
L1: add eax, 1
jmp L1
即使答案很复杂,也需要了解CPU管道的内部情况。
我建议与Godbolt一起玩,以了解有关编译器正在做什么的更多信息。例如https://godbolt.org/z/59XUSu
答案 1 :(得分:1)
您可以直接测量“空循环”的速度,但是要说服C ++编译器发出它并不容易。可以用asm volatile("")
欺骗GCC和Clang,但是MSVC内联汇编一直不同,并且对于64位程序完全禁用。
可以use MASM来回避该限制:
.MODEL FLAT
.CODE
_testfun PROC
sub ecx, 1
jnz _testfun
ret
_testfun ENDP
END
使用extern "C" void testfun(unsigned N);
将其导入您的代码中。
答案 2 :(得分:0)
在您的for循环中尝试volatile int i = 0
。 volatile
关键字告诉编译器此变量可能由于外部事件或线程而随时更改,因此,对于将来的变量可能无法做出相同的假设。