Оцените одну функцию

Как вы оцениваете функцию? Глядя на результаты Callgrind, я обнаружил, что моя программа проводит много времени в pow, Поскольку мне не нужна полная рабочая точность, я подумал, что мог бы создать справочную таблицу и использовать линейную интерполяцию между точками в таблице. Чтобы иметь возможность оценить подход с использованием таблицы поиска, мне нужно измерить время. Итак, я сделал это:

#ifdef __WAND__
target[name[test2.exe] type[application] platform[;Windows]]
target[name[test2] type[application]]
#endif

#include <herbs/main/main.h>
#include <herbs/tictoc/tictoc.h>
#include <herbs/array_fixedsize/array_fixedsize.h>
#include <random>
#include <cstdio>
#include <cmath>

class GetRand
{
public:
GetRand(double min,double max):U(min,max){}

bool operator()(double* val,size_t n,size_t N)
{
*val=U(randsource);
return 1;
}

private:
std::mt19937 randsource;
std::uniform_real_distribution<double> U;
};

int MAIN(int argc,charsys_t* argv[])
{
Herbs::ArrayFixedsize<double> vals(1024*1024*128,GetRand(-4,4));

const size_t N=16;
auto n=N;
while(n)
{
double start=0;
auto ptr=vals.begin();
{
Herbs::TicToc timestamp(start);
while(ptr!=vals.end())
{
pow(2,*ptr);
++ptr;
}
}
//  I have set cpu-freq to 1.6 GHz using cpufreq-set
printf("%.15g\t",1.6e9*start/vals.length());
--n;
}
return 0;
}

При запуске этой программы выход составляет около 2,25 циклов за итерацию. Это кажется очень низким, так как реализация pow кажется (это callgrind дал мне __ieee754_pow).

Цикл тестирования в сборке выглядит так при компиляции для GNU / Linux на x86-64:

    call    _ZN5Herbs6TicTocC1ERd@PLT
movq    %r14, %rbx
.p2align 4,,10
.p2align 3
.L28:
vmovsd  (%rbx), %xmm1
vucomisd    .LC6(%rip), %xmm1
jb  .L25
vmovsd  .LC7(%rip), %xmm0
call    pow@PLT
.L25:
addq    $8, %rbx
cmpq    %r12, %rbx
jne .L28
movq    %rbp, %rdi
call    _ZN5Herbs6TicTocD1Ev@PLT

По крайней мере powназывается. Могу ли я доверять выводу или есть какая-то черная магия, которая устраняет вещи?

-2

Решение

Есть несколько вещей, которые вы должны учитывать при тестировании функций.

1) Убедитесь, что отсутствие кеша не оказывает существенного влияния на результаты. В вашем случае вы перебираете большой массив данных, где вы получаете тонны промахов кеша. Вместо этого используйте меньший массив, который легко помещается в кэш L1 и проходит по нему несколько раз.

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

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

4) Профиль с полной оптимизацией и включенным встраиванием.

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

1

Другие решения


По вопросам рекламы [email protected]