Re[2]: Замена типа переменной long int на long long без вычислений замедляет код
От: getch  
Дата: 31.08.13 07:23
Оценка:
KA>Давай уже генери asm код и показывай что там такое!
Сгенерил код для исходника в первом посте. Ассемблера не знаю, поэтому приложил варианты сразу от нескольких ключей (/FA, /FAs, /FAcs).
Отличий совсем немного:


Re[2]: Замена типа переменной long int на long long без вычислений замедляет код
От: Кодт Россия  
Дата: 31.08.13 17:39
Оценка:
Здравствуйте, getch, Вы писали:

<>

Подозреваю, что дело в оптимизаторе.
Если нет места, где счётчик используется, то этот счётчик можно просто выкинуть.
Если место есть (и компилятор не смог догадаться, что условие всегда ложно; либо догадался, но далеко идущих выводов не сделал), — то не выкидывает и молотит 32- или 64-битную арифметику соответственно.
Перекуём баги на фичи!
Re[3]: Замена типа переменной long int на long long без вычислений замедляет код
От: getch  
Дата: 31.08.13 19:38
Оценка:
К>Если нет места, где счётчик используется, то этот счётчик можно просто выкинуть.

Поясните дилетанту, что имеется в виду под счетчиком? Как помочь компилятору не тупить?

К сожалению, походу обсуждения родилась непонятная проблема, которая актуальна и для int и для long:
G>Т.е. у меня замедление, а у вас наоборот — ускорение.

G>Но самое странное, когда (чтобы не перепутать случаи) добавил эти строчки:


