Страницы

Поиск по вопросам

пятница, 12 апреля 2019 г.

Производительность алгоритмов std

Потихоньку учусь использовать алгоритмы стандартной библиотеки C++. Самодокументирование кода - это хорошо, но решил проверить и производительность. Например, std::accumulate. Qt Creator, mingw, windows, QTest. В объекте создаём приватные поля:
QVector results_; float sum_; static const size_t size_=65536; float arr_[size_];
В конструкторе пишем:
std::fill(arr_,arr_+size_,1.0);
Создаём три слота:
void Tester::benchmarkSimpleLoopOverArray() { sum_=0; QBENCHMARK { for (size_t i=0;ivoid Tester::benchmarkSmartLoopOverArray() { sum_=0; float * pf=arr_; float * pf0=pf+size_; QBENCHMARK { for (;pfvoid Tester::benchmarkAccumulateOverArray() { sum_=0; QBENCHMARK { sum_=std::accumulate(arr_,arr_+size_,0.0); } results_<В results_ результаты заносим, чтобы компилятор не выбросил ненароком весь цикл, увидев, что его результат не используется. Результаты меня обескуражили:
RESULT : Tester::benchmarkSimpleLoopOverArray(): 0.061 msecs per iteration (total: 63, iterations: 1024) PASS : Tester::benchmarkSimpleLoopOverArray() RESULT : Tester::benchmarkSmartLoopOverArray(): 0.0000046 msecs per iteration (total: 78, iterations: 16777216) PASS : Tester::benchmarkSmartLoopOverArray() RESULT : Tester::benchmarkAccumulateOverArray(): 0.0532 msecs per iteration (total: 109, iterations: 2048) PASS : Tester::benchmarkAccumulateOverArray()
На арифметике указателей получаем выигрыш на 4 порядка! Ради такого я готов написать пару лишних строк кода и строчку комментария.
Соответственно, вопросы:
Можно ли как-то улучшить производительность алгоритмов, как мы это сделали с обычным циклом? Я пробовал генерить два таких же указателя для начала и конца массива, результат получается хуже - таким же, как у наивного цикла. Может ли кто-нибудь проверить, сохранится ли такое соотношение результата под другими компиляторами/операционками? Ну и самый главный вопрос. Как получился такой огромный выигрыш у "хитрого" цикла? Я не настолько хорошо знаю ассемблер, чтобы залезть в код и посмотреть отличия, но получается, что в наивном цикле и стандартных алгоритмах используется какая-то очень тяжёлая операция, которой нет во втором примере. Какая?
Upd: спасибо zenden2k, проблема действительно была в том, что указатели я выставлял до входа в цикл. В результате второй тест полноценно выполнялся только один раз, а потом на входе в for проверял условие
pf, которое не выполнялось - отсюда бешеное количество итераций и, соответственно, малое время на итерацию.


Ответ

У вас ошибка в коде.
Вот это
float * pf=arr_; float * pf0=pf+size_;
должно быть внутри QBENCHMARK а не снаружи.
Также тесты у вас не равнозначны, потому что используется общая переменная sum_, она принимает разные значения в зависимости от кол-ва итераций. Ее надо инициализировать внутри QBENCHMARK
float sum_ = 0;
Тогда получите приблизительно одинаковые результаты
RESULT : TestClass::benchmarkSmartLoopOverArray(): 0.058 msecs per iteration (total: 60, iterations: 1024) PASS : TestClass::benchmarkSimpleLoopOverArray() RESULT : TestClass::benchmarkSimpleLoopOverArray(): 0.059 msecs per iteration (total: 61, iterations: 1024) PASS : TestClass::benchmarkAccumulateOverArray() RESULT : TestClass::benchmarkAccumulateOverArray(): 0.059 msecs per iteration (total: 61, iterations: 1024) PASS : TestClass::cleanupTestCase() Totals: 5 passed, 0 failed, 0 skipped, 0 blacklisted ********* Finished testing of TestClass *********

Комментариев нет:

Отправить комментарий