Примечание к тестам производительности
От: AlexGin Беларусь  
Дата: 12.03.17 08:47
Оценка: -1 :)))
В этой теме я приводил ссылки на мои тесты производительности, выложенные на гит-хабе:
https://github.com/AlexGin/Math.git

Некоторые наши коллеги заметили, что если в приведенном ниже тесте:
Блокировать / комментировать участок работы с GUI, начинаются жуткие тормоза:
void MainWindow::nativeComputeTrg() // Trigonometry
{
   ui->labelMode->setText("Trigonometry - start of perform...");

   ui->progressBar->setValue(2); // start position of progress-bar

   std::time_t timeStart = std::time(0);
   double resultDbl = 0.0;    
   for(int i = 2; i < N_MULTIPLY; i++)
   {
      for( int j = 0; j < N_GENERATED; j++ )
      {
         double dbMult = (double)(i * 2 * M_PI);
         resultDbl = randDbl[j] + sin(dbMult);
         resultDbl += 0.375;
         outpDbl[j] = resultDbl;
      }
// Если блокировать (комментировать) данный участок - в тригонометрическом тесте видим жуткие тормоза:
      // if (!(i % 100000))
      // {
      //    int val = ui->progressBar->value();
      //    ui->progressBar->setValue(++val);
      //}     
   }
   std::time_t timeStop = std::time(0);
   ui->labelMode->setText("Trigonometry - complete!");

   std::string strMode("Trigonometry");
   ShowDuration(timeStart, timeStop, strMode);
   ui->pushBtnStart->setEnabled(false);
}