G>
int main(int argc, char* argv[])
G>{
G>//  unsigned long int CalcBars = 0, CurrentBar = 100;
G>// Замена на эту строчку замедляет выполнение программы на ~ 15%.
G>  unsigned long long CalcBars = 0, CurrentBar = 100;

G>  std::cout << "SizeOf(Type) = " << sizeof(unsigned long long) << " bytes." << std::endl; // добавил
G>  std::cout << "SizeOf(CalcBars) = " << sizeof(&CalcBars) << " bytes." << std::endl; // добавил
G>  
G>  for (long long i = 0; i < AMOUNT; i++)
G>  .....
G>

G>Результат стал таким:
G>
SizeOf(Type) = 8 bytes.
G>SizeOf(CalcBars) = 4 bytes.
G>Finished 1%, Time = 4 / 489
G>Average Velocity = 20222446 th
G>Finished 2%, Time = 9 / 485
G>Average Velocity = 20189783 th
G>Finished 3%, Time = 14 / 480
G>...

G>Т.е. замедлилось еще в > 2 раза.

G>Попробовал на удаленной машине запустить — аналогично.


G>Помогите разобраться, почему этот бред происходит?!
Re[4]: Замена типа переменной long int на long long без вычислений замедляет код
От: Кодт Россия  
Дата: 31.08.13 21:09
Оценка:
Здравствуйте, getch, Вы писали:

К>>Если нет места, где счётчик используется, то этот счётчик можно просто выкинуть.

G>Поясните дилетанту, что имеется в виду под счетчиком? Как помочь компилятору не тупить?

А, не, это не счётчик. Но всё равно.
Если CalcBars используется, то он передаётся в функцию, и это стоит определённых денег.
Если не используется, то не передаётся.


G>К сожалению, походу обсуждения родилась непонятная проблема, которая актуальна и для int и для long:

G>>Т.е. у меня замедление, а у вас наоборот — ускорение.

G>>Но самое странное, когда (чтобы не перепутать случаи) добавил эти строчки:


G>>[ccode]

G>> std::cout << "SizeOf(Type) = " << sizeof(unsigned long long) << " bytes." << std::endl; // добавил
G>> std::cout << "SizeOf(CalcBars) = " << sizeof(&CalcBars) << " bytes." << std::endl; // добавил
G>>


Ничего странного нет.
Во-первых, sizeof(указатель-на-CalcBars) = sizeof(void*) = 4 байта на x86.
Во-вторых, пока ты не брал адрес, компилятор имел право держать эту переменную "в уме", в каком-то из 64-битных регистров. А с адресом, ты приземлил переменную в локальное хранилище, на стек. Вот тебе, бабушка, и двукратные тормоза.
Перекуём баги на фичи!
Re[5]: Замена типа переменной long int на long long без вычислений замедляет код
От: Кодт Россия  
Дата: 31.08.13 21:14
Оценка:
Здравствуйте, Кодт, Вы писали:

К>Если CalcBars используется, то он передаётся в функцию, и это стоит определённых денег.

К>Если не используется, то не передаётся.

При включённой оптимизации, разумеется.
На ассемблерном листинге видно, что функция Print была проинлайнена внутрь main.
Ну а дальше компилятор сам решает, какие локальные переменные используются, а какие можно было бы и выкинуть.
Перекуём баги на фичи!
Re[5]: Замена типа переменной long int на long long без вычислений замедляет код
От: getch  
Дата: 31.08.13 21:20
Оценка:
К>Во-вторых, пока ты не брал адрес, компилятор имел право держать эту переменную "в уме", в каком-то из 64-битных регистров. А с адресом, ты приземлил переменную в локальное хранилище, на стек. Вот тебе, бабушка, и двукратные тормоза.

Да даже если делаю так:

std::cout << "SizeOf(Type) = " << sizeof(unsigned long long) << " bytes." << std::endl;
// std::cout << "SizeOf(CalcBars) = " << sizeof(&CalcBars) << " bytes." << std::endl; // закомментировал обращение к переменной


Двухкратное просидание производительности никуда не девается.

Понимаю, что можно теоретизировать и высказывать различные гипотезы. Но многие из них разбиваются о реальность, если все же запустить пример у себя. Попробуйте просто.

Почему тормоза таки происходят? Если нет объяснения, то, может, есть рецепт, как избежать этих проблем?
Re[6]: Замена типа переменной long int на long long без вычислений замедляет код
От: getch  
Дата: 31.08.13 21:23
Оценка:
К>На ассемблерном листинге видно, что функция Print была проинлайнена внутрь main.
К>Ну а дальше компилятор сам решает, какие локальные переменные используются, а какие можно было бы и выкинуть.

Непонятно, почему от типа переменной зависит решение компилятора, используется ли она или нет?
Тут очень многие, наверное, используют VS 2012. Как правильно делать-то, чтобы идиотские тормоза не всплывали?
Re[7]: Замена типа переменной long int на long long без вычислений замедляет код
От: Кодт Россия  
Дата: 31.08.13 21:35
Оценка:
Здравствуйте, getch, Вы писали:

G>Непонятно, почему от типа переменной зависит решение компилятора, используется ли она или нет?


Используется она или нет, зависит от наличия sizeof(&CalcBars) и if(does-not-matter) cout << CalcBars.

Если есть адрес — будет переменная на стеке. Это дороже всего.
Если нет адреса — будет в регистре. Это дешевле.
Если не используется — то вообще прекрасно.

Чем дороже, тем ярче разница между long long и long.

G>Тут очень многие, наверное, используют VS 2012. Как правильно делать-то, чтобы идиотские тормоза не всплывали?


Так уже сказал watchmaker — профилировать не что попало надуманное, а реальный код.
Или сломать оптимизацию. Может быть, __declspec (noinline) Print будет достаточно.
Перекуём баги на фичи!
Re[8]: Замена типа переменной long int на long long без вычислений замедляет код
От: getch  
Дата: 31.08.13 21:50
Оценка:
К>Так уже сказал watchmaker — профилировать не что попало надуманное, а реальный код.
К>Или сломать оптимизацию. Может быть, __declspec (noinline) Print будет достаточно.
Говорил уже, как в нагруженном коде получил тормоза. Потом методом исключения создал этот пример. Если этот пример начинаю загружать различными вычислениями, от этого разницы в тормозах никуда не уходит.
На очень сильно загруженной вычислениями задаче происходит падение на 5%. При этом замена int на long делается для переменной, которая практически ну никак не участвует в расчетах.
И прошу заметить, что средняя скорость на любом интервале падает одинаково — 5%. Т.е. эффект такой же, как будто на 5% понизил частоту процессора.

А замедление в два раза после добавления безобидной строчки cout перед циклом (без обращения к CalcBars) — это вообще безобразие.

Никого не хочу задеть, но, судя по ответам, складывается впечатление, что никого особо не заботит падение производительности своих программ. Работает — и хорошо.

Все же хотел бы разобраться, не закрывая глаза на явные странности.
Re: Замена типа переменной long int на long long без вычислений замедляет код на
От: getch  
Дата: 31.08.13 21:57
Оценка:
В ходе обсуждения, возможно, потерялась сама суть. Поэтому повторю. Есть код:

#include <iostream>
#include <windows.h>

void Print( long long Amount, unsigned long long CalcBars )
{
  static int ProcentCount = 0;
  static long long Count = 0, CountP = 0, Procent = Amount / 100;
  static unsigned long int StartTime = GetTickCount();

  CountP++;

    if (CountP > Procent)
    {
      Count += CountP;

      CountP = 0;
      ProcentCount++;
      
   unsigned long int DeltaTime = GetTickCount() - StartTime;

      if (Count != 0)
     std::cout << "Finished " << ProcentCount << "%, Time = " << DeltaTime / 1000 <<
                     " / " << (int)((double)DeltaTime * (Amount - Count) / (Count * 1000.)) << std::endl;

// Замедление в основном происходит здесь    
      if (DeltaTime != 0) 
        std::cout << "Average Velocity = " << CalcBars / DeltaTime << " thousands bars/sec." << std::endl;
    }
  
 return;
}

#define AMOUNT 100000000000LL

int main(int argc, char* argv[])
{
  unsigned long int CalcBars = 0, CurrentBar = 100;
//  unsigned long long CalcBars = 0, CurrentBar = 100; // раскомментировав эту строчу, получите замедление на 15%.

//  std::cout << "SizeOf(Type) = " << sizeof(unsigned long long) << " bytes." << std::endl; // раскомментировав эту строчу, получите замедление в два раза.

  
  for (long long i = 0; i < AMOUNT; i++)
  {
    Print(AMOUNT, CalcBars);

    CalcBars += CurrentBar;
  }

  return(0);
}


Запускается все под Win32-конфигурацией, т.к. Win64 объективно работает по какой-то причине значительно медленней.
Re[9]: Замена типа переменной long int на long long без вычислений замедляет код
От: Ops Россия  
Дата: 01.09.13 00:12
Оценка: 1 (1)
Здравствуйте, getch, Вы писали:

G>А замедление в два раза после добавления безобидной строчки cout перед циклом (без обращения к CalcBars) — это вообще безобразие.


Это ввод-вывод-то безобидный, да еще с endl, который сбрасывает буфферы?
Переубедить Вас, к сожалению, мне не удастся, поэтому сразу перейду к оскорблениям.
Re[2]: Замена типа переменной long int на long long без вычислений замедляет код
От: cserg  
Дата: 01.09.13 00:16
Оценка:
Здравствуйте, getch, Вы писали:

Судя по ассемблерным листингам, замедление происходит в
CalcBars += CurrentBar;

из-за дополнительного adc для unsigned long long.
Re[10]: Замена типа переменной long int на long long без вычислений замедляет ко
От: getch  
Дата: 01.09.13 00:43
Оценка:
Ops>Это ввод-вывод-то безобидный, да еще с endl, который сбрасывает буфферы?

Да, совершенно безобидный, т.к. вызывается всего один раз и перед циклом for:

//  std::cout << "SizeOf(Type) = " << sizeof(unsigned long long) << " bytes." << std::endl; // раскомментировав эту строчу, получите замедление в два раза.

//  std::cout << std::endl; // А ВОТ ТАК НЕ ЗАМЕДЛЯЕТ!

  
  for (long long i = 0; i < AMOUNT; i++)


Либо же я что-то глубоко не понимаю, раз считаю странным замедление скорости выполнения всей программы в два раза лишь от того, что в начале программы что-то вывели на консоль.
Чтобы мне не быть голословным, убедитесь в этом у себя, запустив.
Re[3]: Замена типа переменной long int на long long без вычислений замедляет код
От: getch  
Дата: 01.09.13 01:54
Оценка:
Здравствуйте, cserg, Вы писали:
C>Судя по ассемблерным листингам, замедление происходит в
C>
CalcBars += CurrentBar;

C>из-за дополнительного adc для unsigned long long.

Проверил. Действительно, в этом месте. Спасибо! Есть ли вариант ускорения подобных выражений для long?

P.S. Осталось разобраться с замедлением в два раза...
Re[2]: Замена типа переменной long int на long long без вычислений замедляет код
От: Kubyshev Andrey  
Дата: 01.09.13 03:24
Оценка:
Я скачал и запустил,
Мне кажется у тебя неправильные измерительные приборы.
Вот мой вывод, колебания просто огромны, по ним ничего нельзя замерить.


Finished 1%, Time = 2 / 296
Average Velocity = 405927 thousands bars/se
Finished 2%, Time = 5 / 292
Average Velocity = 406946 thousands bars/se
Finished 3%, Time = 8 / 290
Average Velocity = 405927 thousands bars/se
Finished 4%, Time = 11 / 287
Average Velocity = 47411 thousands bars/sec
Finished 5%, Time = 14 / 284
Average Velocity = 118983 thousands bars/se
Finished 6%, Time = 18 / 282
Average Velocity = 166622 thousands bars/se
Finished 7%, Time = 21 / 279
Average Velocity = 200604 thousands bars/se
Finished 8%, Time = 24 / 276
Average Velocity = 47289 thousands bars/sec
Finished 9%, Time = 27 / 273
Average Velocity = 86992 thousands bars/sec
Finished 10%, Time = 30 / 270
Average Velocity = 118800 thousands bars/se
Finished 11%, Time = 33 / 267
Average Velocity = 14780 thousands bars/sec
Finished 12%, Time = 36 / 264
Average Velocity = 47268 thousands bars/sec
Finished 13%, Time = 39 / 261
Average Velocity = 74809 thousands bars/sec
Finished 14%, Time = 42 / 258
Average Velocity = 98296 thousands bars/sec
Finished 15%, Time = 45 / 255
Average Velocity = 23432 thousands bars/sec
Finished 16%, Time = 48 / 252
Average Velocity = 47243 thousands bars/sec
Finished 17%, Time = 51 / 249
Average Velocity = 68248 thousands bars/sec
Finished 18%, Time = 54 / 246
Average Velocity = 7554 thousands bars/sec.
Finished 19%, Time = 57 / 243
Re[3]: Замена типа переменной long int на long long без вычислений замедляет код
От: getch  
Дата: 01.09.13 07:58
Оценка:
KA>Я скачал и запустил,
KA>Мне кажется у тебя неправильные измерительные приборы.
KA>Вот мой вывод, колебания просто огромны, по ним ничего нельзя замерить.

Измерительные приборы как раз правильные — скорость исполнения на любом этапе выполнения программы одинаковая. Расчетное время окончания вычислений в самом начале выполнения программы полностью совпадает с фактическим. Колебания практически отсутствуют. Возможно, вас ввел в заблуждение сильные изменения показателя "Average Velocity" в режиме unsigned long int. Это простое переполнение типа (100 млрд не вписываются). В режиме unsigned long long переполнения нет — показатель "Average Velocity" стабильный.

В программе нет детских ошибок. Все перепроверил перед тем, как постить. Посмотирите внимательно код — он очень простой.

unsigned long int:
Finished 1%, Time = 1 / 191
Average Velocity = 628620 thousands bars/se
......
Finished 99%, Time = 190 / 1 - расчетное время (191 сек.) совпадает с фактическим.
Average Velocity = 525 thousands bars/sec. - результат переполнения unsigned long int


unsigned long long:
Finished 1%, Time = 2 / 220
Average Velocity = 44843049 thousands bars/sec.
......
[b]Finished 99%, Time = 218 / 2[b] - расчетное время (220 сек.) совпадает с фактическим.
Average Velocity = 45303533 thousands bars/sec. - показатель стабилен: нет переполнения unsigned long long
Re[4]: Замена типа переменной long int на long long без вычислений замедляет код
От: niXman Ниоткуда https://github.com/niXman
Дата: 01.09.13 08:08
Оценка:
Здравствуйте, getch, Вы писали:

странно все это... вы говорите, что производительность критична, но при этом, вы реализовали задачу на уровне первого класса %)
если вы таки не лжете, и производительность таки критична, скажите, почему реализация в один поток? или, что лучше, почему не используется OMP? или, что гораздо лучше — OMP+SIMD.
или ваша задача не имеет практического применения? это просто исследовательская работа на тему "почему 64ех битные операции на 32ух битном хосте выполняются медленнее?"
пачка бумаги А4 стОит 2000 р, в ней 500 листов. получается, лист обычной бумаги стОит дороже имперского рубля =)
Re[9]: Замена типа переменной long int на long long без вычислений замедляет код
От: Кодт Россия  
Дата: 01.09.13 09:01
Оценка:
Здравствуйте, getch, Вы писали:

