Что такое микробенчмаркинг? - PullRequest
53 голосов
/ 16 мая 2010

Я слышал этот термин, но я не совсем уверен, что он означает, поэтому:

  • Что это значит и что не значит?
  • Какие примеры того, что есть, а что нет микробенчмаркинг?
  • Каковы опасности микробенчмаркинга и как вы их избегаете?
    • (или это хорошо?)

Ответы [ 6 ]

64 голосов
/ 16 мая 2010

Это означает именно то, что написано на консервной банке, - это измерение производительности чего-то «маленького», например, системного вызова ядра операционной системы.

Опасность заключается в том, что люди могут использовать любые результаты, полученные в результате микробенчмаркинга, для диктовки оптимизаций. И как мы все знаем:

Мы должны забыть о малой эффективности, скажем, в 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 самых популярных приложений, которые вы открываете больше всего, и предварительно загрузить код приложения в память при загрузке компьютера!

Фактически, существует множество переменных, которые вступают в игру: местность ссылок (например, массивы и связанные списки), влияние кэшей и пропускной способности памяти, включение компилятора, реализация компилятора, переключатели компилятора, количество ядер процессора, оптимизации в уровень процессора, планировщики операционной системы, фоновые процессы операционной системы и т. д.

Таким образом, микробенчмаркинг не совсем полезный показатель во многих случаях. Это определенно не заменяет целые тесты программы четко определенными тестовыми примерами (профилирование). Сначала напишите читаемый код, а затем профиль, чтобы увидеть, что нужно сделать, если таковые имеются.

Я хотел бы подчеркнуть, что микробенчмарки - это не зло как таковые , но их нужно использовать осторожно (это верно для многих других вещей, связанных с компьютерами)

6 голосов
/ 16 мая 2010

Не существует определения микро-бенчмаркинга, но когда я его использую, я имею в виду небольшой искусственный бенчмарк, предназначенный для тестирования производительности некоторого конкретного оборудования 1 или языковой функции. Напротив, лучшим тестом является реальная программа, предназначенная для выполнения реальной задачи. (Проведение жесткой линии между двумя делами бессмысленно, ИМО, и я не буду пытаться.)

Опасность микро-бенчмаркинга заключается в том, что легко написать эталон, который дает результаты, которые полностью вводят в заблуждение. Некоторые распространенные ловушки в микро-бенчмарках Java:

  • написание кода, который может вывести компилятор, бесполезна, и поэтому полностью оптимизируется,
  • без учета «комковатой» природы управления памятью Java и
  • без учета эффектов запуска JVM; например время, затрачиваемое на загрузку и JIT-компиляцию классов, и (и наоборот) ускорение выполнения, которое происходит после компиляции JIT-методов.

Однако даже после того, как вы обратились к вышеупомянутым проблемам, существует системная проблема с эталонным тестированием, которую невозможно устранить. Код и поведение эталонного теста обычно мало связаны с тем, что вас действительно волнует; то есть, как ваше приложение будет работать. Слишком много «скрытых переменных» для вас, чтобы можно было обобщать результаты тестов до типичных программ, не говоря уже о вашей программе.

По этим причинам мы регулярно советуем людям НЕ тратить свое время на микротесты. Вместо этого лучше всего написать простой и естественный код и использовать профилировщик для определения областей, которые необходимо оптимизировать вручную. Интересно, что обычно выясняется, что наиболее существенные проблемы с производительностью в реальных приложениях связаны с неправильным проектированием структур данных и алгоритмов (включая узкие места, связанные с сетями, базами данных и потоками), а не с тем, что типичные микропроцессоры пытаются тест.

@ BalusC предоставил отличную ссылку на материал по этой теме на странице Hotspot FAQ . А вот ссылка на официальный документ IBM от Брайана Гетца .


1 - Эксперты даже не пытались бы проводить аппаратный бенчмаркинг в Java. Между байт-кодами и оборудованием происходит слишком много «сложных вещей», чтобы сделать необработанные / полезные выводы об оборудовании из необработанных результатов. Вам лучше использовать язык, который ближе к аппаратному; например C или даже код сборки.

4 голосов
/ 16 мая 2010
  • Что это значит и что не значит?

Я бы сказал, что микро-бенчмаркинг просто означает измерение чего-то крошечного. Tiny, вероятно, зависит от контекста, но обычно на уровне одного системного вызова или чего-то подобного. Бенчмаркинг относится ко всему выше.

  • Какие примеры того, что есть, а что нет микробенчмаркинг?