Смею заверить товарищей, тормозов не будет — всё будет работать великолепно — если вместо работы с GUI поставить прокачку сообщений OS Windows:
      MSG msg;
      while (::PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
      {    /* Loop of message-processing: */
          ::DispatchMessage(&msg);
      }


// Вариант — для применения вместе с Qt:
    if (!(i % 100000))
       QCoreApplication::processEvents();


ПРИМЕЧАНИЕ:
В результате экспериментов было выяснено — что:
Это явление имеет место в результате бага компилятора MSVC 2015!

Здесь даже не обязательна прокачка сообщений! Так, если вставить в данный участок:
    if (i == 0)
        std::cout << "it's bullshit"; // Мы никогда не достигнем данной точки


Всё восстанавливается.
Также всё восстанавливается, если вычисление sin(dbMult) вынести из внутреннего цикла.
Отредактировано 13.03.2017 7:14 AlexGin . Предыдущая версия . Еще …
Отредактировано 12.03.2017 8:52 AlexGin . Предыдущая версия .
Отредактировано 12.03.2017 8:50 AlexGin . Предыдущая версия .
Отредактировано 12.03.2017 8:49 AlexGin . Предыдущая версия .
Re: Примечание к тестам производительности
От: N. I.  
Дата: 12.03.17 11:56
Оценка: 8 (1) +1
AlexGin:

AG>Смею заверить товарищей, тормозов не будет — всё будет работать великолепно — если вместо работы с GUI поставить прокачку сообщений OS Windows


А почему бы тяжёлое вычисление синуса просто не вынести во внешний цикл, вместо того чтобы надеяться на то, что до этого додумается компилятор? Или, может, оптимизирующие компиляторы у нас теперь дают какие-то документированные стопроцентные гарантии насчёт подобных случаев?
Re[2]: Примечание к тестам производительности
От: AlexGin Беларусь  
Дата: 12.03.17 12:51
Оценка:
Здравствуйте, N. I., Вы писали:

NI>А почему бы тяжёлое вычисление синуса просто не вынести во внешний цикл, вместо того чтобы надеяться на то, что до этого додумается компилятор? Или, может, оптимизирующие компиляторы у нас теперь дают какие-то документированные стопроцентные гарантии насчёт подобных случаев?


Выбросил из внутреннего цикла вычисление синуса — время выполнения тригонометрического теста — практически не поменялось (для MSVC 2015) —
на моей домашней машинке 11 секунд. Завтра посмотрю время выполнения на быстром рабочем компе.
Вот коды консольной версии:
https://github.com/AlexGin/MathConsole/blob/master/MathConcole.cpp
Re: Примечание к тестам производительности
От: pilgrim_ Россия  
Дата: 12.03.17 13:04
Оценка: +5
Здравствуйте, AlexGin, Вы писали:

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

AG>https://github.com/AlexGin/Math.git

AG>Некоторые наши коллеги заметили, что если в приведенном ниже тесте:

AG>Блокировать / комментировать участок работы с GUI, начинаются жуткие тормоза:
  Скрытый текст
AG>
AG>void MainWindow::nativeComputeTrg() // Trigonometry
AG>{
   ui->>labelMode->setText("Trigonometry - start of perform...");

   ui->>progressBar->setValue(2); // start position of progress-bar

AG>   std::time_t timeStart = std::time(0);
AG>   double resultDbl = 0.0;    
AG>   for(int i = 2; i < N_MULTIPLY; i++)
AG>   {
AG>      for( int j = 0; j < N_GENERATED; j++ )
AG>      {
AG>         double dbMult = (double)(i * 2 * M_PI);
AG>         resultDbl = randDbl[j] + sin(dbMult);
AG>         resultDbl += 0.375;
AG>         outpDbl[j] = resultDbl;
AG>      }
AG>// Если блокировать (комментировать) данный участок - в тригонометрическом тесте видим жуткие тормоза:
AG>      // if (!(i % 100000))
AG>      // {
AG>      //    int val = ui->progressBar->value();
AG>      //    ui->progressBar->setValue(++val);
AG>      //}     
AG>   }
AG>   std::time_t timeStop = std::time(0);
   ui->>labelMode->setText("Trigonometry - complete!");

AG>   std::string strMode("Trigonometry");
AG>   ShowDuration(timeStart, timeStop, strMode);
   ui->>pushBtnStart->setEnabled(false);
AG>}
AG>


Разницу в кодогенерации в зависимости от наличия if (xx) во внешнем цикле я показал тут
Автор: pilgrim_
Дата: 11.03.17
, причем условие в if (как и само "тело") может быть любым, в том числе никогда не выполняемым, достаточно вставить:
if(i==0) break;


AG>Смею заверить товарищей, тормозов не будет — всё будет работать великолепно — если вместо работы с GUI поставить прокачку сообщений OS Windows:


Содержимое "тела" в этом блоке if НИКАК не влияет на кодогенерацию, что за магию с "прокачкой сообщений OS Windows" ты себе надумал хз, поставь в тело if'а break; — все будет тоже самое.

...

AG>Итак, соотношение времен выполнения (в пользу MSVC) восстановлено — легким движением руки


Но вот выводы сделаны неверные.
Re[2]: Примечание к тестам производительности
От: AlexGin Беларусь  
Дата: 12.03.17 13:43
Оценка:
Здравствуйте, pilgrim_, Вы писали:

_>Разницу в кодогенерации в зависимости от наличия if (xx) во внешнем цикле я показал тут
Автор: pilgrim_
Дата: 11.03.17
, причем условие в if (как и само "тело") может быть любым, в том числе никогда не выполняемым, достаточно вставить:

_>
_>if(i==0) break;
_>


Кстати, да!
Вы правы, уважаемый pilgrim_!
Очень похоже на bug в нашем любимом компиляторе MSVC 2015

AG>>Смею заверить товарищей, тормозов не будет — всё будет работать великолепно — если вместо работы с GUI поставить прокачку сообщений OS Windows:


_>Содержимое "тела" в этом блоке if НИКАК не влияет на кодогенерацию, что за магию с "прокачкой сообщений OS Windows" ты себе надумал хз, поставь в тело if'а break; — все будет тоже самое.


Магия с прокачкой — это как бы достаточно привычное действие для таких вычислений.
Однако, да — тут собака зарыта!
Я попробую еще вынести эти вычисления в отдельный рабочий поток.

AG>>Итак, соотношение времен выполнения (в пользу MSVC) восстановлено — легким движением руки

_>Но вот выводы сделаны неверные.
По ходу неверные
Скажем так — мои потуги оказались успешными, но собака зарыта — в чём-то другом...

P.S. Сделаю этот тест в working thread и посмотрю на результат!

P.P.S. Кстати — для теста тригонометрии — выбрасывание вычисления синуса из внутреннего цикла во внешний — также даёт эффект:
время прогона становиться нормальным даже без прокачки сообщений...
Re[3]: Примечание к тестам производительности
От: AlexGin Беларусь  
Дата: 12.03.17 14:21
Оценка:
Интересные наблюдения:
Вынос расчёта синуса во внешний цикл, приводит к тому, что можно даже не проводить прокачку сообщений — время всё равно будет считаться корректно.

Возможно, что компилятор сам пытается провести оптимизацию — вынести синус, но это приводит к проблемам — то есть bug MSVC всё-таки есть
Когда же мы вручную выносим расчёт синуса на внешний цикл — всё идёт корректно, и дополнительная прокачка сообщений не требуется...
Отредактировано 12.03.2017 14:29 AlexGin . Предыдущая версия . Еще …
Отредактировано 12.03.2017 14:26 AlexGin . Предыдущая версия .
Re[3]: Примечание к тестам производительности
От: pilgrim_ Россия  
Дата: 12.03.17 14:43
Оценка:
Здравствуйте, AlexGin, Вы писали:

AG>P.P.S. Кстати — для теста тригонометрии — выбрасывание вычисления синуса из внутреннего цикла во внешний — также даёт эффект:

AG>время прогона становиться нормальным даже без прокачки сообщений...

Ну да, это именно то что и ожидалось от оптимизатора — вынести вычисления не зависимые от параметров вложенного цикла на верхний уровень, с чем gcc/icc справились, clang нет (хоть и сделал частичную раскрутку вложенного цикла), vcc(cl) — условно
Re[3]: Примечание к тестам производительности
От: alex_public  
Дата: 12.03.17 16:24
Оценка:
Здравствуйте, AlexGin, Вы писали:

AG>Кстати, да!

AG>Вы правы, уважаемый pilgrim_!
AG>Очень похоже на bug в нашем любимом компиляторе MSVC 2015

Ой, да не уж то осознал, чудо случилось. ) А где же извинения за "попытки притянуть за уши", "пытаетесь навесить ярлык" и т.п.? )))

