什么是微模板标记?
我听说过这个术语,但我不完全确定它是什么意思,所以:
- 这是什么意思,不意味着什么?
- 什么是IS和IS不是微模板标记的一些例子?
- 微板凳标记有哪些危险,如何避免它?
- (还是一件好事?
我听说过这个术语,但我不完全确定它是什么意思,所以:
它的意思是它在锡罐上所说的 - 它正在测量“小”东西的性能,比如对操作系统内核的系统调用。
危险在于,人们可能会使用他们从微基准标记中获得的任何结果来指示优化。众所周知:
我们应该忘记效率低下,比如说大约97%的时间:过早的优化是万恶之源“ - 唐纳德·高德纳
可能有许多因素会扭曲微基准标记的结果。编译器优化就是其中之一。如果被测操作花费的时间太短,以至于无论您用来测量什么,它都需要比实际操作本身更长的时间,那么您的微基准也会被扭曲。
例如,有人可能会对循环的开销进行微基准标记:for
void TestForLoop()
{
time start = GetTime();
for(int i = 0; i < 1000000000; ++i)
{
}
time elapsed = GetTime() - start;
time elapsedPerIteration = elapsed / 1000000000;
printf("Time elapsed for each iteration: %d\n", elapsedPerIteration);
}
显然,编译器可以看到循环绝对不执行任何操作,并且根本不为循环生成任何代码。因此,和 的价值几乎是无用的。elapsed
elapsedPerIteration
即使循环执行某些操作:
void TestForLoop()
{
int sum = 0;
time start = GetTime();
for(int i = 0; i < 1000000000; ++i)
{
++sum;
}
time elapsed = GetTime() - start;
time elapsedPerIteration = elapsed / 1000000000;
printf("Time elapsed for each iteration: %d\n", elapsedPerIteration);
}
编译器可能会看到变量不会用于任何事情,并优化它,并优化for循环。但是等等!如果我们这样做会怎样:sum
void TestForLoop()
{
int sum = 0;
time start = GetTime();
for(int i = 0; i < 1000000000; ++i)
{
++sum;
}
time elapsed = GetTime() - start;
time elapsedPerIteration = elapsed / 1000000000;
printf("Time elapsed for each iteration: %d\n", elapsedPerIteration);
printf("Sum: %d\n", sum); // Added
}
编译器可能足够聪明,意识到这将始终是一个常量值,并优化所有这些值。如今,许多人会对编译器的优化功能感到惊讶。sum
但是编译器无法优化的东西呢?
void TestFileOpenPerformance()
{
FILE* file = NULL;
time start = GetTime();
for(int i = 0; i < 1000000000; ++i)
{
file = fopen("testfile.dat");
fclose(file);
}
time elapsed = GetTime() - start;
time elapsedPerIteration = elapsed / 1000000000;
printf("Time elapsed for each file open: %d\n", elapsedPerIteration);
}
即使这不是一个有用的测试!操作系统可能会看到文件被非常频繁地打开,因此它可能会将其预加载到内存中以提高性能。几乎所有的操作系统都这样做。当您打开应用程序时,也会发生同样的事情 - 操作系统可能会找出您打开最多的前5个应用程序,并在启动计算机时将应用程序代码预加载到内存中!
事实上,有无数的变量在起作用:引用的局部性(例如数组与链接列表),缓存和内存带宽的影响,编译器内联,编译器实现,编译器开关,处理器内核数,处理器级别的优化,操作系统调度程序,操作系统后台进程等。
因此,在很多情况下,微基准标记并不是一个有用的指标。它绝对不会用定义良好的测试用例(分析)取代整个程序基准测试。首先编写可读的代码,然后分析以查看需要执行的操作(如果有)。
我想强调的是,微弯道本身并不是邪恶的,但人们必须谨慎使用它们(对于与计算机有关的许多其他事情都是如此)。
没有微观基准测试的定义,但是当我使用它时,我的意思是一个小型的人为基准测试,旨在测试某些特定硬件1或语言功能的性能。相比之下,更好的基准测试是旨在执行实际任务的真实程序。(IMO,在这两种情况之间划一条强硬的界线是没有意义的,我不会尝试。
微基准测试的危险在于,很容易编写一个给出完全误导性结果的基准测试。Java微基准测试中的一些常见陷阱是:
但是,即使您已经解决了上述问题,也存在无法解决的基准测试系统性问题。基准测试的代码和行为通常与您真正关心的内容关系不大。即您的应用程序将如何执行。有太多的“隐藏变量”,你无法从基准推广到典型程序,更不用说推广到你的程序了。
出于这些原因,我们经常建议人们不要浪费时间在微观基准测试上。相反,最好编写简单而自然的代码,并使用探查器来确定需要手动优化的区域。有趣的是,通常事实证明,实际应用程序中最重要的性能问题是由于数据结构和算法(包括网络,数据库和线程相关的瓶颈)的不良设计,而不是典型的微基准测试试图测试的那种东西。
@BalusC在热点常见问题解答页面中提供了指向此主题材料的绝佳链接。这是Brian Goetz的IBM白皮书的链接。
1 - 专家甚至不会尝试在Java中进行硬件基准测试。字节码和硬件之间发生了太多的“复杂事情”,无法从原始结果中得出关于硬件的有效/有用的结论。您最好使用更接近硬件的语言;例如,C甚至汇编代码。