Страницы

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

понедельник, 9 декабря 2019 г.

C++, разное время выполнения программы

#cpp


Возникла следующая проблема: имеется вот такой участок кода

start = clock();
LLTWithBlock(n, 4, a);
end = clock();
duration = (double)(end - start);
std::cout << "Alltime: " << duration << std::endl;


Время выполнения 8 секунд.
После чего заменяю константу "4" на переменную "r" (тоже равную 4, считывается из файла)

LLTWithBlock(n, r, a);


И новое время выполнения становится 22 секунды. Я отдаленно понимаю, что в ассемблерном
 коде будут создаваться разные экземпляры функции, но откуда такая разница во времени
выполнения?

P.S. Время замерялось не один раз, данная ситуация повторяется при каждом запуске
программы.

upd: меняется конкретно время выполнения следующей функции (вызываемой внутри)

void A_LLT(unsigned n, unsigned r, unsigned numBlock, double *b, double *bb, double *a){
unsigned adr;
unsigned cnt = r*(numBlock + 1);//номер 1 строки в текущем блоке(прямоугольном) в
 большой матрице
unsigned nb = (n - cnt) / r, end;//n/r число целых блоков размера р в матрице.
                                 //Отнимаем 1(мы его раскладываем) и нумблок(число
уже разложенных блоков) 

for (unsigned ib = 0; ib < nb; ib++)//ход по квадратным блокам
{
    end = ib * r + r;
    for (unsigned i = ib * r; i < end; i++)//по строкам в блоке
    {
        adr = (cnt + i) * (cnt + i + 1) / 2 + cnt;
        for (unsigned j = 0; j <= i; j++, adr++)//по строкам 
        {
            for (unsigned k = 0; k < r; k += 4)
            {
                a[adr] -= b[i*r + k] * b[j*r + k];
                a[adr] -= b[i*r + k + 1] * b[j*r + k + 1];
                a[adr] -= b[i*r + k + 2] * b[j*r + k + 2];
                a[adr] -= b[i*r + k + 3] * b[j*r + k + 3];
            }
        }
    }
}

for (unsigned i = nb * r, end = i + n % r; i < end; i++)//по строкам в блоке  n -
r - numBlock * r
{
    adr = (cnt + i) * (cnt + i + 1) / 2 + cnt;
    for (unsigned j = 0; j <= i; j++, adr++)//по строкам 
    {
        for (unsigned k = 0; k < r; k += 4)
        {
            a[adr] -= b[i*r + k] * b[j*r + k];
            a[adr] -= b[i*r + k + 1] * b[j*r + k + 1];
            a[adr] -= b[i*r + k + 2] * b[j*r + k + 2];
            a[adr] -= b[i*r + k + 3] * b[j*r + k + 3];
        }
    }
}}

    


Ответы

Ответ 1



Код просмотрен в дизассемблере, совершенно разный. Очень сильная оптимизация кода и очень активное использование SSE. На моей машине разница получилась примерно той же - в 3 раза.

Ответ 2



Код конечно есть, но нет явной заготовки для бенчмарка, пришлось ручками "угадывать". Но даже беглый анализ кода показывает явные оптимизации, которые может сделать компилятор. Например, в коде много вызовов вида b[i*r + k]. Если значение всех переменных компилятору неизвестны, он вынужден вычислять одно умножение, потом сложение, потом ещё одно умножение и сложение(вычисленный индекс умножается на размер элемента и складывается с начальным адресом массива). А это не такая уж и тривиальная задача. Если же компилятор может доказать, что r = 4, то он может это все заменить одной инструкцией lea ( и просмотр исходного кода показывает, что похоже это так и происходит). Дальше посмотрим на некоторые участки кода, которые это все может затронуть. // внутри Dot int m = n % 4; for (unsigned i = 0; i < m; i++) sum += a[i] * b[i]; for (unsigned i = m; i

Ответ 3



Соберите программу с выводом ассемблерного листинга (для gcc это флаг -s), и сравните код между вызовами clock() Если компилятор все время выдает одинаковый код, как с переменной, так и с константой - проблемы где-то в условиях эксперимента, так как чудес не бывает. Если же код разный, а производительность важна - у вас два варианта добиваться нужной скорости, изменяя параметры компиляции написать огромное количество if на все возможные значения переменной, которые будут вызывать код с константой.

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

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