AG>Магия с прокачкой — это как бы достаточно привычное действие для таких вычислений.


Это не должно быть привычным действием, если ты хоть немного понимаешь происходящее. Никаким циклами сообщений и т.п. ты не можешь ускорить вычисления — это невозможно в принципе. Добавление цикла сообщений может позволить твоему GUI не зависать в процессе проведения вычислений (кстати, это достигается ценой небольшого их замедления) и только. Если ты думаешь по другому, то тебе надо серьёзно переосмыслить всё своё понимание работы современных компьютеров. )
Re[4]: Примечание к тестам производительности
От: alex_public  
Дата: 12.03.17 16:36
Оценка:
Здравствуйте, pilgrim_, Вы писали:

_>Ну да, это именно то что и ожидалось от оптимизатора — вынести вычисления не зависимые от параметров вложенного цикла на верхний уровень, с чем gcc/icc справились, clang нет (хоть и сделал частичную раскрутку вложенного цикла), vcc(cl) — условно


Там кстати не так всё просто. Потому как эффект наблюдается и на таком http://rsdn.org/forum/cpp/6723478.1
Автор: alex_public
Дата: 12.03.17
упрощённом коде, в котором нечего "выносить на другой уровень". ))) Т.е. у msvc это не недоработка оптимизации, а какой-то откровенный баг.
Re[5]: Примечание к тестам производительности
От: pilgrim_ Россия  
Дата: 12.03.17 19:30
Оценка: 8 (1)
Здравствуйте, alex_public, Вы писали:

_>Здравствуйте, pilgrim_, Вы писали:


_>>Ну да, это именно то что и ожидалось от оптимизатора — вынести вычисления не зависимые от параметров вложенного цикла на верхний уровень, с чем gcc/icc справились, clang нет (хоть и сделал частичную раскрутку вложенного цикла), vcc(cl) — условно


_>Там кстати не так всё просто. Потому как эффект наблюдается и на таком http://rsdn.org/forum/cpp/6723478.1
Автор: alex_public
Дата: 12.03.17
упрощённом коде, в котором нечего "выносить на другой уровень". ))) Т.е. у msvc это не недоработка оптимизации, а какой-то откровенный баг.


Посмотрел, да баг комплексный
https://godbolt.org/g/cv4vTE

f1 (без If) — по старинке:
f1, COMDAT PROC
        xor      r8d, r8d
        lea      r10, OFFSET FLAT:__ImageBase
        npad     6
$LL4@f1:
        mov      eax, DWORD PTR ?inputs@@3PAHA[r8+r10]
        mov      ecx, 4999999       ; 004c4b3fH
        lea      r9d, DWORD PTR [rax+rax]
        lea      eax, DWORD PTR [rax+rax*2]
$LL7@f1:
        mov      edx, eax
        add      eax, r9d
        sub      rcx, 1
        jne      SHORT $LL7@f1
        mov      DWORD PTR ?outputs@@3PAHA[r8+r10], edx
        add      r8, 4
        cmp      r8, 4000   ; 00000fa0H
        jl       SHORT $LL4@f1
        ret      0