В этой статье перечислены измерения времени системного вызова getpid () и измерения времени для копирования памяти с использованием memcpy () в качестве примеров микро-бенчмаркинга .

Любое измерение реализации алгоритма и т. Д. Не будет считаться микробенчмаркингом. Особенно отчеты о результатах, в которых перечислены задачи с уменьшением времени выполнения, вероятно, редко считаются микро-бенчмаркингами.

  • В чем опасность микробенчмаркинга и как его избежать?

Очевидная опасность состоит в том, что это побуждает разработчиков оптимизировать неправильные части программы. Другая опасность состоит в том, что очень сложно точно измерить что-то маленькое. Вероятно, самый простой способ избежать этого - просто получить представление о том, где больше всего времени тратится на программу.

Люди обычно говорят: «не делайте микробанчмаркинг», но то, что они, вероятно, имеют в виду, «не принимайте решения по оптимизации на основе микропроцессоров».

  • (или это хорошо?)

Это вовсе не так уж плохо, как другие здесь, и многие веб-страницы, кажется, предлагают. У него есть свои места. Я работаю с переписыванием программы и выполнением аспектов выполнения и т. Д. Мы обычно публикуем микробенчмарки наших добавленных инструкций, не для того, чтобы проводить какие-либо оптимизации, но чтобы убедиться, что наш дополнительный код практически не влияет на выполнение переписанной программы. *

Однако это искусство, особенно в контексте виртуальной машины с JIT, временем прогрева и т. Д. Хорошо описанный подход для Java описан здесь .

2 голосов
/ 03 сентября 2016

в книге (Java Performance The Definition Guide) у него есть это определение и пример о микробенчмарках

  1. Microbenchmarks

    Микробенчмарк - это тест, предназначенный для измерения очень маленькой единицы производительность: время вызова синхронизированного метода по сравнению с несинхронизированный метод; накладные расходы на создание потока по сравнению с Asing пул потоков; время выполнения одного арифметического алгоритма против альтернативная реализация; и т. д.

    Микробенчмарки могут показаться хорошей идеей, но они очень сложно написать правильно. рассмотрим следующий код, который это попытка написать mocrobenchmark, который проверяет производительность Различная реализация метода для вычисления 50-го Фибоначчи номер:

public void doTest(){
double l;
long then = System.currentTimeMillis();

for(int i = 0; i < nLoops; i++){
 l = fibImpl1(50);
}

long now = system.currentTimeMillis();
System.out.println("Elapsed time: " + (now - then))

}

...

private double fibImpl1(int n){
if(n < 0) throw new IllegalArgumentException("Must be > 0");
if(n == 0) return 0d;
if(n == 1) return 1d;
double d = fibImpl1(n - 2) + fibImpl(n - 1);
if(Double.isInfinited(d)) throw new ArithmeticException("Overflow");
return d;
}

Микробенчмарки должны использовать свои результаты.

Самая большая проблема с этим кодом заключается в том, что он никогда фактически не меняет состояние программы. Поскольку результат вычисления Фибоначчи никогда не используется, компилятор может отказаться от этого вычисления. Умный компилятор (включая текущие компиляторы Java 7 и 8)

в итоге выполнит этот код:

long then = System.currentTimeMillis();
long now = System.currentTimeMillis();
System.out.println("Elapsed time: " + (now - then));

Как результат, истекшее время будет составлять всего несколько миллисекунд, независимо от реализации метода Фибоначчи или числа раз, которое цикл должен быть выполнен.

Есть способ обойти эту конкретную проблему: убедиться, что каждый результат прочитан, а не просто засчитан. На практике изменение определения l с локальной переменной на переменную экземпляра (объявленную с ключевым словом volatile) позволит измерить производительность метода.

1 голос
/ 16 мая 2010

Вот несколько хороших статей Брайана Гетца, объясняющих, почему (микро) бенчмаркинг особенно сложен в Java:

0 голосов
/ 16 мая 2010

Микробенчмаркинг - это бенчмаркинг, я не думаю, что он стоит. Эффективный бенчмаркинг - это бенчмаркинг, я думаю, стоит потраченного времени.

Вообще говоря, микробенчмаркинг (как говорит in silico) пытается измерить производительность какой-то очень гранулированной задачи, которую трудно выполнить хорошо, и обычно бессмысленно в контексте реальных проблем с производительностью.

...