我听说过这个词,但我不太清楚它是什么意思,所以:
发布于 2010-05-15 21:47:15
它的意思就像它在罐头上说的那样--它衡量的是一些“小”的东西的性能,比如对操作系统内核的系统调用。
危险在于,人们可能会利用从微观基准中获得的任何结果来决定优化。我们都知道:
我们应该忘记小效率,大约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);
}
编译器可能会看到变量sum
不会被用于任何东西,并且会对它进行优化,并且也会对for循环进行优化。但是等等!如果我们这样做:
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个应用程序,并在启动计算机时将应用程序代码预加载到内存中!
事实上,有无数变量发挥作用:引用的局部性(例如数组与链表)、缓存和内存带宽的影响、编译器内联、编译器实现、编译器开关、处理器核数、处理器级的优化、操作系统调度器、操作系统后台进程等。
因此,在很多情况下,微基准并不是一个有用的指标。它绝对不会用定义良好的测试用例(分析)取代整个程序的基准测试。首先编写可读代码,然后编写概要文件,看看需要做什么(如果有的话)。
我想强调的是,微基准本身并不是有害的,但人们必须小心使用它们(对于许多其他与计算机有关的事情来说都是这样)。
发布于 2010-05-15 22:30:02
我想说的是,微型基准仅仅意味着测量微小的东西。微型可能与上下文有关,但通常取决于单个系统调用或类似的级别。标杆指的是上面提到的一切。
这 (归档)文章列出了测量getpid()系统调用的时间,以及使用memcpy()作为微基准测试示例来测量复制内存的时间。
任何对算法、实现等的测量都不能算作微观基准。特别是结果报告,列出执行时间减少的任务,很可能很少被算作微基准。
显而易见的危险是,它诱使开发人员优化程序的错误部分。另一个危险是,要精确地测量一些小的东西是众所周知的困难。避免这种情况的最简单的方法可能是很好地了解在程序中花费的时间最多的地方。
人们通常说“不做微基准测试”,但他们的意思可能是“不要基于微基准做出优化决策”。
这本身并不是一件坏事,因为这里的其他人,许多网页似乎表明。它有它的地方。我使用程序重写和运行时方面编织等。我们通常发布添加的指令的微基准,不是为了指导任何优化,而是确保我们的额外代码对重写程序的执行几乎没有影响。
然而,这是一门艺术,特别是在具有JIT、热身时间等的VM环境中。一种描述良好的这里 (归档)方法。
发布于 2016-09-03 07:57:01
“Java性能:最终指南”一书中有关于微基准的定义和例子:
公共doTest(){ double l;long then = System.currentTimeMillis();for(int i= 0;i< nLoops;i++){ l= fibImpl1(50);} long now = system.currentTimeMillis();System.out.println(“运行时间:”+(现在-那时))}.私有双fibImpl1(int ){ if(n < 0)抛出新的IllegalArgumentException(“必须> 0");if(n == 0)返回0d;if(n == 1)返回1d;double d=fibImpl1(n-2)+fibImpl(n-1);if(Double.isInfinited(d))抛出新的ArithmeticException(”溢出“);返回d;}
微基准必须使用它们的结果。
这段代码最大的问题是它从来不改变任何程序状态。由于没有使用Fibonacci计算的结果,所以编译器可以随意放弃该计算,智能编译器(包括当前的Java 7和8编译器)最终将执行以下代码:
长时间= System.currentTimeMillis();长时间= System.currentTimeMillis();System.out.println(“运行时间:”+(现在-那时));
因此,不管Fibonacci方法的实现是什么,或者循环应该执行的次数,经过的时间都只有几毫秒。
有一种方法可以解决这个问题:确保读取每个结果,而不是简单地写入结果。实际上,将l的定义从局部变量更改为实例变量(使用挥发性关键字声明)将允许度量方法的性能。
https://stackoverflow.com/questions/2842695
复制