我目睹了以下奇怪的行为。我有两个函数,几乎相同 - 它们测量执行某个操作所需的循环次数。在一个函数中,在循环内部我增加一个变量;在另一个没有任何反应。变量是易变的,因此它们不会被优化掉。这些是功能:
unsigned int _osm_iterations=5000;
double osm_operation_time(){
// volatile is used so that j will not be optimized, and ++ operation
// will be done in each loop
volatile unsigned int j=0;
volatile unsigned int i;
tsc_counter_t start_t, end_t;
start_t = tsc_readCycles_C();
for (i=0; i<_osm_iterations; i++){
++j;
}
end_t = tsc_readCycles_C();
if (tsc_C2CI(start_t) ==0 || tsc_C2CI(end_t) ==0 || tsc_C2CI(start_t) >= tsc_C2CI(end_t))
return -1;
return (tsc_C2CI(end_t)-tsc_C2CI(start_t))/_osm_iterations;
}
double osm_empty_time(){
volatile unsigned int i;
volatile unsigned int j=0;
tsc_counter_t start_t, end_t;
start_t = tsc_readCycles_C();
for (i=0; i<_osm_iterations; i++){
;
}
end_t = tsc_readCycles_C();
if (tsc_C2CI(start_t) ==0 || tsc_C2CI(end_t) ==0 || tsc_C2CI(start_t) >= tsc_C2CI(end_t))
return -1;
return (tsc_C2CI(end_t)-tsc_C2CI(start_t))/_osm_iterations;
}
那里有一些非标准功能,但我相信你会管理。
问题是,第一个函数返回 4 ,而第二个函数(假设更少)返回 6 ,尽管第二个函数明显少于第一个函数之一。
这对任何人都有意义吗?
实际上我做了第一个函数,所以我可以减少测量第二个函数的循环开销。你知道怎么做吗(因为这种方法并没有真正削减它)?
我在使用Ubuntu(我认为是64位)。
非常感谢。
答案 0 :(得分:4)
我在这里可以看到几件事。一个是两个循环的代码看起来相同。其次,编译器可能会意识到变量i
和变量j
将始终具有相同的值并优化其中一个。您应该查看生成的程序集,看看到底发生了什么。
另一种理论认为,对循环内部主体的更改已经影响了代码的可共享性 - 这可能会将其移动到缓存行或其他一些东西中。
由于代码非常简单,您可能会发现很难获得准确的计时值,即使您正在进行5000次迭代,您可能会发现时间在您正在使用的计时代码的误差范围内。一台现代计算机可能会在不到一毫秒的时间内运行 - 也许你应该增加迭代次数?
要在gcc中查看生成的程序集,specify the -S compiler option:
问:我怎么看一下汇编代码 由海湾合作委员会产生?问:我怎样才能创建一个文件 查看C代码及其程序集 一起翻译?
答:使用-S(注意:大写S)开关 到GCC,它将发射组件 代码扩展名为.s的文件。 例如,以下命令:
gcc -O2 -S -c foo.c
将保留生成的汇编代码 在文件foo.s。
如果你想一起看C代码 随着组件被转换为, 使用这样的命令行:
gcc -c -g -Wa,-a,-ad [其他海湾合作委员会 选项] foo.c&gt; foo.lst
将输出组合 C /汇编列表到文件 foo.lst。
答案 1 :(得分:1)
这种事情在很大程度上取决于编译器优化和计时器分辨率。无论单位如何,您提供的结果(4和6)都很低。要正确地进行基准测试,您应该将这两个函数包装在一个执行它们几千次的循环中。
答案 2 :(得分:0)
有时很难猜测这种事情,特别是由于迭代次数较少。但是,可能发生的一件事是,增量可以在一个自由的整数执行单元上执行,获得一定程度的并行性,因为它没有对i的值进行删除。
由于您提到这是64位操作系统,因此几乎可以肯定所有这些值都在寄存器中,因为x86_64架构中有更多寄存器。除此之外,我会说执行更多迭代,看看结果有多稳定。
答案 3 :(得分:0)
如果您真的想要测试一段代码的操作(在这种情况下为"j++;"
),那么您最好不要执行以下操作:
1 /在两个单独的可执行文件中执行它,因为可执行文件中的位置可能会影响代码。
2 /确保使用CPU时间而不是经过的时间(我不确定"tsc_readCycles_C()"
给你的是什么)。这是为了避免CPU加载其他任务的错误结果。
3 /关闭编译器优化(例如,"gcc -O0"
)以确保gcc
不会放入任何可能会使结果出现偏差的奇特内容。
4 /如果您使用实际结果,则无需担心volatile
,例如放置:
printf ("%d\n",j);
循环后或:
FILE *fx = fopen ("/dev/null","w");
fprintf (fx, "%d\n", j);
fclose (fx);
如果您根本不想要任何输出。我不记得volatile是编译器的建议还是强制执行。
5 / 5,000的迭代似乎有点偏低,“噪音”可能会影响读数。也许更高的价值会更好。如果您计算更大的代码并且刚刚将"j++;"
作为占位符,那么这可能不是问题。
答案 4 :(得分:0)
当我运行与此类似的测试时,我通常会:
我仍然无法解释你的观察结果,但是如果你确定你已经正确识别了你的功能(不明显的情况是因为早先有拷贝''''''''''例如),然后查看汇编器输出是剩下的主要选项。