Как правильно сравнить [шаблонной] программы C++

К сожалению, нет подписок, которые занимаются удалением событий.

7
задан ascobol 14 January 2009 в 08:35
поделиться

11 ответов

Если Вы хотите вынудить какой-либо компилятор не отбросить результат, иметь его, пишут результат в энергозависимый объект. Та операция не может быть оптимизирована по определению.

template<typename T> void sink(T const& t) {
   volatile T sinkhole = t;
}

Никакой iostream наверху, просто копия, которая должна остаться в сгенерированном коде. Теперь при сборе результатов большого количества операций лучше не отбросить их один за другим. Эти копии могут все еще добавить немного служебные. Вместо этого так или иначе соберите все результаты в единственном энергонезависимом объекте (таким образом, все отдельные результаты необходимы), и затем присвойте тот объект результата энергозависимому. Например, если Ваши отдельные операции, все производят строки, можно вызвать оценку путем добавления всех символьных значений вместе по модулю 1 <<32. Это добавляет едва любые издержки; строки, вероятно, будут в кэше. Результатом дополнения впоследствии будет assigned-volatile, таким образом, каждый символ в каждом жале должен будет на самом деле быть вычислен, никакие позволенные ярлыки.

6
ответ дан 6 December 2019 в 19:43
поделиться

Если у Вас нет действительно агрессивного компилятора (может произойти), я предложил бы вычислить, контрольная сумма (просто добавляют все результаты вместе), и произведите контрольную сумму.

Кроме этого, Вы могли бы хотеть посмотреть на сгенерированный ассемблерный код прежде, чем выполнить любые сравнительные тесты, таким образом, можно визуально проверить, что любые циклы на самом деле выполняются.

1
ответ дан 6 December 2019 в 19:43
поделиться

Если это возможно для Вас, Вы могли бы попытаться разделить свой код на:

  • библиотека Вы хотите протестировать скомпилированный со всей включенной оптимизацией
  • тестовая программа, динамично связывая библиотеку, с выключенной оптимизацией

Иначе Вы могли бы указать другой уровень оптимизации (похоже на использование gcc...) для теста functio n с оптимизировать атрибутом (см. http://gcc.gnu.org/onlinedocs/gcc/Function-Attributes.html#Function-Attributes).

1
ответ дан 6 December 2019 в 19:43
поделиться

Компиляторам только позволяют устранить ответвления кода, которых не может произойти. Пока это не может исключить это, ответвление должно быть выполнено, это не устранит его. Пока существует некоторая зависимость по данным где-нибудь, код будет там и будет выполнен. Компиляторы не слишком умны об оценке, какие аспекты программы не будут выполнены и не пытаются, потому что это - проблема NP и едва вычислимый. У них есть некоторые простые проверки такие что касается if (0), но это об этом.

Мое скромное мнение - то, что Вы были возможно поражены некоторой другой проблемой раньше, такой как способ, которым C/C++ оценивает булевы выражения.

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

Отвечать на Ваш вопрос о в пробирке тестировании: Да, сделайте это. Если Ваше приложение так строго ограничено во времени, сделайте это. С другой стороны, Ваше описание намекает на другую проблему: если Ваши дельты находятся в период времени 1e-3 секунд, то это походит на проблему вычислительной сложности, так как рассматриваемый метод нужно назвать очень, очень часто (для немногих выполнений, 1e-3 секунды neglectible).

Проблемная область Вы моделируете ОЧЕНЬ сложные звуки и наборы данных, вероятно, огромна. Такими вещами всегда является интересное усилие. Удостоверьтесь, что Вы абсолютно имеете правильные структуры данных и алгоритмы сначала, тем не менее, и микрооптимизируете все, что Вы хотите после этого. Так, я сказал бы что взгляд на целый контекст сначала.;-)

Из любопытства, какова проблема, которую Вы вычисляете?

1
ответ дан 6 December 2019 в 19:43
поделиться

Вы имеете большой контроль на оптимизации для Вашей компиляции.-O1,-O2, и так далее являются просто псевдонимами для набора переключателей.

Из страниц справочника

       -O2 turns on all optimization flags specified by -O.  It also turns
       on the following optimization flags: -fthread-jumps -falign-func‐
       tions  -falign-jumps -falign-loops  -falign-labels -fcaller-saves
       -fcrossjumping -fcse-follow-jumps  -fcse-skip-blocks
       -fdelete-null-pointer-checks -fexpensive-optimizations -fgcse
       -fgcse-lm -foptimize-sibling-calls -fpeephole2 -fregmove -fre‐
       order-blocks  -freorder-functions -frerun-cse-after-loop
       -fsched-interblock  -fsched-spec -fschedule-insns  -fsched‐
       ule-insns2 -fstrict-aliasing -fstrict-overflow -ftree-pre
       -ftree-vrp