G>На очень сильно загруженной вычислениями задаче происходит падение на 5%. При этом замена int на long делается для переменной, которая практически ну никак не участвует в расчетах.


Ну как же она не используется, если в цикле делается её инкремент на каждой итерации.
Дальше всё упирается в оптимизирующий компилятор: сможет он её выбросить за ненадобностью, приземлить в регистр или приземлить на стек.

G>А замедление в два раза после добавления безобидной строчки cout перед циклом (без обращения к CalcBars) — это вообще безобразие.


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

G>Никого не хочу задеть, но, судя по ответам, складывается впечатление, что никого особо не заботит падение производительности своих программ. Работает — и хорошо.


Ешё как заботит. За один процент бьёмся.
Я вот, например, тремя профайлерами воспользовался, чтобы найти узкие места. Несколько мест изничтожил, одно само внезапно испарилось, зато пара мест не поддаётся пониманию.
Например, код
bool isZero = some->var == 0;

три инструкции (воспроизвожу по памяти)
mov edx, dword ptr [some+var]
test edx, edx
setz cl

казалось бы, если уж тупить, то в загрузке в регистр, а не в проверке и записи — но профайлеры тычут пальцем в test, там почти на два порядка !!! больше времени тратится, чем в предшествующей и последующей инструкциях.
Перекуём баги на фичи!
Re[5]: Замена типа переменной long int на long long без вычислений замедляет код
От: getch  
Дата: 01.09.13 09:41
Оценка:
X>странно все это... вы говорите, что производительность критична, но при этом, вы реализовали задачу на уровне первого класса %)
X>если вы таки не лжете, и производительность таки критична, скажите, почему реализация в один поток? или, что лучше, почему не используется OMP? или, что гораздо лучше — OMP+SIMD.
X>или ваша задача не имеет практического применения? это просто исследовательская работа на тему "почему 64ех битные операции на 32ух битном хосте выполняются медленнее?"почему 64ех битные операции на 32ух битном хосте выполняются медленнее?

