当前位置: 首页 > 面试题库 >

Java 什么是微基准测试?

尹俊贤
2023-03-14
问题内容

我听说过使用过这个术语,但是我不确定它的含义,因此:

  • 它是什么意思,不是什么意思?
  • 什么是IS和IS N’T微基准测试的一些示例?
  • 微基准测试有哪些危险,如何避免?
  • (或者这是好事吗?)

问题答案:

它的含义与锡罐上所说的完全一样-它正在衡量“小”东西的性能,例如对操作系统内核的系统调用。

危险在于人们可能会使用从微基准测试中获得的任何结果来指示优化。众所周知:

我们应该忘掉效率低下的问题,比如说大约有97%的时间:过早的优化是万恶之源” –唐纳德·努斯(Donald Knuth)

可能有许多因素会扭曲微基准测试的结果。编译器优化就是其中之一。如果要测量的操作花费的时间很少,以至于你用来测量的时间要比实际操作本身花费的时间更长,那么你的微基准测试也会出现偏差。

例如,有人可能会对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);
}

显然,编译器可以看到该html" target="_blank">循环绝对不执行任何操作,并且根本不会为该循环生成任何代码。因此,价值elapsedelapsedPerIteration是几乎无用。

即使循环执行了一些操作:

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个应用程序,并在启动计算机时将应用程序代码预加载到内存中!

实际上,有无数变量在起作用:引用的局部性(例如数组与链表),缓存和内存带宽的影响,编译器内联,编译器实现,编译器切换,处理器核心数量,处理器级别的优化,操作系统调度程序,操作系统后台进程等。

因此,在许多情况下,微基准测试并不是完全有用的指标。它绝对不能用定义良好的测试用例(分析)代替整个程序的基准测试。首先编写可读代码,然后进行概要分析以查看需要完成的操作(如果有)。

我想强调一点,微基准本身并不是邪恶的,但人们必须谨慎使用它们(对于与计算机相关的许多其他事情,这是正确的)



 类似资料:
  • 问题内容: 我对此很好奇。 我想检查哪个功能更快,所以我创建了一些代码,执行了很多次。 “第二个”循环更快,因此,我认为hadoop的Bytes类比String类的函数更快。然后,我更改了循环顺序,然后c.getBytes()变得更快。我执行了很多次,结论是,我不知道为什么,但是在执行第一个代码后,我的VM中发生了一些事情,因此第二个循环的结果变得更快。 问题答案: 这是经典的Java基准测试问题

  • 问题内容: 我已经编写了一个小型Java应用程序,我需要为其获取性能指标,例如内存使用率,运行时间等。有没有简单易用的性能衡量工具? 问题答案: Yourkit非常好(30天免费试用)。Eclipse还内置了TPTP工具。

  • 受另一个关于堆栈溢出的问题的启发,我编写了一个微型基准来检查,什么更有效: 有条件地检查零除数或 捕获和处理 下面是我的代码: 我对JMH完全陌生,不确定代码是否正确。 我的基准是正确的吗?你看到任何错误吗? 旁白:请不要建议询问https://codereview.stackexchange.com.对于Codereview,代码必须已按预期工作。我不确定这个基准是否能按预期工作。

  • 问题内容: 使用进行基准测试时,会看到以下结果。 根据我的理解: 是迭代次数。 是一次迭代完成所需的大概时间 但即使阅读文档,我无法找出什么和意味着什么。 我的猜测是allocs / op与垃圾回收和内存分配有关(越少越好)。 任何人都可以很好地解释这些值的含义。也很高兴知道为什么要减少这些步骤以及减少它们的主要步骤(我意识到这是针对测试的,但是在某些情况下可能会有一些通用的提示) 问题答案: 表

  • 11.4. 基准测试 基准测试是测量一个程序在固定工作负载下的性能。在Go语言中,基准测试函数和普通测试函数写法类似,但是以Benchmark为前缀名,并且带有一个*testing.B类型的参数;*testing.B参数除了提供和*testing.T类似的方法,还有额外一些和性能测量相关的方法。它还提供了一个整数N,用于指定操作执行的循环次数。 下面是IsPalindrome函数的基准测试,其中循

  • GoCPPLua (JIT) 策略执行的负载在model_b_test.go中进行基准测试。 测试是: 英特尔 酷睿 i7-6700HQ CPU @ 2.60GHz, 2601 Mhz, 4 核, 8 处理器 go test -bench= -benchmem 的测试结果如下 (op = 一次 Enforce() 调用, ms = 毫秒, KB = 千字节): 测试用例 规则大小 时间开销 (m