Можно настроить и использовать эту команду, чтобы помочь Вам сузить который опции заняться расследованиями.

       ...
       Alternatively you can discover which binary optimizations are
       enabled by -O3 by using:

               gcc -c -Q -O3 --help=optimizers > /tmp/O3-opts
               gcc -c -Q -O2 --help=optimizers > /tmp/O2-opts
               diff /tmp/O2-opts /tmp/O3-opts Φ grep enabled

После того как Вы находите оптимизацию преступника, Вам не должен быть нужен cout's.

1
ответ дан 6 December 2019 в 19:43
поделиться

Вы могли создать фиктивную функцию в отдельном cpp файле, который ничего не делает, но берет в качестве аргумента, вообще тип Вашего результата вычисления. Затем можно вызвать ту функцию с результатами вычисления, вынудив gcc сгенерировать промежуточный код, и единственный штраф является стоимостью вызова функции (который не должен скашивать результаты, если Вы не называете его много!).

1
ответ дан 6 December 2019 в 19:43
поделиться

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

StartBenchmarking(); // ie, read a performance counter
for (int i=0; i<500; ++i)
 {
   coords[i][0] = 3.23;
   coords[i][1] = 1.345;
   coords[i][2] = 123.998;
 }
StopBenchmarking(); // what comes after this won't go into the timer

// this is just to force the compiler to use coords
double foo;
for (int j = 0 ; j < 500 ; ++j )
{
  foo += coords[j][0] + coords[j][1] + coords[j][2]; 
}
cout << foo;

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

void test1( volatile double *coords )
{
  //perform a simple initialization of all coordinates:
  for (int i=0; i<1500; i+=3)
  {
    coords[i+0] = 3.23;
    coords[i+1] = 1.345;
    coords[i+2] = 123.998;
  }
}

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

В целом в пробирке тестирование как это очень полезно, пока Вы помните, что это не целая история. Я обычно тестирую свои новые математические подпрограммы в изоляции как это так, чтобы я мог быстро выполнить итерации в просто кэше и конвейерно обработать характеристики своего алгоритма на последовательных данных.

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

1
ответ дан 6 December 2019 в 19:43
поделиться
#include <iostream>

// Mark coords as extern.
// Compiler is now NOT allowed to optimise away coords
// This it can not remove the loop where you initialise it.
// This is because the code could be used by another compilation unit
extern double coords[500][3];
double coords[500][3];

int main()
{

//perform a simple initialization of all coordinates:
for (int i=0; i<500; ++i)
 {
   coords[i][0] = 3.23;
   coords[i][1] = 1.345;
   coords[i][2] = 123.998;
 }


std::cout << "hello world !"<< std::endl;
return 0;
}
1
ответ дан 6 December 2019 в 19:43
поделиться

Я не знаю, имеет ли GCC подобную функцию, но с VC ++ можно использовать:

#pragma optimize

выборочно поворачивать оптимизацию вкл\выкл. Если GCC имеет подобные возможности, Вы могли бы создать с полной оптимизацией и просто выключить ее в случае необходимости, чтобы удостовериться, что Ваш код называют.

0
ответ дан 6 December 2019 в 19:43
поделиться

при запуске читайте из файла. в Вашем коде скажите если (вводил == "x"), суд <<result_of_benchmark;

Компилятор не сможет устранить вычисление, и если Вы удостоверитесь, что вход не является "x", то Вы не сравните iostream.

0
ответ дан 6 December 2019 в 19:43
поделиться

Просто небольшой пример нежелательной оптимизации:

#include <vector>
#include <iostream>

using namespace std;

int main()
{
double coords[500][3];

//perform a simple initialization of all coordinates:
for (int i=0; i<500; ++i)
 {
   coords[i][0] = 3.23;
   coords[i][1] = 1.345;
   coords[i][2] = 123.998;
 }


cout << "hello world !"<< endl;
return 0;
}

Если Вы прокомментируете код от "двойных проводов [500] [3] то", в конец для цикла он генерирует точно тот же ассемблерный код (просто попробованный g ++ 4.3.2). Я знаю, что этот пример слишком прост, и я не смог показать это поведение со станд.:: вектор простой структуры "Координат".

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

То же должно также применяться с виртуальными функциями (но я не доказываю его здесь). Используемый в контексте, где статическая ссылка сделала бы задание, я довольно уверен, что достойные компиляторы должны устранить дополнительную косвенность, призывают к виртуальной функции. Я могу попробовать этот вызов в цикле и прийти к заключению, что вызывание виртуальной функции не является таким грандиозным предприятием. Затем я назову это сотней тысячи времен в контексте, где компилятор не может предположить то, что будет точным типом указателя и иметь 20%-е увеличение времени выполнения...

0
ответ дан 6 December 2019 в 19:43
поделиться
Другие вопросы по тегам:

Похожие вопросы: