当我添加一段代码来测量 CPU 执行时间时,例如:
int main()
{
clock_t init_time = clock(); // (1)
your_fun(); // (2)
printf("Seconds: %f", (double)(clock() - init_time) / CLOCKS_PER_SEC); // (3)
}
如果我以完全优化的方式编译那段代码,是否可以将代码重新排序,使 init_time
初始化在 your_fun
之后执行,这样你就什么都不测量了?换句话说,clock
是否有任何内存屏障机制来保护测量 block 不被重新排序?
如果 clock
不是抗重排序的:如果我将 (1) 和 (3) 移动到在不同编译单元中实现的新函数,那么编译器在编译 main
, 看不到里面的东西,能不能防止这样的重排序?
我一直有这个问题,因为我通常会看到等待执行的时间(秒)与打印的执行时间(毫秒甚至 0)之间相互矛盾的执行时间。
C++ 时钟或本地时间函数怎么样?他们有类似的问题吗?
最佳答案
对时间读取函数的调用是系统调用:测量将准确地发生在正确的位置,相对于其他系统调用,包括 I/O 操作。这不是您要的:您正在为一个纯操作计时,一个没有 SE(副作用)的计算。
您希望计算恰好发生在两个系统调用之间的正确位置;您需要订购它,而不是完全优化它。
像往常一样,要阻止优化,请使用volatile:所有volatile操作都是副作用,就像一个系统调用(比如调用read
或write
)。
注意:您可能指望真正的单独编译来达到同样的效果;那是如果您确定不会应用全局程序优化。
根据定义,副作用永远无法重新排序。此外, volatile 读取具有不确定的值,即使是常量也是如此:
const volatile int indeterminate_zero = 0;
任何表达式中 indeterminate_zero
的每个实例都会产生编译器无法假定为 0 的 0。
您需要将计算与副作用放在一起,并使其依赖于未知值,以便计算准确地发生在您想要的位置。
在正确的位置插入 volatile 操作将以普通内存读取或写入的微不足道的成本做到这一点:对 volatile 全局整数类型的操作与常规(非 volatile)的成本完全一样) 在几乎所有编译器上禁用优化的类似操作。
重要的是,您将 volatile 读取视为特殊的 scanf
并将写入视为特殊的 printf
,两者都不与任何 STDIO 流交互,并且它们的成本都极低,并且比调用普通系统调用(如 getpid
、getppid
、0 字节的 write
...)更可移植。
(我没有发布带有可编译代码的完整答案,因为您没有提出带有我可以重新表述的可运行代码的完整问题。)
如果您不想打扰所有这些,只需使用单独的编译即可。所有常用的编译器都支持单独编译(编译器可以合法地强制您一次提交所有代码,但据我所知,没有一个这样做)。
关于c++ - 使用C时间函数测量时间: are they code-reordering resistant?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/67370546/