f1 ENDP


f2 (с if упрощенным до if(i==0) break; ) — с SSE:
f2, COMDAT PROC
        mov      r8d, DWORD PTR __isa_available
        lea      r9, OFFSET FLAT:__ImageBase
        mov      edx, 2
        npad     13
$LL4@f2:
        xor      eax, eax
        movd     xmm1, edx
        pshufd   xmm1, xmm1, 0
        cmp      r8d, 2
        jl       SHORT $LL24@f2
$LL7@f2:
        movdqu   xmm0, XMMWORD PTR ?inputs@@3PAHA[rax+r9]
        pmulld   xmm0, xmm1
        movdqu   XMMWORD PTR ?outputs@@3PAHA[rax+r9], xmm0
        movdqu   xmm0, XMMWORD PTR ?inputs@@3PAHA[rax+r9+16]
        pmulld   xmm0, xmm1
        movdqu   XMMWORD PTR ?outputs@@3PAHA[rax+r9+16], xmm0
        add      rax, 32              ; 00000020H
        cmp      rax, 4000                ; 00000fa0H
        jl       SHORT $LL7@f2
        jmp      SHORT $LN6@f2
$LL24@f2:
        mov      ecx, DWORD PTR ?inputs@@3PAHA[rax+r9]
        imul     ecx, edx
        mov      DWORD PTR ?outputs@@3PAHA[rax+r9], ecx
        add      rax, 4
        cmp      rax, 4000                ; 00000fa0H
        jl       SHORT $LL24@f2
$LN6@f2:
        test     edx, edx
        je       SHORT $LN12@f2
        inc      edx
        cmp      edx, 10000000            ; 00989680H
        jl       SHORT $LL4@f2
$LN12@f2:
        ret      0
f2 ENDP


т.е. наличие if принуждает cl использовать SSE (+ небольшая раскрутка цикла — в итоге всего 125 итераций вместо 1000).
И кстати, в варианте с синусом этот if, помимо того что синус вычислялся во внешнем цикле, также приводил к использованию SSE для сложения (по 4 дабла за раз), в отличие от варианта без if.

Что характерно, в обоих вариантах с if (ссинусом и без) "мертвое" условие if(i==0) break; приводит к генерации if (true) continue;, вместо ожидаемого удаления нафик.
        test     edx, edx //тогда как edx никогда не будет нулем: mov      edx, 2
        je       SHORT $LN12@f2
Re[5]: Примечание к тестам производительности
От: N. I.  
Дата: 12.03.17 19:31
Оценка: 6 (1) :)
alex_public:

_>>Ну да, это именно то что и ожидалось от оптимизатора — вынести вычисления не зависимые от параметров вложенного цикла на верхний уровень, с чем gcc/icc справились, clang нет (хоть и сделал частичную раскрутку вложенного цикла), vcc(cl) — условно


_>Там кстати не так всё просто. Потому как эффект наблюдается и на таком http://rsdn.org/forum/cpp/6723478.1
Автор: alex_public
Дата: 12.03.17
упрощённом коде, в котором нечего "выносить на другой уровень". )))


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

void f1()
{
    for (int i = 2; i < N_MULTIPLY; i++)
    {
        float const multiplier = static_cast<float>(i);
        for (int j = 0; j < N_GENERATED; j++)
            outputs[j] = inputs[j] * multiplier;
    }
}

Однако, если хочется ещё большей комичности происходящего, можно попробовать в f1 заменить

outputs[j] = inputs[j] * i;

на

outputs[j] = i;

У меня это приводит к увеличению времени выполнения f1 примерно на 20%.
Отредактировано 12.03.2017 20:22 N. I. . Предыдущая версия .
Re: Примечание к тестам производительности
От: AlexGin Беларусь  
Дата: 13.03.17 07:24
Оценка:
Здравствуйте, AlexGin, Вы писали:

ПРИМЕЧАНИЕ:
В результате экспериментов было выяснено — что:
Это явление имеет место в результате бага компилятора MSVC 2015!

Здесь даже не обязательна прокачка сообщений! Так, если вставить в данный участок:
    if (i == 0)
        std::cout << "it's bullshit"; // Мы никогда не достигнем данной точки


Всё восстанавливается.
Также всё восстанавливается, если вычисление sin(dbMult) вынести из внутреннего цикла.

Sorry, если во время дискуссии кого-либо обидел,
например того же alex_public
Следует заметить, что тут у меня не было моего злого умысла, просто желание разобраться по сути!
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.