Все как раз очень практично. Дело в том, что мне нужно оптимизировать именно однопоточный вариант, т.к. далее планирую ускорить расчеты в ~1000 раз через запуск в облаке (режим мат. моделирования). Там C-подобный однопоточный язык, но для облака в целях безопасности не разрешается использование DLL. Зато есть встроенная поддержка OpenCL, которую потом добавлю, когда разберусь с ней.

Поэтому такой десткий сад на первый взгляд. К тому же я дилетант в программировании.
Re[10]: Замена типа переменной long int на long long без вычислений замедляет ко
От: getch  
Дата: 01.09.13 10:05
Оценка:
Здравствуйте, Кодт, Вы писали:

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


G>>На очень сильно загруженной вычислениями задаче происходит падение на 5%. При этом замена int на long делается для переменной, которая практически ну никак не участвует в расчетах.
К>Ну как же она не используется, если в цикле делается её инкремент на каждой итерации.
К>Дальше всё упирается в оптимизирующий компилятор: сможет он её выбросить за ненадобностью, приземлить в регистр или приземлить на стек.

Да, в этом разобрались, спасибо.

G>>А замедление в два раза после добавления безобидной строчки cout перед циклом (без обращения к CalcBars) — это вообще безобразие.
К>Вот это действительно странно.
К>Попробуй сравнить дизасмы программ со строчкой и без неё. Скорее всего, у оптимизатора крыша поехала.

Приложил Asm. К сожалению, не знаю ассемблера. Странно только, что длина Asm варианта с выводом на консоль (который в 2 раза медленней) меньше, чем без вывода.

G>>Никого не хочу задеть, но, судя по ответам, складывается впечатление, что никого особо не заботит падение производительности своих программ. Работает — и хорошо.
К>Ешё как заботит. За один процент бьёмся.
К>Я вот, например, тремя профайлерами воспользовался, чтобы найти узкие места. Несколько мест изничтожил, одно само внезапно испарилось, зато пара мест не поддаётся пониманию.

По наивности своей идеализировал возможности столь популярного оптимизатора компилятора VSC++. Теперь понимаю, что оптимизатор далеко не идеал.
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.