Провёл на досуге исследование на тему сообщение об ошибках с помощью исключений против сообщения об ошибках с помощью возвращаемых значений с т.з. скорости выполнения кода и кодогенерации. Возможно кому-то ещё будет интересно.
Цель можно охарактеризовать примерно так: исследовать скорость работы некоторых ключевых паттернов кода, реализованных с применением исключений для сообщения об ошибках, или с помощью возвращаемых значений.
Простой случай
Что б не было скучно сразу начну с некоторых результатов.
rv (success)
rv (fail)
ex (success)
ex (w/o try)
ex (fail)
msvc71
12
8
4
4
66928
msvc8
12
8
4
4
17140
gcc41
12
20
8
8
50476
gcc32
4
10
3
1
267444
Сейчас поясню, что что значит.
rv (success)
вызов функции, которая сообщает об ошибке с помощью возвращаемого значения (rv – return value). Функция возвращает значение, что ошибки не произошло – реально возвращается bool true. После вызова происходит проверка возвращаемого значения. Сама функция никакой работы не делает, т.е. пустая
rv (fail)
аналогично предыдущему, только функция возвращает неудачу — false
ex (success)
функция сообщает об ошибке с помощью исключения (ex — exception), соответственно её вызов обрамлён в try/catch. Функция исключение не кидает
ex (w/o try)
аналогично предыдущему, только вызов функции не обрамлён в try/catch — что бы увидеть влияние входа/выхода в try блок
ex (fail)
ex (success), только функция кидает исключение
msvc71
WinXP on Pentium4, msvc7.1
msvc8
WinXP on Pentium4, msvc8sp1
gcc41
MinGW on Pentium4, gcc4.1.1
gcc32
Linux2.4 on Pentium4, gcc3.2.3
Компилировал со всеми оптимизациями, при этом следил, чтобы исследуемые функции _не_ встраивались. Время замерял с помощью rdtsc, соответственно результаты приведены в тактах процессора. Каждый тест запускал 1000 раз, потом находил минимальный результат.
Сразу оговорюсь, что на полную объективность не претендую. Какие результаты получил в своих конкретных условиях, при определённых настройках компилятора и т.д., такие и привожу. Но тем не менее, уверен, что основные тенденции уловлены правильно. Тем более сгенерированный код во всех случаях изучался на правдоподобность.
Выводы
Выводы для такого тривиального случая с простыми функциями.
Исключения быстрее, когда реально исключения не кидаются. За счёт чего? За счёт дополнительных инструкций на проверку возвращаемого значения, и на переход (jmp).
Вход/выход в try блок [как ни странно] не влияет на производительность.
Кидание исключения очень дорого. Порядка 20мкс под Win/msvc71 и порядка 100мкс под Linux (почему так долго под Linux не знаю). А под msvc8 ухитрились сделать за 7мкс, хотя они всё равно реализованы поверх SEH.
Более интересный случай
В первом случае у исключений было небольшое приемущество, точнее немного неадекватный случай, т.к. программа не выделяла никаких ресурсов. Когда выделяются ресурсы для исключений начинается самое интересное, т.к. приходится помещать фреймы на стек, следить за созданием объектов и т.д. Теперь замерим скорость с ресурсами. Сразу поясню, что я подразумеваю под ресурсами. В условиях исключений под ресурсом подразумеваю объект класса с нетривиальным деструктором (в котором происходит освобождение ресурса) и с конструктором (который выделяет ресурс, при этом может провалиться и кинуть исключение). В условиях возвращаемых значений я рассмотрел 2 варианта. Первый – в стиле С++ — класс, в деструкторе также освобождает ресурс, а конструктор заменил на функцию bool init(), которая выделяет ресурс и сообщает о успешности. Второй вариант – в стиле С – ресурс представляет из себя некий int, и 2 функции int constructor(int*) и void destructor(int). Все функции выделения/освобождения ресурса сами по себе ничего не делают, кроме как вызываются.
Функции тестировал те же, только отличие в том, что функция в начале работы «выделяет» ресурс, потом «освобождает» ресурс и возвращается. Соответственно в присутствии исключений, т.к. у локального объекта есть деструктор, компилятору приходится создавать фреймы на стеке и т.д. Замерял только в «успешном» случае, т.к. если будет кидаться исключение, то уже понятно, что это будет настолько долго, что и сравнивать нечего.
Собственно результаты:
с
с++/rv
с++/ex
msvc71
24
24
24
msvc8
24
24
20
gcc41
32
88
84
gcc32
26
32
22
Для большей правдоподобности и интриги код в стиле С компилировал именно как С код, а не С++, соотв. пользовался не g++, а gcc.
Выводы
Выводы для случая с функциями с выделением ресурса.
Под msvc71 всё ровненько – даже не интерсно. Т.е. накладные расходы на проверки возвращаемых значений скомпенсировались накладными расходами на поддержку исключений.
gcc4.1.1 под mingw как-то совсем неадекватно себя ведёт при необходимости освобождать ресурсы... (почему так не знаю)
gcc3.2.2 под Linux – результаты примерно одинаковые. Только в С++ при использовании возвращаемых значений накладываются сразу 2 типа накладных расходов, поэтому получается значительно дольше.
Поведение в асимптотике
Теперь следующий тест. Очень много функций и очень много ресурсов. Чтобы поглядеть как ведёт себя производительность в асимптотике.
Функции такого вида:
F6()
{
повторить 6 раз // в коде это записано не в виде цикла, а развёрнуто
{
выделяем 6 ресурсов
вызываем F5();
}
}
Соответственно функция F5 аналогичная, только вызывает F4, и так далее. F0 – ничего не делает – сразу возвращается.
В С варианте это было записано примерно так:
int f6()
{
int o1, o2;
int res = 0;
if (!constructor(&o1)) goto end;
if (!constructor(&o2)) goto do1;
if (!f5()) goto do2;
res = 1;
do2: destructor(o2);
do1: destructor(o1);
end: return res;
}
только экстраполировано на выделение 6 ресурсов и это всё повторяется 6 раз.
В варианте с++/rv:
bool f6()
{
obj o1, o2;
if (!o1.init()) return false;
if (!o2.init()) return false;
if (!f5()) return false;
return true;
}
В варианте с++/ex:
bool f6()
{
obj o1, o2;
f5();
}
Результаты:
с
с++/rv
с++/ex
msvc71
6363004
6231060
6182104
msvc8
6361992
6258460
6181740
gcc41
9016944
9760084
9385664
gcc32
8510423
6640676
5545090
Выводы
Ну что тут можно сказать. В принципе код с исключениями немного быстрее, но совсем немного (но это уже приятно ). Единственное он медленнее на gcc41, но вообще как-то совсем неадекватно сгенерировал код...
Ещё тест
Ещё я проводил аналогичный тест, но во время выполнения эмулировал возникновение ошибки (либо кидалось исключение, либо возвращался false). Результаты для краткости приводить не буду. Но суть такая, что в данном случае код с исключениями всё равно остаётся быстрее, т.к. время работы функции очень большое и за ним время кидания исключения нивелируется.
Размер кода
Так же есть ещё один аспект, влияющий на производительность, но который ускользает при таких замерах – размер сгенерированного кода. Чем больше кода – тем больше промахов кэша и переходов через границы страницы памяти – это может достаточно сильно влиять на производительность. Соответственно я замерил размер кода для одной функции типа f6(), которые я приводил выше. Результаты:
с
с++/rv
с++/ex
instr
386
442
232
size
1320
2359
1329
Замерял на msvc71. instr – кол-во ассемблерных команд. Size – размер функции в байтах.
У исключений значительно меньше инструкций, тем не менее размер кода равен размеру кода на С с возвращаемыми значениями. Как ни странно... Связано с тем, что команды там больше по размеру. Код на С++ с возвращаемыми значениями тут сильно проигрывает – так там инструкции и на проверки возвращаемых значений и на поддержку исключений.
Тест без ресурсов
Так же провёл следующий тест. Функции аналогичные предыдущим, но не выделяются ресурсы – просто идёт много форвардов на другие функции, а те на другие и т.д.
с
с++/rv
с++/ex
msvc71
970404
746460
653144
msvc8
970404
746464
653144
gcc41
858432
858444
765124
gcc32
705457
633707
521712
Размер кода:
с
с++/rv
с++/ex
instr
22
21
6
size
59
58
25
Выводы
Тут картина уже интереснее – исключения значительно вырываются вперёд. На msvc на 50% (!) на gcc на 35% (!). По размеру код на 136% (!).
Тут можно сделать важный вывод – если есть функции, в которых нет локальных переменных/аргументов с нетривиальными деструкторами – при использовании исключений они будут работать до 50% быстрее и размер кода будет меньше до 136%.
smart_ptr vs. smart_ptr&
Попутно я исследовал следующий вопрос – имеет ли смысл передавать умные указатели по ссылке вместо передачи по значению. Мы зачастую (ну я по-крайней мере ) передаю просто boost::shared_ptr без ссылок – по значению.
Функции аналогичные функциям из предыдущего теста – просто много форвардинга вызовов функций, без выделения ресурсов, но при этом между функцими передавался один boost::intrusive_ptr, в одном случае по значению, в другом по ссылке.
boost::intrusive_ptr
boost::intrusive_ptr
msvc71
2226100
746492
msvc8
2237212
746492
gcc41
3153288
783844
gcc32
2301066
550930
Размер кода:
boost::intrusive_ptr
boost::intrusive_ptr
instr
76
17
size
231
45
Выводы
Разница поразительная. При передаче по ссылке скорость выросла в 4.18 раза, размер кода уменьшился в 5.13 раза. Накладные расходы идут не только на увеличения/уменьшения счётчика, но так же на поддержание фрейма на стеке, и на контроль конструирования объектов – что бы знать какие рушить при исключении.
Более реалистичный тест
Более реалистичный тест по количеству ресурсов и вызовов функций. С исключениями тестовые функции выглядят так:
void f6()
{
obj o1, o2, o3;
f5();
f5();
}
f5() выглядит аналогично, только вызывает f4() и т.д. f0 ничего не делает. Код с возвращаемыми значениями аналогичный, только... переписан соответственно с возвращаемыми значениями. Получается 64 вызова самой вложенной функции и примерно 200 объектов с деструкторами на тест, что более менее сравнимо с некой функциональностью в современной программе.
Смотрим, что получилось:
с
с++/rv
с++/ex
msvc71
4772
4804
4652
msvc8
4768
4792
4704
gcc41
7604
9924
8924
gcc32
5510
5127
4451
Тут получается, что исключения чуточку впереди (не считая gcc41 под mingw, который опять неадекватно сгенерировал код).
Далее сделал следующее – из 100 вызовов функции 1 проваливается (кидается исключение, или возвращается код возврата с ошибкой), и я беру не менимальное из всех замеров время, а усредняю по всем 100 замерам. Т.о. эмулируется ситуация, что в программе иногда происходит ошибка.
Смотрим, что получилось:
с
с++/rv
с++/ex
msvc71
4816
5195
4946
msvc8
4813
5384
4843
gcc41
7682
10059
9276
gcc32
5976
5135
7505
Тут уже видно, что исключения и возвращаемые значения практически сравнялись – где-то уже немного отстали, где-то уже почти отстали. Соответственно, если увеличивать процент провалов, то исключения уже начнут существенно отставать.
Выводы
Общие выводы какие возникли – возвращаемые значения типа писсимистической стратегии – т.е. чуть что сделали сразу проверяем, а не плохо ли всё, исключения – оптимистическая стратегия – делаем всё подряд без разбора и проверок, авось всё хорошо.
В принципе возникает такое ощущение, что исключения всё-таки немного быстрее (если не происходит ошибок). Соответственно если для какой-то функции вероятность провала более 1%, то надо использовать коды возврата, если менее 1%, то надо использовать исключения для повышения производительности. Да-да, вы не ослышались – для поднятия производительности надо использовать исключения.
Так же стоит отметить, что в msvc8 по сравнению с msvc71 резко снизилась стоимость кидания исключения. Но тут у них есть предел, т.к. исключения реализованы поверх SEH, значит кидание исключения всегда будет связано с переходом в ядро.
Хочется ожидать большего от gcc, т.к. они не ограничены этим фактором и могут сделать действительно быстрые исключения. Сейчас уже есть некоторые исследования на тему реализации быстрых исключений, будем надеятся, что их воплотят в gcc в ближайшем будущем. Тогда чаша весов ещё более сильно перевесит в сторону исключений.
Так же совершенна непонятна ситуация с gcc4.1.1/mingw – и кодогенерация какая-то непонятная и исключения ооочень медленные.
Так же очевидно, что использование возвращаемых значений под С++ — это совсем плохая идея – издержки от двух стихий, а плюсов нет в противопоставление с возвращаемыми значениями под С.
Ещё момент — при использовании исключений принципиально различается кодогенерация в зависимости от того, есть ли в функции локальные объекты/аргументы с нетривиальными деструкторами — если нет, то просто идут вызовы других функций, если есть, то тут уже начинается: занесение фрейма на стек, подсчёт сконструированных объектов, снятие фрейма со стека. Получается значительная разница и по скорости и по размеру кода.
Ну вот собственно всё, что хотелось сказать. Коменты и замечания преведствуются Особенно интересно было бы услышать, если я пропустил какие-то важные аспекты в исследовании, которые могут сущуственно сказаться на производительности/размере кода/или ещё чём-то в разрезе исключения/возвращаемые значения.
R>Общие выводы какие возникли – возвращаемые значения типа писсимистической стратегии – т.е. чуть что сделали сразу проверяем, а не плохо ли всё, исключения – оптимистическая стратегия – делаем всё подряд без разбора и проверок, авось всё хорошо.
Наоборот, при кидании исключения мы никогда не пройдем мимо ошибок (если конечно catch(...) не использовать). И будем обрабатывать ошибку на том уровне, на которм надо, а в случае возрата, нам еще надо передать управление на другой уровень, если обработка идет там.
R>В принципе возникает такое ощущение, что исключения всё-таки немного быстрее (если не происходит ошибок). Соответственно если для какой-то функции вероятность провала более 1%, то надо использовать коды возврата, если менее 1%, то надо использовать исключения для повышения производительности. Да-да, вы не ослышались – для поднятия производительности надо использовать исключения.
1% это очень много. Изначально использовал исключения и не сомневался, в этом. В обработке ошибок производительность занимает далеко не первое место.
R>Так же стоит отметить, что в msvc8 по сравнению с msvc71 резко снизилась стоимость кидания исключения. Но тут у них есть предел, т.к. исключения реализованы поверх SEH, значит кидание исключения всегда будет связано с переходом в ядро.
SEH для С++ с ядром не имеет ничего общего. Это всего лишь цепочка блоков. Вряд ли исключения изменились, возможно оптимизировали деструктор или вызовы его уменьшили. или FPO оптимизацию подняли.
R>Так же очевидно, что использование возвращаемых значений под С++ — это совсем плохая идея – издержки от двух стихий, а плюсов нет в противопоставление с возвращаемыми значениями под С. R>Ещё момент — при использовании исключений принципиально различается кодогенерация в зависимости от того, есть ли в функции локальные объекты/аргументы с нетривиальными деструкторами — если нет, то просто идут вызовы других функций, если есть, то тут уже начинается: занесение фрейма на стек, подсчёт сконструированных объектов, снятие фрейма со стека. Получается значительная разница и по скорости и по размеру кода.
..... R>Ну вот собственно всё, что хотелось сказать. Коменты и замечания преведствуются Особенно интересно было бы услышать, если я пропустил какие-то важные аспекты в исследовании, которые могут сущуственно сказаться на производительности/размере кода/или ещё чём-то в разрезе исключения/возвращаемые значения.
R>
1. Спасибо за проделаную работу. Я никогда не понимал , почему так распросранен миф среди С++ программистов , о "накладности" исключений. Это же очевидно что их импдементацию можно сделать не накладнее чем коды возврата. Но с учетом того что создатели компилятора могут использовать нетривиальные трюки.
2. Спасибо за исследование размера кода , это тоже один из мифов.
3. Не исследован случай когда идет передача кода возврата на несколько уровней вверх (хотя бы на три).
4. Самый большой недостакок и пожалуй единственный , а где код ? Думаю тут найдутся люди, которые помогут его причесать.
5. А после некоторого обсуждения на форуме , не написал бы ты статью с асемблерными листингами ?
6. Очень интересен анал хотя бы под одну embeded платформу.
... << RSDN@Home 1.1.4 stable SR1 rev. 568>>
Ищу работу, 3D, SLAM, computer graphics/vision.
Re[2]: [Investigation] Exceptions vs. Return Values
M>6. Очень интересен анал хотя бы под одну embeded платформу.
Несмотря на явную двусмысленность этого предложения , абсолютно полностью согласен с тем что было бы крайне интересно исследовать не-x86 процессоры, особенно — ARMы и иже с ними.
... << RSDN@Home 1.1.4 stable SR1 rev. 568>>
Re[2]: [Investigation] Exceptions vs. Return Values
Здравствуйте, Сергей Мухин, Вы писали:
СМ>Здравствуйте, remark, Вы писали:
СМ>работа проделана большая,
R>>
Выводы
R>>Общие выводы какие возникли – возвращаемые значения типа писсимистической стратегии – т.е. чуть что сделали сразу проверяем, а не плохо ли всё, исключения – оптимистическая стратегия – делаем всё подряд без разбора и проверок, авось всё хорошо.
СМ>Наоборот, при кидании исключения мы никогда не пройдем мимо ошибок (если конечно catch(...) не использовать). И будем обрабатывать ошибку на том уровне, на которм надо, а в случае возрата, нам еще надо передать управление на другой уровень, если обработка идет там.
Полностью согласен, тем не менее не понимаю, к чему относится "Наоборот"...
R>>В принципе возникает такое ощущение, что исключения всё-таки немного быстрее (если не происходит ошибок). Соответственно если для какой-то функции вероятность провала более 1%, то надо использовать коды возврата, если менее 1%, то надо использовать исключения для повышения производительности. Да-да, вы не ослышались – для поднятия производительности надо использовать исключения.
СМ>1% это очень много. Изначально использовал исключения и не сомневался, в этом. В обработке ошибок производительность занимает далеко не первое место.
Да, 1% это много. Сервера зачастую обрабатывают тысячи, десятки тысяч, сотни тысяч... транзакций без ошибок.
Тем не менее такие данные мне лично помогут более точно ориентироваться. А именно — допустим на сетевой сервер поступают некие "заявки". Если заявки приходят из доверенного источника и по надежному каналу, то типа как ошибок в данных быть не должно (ну то есть они бывают допустим в 0.001%) тут однозначно можно использовать исключения при валидации заявки. Если же заявки приходят от недоверенных источников и статистика такая, что ошибки при валидации заявки бывают в 5% случаев — т.е. "шлют всякую фигню" образно выражаясь — можно подумать о возвращаемом значении при валидации — иначе обработка ошибок для некорректных заявок может снизить пропускную способность и для корректных заявок.
R>>Так же стоит отметить, что в msvc8 по сравнению с msvc71 резко снизилась стоимость кидания исключения. Но тут у них есть предел, т.к. исключения реализованы поверх SEH, значит кидание исключения всегда будет связано с переходом в ядро.
СМ>SEH для С++ с ядром не имеет ничего общего. Это всего лишь цепочка блоков. Вряд ли исключения изменились, возможно оптимизировали деструктор или вызовы его уменьшили. или FPO оптимизацию подняли.
Тут я возможно ступил, у меня почему-то подсознательно SEH ассоциировался с ядром — надо будет ещё продебажить... хотя вроде я там видел переход в ядро... ладно ещё уточню...
Здравствуйте, remark, Вы писали:
R>У исключений значительно меньше инструкций, тем не менее размер кода равен размеру кода на С с возвращаемыми значениями. Как ни странно... Связано с тем, что команды там больше по размеру. Код на с возвращаемыми значениями тут сильно проигрывает – так там инструкции и на проверки возвращаемых значений и на поддержку исключений.
забыли сравнить с С++ кодом, генерированным без поддержки исключений
отключение поддержки исключений при генерации OS (WinCE 5.0, ARMV4I) экономит между прочим около 400кБ, так что здесь не всё так однозначно.
Re[2]: [Investigation] Exceptions vs. Return Values
Здравствуйте, minorlogic, Вы писали:
M>Здравствуйте, remark, Вы писали:
M>..... R>>Ну вот собственно всё, что хотелось сказать. Коменты и замечания преведствуются Особенно интересно было бы услышать, если я пропустил какие-то важные аспекты в исследовании, которые могут сущуственно сказаться на производительности/размере кода/или ещё чём-то в разрезе исключения/возвращаемые значения.
R>>
M>1. Спасибо за проделаную работу. Я никогда не понимал , почему так распросранен миф среди С++ программистов , о "накладности" исключений. Это же очевидно что их импдементацию можно сделать не накладнее чем коды возврата. Но с учетом того что создатели компилятора могут использовать нетривиальные трюки.
Да, тут сейчас какая-то двойственность получается — если исключения не летят, то они на сколько-то быстрее, а если летят, то значительно медленнее. Тем не менее я не понимаю, почему кидание такое медленное. И я уверен, что тут есть простор для оптимизации и в будущем это будет _значительно_ быстрее.
M>2. Спасибо за исследование размера кода, это тоже один из мифов.
То, что меня удивило, что С код такой же по размеру, несмотря на то, что инструкций там значительно больше... но это если есть "ресурсы", если нет, то С код значительно больше.
M>3. Не исследован случай когда идет передача кода возврата на несколько уровней вверх (хотя бы на три).
А неважно, всё равно кидание будет значительно медленнее.
M>4. Самый большой недостакок и пожалуй единственный , а где код ? Думаю тут найдутся люди, которые помогут его причесать.
Ну там куски кода для случая с "ресурсами" я приводил. Я думаю, что там можно представить как он выглядел. Дело в том, что его очень много и реально он описан на макросах... поэтому там не особо есть что приводить.
Что я понял уже когда я это всё доделал, что надо было код не кустарным способом делать, а сделать генератор кода, которому задаёшь параметр — кол-во "ресурсов, кол-во уровней вызовов и т.д. и что бы он генерировал много красивого кода. И снабдить это ещё скриптами для автоматической сборки и что бы результаты выводились сразу в виде красивых табличек. Тогда бы я это выложил и все желающие запускали бы это на своих платформах и потом выкладывали бы сюда свои результаты... тогда и для ARM'ов бы можно было протестировать и более точно проследить зависимости скорости/размера кода от различных параметров типа кол-ва "ресурсов".
M>5. А после некоторого обсуждения на форуме , не написал бы ты статью с асемблерными листингами ?
Теоретически можно, я об этом думал, даже когда-то давно скачивал пак для написания статей. Но всё же времени это потребует значительно больше.
M>6. Очень интересен анал хотя бы под одну embeded платформу.
К сожалению это не ко мне. Может руки дойдут это оформить в виде набора файлов, который можно запустить на своей платформе и выложить результаты...
Здравствуйте, superlexx, Вы писали:
S>Здравствуйте, remark, Вы писали:
R>>У исключений значительно меньше инструкций, тем не менее размер кода равен размеру кода на С с возвращаемыми значениями. Как ни странно... Связано с тем, что команды там больше по размеру. Код на с возвращаемыми значениями тут сильно проигрывает – так там инструкции и на проверки возвращаемых значений и на поддержку исключений.
S>забыли сравнить с С++ кодом, генерированным без поддержки исключений
Я думаю, что это будет аналогично С коду. Единственное отличие — функции освобождения ресурсов будут вызываться неявно, но всё равно в тех же местах, где они в С коде вызываются явно.
Тем более я такую настройку лично не уважаю Т.к. получается, что нельзя использовать конструкторы, операторы и т.д.
S>отключение поддержки исключений при генерации OS (WinCE 5.0, ARMV4I) экономит между прочим около 400кБ, так что здесь не всё так однозначно.
Там часть кода компилируется при билде системы, часть — линкуется из предоткомпилированных под конкретный проц библиотек.
Так что собрать под какой-то свой проц — не получится, только под те что поддерживаются MS.
R>Провёл на досуге исследование на тему сообщение об ошибках с помощью исключений против сообщения об ошибках с помощью возвращаемых значений с т.з. скорости выполнения кода и кодогенерации. Возможно кому-то ещё будет интересно.
Во-первых, нужен исходный код. А во-вторых, все зависит от величины прослойки, которая ведет к функции api. В зависимости от типа кода она будет разной. Если очень тонкой — тогда исключения просто не могут быть быстрее ввиду того, что функции апи используют возвращаемые значение, которые необходимо будет транслировать в исключения ровно такой же проверкой. Если же, наоборот, задача исключительно вычислительная — тогда конечно, исключения могут и выигрывать. В общем, все зависит от типа задачи, а такие тесты — средняя температура по больнице, которая ни о чем не говорит, на мой взгляд.
Здравствуйте, remark, Вы писали:
R>Здравствуйте, Сергей Мухин, Вы писали:
R>Полностью согласен, тем не менее не понимаю, к чему относится "Наоборот"...
"наоборот" относится к фразе "делаем всё подряд без разбора и проверок, авось всё хорошо" т.к. по этой фразе можно понять, что исключения позволяют писать код в которм ошибки так и шастают а программист об этом и не знает.
---
С уважением,
Сергей Мухин
Re[3]: [Investigation] Exceptions vs. Return Values
Здравствуйте, remark, Вы писали:
R>Здравствуйте, Сергей Мухин, Вы писали:
СМ>>SEH для С++ с ядром не имеет ничего общего. Это всего лишь цепочка блоков. Вряд ли исключения изменились, возможно оптимизировали деструктор или вызовы его уменьшили. или FPO оптимизацию подняли.
R>Тут я возможно ступил, у меня почему-то подсознательно SEH ассоциировался с ядром — надо будет ещё продебажить... хотя вроде я там видел переход в ядро... ладно ещё уточню...
Здравствуйте, Vain, Вы писали:
V>Здравствуйте, remark, Вы писали:
R>>
Exceptions vs. Return Values
V>Интересно бы было ещё узнать статистику для retv в стиле Win32 SetLastError/GetLastError, имхо тоже было бы полезно.
Это уже просто надстройка над возвращаемыми значениями. В успешном случае производительность будет равна производительности с возвращаемыми значениями, а когда происходят ошибки, то добавляется небольшое пенальти на вызов функций SetLastError/GetLastError, но оно, конечно, не сравнимо с киданием исключений. Т.ч. я думаю, тут ничего принципиально не изменится.
Здравствуйте, Andrew S, Вы писали:
R>>Провёл на досуге исследование на тему сообщение об ошибках с помощью исключений против сообщения об ошибках с помощью возвращаемых значений с т.з. скорости выполнения кода и кодогенерации. Возможно кому-то ещё будет интересно.
AS>Во-первых, нужен исходный код.
Он там есть для случая с выделением "ресурса". Тривиальные случаи, я думал, не вызовут вопросов.
Пустая функция с возвращаемым значением выглядела вот так:
bool f()
{
return true;
}
Вызывалась вот так:
if (!f())
++error_count;
Пустая функция с исключениями выглядела вот так:
void f()
{
}
Вызывалась вот так:
try
{
f();
}
catch (...)
{
++error_count;
}
AS>А во-вторых, все зависит от величины прослойки, которая ведет к функции api.
Влияние именно этой величины я и исследовал. Т.е. вначале смотрел, когда эта величина 1 (функция), потом в асимптотическом случае, когда очень много функций, потом в случае, когда величина прослойки более-менее соизмерима с неким реальным проектом.
В принципе у меня есть идея на будущее сделать генератор кода и построить графики производительности в зависимости от "глубины" прослойки, "ширины" прослойки и кол-ва "ресурсов", которые выделяются в прослойке. Но пока получилось только так.
AS>В зависимости от типа кода она будет разной. Если очень тонкой — тогда исключения просто не могут быть быстрее ввиду того, что функции апи используют возвращаемые значение, которые необходимо будет транслировать в исключения ровно такой же проверкой.
Это всё есть. Если прослойка 0 — то одинаково, т.к ничего не различается. Если 1 — то исключения быстрее, Если много — то исключения ещё быстрее. Правда ситуация меняется, когда есть "ресурсы".
AS>Если же, наоборот, задача исключительно вычислительная — тогда конечно, исключения могут и выигрывать. В общем, все зависит от типа задачи, а такие тесты — средняя температура по больнице, которая ни о чем не говорит, на мой взгляд.
Естественно всё зависит от задачи. Но я то исследовал не задачи, а примитивы. Вычислительную сложность которых можно просто складывать и оценить результат.
V>>Интересно бы было ещё узнать статистику для retv в стиле Win32 SetLastError/GetLastError, имхо тоже было бы полезно.
R>Это уже просто надстройка над возвращаемыми значениями. В успешном случае производительность будет равна производительности с возвращаемыми значениями, а когда происходят ошибки, то добавляется небольшое пенальти на вызов функций SetLastError/GetLastError, но оно, конечно, не сравнимо с киданием исключений. Т.ч. я думаю, тут ничего принципиально не изменится.
Я имею ввиду ещё и использование TLS
R>
[In theory there is no difference between theory and practice. In
practice there is.]
[Даю очевидные ответы на риторические вопросы]
R>Ну вот собственно всё, что хотелось сказать. Коменты и замечания преведствуются Особенно интересно было бы услышать, если я пропустил какие-то важные аспекты в исследовании, которые могут сущуственно сказаться на производительности/размере кода/или ещё чём-то в разрезе исключения/возвращаемые значения.
В gcc для оптимизации jmp можно использовать макросы likely/unlikely.
А для msvc эти макросы можно сделать пустышками.
Re[2]: [Investigation] Exceptions vs. Return Values
Здравствуйте, MShura, Вы писали:
R>>Ну вот собственно всё, что хотелось сказать. Коменты и замечания преведствуются Особенно интересно было бы услышать, если я пропустил какие-то важные аспекты в исследовании, которые могут сущуственно сказаться на производительности/размере кода/или ещё чём-то в разрезе исключения/возвращаемые значения.
MS>В gcc для оптимизации jmp можно использовать макросы likely/unlikely. MS>А для msvc эти макросы можно сделать пустышками.
и какое это имеет отношение к subj?
---
С уважением,
Сергей Мухин
Re[3]: [Investigation] Exceptions vs. Return Values
AS>>А во-вторых, все зависит от величины прослойки, которая ведет к функции api.
R>Влияние именно этой величины я и исследовал. Т.е. вначале смотрел, когда эта величина 1 (функция), потом в асимптотическом случае, когда очень много функций, потом в случае, когда величина прослойки более-менее соизмерима с неким реальным проектом. R>В принципе у меня есть идея на будущее сделать генератор кода и построить графики производительности в зависимости от "глубины" прослойки, "ширины" прослойки и кол-ва "ресурсов", которые выделяются в прослойке. Но пока получилось только так.
AS>>В зависимости от типа кода она будет разной. Если очень тонкой — тогда исключения просто не могут быть быстрее ввиду того, что функции апи используют возвращаемые значение, которые необходимо будет транслировать в исключения ровно такой же проверкой.
R>Это всё есть. Если прослойка 0 — то одинаково, т.к ничего не различается. Если 1 — то исключения быстрее, Если много — то исключения ещё быстрее. Правда ситуация меняется, когда есть "ресурсы".
Вот тут надо осторожнее в смысле организации цикла. Оптимизирующий компилятор вполне способен вынести установку фрейма исключений в "пустых" функциях за пределы цикла (тогда как для "нормальных" функций и тем более функций апи он этого делать не имеет права) — соответственно, такие тесты покажут фикцию. Как я понимаю, ты называешь эту ситуацию "ресурсы" (вообще, надо бы использовать нормальную терминологию — например, стек фрейм локальных переменных), тогда как на самом деле это вполне обычные локальные переменные — т.е. требование наличия своего стек фрейма, что скорее правило, а не исключение. В общем, надо моделировать _реальный_ код, а не пустые функции. А для этого надо иметь статистику по виду и функциональности функций. У тебя таковая есть?
А вообще, и замерять то особо ничего не надо — достаточно анализа ассемблерного листинга. Установка фрейма исключений в цикле — довольно дорогая операция, значительно дороже, чем проверка ошибки (особенно с учетом работающего механизма предсказания переходов). Поэтому обычно и стараются вынести это за пределы цикла — тогда, если код _только_ вычислительный, или же "толстая" прослойка до финальной функции (где проверка и генерация исключения) получится выигрыш. В общем, нужно нечто вроде fctest — где есть реальные паттерны работы дисковой подсистемы, поэтому и результаты реальны. А так — все это сферический конь в вакууме.
AS>>Если же, наоборот, задача исключительно вычислительная — тогда конечно, исключения могут и выигрывать. В общем, все зависит от типа задачи, а такие тесты — средняя температура по больнице, которая ни о чем не говорит, на мой взгляд.
R>Естественно всё зависит от задачи. Но я то исследовал не задачи, а примитивы. Вычислительную сложность которых можно просто складывать и оценить результат.
Не все так просто. Даже замеры времени по rdtsc — это неправильно (если уж придираться), поскольку учитывает не время потока, а все такты процессора. В случае многозадачной системы это как минимум неверно. Выложи исходники — я тебя уверяю, народ раскритикует по самые В общем, для начала неплохо бы тебе разобраться в механизме функционирования всего этого на самом низком уровне (тем более, даже для разных компиляторов он отличается — например, 6, 7.1 и 8 имеют разные механизмы раскрутки фрейма исключений), а потом уже, вооружившись знаниями о том, что и как делает компилятор (а также и ОС), писать паттерны поведения. Тогда бы действительно получилось интересное исследование на тему.
AS>А вообще, и замерять то особо ничего не надо — достаточно анализа ассемблерного листинга. Установка фрейма исключений в цикле — довольно дорогая операция, значительно дороже, чем проверка ошибки (особенно с учетом работающего механизма предсказания переходов).
на вскидку — установка фрейма пара команд, ну мб три. снятие еще пара. и БЕЗ переходов. а вот проверка та же пара, но с переходом да и установка еще одна (в самой ф-ии). (это на I86). Итого 4(5) против 3.
я уже написал "проделана большая работа". т.е. измерялось только как определяется какие деструкторы надо вызвать — какие нет.
Поэтому обычно и стараются вынести это за пределы цикла — тогда, если код _только_ вычислительный, или же "толстая" прослойка до финальной функции (где проверка и генерация исключения) получится выигрыш. В общем, нужно нечто вроде fctest — где есть реальные паттерны работы дисковой подсистемы, поэтому и результаты реальны. А так — все это сферический конь в вакууме.
AS>>>Если же, наоборот, задача исключительно вычислительная — тогда конечно, исключения могут и выигрывать. В общем, все зависит от типа задачи, а такие тесты — средняя температура по больнице, которая ни о чем не говорит, на мой взгляд.
ть и оценить результат.
AS>Не все так просто. Даже замеры времени по rdtsc — это неправильно (если уж придираться), поскольку учитывает не время потока, а все такты процессора. В случае многозадачной системы это как минимум неверно. Выложи исходники — я тебя уверяю, народ раскритикует по самые
согласен
---
С уважением,
Сергей Мухин
Re[3]: [Investigation] Exceptions vs. Return Values
R>>>Ну вот собственно всё, что хотелось сказать. Коменты и замечания преведствуются Особенно интересно было бы услышать, если я пропустил какие-то важные аспекты в исследовании, которые могут сущуственно сказаться на производительности/размере кода/или ещё чём-то в разрезе исключения/возвращаемые значения.
MS>>В gcc для оптимизации jmp можно использовать макросы likely/unlikely. MS>>А для msvc эти макросы можно сделать пустышками.
СМ>и какое это имеет отношение к subj?
Я выделил отношение.
Я не проверял количественное влияние этих макросов, но ядерщики linux советуют использовать их использовать.
Если используются rv, то ветку (условие) обрабатывающую ошибку можно считать unlikely.
Например ошибку выделения памяти можно считать маловероятной
Здравствуйте, Vain, Вы писали:
V>Здравствуйте, remark, Вы писали:
R>>>>
Exceptions vs. Return Values
V>>>Интересно бы было ещё узнать статистику для retv в стиле Win32 SetLastError/GetLastError, имхо тоже было бы полезно.
R>>Это уже просто надстройка над возвращаемыми значениями. В успешном случае производительность будет равна производительности с возвращаемыми значениями, а когда происходят ошибки, то добавляется небольшое пенальти на вызов функций SetLastError/GetLastError, но оно, конечно, не сравнимо с киданием исключений. Т.ч. я думаю, тут ничего принципиально не изменится.
V>Я имею ввиду ещё и использование TLS
TLS очень быстро — одно косвенное обращение — несколько тактов. С исключениями ни в какое сравнение всё равно не идёт.
R>>
Здравствуйте, MShura, Вы писали:
R>>>>Ну вот собственно всё, что хотелось сказать. Коменты и замечания преведствуются Особенно интересно было бы услышать, если я пропустил какие-то важные аспекты в исследовании, которые могут сущуственно сказаться на производительности/размере кода/или ещё чём-то в разрезе исключения/возвращаемые значения.
MS>>>В gcc для оптимизации jmp можно использовать макросы likely/unlikely. MS>>>А для msvc эти макросы можно сделать пустышками.
СМ>>и какое это имеет отношение к subj?
MS>Я выделил отношение.
Естественно, я запускал код не один раз. Я запускал код минимум тысячу раз и брал _минимальное_. Т.е. это время с учётом прогрева всех кэшей и бранч-предиктора. Т.ч. я не думаю, что это как-то повлияет на результат.
Здравствуйте, Сергей Мухин, Вы писали:
СМ>Здравствуйте, remark, Вы писали:
R>>Здравствуйте, Сергей Мухин, Вы писали:
СМ>>>SEH для С++ с ядром не имеет ничего общего. Это всего лишь цепочка блоков. Вряд ли исключения изменились, возможно оптимизировали деструктор или вызовы его уменьшили. или FPO оптимизацию подняли.
R>>Тут я возможно ступил, у меня почему-то подсознательно SEH ассоциировался с ядром — надо будет ещё продебажить... хотя вроде я там видел переход в ядро... ладно ещё уточню...
СМ>вот хорошая стаья про SEH здесь
Здравствуйте, Сергей Мухин, Вы писали:
СМ>на вскидку — установка фрейма пара команд, ну мб три. снятие еще пара. и БЕЗ переходов. а вот проверка та же пара, но с переходом да и установка еще одна (в самой ф-ии). (это на I86). Итого 4(5) против 3.
Насколько я лазил по коду, генерируемому ICC, почти везде где можно он использует cmov
так что с переходами тут тоже не все ясно...
... << RSDN@Home 1.1.4 stable SR1 rev. 568>>
Re[4]: [Investigation] Exceptions vs. Return Values
Здравствуйте, Andrew S, Вы писали:
AS>Даже замеры времени по rdtsc — это неправильно (если уж придираться), поскольку учитывает не время потока, а все такты процессора.
Увы, но более точного метода измерения кроме как
AS>Вот тут надо осторожнее в смысле организации цикла. Оптимизирующий компилятор вполне способен вынести установку фрейма исключений в "пустых" функциях за пределы цикла
Непонятно — в "пустых" функциях не устанавливается фрейм исключений...
AS>(тогда как для "нормальных" функций и тем более функций апи он этого делать не имеет права) — соответственно, такие тесты покажут фикцию. Как я понимаю, ты называешь эту ситуацию "ресурсы" (вообще, надо бы использовать нормальную терминологию — например, стек фрейм локальных переменных), тогда как на самом деле это вполне обычные локальные переменные — т.е. требование наличия своего стек фрейма, что скорее правило, а не исключение.
Нет, под "ресурсами" я подразумеваю не просто локальные переменные, а локальные переменные с нетривиальным деструктором, если в терминах с++ или локальную переменную, для которой надо вызвать destructor() перед выходом из функции, если в терминах с.
Просто локальные переменные в данном контексте имхо не интересны, т.к. всё влияние будет заключаться в одной инструкции увеличении указателя стека, причём и в С и в С++.
AS>В общем, надо моделировать _реальный_ код, а не пустые функции. А для этого надо иметь статистику по виду и функциональности AS>функций. У тебя таковая есть?
Такая задача не ставилась, т.к. это будет очень серьёзное исследование. И имхо оно здесь не надо, т.к. я исследовал примитивы, вычислительную сложность можно просто сложить для получения результата.
AS>А вообще, и замерять то особо ничего не надо — достаточно анализа ассемблерного листинга. AS> AS>Не был бы так однозначен. По крайней мере кол-во инструкций и время выполнения связаны не линейно.
Тем более я, как инженер, больше верю своим глазам
AS>Установка фрейма исключений в цикле — довольно дорогая операция, значительно дороже, чем проверка ошибки
Почему? Там не более 2-3 инструкций.
AS>(особенно с учетом работающего механизма предсказания переходов). Поэтому обычно и стараются вынести это за пределы цикла — тогда, если код _только_ вычислительный, или же "толстая" прослойка до финальной функции (где проверка и генерация исключения) получится выигрыш.
Может быть ты приведёшь какие-то замеры Мои замеры пока показывают обратную картину....
AS>В общем, нужно нечто вроде fctest — где есть реальные паттерны работы дисковой подсистемы, поэтому и результаты реальны. А так — все это сферический конь в вакууме.
Я всё же пока не вижу такой необходимости.
AS>>>Если же, наоборот, задача исключительно вычислительная — тогда конечно, исключения могут и выигрывать. В общем, все зависит от типа задачи, а такие тесты — средняя температура по больнице, которая ни о чем не говорит, на мой взгляд.
R>>Естественно всё зависит от задачи. Но я то исследовал не задачи, а примитивы. Вычислительную сложность которых можно просто складывать и оценить результат.
AS>Не все так просто. Даже замеры времени по rdtsc — это неправильно (если уж придираться), поскольку учитывает не время потока, а все такты процессора. В случае многозадачной системы это как минимум неверно.
Не было никакой "многозадачной системы" и время я брал минимальное за много выполнений.
Это время не менялось от запуска к запуску. Имхо ты говоришь что-то не то...
AS>Выложи исходники — я тебя уверяю, народ раскритикует по самые
Код чего интересует?
Сами тестируемые функции? Или что?
AS>В общем, для начала неплохо бы тебе разобраться в механизме функционирования всего этого на самом низком уровне
AS>(тем более, даже для разных компиляторов он отличается — например, 6, 7.1 и 8 имеют разные механизмы раскрутки фрейма исключений),
AS>а потом уже, вооружившись знаниями о том, что и как делает компилятор (а также и ОС), писать паттерны поведения.
AS>Тогда бы действительно получилось интересное исследование на тему.
AS>>А вообще, и замерять то особо ничего не надо — достаточно анализа ассемблерного листинга. Установка фрейма исключений в цикле — довольно дорогая операция, значительно дороже, чем проверка ошибки (особенно с учетом работающего механизма предсказания переходов).
СМ>на вскидку — установка фрейма пара команд, ну мб три. снятие еще пара. и БЕЗ переходов. а вот проверка та же пара, но с переходом да и установка еще одна (в самой ф-ии). (это на I86). Итого 4(5) против 3.
Установка фрейма. Вот типичный вариант (часть команд создания стек фрейма я включил сознательно, об этом ниже):
1. Команды работы с памятью (за исключением одной)
2. Размеры команд
3. Префиксы (поскольку используется FS)
4. Ведет к ebp-based стек фрейму. Т.е. минус один регистр. Как это важно на х86 — думаю, объяснять не надо.
Это все тянет тактов на 20-30 минимум (а на деле будет прилично больше). Про раскрутку мы не говорим — там кода в разы больше (в случае вин32), начиная от отработки исключения с переходом в KM и обратно, до собственно функции раскрутки, сначала самой ОС и потом уже stdlib. В общем, все не так тривиально, как тут пытаются показать — вплоть до влияния на производительность _самой_ функции, а не только окружения, которое ее вызывает.
Собственно, сама реализация всего этого в том же VC вызывает кучу вопросов — например, зачем было вообще пользоваться средствами OS для реализации software-only исключений.
AS>>Вот тут надо осторожнее в смысле организации цикла. Оптимизирующий компилятор вполне способен вынести установку фрейма исключений в "пустых" функциях за пределы цикла
R>Непонятно — в "пустых" функциях не устанавливается фрейм исключений...
Ну и зачем это тогда тестировать?
AS>>(тогда как для "нормальных" функций и тем более функций апи он этого делать не имеет права) — соответственно, такие тесты покажут фикцию. Как я понимаю, ты называешь эту ситуацию "ресурсы" (вообще, надо бы использовать нормальную терминологию — например, стек фрейм локальных переменных), тогда как на самом деле это вполне обычные локальные переменные — т.е. требование наличия своего стек фрейма, что скорее правило, а не исключение.
AS>>В общем, надо моделировать _реальный_ код, а не пустые функции. А для этого надо иметь статистику по виду и функциональности AS>функций. У тебя таковая есть?
R>Такая задача не ставилась, т.к. это будет очень серьёзное исследование. И имхо оно здесь не надо, т.к. я исследовал примитивы, вычислительную сложность можно просто сложить для получения результата.
Понятно. Дальше можно не продолжать.
AS>>А вообще, и замерять то особо ничего не надо — достаточно анализа ассемблерного листинга. AS>> AS>>Не был бы так однозначен. По крайней мере кол-во инструкций и время выполнения связаны не линейно. R>Тем более я, как инженер, больше верю своим глазам
Для предположения — достаточно. Чтобы зря не истекать слюной, например, по поводу пустых функций, а также понимать, почему как и где изменяется размер кода, а не делать эмпирические выводы.
AS>>Установка фрейма исключений в цикле — довольно дорогая операция, значительно дороже, чем проверка ошибки
R>Почему? Там не более 2-3 инструкций.
Наивно... Ты посмотри, _какие_ там инструкции.
AS>>(особенно с учетом работающего механизма предсказания переходов). Поэтому обычно и стараются вынести это за пределы цикла — тогда, если код _только_ вычислительный, или же "толстая" прослойка до финальной функции (где проверка и генерация исключения) получится выигрыш.
R>Может быть ты приведёшь какие-то замеры Мои замеры пока показывают обратную картину....
Зачем? Замеры зависят от конкретной задачи. Нужно приводить не "какие-то" замеры, как сделал ты, а замеры на конкретных паттернах, взятых _статистическ_ с реальных задач — например, вызов апи базы данных, вычислительная задача, работа с графикой etc.
AS>>Не все так просто. Даже замеры времени по rdtsc — это неправильно (если уж придираться), поскольку учитывает не время потока, а все такты процессора. В случае многозадачной системы это как минимум неверно.
R>Не было никакой "многозадачной системы" и время я брал минимальное за много выполнений. R>Это время не менялось от запуска к запуску. Имхо ты говоришь что-то не то...
Понятно. Ну, что ж, могу лишь сказать, что это по меньшей мере неверно. Надо привязывать поток к одному ядру и устанавливать приоритеты на реалтайм — тогда хоть как то можно говорить о нормальном измерении при помощи этой методики.
AS>>Выложи исходники — я тебя уверяю, народ раскритикует по самые
R>Код чего интересует? R>Сами тестируемые функции? Или что?
Здравствуйте, Andrew S, Вы писали:
AS>>>Вот тут надо осторожнее в смысле организации цикла. Оптимизирующий компилятор вполне способен вынести установку фрейма исключений в "пустых" функциях за пределы цикла
R>>Непонятно — в "пустых" функциях не устанавливается фрейм исключений...
AS>Ну и зачем это тогда тестировать?
Что бы определить скорость работы различных методов сообщения об ошибках. Фрейм исключений — это только часть.
Не понимаю, что тебе не понятно? Вот гляди пример, на коотором можно сравнивать, при этом фреймов исключений нет:
void f_ex(int i)
{
ex2(i+1);
ex3(i+2);
ex4(i+3);
}
int f_rv(int i)
{
if (!f_rv2(i+1)) return 0;
if (!f_rv3(i+2)) return 0;
if (!f_rv4(i+3)) return 0;
return 1;
}
AS>>>(тогда как для "нормальных" функций и тем более функций апи он этого делать не имеет права) — соответственно, такие тесты покажут фикцию. Как я понимаю, ты называешь эту ситуацию "ресурсы" (вообще, надо бы использовать нормальную терминологию — например, стек фрейм локальных переменных), тогда как на самом деле это вполне обычные локальные переменные — т.е. требование наличия своего стек фрейма, что скорее правило, а не исключение.
AS>>>В общем, надо моделировать _реальный_ код, а не пустые функции. А для этого надо иметь статистику по виду и функциональности AS>функций. У тебя таковая есть?
R>>Такая задача не ставилась, т.к. это будет очень серьёзное исследование. И имхо оно здесь не надо, т.к. я исследовал примитивы, вычислительную сложность можно просто сложить для получения результата.
AS> Понятно. Дальше можно не продолжать.
AS>>>А вообще, и замерять то особо ничего не надо — достаточно анализа ассемблерного листинга. AS>>> AS>>>Не был бы так однозначен. По крайней мере кол-во инструкций и время выполнения связаны не линейно. R>>Тем более я, как инженер, больше верю своим глазам
AS>Для предположения — достаточно. Чтобы зря не истекать слюной, например, по поводу пустых функций, а также понимать, почему как и где изменяется размер кода, а не делать эмпирические выводы.
AS>>>Установка фрейма исключений в цикле — довольно дорогая операция, значительно дороже, чем проверка ошибки
R>>Почему? Там не более 2-3 инструкций.
AS>Наивно... Ты посмотри, _какие_ там инструкции.
AS>>>(особенно с учетом работающего механизма предсказания переходов). Поэтому обычно и стараются вынести это за пределы цикла — тогда, если код _только_ вычислительный, или же "толстая" прослойка до финальной функции (где проверка и генерация исключения) получится выигрыш.
R>>Может быть ты приведёшь какие-то замеры Мои замеры пока показывают обратную картину....
AS>Зачем? Замеры зависят от конкретной задачи. Нужно приводить не "какие-то" замеры, как сделал ты, а замеры на конкретных паттернах, взятых _статистическ_ с реальных задач — например, вызов апи базы данных, вычислительная задача, работа с графикой etc.
AS>>>Не все так просто. Даже замеры времени по rdtsc — это неправильно (если уж придираться), поскольку учитывает не время потока, а все такты процессора. В случае многозадачной системы это как минимум неверно.
R>>Не было никакой "многозадачной системы" и время я брал минимальное за много выполнений. R>>Это время не менялось от запуска к запуску. Имхо ты говоришь что-то не то...
AS>Понятно. Ну, что ж, могу лишь сказать, что это по меньшей мере неверно. Надо привязывать поток к одному ядру и устанавливать приоритеты на реалтайм — тогда хоть как то можно говорить о нормальном измерении при помощи этой методики.
Зачем привязывать к единственному ядру? Или ты боишься, что у меня часть вычислений убежало на соседний комп?
Зачем поднимать приоритеты? Или ты думаешь, что поток с более высоким приоритетом будет выполняться быстрее?
AS>>>Выложи исходники — я тебя уверяю, народ раскритикует по самые
R>>Код чего интересует? R>>Сами тестируемые функции? Или что?
AS>Все — полный тест юнит.
Я не понимаю твой поинт имхо уже наполовину переходящий в наезды. Ты хочешь сказать, что либо я выдумал цифры, либо неправильно мерял?
AS>>>А вообще, и замерять то особо ничего не надо — достаточно анализа ассемблерного листинга. Установка фрейма исключений в цикле — довольно дорогая операция, значительно дороже, чем проверка ошибки (особенно с учетом работающего механизма предсказания переходов).
СМ>>на вскидку — установка фрейма пара команд, ну мб три. снятие еще пара. и БЕЗ переходов. а вот проверка та же пара, но с переходом да и установка еще одна (в самой ф-ии). (это на I86). Итого 4(5) против 3.
AS>Установка фрейма. Вот типичный вариант (часть команд создания стек фрейма я включил сознательно, об этом ниже):
AS>
Здравствуйте, Andrew S, Вы писали:
AS>А вообще, и замерять то особо ничего не надо — достаточно анализа ассемблерного листинга.
ИМХО, совсем недостаточно. Ты точно знаешь какие команды могут процом паралельно выполнятся и как они влияют друг на друга?
"For every complex problem, there is a solution that is simple, neat,
and wrong."
Re[7]: [Investigation] Exceptions vs. Return Values
AS>>Ну и зачем это тогда тестировать?
R>Что бы определить скорость работы различных методов сообщения об ошибках. Фрейм исключений — это только часть. R>Не понимаю, что тебе не понятно? Вот гляди пример, на коотором можно сравнивать, при этом фреймов исключений нет:
R>
Ну и где тут реальный код? С трансляцией исключений в собственнные и т.п.? Я не вижу поинта для сравенения, уж извини. Ведь проверка ошибок для этого и используется — чтобы на основе возвращаемого значения сформировать свое. В реальной ситуации с исключениями часто ровно та же задача — у нас есть набор наших исключений и нам надо "чужие" исключения привести к нашим.
AS>>Понятно. Ну, что ж, могу лишь сказать, что это по меньшей мере неверно. Надо привязывать поток к одному ядру и устанавливать приоритеты на реалтайм — тогда хоть как то можно говорить о нормальном измерении при помощи этой методики.
R>Зачем привязывать к единственному ядру? Или ты боишься, что у меня часть вычислений убежало на соседний комп?
Куда убежать??? Вероятно, про багу с рассинхронизаций rdtsc на разных ядрах ты не слышал?
R>Зачем поднимать приоритеты? Или ты думаешь, что поток с более высоким приоритетом будет выполняться быстрее?
Круто.
AS>>>>Выложи исходники — я тебя уверяю, народ раскритикует по самые
R>>>Код чего интересует? R>>>Сами тестируемые функции? Или что?
AS>>Все — полный тест юнит.
R>Я не понимаю твой поинт имхо уже наполовину переходящий в наезды. Ты хочешь сказать, что либо я выдумал цифры, либо неправильно мерял?
Это не наезд, а критический анализ. То, что тебе он не нравится — уже означает его претензии на валидность Так чего тебе бояться — я не понимаю причин для того, чтобы не выложить полный проект. Если жалко трудов — так и скажи. Но только по результатам судить о тесте — уж извини. Это действительно инженерный подход, который для исследований не годится.
AS>>А вообще, и замерять то особо ничего не надо — достаточно анализа ассемблерного листинга. AJD>ИМХО, совсем недостаточно. Ты точно знаешь какие команды могут процом паралельно выполнятся и как они влияют друг на друга?
Достаточно для оценки ситуации и выводов о необходимости замеров. Мерить все подряд не понимая, что собственно измеряешь — смысл?
AS>>Даже замеры времени по rdtsc — это неправильно (если уж придираться), поскольку учитывает не время потока, а все такты процессора. CC>Увы, но более точного метода измерения кроме как CC>
Здравствуйте, Andrew S, Вы писали:
AS>Достаточно для оценки ситуации и выводов о необходимости замеров.
Если код отличается на несколко инстукций, тяжело что-то сказать прое его скорость в общем случае.
AS>Мерить все подряд не понимая, что собственно измеряешь — смысл?
Это да.
"For every complex problem, there is a solution that is simple, neat,
and wrong."
Re: [Investigation] Exceptions vs. Return Values
От:
Аноним
Дата:
02.04.07 09:05
Оценка:
То, что исключения позволяют сократить один if при вызове функции, по-моему, с лихвой компенсируется тем, что исключения вынуждают пользоваться обертками в виде умных указателей.
Re[7]: [Investigation] Exceptions vs. Return Values
AS>>Достаточно для оценки ситуации и выводов о необходимости замеров. AJD>Если код отличается на несколко инстукций, тяжело что-то сказать прое его скорость в общем случае.
В данном случае разница довольно существенна. К тому же я не призываю отказаться от измерений — а лишь понимать, что именно измеряется. А не просто анализировать статистику.
Здравствуйте, Andrew S, Вы писали:
AS>И еще не забывай про привязку к одному ядру. А вот что автор тестов ответил на все это — можешь прочитать сам
Да, совсем забыл:
SetProcessAffinityMask (GetCurrentProcess (),1);
// и для гарантии контрольный в голову!!!
SetThreadAffinityMask (GetCurrentThread (),1);
... << RSDN@Home 1.1.4 stable SR1 rev. 568>>
Re[8]: [Investigation] Exceptions vs. Return Values
Здравствуйте, Andrew S, Вы писали:
AS>Куда убежать??? Вероятно, про багу с рассинхронизаций rdtsc на разных ядрах ты не слышал?
Кстати интел утверждает что "ссинхронизировать" tsc разных ядер можно путем вызова QueryPerormanceCounter.
Правда как именно они это делают я так и не понял. А трасить QueryPerormanceCounter уж очень неохота...
Хотя результаты моих тестов как на HT (P630) так и на дуалах (P915) расхождения в чистых rdtsc не выявили.
Впрочем лучше уж будем и дальше перестраховываться...
... << RSDN@Home 1.1.4 stable SR1 rev. 568>>
Re[7]: [Investigation] Exceptions vs. Return Values
Здравствуйте, remark, Вы писали:
R>Зачем привязывать к единственному ядру?
Затем, что ОС по своей прихоти может перебросить поток на другое ядро. С учетом таких забавных современных заморочек как динамическое управление частотой проца у разных ядер может оказаться разная частота — соответственно разное значение tsc. Дошло?
R>Зачем поднимать приоритеты?
Затем, чтоб на данном ядре нашему потоку отдавалось как можно больше процессорного времени. Чтоб в замеры не вмешивалось время исполнения не нашего кода...
AS>>>>Выложи исходники
Да, исходники в студию. Потому как мне например руки чешутся посмотреть на результаты их работы из под ICC
Заодно доработаем их всей толпой до полноценного теста...
... << RSDN@Home 1.1.4 stable SR1 rev. 568>>
Re[2]: [Investigation] Exceptions vs. Return Values
Здравствуйте, minorlogic, Вы писали:
M> ...
M>6. Очень интересен анал хотя бы под одну embeded платформу.
Для ARM-платформ все далеко не так радужно. Если привязываться к коду использующему исключения генерируемому gcc для ARM, то там имеют место быть так называемые stack unwind tables — для каждой функции генерируется такое безобразие. Размер кода увеличивается довольно серъезно. По своему опыту помню, как после перекомпиляции с ключом -fexceptions 300 килобайтная программа стала весить 450. Довольно серъезный недостаток для embedded devices.
Как обыкновение под embedded платформы используют два ключа компиляции: -fno-exceptions и -fno-rtti(правда поддержка rtti обходится сравнительно небольшим довеском к коду).
Причина по которой gcc генерирует столь медленный код для win32 думается связана с тем, что gcc использует общую схему обработки исключений, который в общих чертах независим от платформы. Win32 SEH не используется gcc.
Вам не стыдно?
Вы не упоямянули какую модель обработки исключений использовали, синхронную или асинхронную. Вы же не будете утверждать что между ними нет никакой существенной разницы?
Не указана потоковоя модель ST\MT и способ линковки рантайма lib\dll
Какой класс исключения вы использовали? (Я надеюсь он был производным от std::runtime_error? или вы bool кидали? )
Как насчет полиморфного поведения?
Как насчет ловли ссылок на один из базовых классов в иерархии?
Как насчет ловли ссылок на один из производных классов в иерархии?
Двойка вам за научный подход.
R>
... << RSDN@Home 1.1.4 stable SR1 rev. 568>>
Re[2]: [Investigation] Exceptions vs. Return Values
Здравствуйте, rm822, Вы писали:
R>Здравствуйте, remark
R>Вам не стыдно? R>Вы не упоямянули какую модель обработки исключений использовали, синхронную или асинхронную. Вы же не будете утверждать что между ними нет никакой существенной разницы?
Возможно стыд мне и позор, но первый раз про такое слышу. Или слышал под другим названием.
Что за синхроннная/асинхронная модель?
R>Не указана потоковоя модель ST\MT и способ линковки рантайма lib\dll
Это никак не повлияет, поэтому и не указывал. Там нет вызовов из рантайма. Возможно за исключением кидания исключения, но там небольшой оверхед на синхронизацию никак не скажется.
R>Какой класс исключения вы использовали? (Я надеюсь он был производным от std::runtime_error? или вы bool кидали? )
int
Я думаю это тоже неважно, за исключением небольшого оверхеда при кидании.
R>Как насчет полиморфного поведения?
А что насчёт него?
R>Как насчет ловли ссылок на один из базовых классов в иерархии?
А что насчёт него?
R>Как насчет ловли ссылок на один из производных классов в иерархии?
Здравствуйте, Аноним, Вы писали:
А>То, что исключения позволяют сократить один if при вызове функции, по-моему, с лихвой компенсируется тем, что исключения вынуждают пользоваться обертками в виде умных указателей.
Умные указатели подпадают под категорию того, что я называл "ресурсами", т.е. объектами с нетривиальным деструктором. Никак это не компенсируется.
Здравствуйте, rm822, Вы писали:
R>Здравствуйте, remark
R>Вам не стыдно? R>Вы не упоямянули какую модель обработки исключений использовали, синхронную или асинхронную. Вы же не будете утверждать что между ними нет никакой существенной разницы?
R>Не указана потоковоя модель ST\MT и способ линковки рантайма lib\dll
при чем здесь это?
R>Какой класс исключения вы использовали? (Я надеюсь он был производным от std::runtime_error? или вы bool кидали? ) R>Как насчет полиморфного поведения? R>Как насчет ловли ссылок на один из базовых классов в иерархии? R>Как насчет ловли ссылок на один из производных классов в иерархии?
R>Двойка вам за научный подход.
R>>
Re[3]: [Investigation] Exceptions vs. Return Values
Здравствуйте, StevenIvanov, Вы писали:
SI>Здравствуйте, minorlogic, Вы писали:
M>> ...
M>>6. Очень интересен анал хотя бы под одну embeded платформу.
SI>Для ARM-платформ все далеко не так радужно. Если привязываться к коду использующему исключения генерируемому gcc для ARM, то там имеют место быть так называемые stack unwind tables — для каждой функции генерируется такое безобразие. Размер кода увеличивается довольно серъезно. По своему опыту помню, как после перекомпиляции с ключом -fexceptions 300 килобайтная программа стала весить 450. Довольно серъезный недостаток для embedded devices.
Это сравнение теплого и мягкого. Надо не просто перекомпилировать программу с исключениями или без. Т.к. сообщение об ошибках с помощью возвращаемых значений могут раздуть код до 3 раз. Сравни:
call f
и:
call f
test eax, eax
je ...
Вопрос: какая программа будет меньше, использующая исключения и скомпилированная с исключениями или неиспользующая исключения и скомпилированная без исключений.
А то, что программа написанная без исключений, но скомпилированная с исключениями будет больше программы написанной без исключений, и скомпилированной без исключениями — это понятно.
Re[2]: [Investigation] Exceptions vs. Return Values
От:
Аноним
Дата:
02.04.07 10:43
Оценка:
В дополнение к сказанному.
Этот сэкономленный if играет роль только когда тело функции пустое. Если функция хоть что-то делает, тем более в цикле, то такая экономия не играет никакой роли. Так что эксперимент поставлен абсолютно некорректно.
Олег Алексеев.
Re[3]: [Investigation] Exceptions vs. Return Values
Здравствуйте, remark, Вы писали:
R>Здравствуйте, rm822, Вы писали:
R>>Здравствуйте, remark
R>>Вам не стыдно? R>>Вы не упоямянули какую модель обработки исключений использовали, синхронную или асинхронную. Вы же не будете утверждать что между ними нет никакой существенной разницы?
R>Возможно стыд мне и позор, но первый раз про такое слышу. Или слышал под другим названием. R>Что за синхроннная/асинхронная модель?
это vc specific:
/EHa
/EHsc
[]
Re[7]: [Investigation] Exceptions vs. Return Values
Здравствуйте, Andrew S, Вы писали:
AS>>>>А вообще, и замерять то особо ничего не надо — достаточно анализа ассемблерного листинга. Установка фрейма исключений в цикле — довольно дорогая операция, значительно дороже, чем проверка ошибки (особенно с учетом работающего механизма предсказания переходов).
СМ>>>на вскидку — установка фрейма пара команд, ну мб три. снятие еще пара. и БЕЗ переходов. а вот проверка та же пара, но с переходом да и установка еще одна (в самой ф-ии). (это на I86). Итого 4(5) против 3.
AS>>Установка фрейма. Вот типичный вариант (часть команд создания стек фрейма я включил сознательно, об этом ниже):
AS>>
Что ты хочешь этим сказать? А ты погляди код, который генерируется при вызове функций, возвращающих значения. Там тоже есть код, там не по велению волшебной палочки всё происходит.
Ситуация такая: при использовании исключений у функции есть пролог/эпилог, зато само тело меньше, т.к. нет постоянных проверок.
при использовании возвращаемых значнеий — нет пролога/эпилога, зато каждый вызов функции/выделение ресурса обходится дороже.
Вот именно это я исследовал. Что же в итоге лучше. Что перевешивает. То, что есть пролог/эпилог — это понятно, понятно, что это всё не за бесплатно.
з.ы. кстати, если в функции нет объектов с нетривиальными деструкторами, то исключения выглядят значительно привлекательнее, т.к. и эпилога/пролога нет и вызовы функции дешевле.
Здравствуйте, CreatorCray, Вы писали:
CC>Здравствуйте, Andrew S, Вы писали:
AS>>И еще не забывай про привязку к одному ядру. А вот что автор тестов ответил на все это — можешь прочитать сам CC>Да, совсем забыл: CC>
CC> SetProcessAffinityMask (GetCurrentProcess (),1);
CC> // и для гарантии контрольный в голову!!!
CC> SetThreadAffinityMask (GetCurrentThread (),1);
CC>
О чём вы вообще говорите?! Ну не может код выполняться на разных процессорах/ядрах, если всего один процессор и одно ядро!
И не может так быть, что бы кусок кода, выполняемый 24 такта 1000 раз подряд, каждый из этих 1000 раз прерывался таймером.
Я не понимаю, о чём вы говорите.
Здравствуйте, Andrew S, Вы писали:
AS>Ну и где тут реальный код? С трансляцией исключений в собственнные и т.п.? Я не вижу поинта для сравенения, уж извини. Ведь проверка ошибок для этого и используется — чтобы на основе возвращаемого значения сформировать свое. В реальной ситуации с исключениями часто ровно та же задача — у нас есть набор наших исключений и нам надо "чужие" исключения привести к нашим.
Ну хорошо, а ты что утверждаешь, что реальный код — это только код с трансляцией исключений?
Я надеюсь, что нет. Тогда почему не начать с простого случая?
Я тебя уверяю, что есть множество кода, который никакими трансляциями не занимается.
AS>>>Понятно. Ну, что ж, могу лишь сказать, что это по меньшей мере неверно. Надо привязывать поток к одному ядру и устанавливать приоритеты на реалтайм — тогда хоть как то можно говорить о нормальном измерении при помощи этой методики.
R>>Зачем привязывать к единственному ядру? Или ты боишься, что у меня часть вычислений убежало на соседний комп?
AS>Куда убежать??? Вероятно, про багу с рассинхронизаций rdtsc на разных ядрах ты не слышал?
Нет, чтобы на одном ядре таймер сам с собой рассинхронизировался, я не слышал. А что такое бывает?
R>>Зачем поднимать приоритеты? Или ты думаешь, что поток с более высоким приоритетом будет выполняться быстрее?
AS> Круто.
Вот я тоже так подумал.
AS>>>>>Выложи исходники — я тебя уверяю, народ раскритикует по самые
R>>>>Код чего интересует? R>>>>Сами тестируемые функции? Или что?
AS>>>Все — полный тест юнит.
R>>Я не понимаю твой поинт имхо уже наполовину переходящий в наезды. Ты хочешь сказать, что либо я выдумал цифры, либо неправильно мерял? AS>Это не наезд, а критический анализ. То, что тебе он не нравится — уже означает его претензии на валидность Так чего тебе бояться — я не понимаю причин для того, чтобы не выложить полный проект. Если жалко трудов — так и скажи. Но только по результатам судить о тесте — уж извини. Это действительно инженерный подход, который для исследований не годится.
Мне не жалко трудов, просто проект получился достаточно наколеночный, т.к. когда я начинал, то не собирался делать что-то особо основательное. Но если ты так настаиваешь, сейчас всё это запакую и выложу.
Здравствуйте, remark, Вы писали:
R>Здравствуйте, rm822, Вы писали:
R>>Здравствуйте, remark
R>>Вам не стыдно? R>>Вы не упоямянули какую модель обработки исключений использовали, синхронную или асинхронную. Вы же не будете утверждать что между ними нет никакой существенной разницы?
R>Возможно стыд мне и позор, но первый раз про такое слышу. Или слышал под другим названием. R>Что за синхроннная/асинхронная модель? http://msdn2.microsoft.com/en-us/library/1deeycx5.aspx
R>>Не указана потоковоя модель ST\MT и способ линковки рантайма lib\dll
R>Это никак не повлияет, поэтому и не указывал. Там нет вызовов из рантайма. Возможно за исключением кидания исключения, но там небольшой оверхед на синхронизацию никак не скажется.
Вы проверяли? Если нет то вы не знаете, это только догадки, а разработчики как известно не могут с достаточной достоверностью предсказывать узкие места.
R>>Какой класс исключения вы использовали? (Я надеюсь он был производным от std::runtime_error? или вы bool кидали? )
R>int
R>Я думаю это тоже неважно, за исключением небольшого оверхеда при кидании.
Вы думаете, но вы не знаете. Это важно. Дополнительный new\delete + вступают в силу заморочки с полиморфным поведением.
То что кто-то будет кидать Int крайне маловероятно, а здравый смысл требует гомоморфную иерархию классов исключений. std использует в качестве базы std::exception. Даже если вы хотите использовать какой-то свой класс как пращура, то все равно он должен быть наследником std::exception
R>>Как насчет полиморфного поведения? R>А что насчёт него?
сколько займет вызов виртуального деструктора?
R>>Как насчет ловли ссылок на один из базовых классов в иерархии? R>А что насчёт него?
try{
throw std::runtime_error("")
} catch(std::exception&) //сколько займет проверка того что std::runtime_error наследник std::exception?
{}
R>>Как насчет ловли ссылок на один из производных классов в иерархии? R>А что насчёт него?
try{
throw std::exception("")
} catch(std::runtime_error&) //сколько займет проверка того что здесь catch не сработает?
{}
вполне реальная ситуация
try{
...
}
catch(SqlException& e) {....}
catch(IOException& e) {....}
catch(std::exception& e) { что-то не срослось }
R>>Двойка вам за научный подход.
R>>>
... << RSDN@Home 1.1.4 stable SR1 rev. 568>>
Re[3]: [Investigation] Exceptions vs. Return Values
Здравствуйте, Аноним, Вы писали:
А>В дополнение к сказанному. А>Этот сэкономленный if играет роль только когда тело функции пустое. Если функция хоть что-то делает, тем более в цикле, то такая экономия не играет никакой роли. Так что эксперимент поставлен абсолютно некорректно.
Ты сам себе противоречишь — вначале говоришь, что надо, что б функция что-то делала, а потом, говоришь, что тогда все результаты будут не видны.
Имхо я как раз всё сделал как надо — меряю вклад тогда, когда он виден. А не наоборот.
А>Олег Алексеев.
[]
R>>>Какой класс исключения вы использовали? (Я надеюсь он был производным от std::runtime_error? или вы bool кидали? )
R>>int R> R>>Я думаю это тоже неважно, за исключением небольшого оверхеда при кидании. R>Вы думаете, но вы не знаете. Это важно. Дополнительный new\delete + вступают в силу заморочки с полиморфным поведением. R>То что кто-то будет кидать Int крайне маловероятно, а здравый смысл требует гомоморфную иерархию классов исключений. std использует в качестве базы std::exception. Даже если вы хотите использовать какой-то свой класс как пращура, то все равно он должен быть наследником std::exception
ничего он не должен. Это рекомендация
R>>>Как насчет полиморфного поведения? R>>А что насчёт него? R>сколько займет вызов виртуального деструктора?
кто кидает указатели? Нафиг это надо?
R>>>Как насчет ловли ссылок на один из базовых классов в иерархии? R>>А что насчёт него? R>try{ R> throw std::runtime_error("") R>} catch(std::exception&) //сколько займет проверка того что std::runtime_error наследник std::exception? R>{}
хз, думаю, недолго
R>>>Как насчет ловли ссылок на один из производных классов в иерархии? R>>А что насчёт него? R>try{ R> throw std::exception("") R>} catch(std::runtime_error&) //сколько займет проверка того что здесь catch не сработает? R>{}
the same
R>вполне реальная ситуация R>try{ R> ... R>} R>catch(SqlException& e) {....} R>catch(IOException& e) {....} R>catch(std::exception& e) { что-то не срослось }
R>>>Двойка вам за научный подход.
R>>>>
Re[4]: [Investigation] Exceptions vs. Return Values
Здравствуйте, Константин Л., Вы писали:
КЛ>Здравствуйте, remark, Вы писали:
R>>Здравствуйте, rm822, Вы писали:
R>>>Здравствуйте, remark
R>>>Вам не стыдно? R>>>Вы не упоямянули какую модель обработки исключений использовали, синхронную или асинхронную. Вы же не будете утверждать что между ними нет никакой существенной разницы?
R>>Возможно стыд мне и позор, но первый раз про такое слышу. Или слышал под другим названием. R>>Что за синхроннная/асинхронная модель?
КЛ>это vc specific:
КЛ>/EHa КЛ>/EHsc
КЛ>[]
Аааа. Понял о чём речь.
Ну вы же видите результаты, когда исключение кидается — всякие вариации, типа синхронной/асинхронной модели или кидать int или runtime_error, версия рантайма — это уже как мёртвому припарка — кидание исключений СУЩЕСТВЕННО НА МНОГО более медленные. Я не думаю, что из-за настроек время станет вдруг не 20мкс, а 10нс.
Здравствуйте, remark, Вы писали:
R>О чём вы вообще говорите?! Ну не может код выполняться на разных процессорах/ядрах, если всего один процессор и одно ядро!У
У меня например 1 проц — 2 ядра... При особом желании могу нарыть как 2 процессорную так и четырехядерную машины... На них еще как может.
Речь тут о правильной методике тестирования. Разумеется если у тебя однояйцевый проц, то тогда тебе не надо париться касательно ядер. Но для правильного результата надо учитывать нюансы исполнения кода как на 1 так и на 2+ ядерниках.
R>И не может так быть, что бы кусок кода, выполняемый 24 такта 1000 раз подряд, каждый из этих 1000 раз прерывался таймером.
Колво циклов надо увеличить по любому — слишком мало — будет сказываться погрешность измерений.
R>
разумеется
... << RSDN@Home 1.1.4 stable SR1 rev. 568>>
Re[4]: [Investigation] Exceptions vs. Return Values
Здравствуйте, rm822, Вы писали:
R>>>Двойка вам за научный подход.
Тут основная проблема, что бы сделать тесты справедливые для обоих стратегий.
Т.е. что я делаю — беру некую предельно простую абстракцию — ресурс — приписываю ему семантику — его надо выделить, причём выделение может провалится, и его обязательно надо освободить при выходе из функции. Далее я моделирую эту абстракцию на С++ и на С предельно "честно" по отношению к обоим языкам и их возможностям — на С++ — объект с деструктором, на С — функции construct/destroy. И сравниваю именно эти реализации одной и той же задачи разными средствами. Какая реализация быстрее?
Сравнивать тёплое с мягким нет смысла.
Т.е. нет смысла мерить типа "а что если мы в с++ вставим дополнительный new/delete" — ты тогда и в С вставь тоже что-то, что будет решать ту задачу, для решения которой ты в С++ вставил дополнительный new/delete.
Т.е. ты придумай некую абстрактную задачу, которая бы на С++ требовала применения иерархий ошибок/динамического полиморфизма и т.д., потом максимально "честно" вырази её решение на С, причём там, что бы тебе потом не говорили — а я бы это сделал подругому и быстрее. И вот тогда будем мерить, где решение задачи "мега динамической полиморфной обработки ошибок" работает быстрее.
Re[4]: [Investigation] Exceptions vs. Return Values
От:
Аноним
Дата:
02.04.07 11:30
Оценка:
Здравствуйте, remark, Вы писали:
R>Ты сам себе противоречишь — вначале говоришь, что надо, что б функция что-то делала, а потом, говоришь, что тогда все результаты будут не видны. R>Имхо я как раз всё сделал как надо — меряю вклад тогда, когда он виден. А не наоборот.
Я хочу сказать, что практическая ценность этого измерения незначительна. Мы же на практике не пишем пустых функций.
Ещё я боюсь, что сейчас появится куча людей, утверждающих, что преимущество исключений доказано экспериментально, но забывающих указать в каких условиях оно проявляется. Между тем, написать exception-safe программу ох как непросто.
Олег Алексеев.
Re[9]: [Investigation] Exceptions vs. Return Values
Здравствуйте, CreatorCray, Вы писали:
CC>Здравствуйте, remark, Вы писали:
R>>О чём вы вообще говорите?! Ну не может код выполняться на разных процессорах/ядрах, если всего один процессор и одно ядро!У CC>У меня например 1 проц — 2 ядра... При особом желании могу нарыть как 2 процессорную так и четырехядерную машины... На них еще как может.
Либо я чего-то не понимаю...
То, что ты можешь нарыть 2 процессорную тачку никак не сказывается на том, будет ли у меня погрешность измерений, если я на одноядерной тачке не привяжу потоки к ядрам.
На одноядерной тачке не надо ничего привязывать. Я учёл все ньюансы, адекватные в конкретной ситуации. Если бы я запускал на многоядерной такчке, тогда бы я парился по поводу привязки и т.д.
Я не понимаю о чём речь. Почему Andrew S говорит, что я забыл сделать какую-то привязку, поднять приоритеты и т.д. Слов просто нет.
Не знаю даже, что вам ещё сказать. Или если вы тут о чём-то своём говорите, типа "как мы будем мерить производительность на многоядерной тачке", то то не говорите, что я что-то забыл.
R>>И не может так быть, что бы кусок кода, выполняемый 24 такта 1000 раз подряд, каждый из этих 1000 раз прерывался таймером. CC>Колво циклов надо увеличить по любому — слишком мало — будет сказываться погрешность измерений.
Именно поэтому потом я делал замеры в "асимптотическом" случае.
R>> CC>разумеется
[]
R>Я не понимаю о чём речь. Почему Andrew S говорит, что я забыл сделать какую-то привязку, поднять приоритеты и т.д. Слов просто нет. R>Не знаю даже, что вам ещё сказать. Или если вы тут о чём-то своём говорите, типа "как мы будем мерить производительность на многоядерной тачке", то то не говорите, что я что-то забыл.
ну это он для надежности. Вдруг какой-либо другой поток вклинится в подсчитанные такты.
[]
Re[5]: [Investigation] Exceptions vs. Return Values
Здравствуйте, Аноним, Вы писали:
А>Здравствуйте, remark, Вы писали:
R>>Ты сам себе противоречишь — вначале говоришь, что надо, что б функция что-то делала, а потом, говоришь, что тогда все результаты будут не видны. R>>Имхо я как раз всё сделал как надо — меряю вклад тогда, когда он виден. А не наоборот.
А>Я хочу сказать, что практическая ценность этого измерения незначительна. Мы же на практике не пишем пустых функций.
А>Ещё я боюсь, что сейчас появится куча людей, утверждающих, что преимущество исключений доказано экспериментально, но забывающих указать в каких условиях оно проявляется. Между тем, написать exception-safe программу ох как непросто.
конечно, поэтому давайте писать простые совсем не exception-safe программы .
Его поинт в том, что сама по себе поддержка исключений вносит практически нулевой оверхэд.
А>Олег Алексеев.
А>
Re[8]: [Investigation] Exceptions vs. Return Values
R>Что ты хочешь этим сказать? А ты погляди код, который генерируется при вызове функций, возвращающих значения. Там тоже есть код, там не по велению волшебной палочки всё происходит.
Я хочу сказать, что там не 2-3 команды, а десяток довольно толстых как сточки зрения кода, так и данных, команд.
R>Ситуация такая: при использовании исключений у функции есть пролог/эпилог, зато само тело меньше, т.к. нет постоянных проверок. R>при использовании возвращаемых значнеий — нет пролога/эпилога, зато каждый вызов функции/выделение ресурса обходится дороже.
R>Вот именно это я исследовал. Что же в итоге лучше. Что перевешивает. То, что есть пролог/эпилог — это понятно, понятно, что это всё не за бесплатно.
Чушь. Твоя запись if (!fun()) return val как раз не общий случай. Его я могу оптимизировать до одной проверки на все функции. val1 = fun1() ... valn = funn(); return val1 | val2 | ... | valn. В принципе, это может сделать и сам компилятор (более того, он это хоть и коряво, на пытается при помощи setne сделать). И что это покажет? Я говорю — нужны реальные паттерны.
R>з.ы. кстати, если в функции нет объектов с нетривиальными деструкторами, то исключения выглядят значительно привлекательнее, т.к. и эпилога/пролога нет и вызовы функции дешевле.
Не вышлядят. Блин, ну сколько можно — я ж уже показал, что в этом случае используется ebp стек фрейм. Попробуй лучше исследовать, как это влияет на перфоманс вычислительной функции, а?
Я к чему — пустые функции должны идти в dev\null. Использование исключений значительно влияет на кодогенерацию. Точка.
Здравствуйте, remark, Вы писали:
R>Здравствуйте, rm822, Вы писали:
R>>>>Двойка вам за научный подход.
R>Тут основная проблема, что бы сделать тесты справедливые для обоих стратегий. R>Т.е. что я делаю — беру некую предельно простую абстракцию — ресурс — приписываю ему семантику — его надо выделить, причём выделение может провалится, и его обязательно надо освободить при выходе из функции. Далее я моделирую эту абстракцию на С++ и на С предельно "честно" по отношению к обоим языкам и их возможностям — на С++ — объект с деструктором, на С — функции construct/destroy. И сравниваю именно эти реализации одной и той же задачи разными средствами. Какая реализация быстрее? R>Сравнивать тёплое с мягким нет смысла. R>Т.е. нет смысла мерить типа "а что если мы в с++ вставим дополнительный new/delete" — ты тогда и в С вставь тоже что-то, что будет решать ту задачу, для решения которой ты в С++ вставил дополнительный new/delete. R>Т.е. ты придумай некую абстрактную задачу, которая бы на С++ требовала применения иерархий ошибок/динамического полиморфизма и т.д., потом максимально "честно" вырази её решение на С, причём там, что бы тебе потом не говорили — а я бы это сделал подругому и быстрее. И вот тогда будем мерить, где решение задачи "мега динамической полиморфной обработки ошибок" работает быстрее.
Реальность такова что для обработки исключений используется иерахия классов а не int.(Обычно) Для обработки ошибок error-кодами используется int а не классы. (Обычно) И сравнивать нужно именно теплое с мягким, потому что такова реальность.
R>
... << RSDN@Home 1.1.4 stable SR1 rev. 568>>
Re[11]: [Investigation] Exceptions vs. Return Values
Здравствуйте, Константин Л., Вы писали:
КЛ>Здравствуйте, remark, Вы писали:
КЛ>[]
R>>Я не понимаю о чём речь. Почему Andrew S говорит, что я забыл сделать какую-то привязку, поднять приоритеты и т.д. Слов просто нет. R>>Не знаю даже, что вам ещё сказать. Или если вы тут о чём-то своём говорите, типа "как мы будем мерить производительность на многоядерной тачке", то то не говорите, что я что-то забыл.
КЛ>ну это он для надежности. Вдруг какой-либо другой поток вклинится в подсчитанные такты.
Во-первых, thread affinity тут всё равно ни при чём
Во-вторых, от "вклинивания" на win32/linux всё равно никак не защититься
КЛ>[]
AS>>Ну и где тут реальный код? С трансляцией исключений в собственнные и т.п.? Я не вижу поинта для сравенения, уж извини. Ведь проверка ошибок для этого и используется — чтобы на основе возвращаемого значения сформировать свое. В реальной ситуации с исключениями часто ровно та же задача — у нас есть набор наших исключений и нам надо "чужие" исключения привести к нашим.
R>Ну хорошо, а ты что утверждаешь, что реальный код — это только код с трансляцией исключений? R>Я надеюсь, что нет. Тогда почему не начать с простого случая? R>Я тебя уверяю, что есть множество кода, который никакими трансляциями не занимается.
А я тебя уверяю в обратном. Сравни std и com исключения. Обычно в проекте своя иерархия исключений, и они транслируются на разных уровнях вызова. Как в этом случае добиться преймущества от исключений на уровне вызова библиотечных методов\функций — вопрос интересный.
AS>>>>Понятно. Ну, что ж, могу лишь сказать, что это по меньшей мере неверно. Надо привязывать поток к одному ядру и устанавливать приоритеты на реалтайм — тогда хоть как то можно говорить о нормальном измерении при помощи этой методики.
R>>>Зачем привязывать к единственному ядру? Или ты боишься, что у меня часть вычислений убежало на соседний комп?
AS>>Куда убежать??? Вероятно, про багу с рассинхронизаций rdtsc на разных ядрах ты не слышал?
R>Нет, чтобы на одном ядре таймер сам с собой рассинхронизировался, я не слышал. А что такое бывает?
А при чем тут один. Ты тестировал на одной конфигурации?
R>>>Зачем поднимать приоритеты? Или ты думаешь, что поток с более высоким приоритетом будет выполняться быстрее?
AS>> Круто.
R>Вот я тоже так подумал.
Да. Сильно. А ты не подумал, что выбор минимального из результатов — это в корне неправильный подход к статистике?
R>Мне не жалко трудов, просто проект получился достаточно наколеночный, т.к. когда я начинал, то не собирался делать что-то особо основательное. Но если ты так настаиваешь, сейчас всё это запакую и выложу.
Да нет, я не настаиваю. Просто если ты хочешь называть это исследованием и чтобы его результаты имели практическую ценность и обоснованность — должен быть виден код, который приводит к таким результатам. Скажи честно, ты смотрел для каждого варианта то, что генерит компилятор на уровне ассемблера? Ты разбирался как каждый из них работает с исключениями и с возвращающими значениями и влияние этого на кодогенерацию в целом? А ведь это все нужно, чтобы не просто статистику поиметь, а сделать из нее выводы — самое ценное, что есть в исследовании. Я выводов пока (как и самого исследования) пока тут не вижу, уж извини.
Здравствуйте, Константин Л., Вы писали:
КЛ>Его поинт в том, что сама по себе поддержка исключений вносит практически нулевой оверхэд.
Если бы более точным, то я хочу померить оверхед обработки ошибок отдельно Допустим получается 100нс/1000 строк кода при использовании кодов возврата и 95нс/1000 строк кода.
Дальше каждый сам волен решать — важно ему это или нет. Важно ли ему эти 5 нс.
Но утвержать, что это просто не важно, имхо, некорректно.
А>>Олег Алексеев.
А>>
Здравствуйте, Andrew S, Вы писали:
AS>>>Ну и где тут реальный код? С трансляцией исключений в собственнные и т.п.? Я не вижу поинта для сравенения, уж извини. Ведь проверка ошибок для этого и используется — чтобы на основе возвращаемого значения сформировать свое. В реальной ситуации с исключениями часто ровно та же задача — у нас есть набор наших исключений и нам надо "чужие" исключения привести к нашим.
R>>Ну хорошо, а ты что утверждаешь, что реальный код — это только код с трансляцией исключений? R>>Я надеюсь, что нет. Тогда почему не начать с простого случая? R>>Я тебя уверяю, что есть множество кода, который никакими трансляциями не занимается.
AS>А я тебя уверяю в обратном. Сравни std и com исключения. Обычно в проекте своя иерархия исключений, и они транслируются на разных уровнях вызова. Как в этом случае добиться преймущества от исключений на уровне вызова библиотечных методов\функций — вопрос интересный.
com исключения? это что?
Re[6]: [Investigation] Exceptions vs. Return Values
Здравствуйте, rm822, Вы писали:
R>Здравствуйте, remark, Вы писали:
R>>Здравствуйте, rm822, Вы писали:
R>>>>>Двойка вам за научный подход.
R>>Тут основная проблема, что бы сделать тесты справедливые для обоих стратегий. R>>Т.е. что я делаю — беру некую предельно простую абстракцию — ресурс — приписываю ему семантику — его надо выделить, причём выделение может провалится, и его обязательно надо освободить при выходе из функции. Далее я моделирую эту абстракцию на С++ и на С предельно "честно" по отношению к обоим языкам и их возможностям — на С++ — объект с деструктором, на С — функции construct/destroy. И сравниваю именно эти реализации одной и той же задачи разными средствами. Какая реализация быстрее? R>>Сравнивать тёплое с мягким нет смысла. R>>Т.е. нет смысла мерить типа "а что если мы в с++ вставим дополнительный new/delete" — ты тогда и в С вставь тоже что-то, что будет решать ту задачу, для решения которой ты в С++ вставил дополнительный new/delete. R>>Т.е. ты придумай некую абстрактную задачу, которая бы на С++ требовала применения иерархий ошибок/динамического полиморфизма и т.д., потом максимально "честно" вырази её решение на С, причём там, что бы тебе потом не говорили — а я бы это сделал подругому и быстрее. И вот тогда будем мерить, где решение задачи "мега динамической полиморфной обработки ошибок" работает быстрее.
R>Реальность такова что для обработки исключений используется иерахия классов а не int.(Обычно) Для обработки ошибок error-кодами используется int а не классы. (Обычно) И сравнивать нужно именно теплое с мягким, потому что такова реальность.
Re[6]: [Investigation] Exceptions vs. Return Values
От:
Аноним
Дата:
02.04.07 12:07
Оценка:
Здравствуйте, Константин Л., Вы писали:
КЛ>Его поинт в том, что сама по себе поддержка исключений вносит практически нулевой оверхэд.
В чём его поинт, автор уже написал:
>> Цель можно охарактеризовать примерно так: исследовать скорость работы некоторых ключевых паттернов кода, реализованных с применением исключений для сообщения об ошибках, или с помощью возвращаемых значений.
Написание пустых функций не есть "ключевой паттерн кода" (по-моему).
Нулевой оверхэд видел своими глазами (правда только у GCC). Назвать его нулевым по объему дополнительного кода язык не поворачивается. Поверьте, я не ратую за отмену исключений. Я за трезвую оценку и объективные исследования. Думаю, что критические отзывы пойдут только на пользу исследованиям.
Олег Алексеев.
Re[9]: [Investigation] Exceptions vs. Return Values
Здравствуйте, Andrew S, Вы писали:
R>>Что ты хочешь этим сказать? А ты погляди код, который генерируется при вызове функций, возвращающих значения. Там тоже есть код, там не по велению волшебной палочки всё происходит.
AS>Я хочу сказать, что там не 2-3 команды, а десяток довольно толстых как сточки зрения кода, так и данных, команд.
R>>Ситуация такая: при использовании исключений у функции есть пролог/эпилог, зато само тело меньше, т.к. нет постоянных проверок. R>>при использовании возвращаемых значнеий — нет пролога/эпилога, зато каждый вызов функции/выделение ресурса обходится дороже.
R>>Вот именно это я исследовал. Что же в итоге лучше. Что перевешивает. То, что есть пролог/эпилог — это понятно, понятно, что это всё не за бесплатно.
AS>Чушь. Твоя запись if (!fun()) return val как раз не общий случай. Его я могу оптимизировать до одной проверки на все функции. val1 = fun1() ... valn = funn(); return val1 | val2 | ... | valn. В принципе, это может сделать и сам компилятор (более того, он это хоть и коряво, на пытается при помощи setne сделать). И что это покажет? Я говорю — нужны реальные паттерны.
Это не оптимизация общего случая — пример — вложенные ресурсы.
Ну допустим, я бы потратил пол-года и переписал некий проект bla-bla с одной стратегии на другую и выдал бы в итоге, что с исключениями bla-bla стал работать на 1% быстрее и размер уменьшился на 2%. Ну и что? По-твоему это бы дало больше информации?
Мы же не штампуем целиком проекты, мы пишем отдельные функции — вот тут функци, которая форвардит вызов другой, а вот тут функция, которая вызывает ещё 3 функции, а вот тут функция, которая выделяет ресурс и т.д.
R>>з.ы. кстати, если в функции нет объектов с нетривиальными деструкторами, то исключения выглядят значительно привлекательнее, т.к. и эпилога/пролога нет и вызовы функции дешевле.
AS>Не вышлядят. Блин, ну сколько можно — я ж уже показал, что в этом случае используется ebp стек фрейм. Попробуй лучше исследовать, как это влияет на перфоманс вычислительной функции, а?
А я сколько раз показывал, что код работает на 50% быстрее и занимает на 200% меньше.
AS>Я к чему — пустые функции должны идти в dev\null. Использование исключений значительно влияет на кодогенерацию. Точка.
Так же как и возвращаемые значения. Очевидно. Осталось только понять как.
Здравствуйте, remark, Вы писали:
R>Аааа. Понял о чём речь. R>Ну вы же видите результаты, когда исключение кидается — всякие вариации, типа синхронной/асинхронной модели или кидать int или runtime_error, версия рантайма — это уже как мёртвому припарка — кидание исключений СУЩЕСТВЕННО НА МНОГО более медленные. Я не думаю, что из-за настроек время станет вдруг не 20мкс, а 10нс.
Не совсем так. Асинхронная можель обработки исключений в VC (название неудачное, но так уж назвали) означает, что исключение может кинуть любая функция, в том числе сишная. Плюс SEH-исключения будут ловиться по catch(..). Т.е., компилятор просто будет втыкать код для размотки стека в большем количестве случаев. Соответственно, повлияет это и на нормальное исполнение программы, когда исключения не кидаются.
Одним из 33 полных кавалеров ордена "За заслуги перед Отечеством" является Геннадий Хазанов.
Re[11]: [Investigation] Exceptions vs. Return Values
AS>>>>Ну и где тут реальный код? С трансляцией исключений в собственнные и т.п.? Я не вижу поинта для сравенения, уж извини. Ведь проверка ошибок для этого и используется — чтобы на основе возвращаемого значения сформировать свое. В реальной ситуации с исключениями часто ровно та же задача — у нас есть набор наших исключений и нам надо "чужие" исключения привести к нашим.
R>>>Ну хорошо, а ты что утверждаешь, что реальный код — это только код с трансляцией исключений? R>>>Я надеюсь, что нет. Тогда почему не начать с простого случая? R>>>Я тебя уверяю, что есть множество кода, который никакими трансляциями не занимается.
AS>>А я тебя уверяю в обратном. Сравни std и com исключения. Обычно в проекте своя иерархия исключений, и они транслируются на разных уровнях вызова. Как в этом случае добиться преймущества от исключений на уровне вызова библиотечных методов\функций — вопрос интересный.
КЛ>com исключения? это что?
Например, COleException. В этом случае мы имеем дело аж с 2-мя враппингами — перевод возвращаемого значения в ole иключение, перевод ole исключения в наше родное. И это все вполне common случай. Я ж говорю — различные библиотечные методы\функции.
Здравствуйте, rm822, Вы писали:
R>>Тут основная проблема, что бы сделать тесты справедливые для обоих стратегий. R>>Т.е. что я делаю — беру некую предельно простую абстракцию — ресурс — приписываю ему семантику — его надо выделить, причём выделение может провалится, и его обязательно надо освободить при выходе из функции. Далее я моделирую эту абстракцию на С++ и на С предельно "честно" по отношению к обоим языкам и их возможностям — на С++ — объект с деструктором, на С — функции construct/destroy. И сравниваю именно эти реализации одной и той же задачи разными средствами. Какая реализация быстрее? R>>Сравнивать тёплое с мягким нет смысла. R>>Т.е. нет смысла мерить типа "а что если мы в с++ вставим дополнительный new/delete" — ты тогда и в С вставь тоже что-то, что будет решать ту задачу, для решения которой ты в С++ вставил дополнительный new/delete. R>>Т.е. ты придумай некую абстрактную задачу, которая бы на С++ требовала применения иерархий ошибок/динамического полиморфизма и т.д., потом максимально "честно" вырази её решение на С, причём там, что бы тебе потом не говорили — а я бы это сделал подругому и быстрее. И вот тогда будем мерить, где решение задачи "мега динамической полиморфной обработки ошибок" работает быстрее.
R>Реальность такова что для обработки исключений используется иерахия классов а не int.(Обычно) Для обработки ошибок error-кодами используется int а не классы. (Обычно) И сравнивать нужно именно теплое с мягким, потому что такова реальность.
Осталось еще добавить, что при обработке ошибок error-кодами (обычно) как минимум в половине случаев забывают проверять код возврата или не знают что с ним делать, и поэтому программа с обработкой ошибок error-кодами все равно будет быстрее Зато с исключениями — правильнее
Одним из 33 полных кавалеров ордена "За заслуги перед Отечеством" является Геннадий Хазанов.
Re[10]: [Investigation] Exceptions vs. Return Values
Здравствуйте, Andrew S, Вы писали:
AS>Да. Сильно. А ты не подумал, что выбор минимального из результатов — это в корне неправильный подход к статистике?
Статистика тут ни при чем. В подобныъ замерах скорости — интерес представляет только минимальное время выполнения.
AS>Да нет, я не настаиваю. Просто если ты хочешь называть это исследованием и чтобы его результаты имели практическую ценность и обоснованность — должен быть виден код, который приводит к таким результатам. Скажи честно, ты смотрел для каждого варианта то, что генерит компилятор на уровне ассемблера? Ты разбирался как каждый из них работает с исключениями и с возвращающими значениями и влияние этого на кодогенерацию в целом? А ведь это все нужно, чтобы не просто статистику поиметь, а сделать из нее выводы — самое ценное, что есть в исследовании. Я выводов пока (как и самого исследования) пока тут не вижу, уж извини.
То есть у вас есть лучшее тестирование , и вы его зажимаете и не выкладываете ?
Ищу работу, 3D, SLAM, computer graphics/vision.
Re[10]: [Investigation] Exceptions vs. Return Values
R>>>Что ты хочешь этим сказать? А ты погляди код, который генерируется при вызове функций, возвращающих значения. Там тоже есть код, там не по велению волшебной палочки всё происходит.
AS>>Я хочу сказать, что там не 2-3 команды, а десяток довольно толстых как сточки зрения кода, так и данных, команд.
R>>>Ситуация такая: при использовании исключений у функции есть пролог/эпилог, зато само тело меньше, т.к. нет постоянных проверок. R>>>при использовании возвращаемых значнеий — нет пролога/эпилога, зато каждый вызов функции/выделение ресурса обходится дороже.
R>>>Вот именно это я исследовал. Что же в итоге лучше. Что перевешивает. То, что есть пролог/эпилог — это понятно, понятно, что это всё не за бесплатно.
AS>>Чушь. Твоя запись if (!fun()) return val как раз не общий случай. Его я могу оптимизировать до одной проверки на все функции. val1 = fun1() ... valn = funn(); return val1 | val2 | ... | valn. В принципе, это может сделать и сам компилятор (более того, он это хоть и коряво, на пытается при помощи setne сделать). И что это покажет? Я говорю — нужны реальные паттерны.
R>Это не оптимизация общего случая — пример — вложенные ресурсы.
Это оптимизация приведенного тобой примера, в которой переход не требуется вообще...
R>Ну допустим, я бы потратил пол-года и переписал некий проект bla-bla с одной стратегии на другую и выдал бы в итоге, что с исключениями bla-bla стал работать на 1% быстрее и размер уменьшился на 2%. Ну и что? По-твоему это бы дало больше информации?
Не проект. Надо выделить несколько функций различных классов, и уже на их основе анализировать эффект.
R>Мы же не штампуем целиком проекты, мы пишем отдельные функции — вот тут функци, которая форвардит вызов другой, а вот тут функция, которая вызывает ещё 3 функции, а вот тут функция, которая выделяет ресурс и т.д.
Пустые функции — это не то. Ну сколько можно, право. Даже уже занятие ebp уменьшает вычислительный потенциал на несколько процентов.
R>>>з.ы. кстати, если в функции нет объектов с нетривиальными деструкторами, то исключения выглядят значительно привлекательнее, т.к. и эпилога/пролога нет и вызовы функции дешевле.
AS>>Не вышлядят. Блин, ну сколько можно — я ж уже показал, что в этом случае используется ebp стек фрейм. Попробуй лучше исследовать, как это влияет на перфоманс вычислительной функции, а?
R>А я сколько раз показывал, что код работает на 50% быстрее и занимает на 200% меньше.
Только ты показывал это на фикции — на пустых функциях. Которые к тому же можно оптимизировать в случае возвращаемых значений.
AS>>Я к чему — пустые функции должны идти в dev\null. Использование исключений значительно влияет на кодогенерацию. Точка.
R>Так же как и возвращаемые значения. Очевидно. Осталось только понять как.
А для этого надо как минимум анализировать результирующий код — чего сделано не было совсем. Ведь так? Ведь в результате (на мой взгляд) ты получил не эффект от использования исключений, а сравнение теплого с мягким...
AS>>Да. Сильно. А ты не подумал, что выбор минимального из результатов — это в корне неправильный подход к статистике?
M>Статистика тут ни при чем. В подобныъ замерах скорости — интерес представляет только минимальное время выполнения.
Ух ты. Т.е. в реальной жизни пользователь запускает программу и всегда попадает на минимум? Везет...
AS>>Да нет, я не настаиваю. Просто если ты хочешь называть это исследованием и чтобы его результаты имели практическую ценность и обоснованность — должен быть виден код, который приводит к таким результатам. Скажи честно, ты смотрел для каждого варианта то, что генерит компилятор на уровне ассемблера? Ты разбирался как каждый из них работает с исключениями и с возвращающими значениями и влияние этого на кодогенерацию в целом? А ведь это все нужно, чтобы не просто статистику поиметь, а сделать из нее выводы — самое ценное, что есть в исследовании. Я выводов пока (как и самого исследования) пока тут не вижу, уж извини.
M>То есть у вас есть лучшее тестирование , и вы его зажимаете и не выкладываете ?
// В этой точке мы имеем максимальный приоритет потока - 31
// Надо это для того, чтоб как можно меньше постороннего кода вщемилось в наш
// Оптимальные измерения будут на многоядерных машинах - там остальной код будет
// большей частью вытеснен на другие ядра. Для одноядерки это только несколько снизит
// кол-во потоков.
SetPriorityClass(GetCurrentProcess(), REALTIME_PRIORITY_CLASS);
SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL);
// Гарантируем что ОС не перебросит поток на другое ядро - таким образом можно смело юзать
// rdtsc т.к. синхронизация TSC нас парить не будет
SetProcessAffinityMask (GetCurrentProcess (),1);
// Ну, это можно было и не делать - но пусть будет на всяк случай
SetThreadAffinityMask (GetCurrentThread (),1);
... << RSDN@Home 1.1.4 stable SR1 rev. 568>>
Re[10]: [Investigation] Exceptions vs. Return Values
Здравствуйте, Andrew S, Вы писали:
AS>>>Ну и где тут реальный код? С трансляцией исключений в собственнные и т.п.? Я не вижу поинта для сравенения, уж извини. Ведь проверка ошибок для этого и используется — чтобы на основе возвращаемого значения сформировать свое. В реальной ситуации с исключениями часто ровно та же задача — у нас есть набор наших исключений и нам надо "чужие" исключения привести к нашим.
R>>Ну хорошо, а ты что утверждаешь, что реальный код — это только код с трансляцией исключений? R>>Я надеюсь, что нет. Тогда почему не начать с простого случая? R>>Я тебя уверяю, что есть множество кода, который никакими трансляциями не занимается.
AS>А я тебя уверяю в обратном. Сравни std и com исключения. Обычно в проекте своя иерархия исключений, и они транслируются на разных уровнях вызова. Как в этом случае добиться преймущества от исключений на уровне вызова библиотечных методов\функций — вопрос интересный.
Я не говорю, что другого не бывает. Но бывает, что не транслируются. Но как мы поймём более сложный случай, если не поймём простой?
AS>>>>>Понятно. Ну, что ж, могу лишь сказать, что это по меньшей мере неверно. Надо привязывать поток к одному ядру и устанавливать приоритеты на реалтайм — тогда хоть как то можно говорить о нормальном измерении при помощи этой методики.
R>>>>Зачем привязывать к единственному ядру? Или ты боишься, что у меня часть вычислений убежало на соседний комп?
AS>>>Куда убежать??? Вероятно, про багу с рассинхронизаций rdtsc на разных ядрах ты не слышал?
R>>Нет, чтобы на одном ядре таймер сам с собой рассинхронизировался, я не слышал. А что такое бывает?
AS>А при чем тут один. Ты тестировал на одной конфигурации?
Я тестировал на двух машиных, на обоих одно ядро.
R>>>>Зачем поднимать приоритеты? Или ты думаешь, что поток с более высоким приоритетом будет выполняться быстрее?
AS>>> Круто.
R>>Вот я тоже так подумал.
AS>Да. Сильно. А ты не подумал, что выбор минимального из результатов — это в корне неправильный подход к статистике?
Я сделал это вполне намеренно. Это вполне распространённый подход при замерах времени в условиях возникновения непредвиденных задержек.
R>>Мне не жалко трудов, просто проект получился достаточно наколеночный, т.к. когда я начинал, то не собирался делать что-то особо основательное. Но если ты так настаиваешь, сейчас всё это запакую и выложу.
AS>Да нет, я не настаиваю. Просто если ты хочешь называть это исследованием и чтобы его результаты имели практическую ценность и обоснованность — должен быть виден код, который приводит к таким результатам. Скажи честно, ты смотрел для каждого варианта то, что генерит компилятор на уровне ассемблера? Ты разбирался как каждый из них работает с исключениями и с возвращающими значениями и влияние этого на кодогенерацию в целом? А ведь это все нужно, чтобы не просто статистику поиметь, а сделать из нее выводы — самое ценное, что есть в исследовании. Я выводов пока (как и самого исследования) пока тут не вижу, уж извини.
Чего-чего, а ассемблера я нагляделся за эти дни. Нагляделся на создание фреймов исключений и проверки возвращаемых значений.
Здравствуйте, Andrew S, Вы писали:
R>>>>Что ты хочешь этим сказать? А ты погляди код, который генерируется при вызове функций, возвращающих значения. Там тоже есть код, там не по велению волшебной палочки всё происходит.
AS>>>Я хочу сказать, что там не 2-3 команды, а десяток довольно толстых как сточки зрения кода, так и данных, команд.
R>>>>Ситуация такая: при использовании исключений у функции есть пролог/эпилог, зато само тело меньше, т.к. нет постоянных проверок. R>>>>при использовании возвращаемых значнеий — нет пролога/эпилога, зато каждый вызов функции/выделение ресурса обходится дороже.
R>>>>Вот именно это я исследовал. Что же в итоге лучше. Что перевешивает. То, что есть пролог/эпилог — это понятно, понятно, что это всё не за бесплатно.
AS>>>Чушь. Твоя запись if (!fun()) return val как раз не общий случай. Его я могу оптимизировать до одной проверки на все функции. val1 = fun1() ... valn = funn(); return val1 | val2 | ... | valn. В принципе, это может сделать и сам компилятор (более того, он это хоть и коряво, на пытается при помощи setne сделать). И что это покажет? Я говорю — нужны реальные паттерны.
R>>Это не оптимизация общего случая — пример — вложенные ресурсы.
AS>Это оптимизация приведенного тобой примера, в которой переход не требуется вообще...
Это понятно, что этот код можно оптимизировать. Я даже больше тебе скажу — весь этот код можно вообще удалить, т.к. он ровном счётом ничего не делает. И мерить это потом. Вот круто. Одна функция выполняется 0, и другая 0.
Суть как раз в том и заключается, что этой оптимизации нет и я эмулирую рельный код, в котором таких оптимизаций не будет. Только я выкинул из него весь шум в виде реальной работы, чтобы не вносить "постоянную составляющую".
R>>Ну допустим, я бы потратил пол-года и переписал некий проект bla-bla с одной стратегии на другую и выдал бы в итоге, что с исключениями bla-bla стал работать на 1% быстрее и размер уменьшился на 2%. Ну и что? По-твоему это бы дало больше информации?
AS>Не проект. Надо выделить несколько функций различных классов, и уже на их основе анализировать эффект.
R>>Мы же не штампуем целиком проекты, мы пишем отдельные функции — вот тут функци, которая форвардит вызов другой, а вот тут функция, которая вызывает ещё 3 функции, а вот тут функция, которая выделяет ресурс и т.д.
AS>Пустые функции — это не то. Ну сколько можно, право. Даже уже занятие ebp уменьшает вычислительный потенциал на несколько процентов.
R>>>>з.ы. кстати, если в функции нет объектов с нетривиальными деструкторами, то исключения выглядят значительно привлекательнее, т.к. и эпилога/пролога нет и вызовы функции дешевле.
AS>>>Не вышлядят. Блин, ну сколько можно — я ж уже показал, что в этом случае используется ebp стек фрейм. Попробуй лучше исследовать, как это влияет на перфоманс вычислительной функции, а?
R>>А я сколько раз показывал, что код работает на 50% быстрее и занимает на 200% меньше.
AS>Только ты показывал это на фикции — на пустых функциях. Которые к тому же можно оптимизировать в случае возвращаемых значений.
Да, всё правильно. Теперь ты можешь добавить в эти пустые функции своё наполнение, прибавить ко всем цифрам постоянную составляющую и получить те же результаты, но с постоянной составляющей.
AS>>>Я к чему — пустые функции должны идти в dev\null. Использование исключений значительно влияет на кодогенерацию. Точка.
R>>Так же как и возвращаемые значения. Очевидно. Осталось только понять как.
AS>А для этого надо как минимум анализировать результирующий код — чего сделано не было совсем. Ведь так? Ведь в результате (на мой взгляд) ты получил не эффект от использования исключений, а сравнение теплого с мягким...
Смотрел-смотрел. Просто я думал, что такой объём кода и так смотреть никто не бует, к тому же время уже подходило к часу ночи — хотелось домой
Здравствуйте, Andrew S, Вы писали:
AS>Я к чему — пустые функции должны идти в dev\null. Использование исключений значительно влияет на кодогенерацию. Точка.
Давай же сформируем эти "общие паттерны". Как ты их себе видишь?
Я думаю, их будет полезно обсудить — все выскажут свои мысли. К тому же приверженцы обоих лагерей смогут предложить лучшие реализации того и другого подхода — чтобы никому не было обидно.
R>>>А я сколько раз показывал, что код работает на 50% быстрее и занимает на 200% меньше.
AS>>Только ты показывал это на фикции — на пустых функциях. Которые к тому же можно оптимизировать в случае возвращаемых значений.
R>Да, всё правильно. Теперь ты можешь добавить в эти пустые функции своё наполнение, прибавить ко всем цифрам постоянную составляющую и получить те же результаты, но с постоянной составляющей.
Не получишь. Как минимум в одном случае у тебя на один рабочий регистр меньше — соответственно, меньше и производительность самой функции (а не пустышки). Право, я уже устал это повторять.
AS>>Я к чему — пустые функции должны идти в dev\null. Использование исключений значительно влияет на кодогенерацию. Точка.
R>Давай же сформируем эти "общие паттерны". Как ты их себе видишь?
Давай. Навскидку я вижу следующие паттерны:
1. Чисто вычислительная функция — когда вызовов апи не производится вообще. Это может быть быть парсер текста, вычисление с использованием матриц и т.п. Желательно распределить их по степени сложности — глубины вызовов и количеству вызовов с проверками в одной функции.
2. Обертки1. Когда ровно один слой и все сводится к вызову апи функции (или метода) и проверке возвращаемого значения\трансляции в исключение.
3. Обертки1. Тоже ровно один слой, но в этом случае апи использует исключение (пример — MFC). Мы транслируем в свое исключение либо в код ошибки\возвращаемое значение.
4. Работа с файлами и анализ текста. Микс тест из 1, 2 и 3.
5. Смешанные вычисления — когда необходимо в процессе вычислений обращаться к апи. Сложность вычислений,процент обращений и характер распределения мест обработки по уровням можно регулировать (либо определить статистически общие варианты и использовать только их).
Наверное, можно придумать еще кучу паттернов, используемых наиболее часто, либо параметризировать эти с учетом статистики.
R>Так же есть ещё один аспект, влияющий на производительность, но который ускользает при таких замерах – размер сгенерированного кода. Чем больше кода – тем больше промахов кэша и переходов через границы страницы памяти – это может достаточно сильно влиять на производительность. Соответственно я замерил размер кода для одной функции типа f6(), которые я приводил выше. Результаты: R>
с
с++/rv
с++/ex
instr
386
442
232
size
1320
2359
1329
R>Замерял на msvc71. instr – кол-во ассемблерных команд. Size – размер функции в байтах.
R>У исключений значительно меньше инструкций, тем не менее размер кода равен размеру кода на С с возвращаемыми значениями. Как ни странно... Связано с тем, что команды там больше по размеру. Код на С++ с возвращаемыми значениями тут сильно проигрывает – так там инструкции и на проверки возвращаемых значений и на поддержку исключений.
Интересно, почему С++ с возвращаемыми значениями так сильно проиграл С? А мне кажется, я знаю причину — оптимизатор MSVC отвратительно работает с bool типами, замена на int даёт выигрыш.
[]
R>
smart_ptr vs. smart_ptr&
Будете смеяться и писать про "root of all evil", но const int& быстрее и компактнее в коде, чем int. Особенно, когда куча шаблонных обёрток вызывает другие обёртки — оптимизатор не справляется с лишними копированиями...
People who are more than casually interested in computers should have at least some idea of what the underlying hardware is like. Otherwise the programs they write will be pretty weird (c) D.Knuth
Re[5]: [Investigation] Exceptions vs. Return Values
Как минимум, можно использовать ещё и счётчики производительности (GetProcessTimes, NtQueryInformationThread + ThreadTimes). Даже без RDTSC погрешность измерения примерно равна длительности кванта планировщика (1-15 мс), что может оказаться лучше чем у способа выше, где она неизвестно какая
People who are more than casually interested in computers should have at least some idea of what the underlying hardware is like. Otherwise the programs they write will be pretty weird (c) D.Knuth
Re[2]: [Investigation] Exceptions vs. Return Values
Здравствуйте, gear nuke, Вы писали:
GN>Будете смеяться и писать про "root of all evil", но const int& быстрее и компактнее в коде, чем int. Особенно, когда куча шаблонных обёрток вызывает другие обёртки — оптимизатор не справляется с лишними копированиями...
Однако... Это на каком компилере?
Здравствуйте, gear nuke, Вы писали:
GN>Интересно, почему С++ с возвращаемыми значениями так сильно проиграл С? А мне кажется, я знаю причину — оптимизатор MSVC отвратительно работает с bool типами, замена на int даёт выигрыш.
Нет, там проблема не в этом.
Там проблема, в том, что есть деструкторы — значит устанавливается и снимается фрейм исключений — т.е. тут двойная работа.
А код С++ компилятор даже лучше генерирует — поскольку деструкторы не вызываются явно у него есть некая свобода в оптимизации.
Здравствуйте, remark, Вы писали:
R>Там проблема, в том, что есть деструкторы — значит устанавливается и снимается фрейм исключений — т.е. тут двойная работа.
Я о "с++/rv"
R>А код С++ компилятор даже лучше генерирует — поскольку деструкторы не вызываются явно у него есть некая свобода в оптимизации.
Не верю Компилятор там один и тот же.
People who are more than casually interested in computers should have at least some idea of what the underlying hardware is like. Otherwise the programs they write will be pretty weird (c) D.Knuth
People who are more than casually interested in computers should have at least some idea of what the underlying hardware is like. Otherwise the programs they write will be pretty weird (c) D.Knuth
Нда, сильно. Вот почему я и говорю о необходимости кода.
1. Тест в общем случае некорректен. В случае использования возвращаемого значения использовать идиому RAI нет необходимости, поэтому первые 4 сравнения идут лесом. Про что я и говорил — различные варианты обработки ошибок влекут использование различных паттернов на различных уровнях вызова.
2. Вариант с rv у тебя с ошибкой
3. Где случай, в котором тебе надо вызывать функцию вне зависимости от результата вызова предыдущей? Т.е. вызвали одну, затем другую, затем третью и т.д. — а потом обрабатываем результат в зависимости от вызова. Т.е. не &&, а ||.
4. Где вложенная обработка исключений, тоже непонятно. Впрочем, это боком относится к (3), но все же? Например, некоторые искючения могут позволить продолжить цикл (info\warning), тогда как другие ведут к выходу.
5. Обрати внимание, что в rv варианте используется esp стек фрейм, а в ex — может и ebp. О чем я тебе уже 100 раз говорил. Итого — мало-мальски сложный код в ex модели будет тормознее, чем в rv. Тот пример у тебя, слишком прост, чтобы это отобразить. Вот, посмотри, что получается в этом случае:
6. Обрати внимание на разницу в способе вызова — в случае ex для настоящих объектов у тебя используеся this_call. Попробуй сделать не эмуляцию контрукторов функциями, а сделать init\deinit в самом tobj. Это съэкономит прилично и места, и времени.
7. Исключения должны быть более сложными — иметь иерархию классов — про это уже говорили. Это позволит устроить уровни отсечения.
А вообще, проблема на мой взгляд вот в чем. Ты берешь за основу модель исключений, и пытаешься приспособить под нее модель возвращаемых значений. Об этом говорит и использование bool в качестве возвращаемого значения (тогда как используется обычно более информативные типы, возвращающие не только успешность, но и саму ошибку — т.е. информации в разы больше), и построение функции (очередность и кратность вызова), уровень обработки (исключение у тебя обрабатывается на самом высоком уровне, тогда как общая практика — обработка части исключений на различных уровнях вызова, добавление отработки даже на одном уровне — то, что ты называешь ресурсами, уже изменяет ситуацию) и т.д. Неудивительно, что в этих условиях rv почти всегда проигрывает. У тебя полностью сферический конь в полном отсутствии воздуха. Я уже неоднократно говорил, что придумать ситуации, когда ex или rv будет выигрывать проблем не составляет. Нас должно интересовать не это, а реально существующие паттерны кода.
PS Эк тебя зацепило Критику надо воспринимать не в штыки, а пытаться хотя бы слушать оппонента.
Здравствуйте, Andrew S, Вы писали:
AS>>>Да. Сильно. А ты не подумал, что выбор минимального из результатов — это в корне неправильный подход к статистике?
M>>Статистика тут ни при чем. В подобныъ замерах скорости — интерес представляет только минимальное время выполнения.
AS>Ух ты. Т.е. в реальной жизни пользователь запускает программу и всегда попадает на минимум? Везет...
Нет , тут дело в методике измерений. Есть масса причин для замедления времени выполнения (прерывания и т.п.), но нет ни ЕДИНОЙ причины , для того чтобы код выполнялся быстрее в какой то из разов.
M>>То есть у вас есть лучшее тестирование , и вы его зажимаете и не выкладываете ?
AS>Я где-то такое говорил?
По постам сложилось впечатление , раз вы знаете и умеете все лучше чем автор топика.
... << RSDN@Home 1.1.4 stable SR1 rev. 568>>
Ищу работу, 3D, SLAM, computer graphics/vision.
Re[2]: [Investigation] Exceptions vs. Return Values
Здравствуйте, gear nuke, Вы писали:
GN>Будете смеяться и писать про "root of all evil", но const int& быстрее и компактнее в коде, чем int. Особенно, когда куча шаблонных обёрток вызывает другие обёртки — оптимизатор не справляется с лишними копированиями...
1 — передача адреса без чтония памяти. la заместо mov
2 — Может E(ABCD)X кончильсь, а указатели остались.
3 — Уж во всяком случае не всегда так
Re[13]: [Investigation] Exceptions vs. Return Values
AS>>>>Да. Сильно. А ты не подумал, что выбор минимального из результатов — это в корне неправильный подход к статистике?
M>>>Статистика тут ни при чем. В подобныъ замерах скорости — интерес представляет только минимальное время выполнения.
AS>>Ух ты. Т.е. в реальной жизни пользователь запускает программу и всегда попадает на минимум? Везет...
M>Нет , тут дело в методике измерений. Есть масса причин для замедления времени выполнения (прерывания и т.п.), но нет ни ЕДИНОЙ причины , для того чтобы код выполнялся быстрее в какой то из разов.
Ух ты какое сильное заявление. Причин может быть куча — выполнение на разных процессорах, состояние кеш-памяти, размер физической памяти и файла подкачки и многое многое другое. Все это может вести к выпадениям как меньшую, так и в большую сторону, тогда как в 99% процентах случаев разброс будет около некоей средней величины. Или опять интересует поведение сферического коня в вакууме? Тогда лучше всего в этом смысле тестировать в системах типа DOS. А тут же обычная практика — отсечение значений, которые выходят за некоторую границу (как сверху, так и снизу), и вычисление среднего относительно остальных.
M>>>То есть у вас есть лучшее тестирование , и вы его зажимаете и не выкладываете ?
AS>>Я где-то такое говорил?
M>По постам сложилось впечатление , раз вы знаете и умеете все лучше чем автор топика.
Фраза как минимум несогласована — моя твоя есть с трудом понимайт. Ну а касательно ваших впечатлений — ну это сугубо ваше дело, зачем это на форум то писать? Есть конструктив по поводу тестов? У меня вот есть. А у вас?
Здравствуйте, <Аноним>, Вы писали:
А>1 — передача адреса без чтония памяти. la заместо mov
Имеется ввиду команда lea? Не пойму, как она может быть лучше.
А>2 — Может E(ABCD)X кончильсь, а указатели остались.
Указатели, как и данные, могут храниться в любом регистре.
Но речь о случае, когда данные (а тем более указатели) вообще не нужно передавать посредством копирования, в функциях вида:
template <typename T>
static inline
bool f(T a, T a2) { return f_impl(a, a2); }
вообще никакой код не должен быть сгенерирован. В современном коде часто встречаются многократные вложения таких примитивных функций, и оптимизатор сдаёт, делая лишние копирования в регистры даже при inline подстановке функций для типа int.
Ещё хуже для UDT типов малого размера (как раз смартпоинтеры)
struct { int _; };
Вполне можно работать с этим типом как с простым int, но компилятор опять же вставляет лишние копирования.
Вот только вчера столкнулся с растущим отсюда багом:
_this$ = -4 ; size = 4
_new_irql$ = 8 ; size = 1
?release@kspin_lock@km@ntl@@QAEXUkirql@23@@Z PROC ; ntl::km::kspin_lock::release, COMDAT
; _this$ = ecx
; Line 70
push ebp
mov ebp, esp
push ecx
mov DWORD PTR _this$[ebp], ecx
; Line 71
mov al, BYTE PTR _new_irql$[ebp]
push eax ; вот этот параметр следует передавать в регисте edx, а не через стек mov ecx, DWORD PTR _this$[ebp]
call DWORD PTR __imp_@KfReleaseSpinLock@8
; Line 72
mov esp, ebp
pop ebp
ret 4
?release@kspin_lock@km@ntl@@QAEXUkirql@23@@Z ENDP ; ntl::km::kspin_lock::release
; Function compile flags: /Odtp
_TEXT ENDS
А>3 — Уж во всяком случае не всегда так
Ну да, часто нет разницы
People who are more than casually interested in computers should have at least some idea of what the underlying hardware is like. Otherwise the programs they write will be pretty weird (c) D.Knuth
Re[16]: [Investigation] Exceptions vs. Return Values
Здравствуйте, Andrew S, Вы писали:
M>>Я не хочу ничего объяснять , если вас заинтересовал этот вопрос , почитайте Криса Касперски.
AS>Так, давайте, что именно? Или просто "отвяжись" — дык тогда может лучше так и говорить?
M>>>Я не хочу ничего объяснять , если вас заинтересовал этот вопрос , почитайте Криса Касперски.
AS>>Так, давайте, что именно? Или просто "отвяжись" — дык тогда может лучше так и говорить?
M>начать можно тут M>http://www.codenet.ru/progr/optimize/kk/
И что там я обнаружу нового?
Вот цитата:
Непосредственные результаты замеров времени исполнения программы в "сыром" виде, как было показано выше, ни на что ни годны. Очевидно, перед использованием их следует обработать. Как минимум откинуть пограничные значения, вызванные нерегулярными наведенными эффектами (ну, например, в самый ответственный для профилировки момент, операционная система принялась что-то сбрасывать на диск), а затем... Затем перед нами встает Буриданова проблема . Мы будем должны либо выбрать результат с минимальным временем исполнения — как наименее подвергнувшийся пагубному влиянию многозадачности, либо же вычислить наиболее типичное время выполнения — как время выполнения в реальных, а не идеализированных "лабораторных" условиях.
Мной опробован и успешно применяется компромиссный вариант, комбинирующий оба этих метода. Фактически, я предлагаю вам отталкиваться от среднеминимального времени исполнения. Сам алгоритм в общих чертах выглядит так: мы выполняем N повторов программы, а затем отбрасываем N/3 максимальных и N/3 минимальных результатов замеров. Для оставшихся N/3 замеров мы находим среднее арифметическое, которое и берем за основной результат. Величина N варьируется в зависимости от конкретной ситуации, но обычно с лихвой хватает 9-12 повторов, т. к. большее количество уже не увеличивает точности результатов.
Ничего не напоминает? А рассуждения о том, что минимальное время выполнения зависит только от программного окружения — очень и очень спорно. Особенно при наличии многопроцессорных и неюниформных юнитов.
Кстати, там же и про RDTSC есть и про то, что и как он меряет. И почему то, что автора топика, неправильно. И читать это полезно как раз не мне (хотя мне это и интересно, впрочем, статью эту я уже читал), а автору топика и тестов...
Теперь по крайней мере вы знаете, чем обусловленно стремление брать минимальное время.
А то что вопрос неоднозначен я знаю. Автору топика возможно это будет интерессно, но я бы вам предложил поправить замеры времени в коде , который он сибирался выложить.
Заранее спасибо.
Бугага! Тест то некорректен... Ну как можно мешать в одном exeшнике два теста которые требуют разных опций компиляции. Ладно б оптимизации так ведь исключений...
Раздели на 2 отдельных Exe шника
один с /EHsc и кодом основанным на исключениях
другой без и с кодом на return value
надо это для того, чтоб в rv тесте не было ничего связанного с исключениями.
запускай их РАЗДЕЛЬНО.
мои результаты:
test_ex: 127
test_rv: 120
Чуть модифицировал тест с целью посмотреть не на минимум а на полное время выполнения — читай среднее время выполнения
int main()
{
int const test_count = 1000000;
#if 1
{
int ec = 0;
tick_t time = 0;
tick_t start = 0;
tick_t t = 0;
for (int i = 0; i < test_count; ++i)
{
start = get_tc();
try
{
test_ex();
}
catch (...)
{
++ec;
}
t = get_tc() - start;
time += t;
}
std::cout << "test_ex: " << time << "\n";
}
#else
{
int ec = 0;
tick_t time = 0;
tick_t start = 0;
tick_t t = 0;
for (int i = 0; i < test_count; ++i)
{
start = get_tc();
if (!test_rv())
++ec;
t = get_tc() - start;
time += t;
}
std::cout << "test_rv: " << time << "\n";
}
#endif
}
Здравствуйте, gear nuke, Вы писали:
GN>Здравствуйте, remark, Вы писали:
R>>Там проблема, в том, что есть деструкторы — значит устанавливается и снимается фрейм исключений — т.е. тут двойная работа.
GN>Я о "с++/rv"
Ну так исключение всё равно может полететь, и деструкторы есть, значит фрейм всё равно надо устанавливать.
R>>А код С++ компилятор даже лучше генерирует — поскольку деструкторы не вызываются явно у него есть некая свобода в оптимизации.
GN>Не верю Компилятор там один и тот же.
В С — там код более явный, поэтому у компилятора меньше свободы, а в С++ менее явный — и у компилятора больше свободы.
Я глядел сгенерированный код "с++/rv" — я там даже сразу не понял, что он там к чему нагенерил — там он как-то хитро пишет прям в стек, т.е. не push/pop, а mov [sp],x и всякого такого рода вещи.
Здравствуйте, gear nuke, Вы писали:
GN>Однако при отключенной (дебаг) получается чушь:
Ну под дебагом-то что код смотреть? Ты приведи пример, где под релизом так.
В msvc помогает "добить" такие места, где не идельная оптимизация, включение link-time code generation — он как раз вот такие "форварды" должен элиминировать.
Здравствуйте, Andrew S, Вы писали:
AS>[code skipped]
AS>Нда, сильно. Вот почему я и говорю о необходимости кода. AS>1. Тест в общем случае некорректен. В случае использования возвращаемого значения использовать идиому RAI нет необходимости, поэтому первые 4 сравнения идут лесом. Про что я и говорил — различные варианты обработки ошибок влекут использование различных паттернов на различных уровнях вызова.
Ну так этот код был в изначальном посте. Я б не воспринимал в штыки, если б ты сразу написал, что с использованием возвращаемых значений пишут вот так-то и так-то, вот код. Кстати, и тут тоже кода нет, я не умею читать мысли — напиши уже давно свой вариант кода, а не вокруг да около.
AS>2. Вариант с rv у тебя с ошибкой
Бывает...
действительно bool res = fasle
AS>3. Где случай, в котором тебе надо вызывать функцию вне зависимости от результата вызова предыдущей? Т.е. вызвали одну, затем другую, затем третью и т.д. — а потом обрабатываем результат в зависимости от вызова. Т.е. не &&, а ||. AS>4. Где вложенная обработка исключений, тоже непонятно. Впрочем, это боком относится к (3), но все же? Например, некоторые искючения могут позволить продолжить цикл (info\warning), тогда как другие ведут к выходу.
А если бы я привёл тот случай, ты бы сказал, а где у тебя этот случай
Что значит, где другой случай? Вот есть этот случай, я его тестирую. У меня в коде, по крайней мере, такие паттерны есть. Мне этого достаточно. По крайней мере для начала.
AS>5. Обрати внимание, что в rv варианте используется esp стек фрейм, а в ex — может и ebp. О чем я тебе уже 100 раз говорил.
Ни там, ни там не вижу использования ebp.
Приведи код и листинг. А то сейчас такое ощущение, что ты код не смотрел.
AS>6. Обрати внимание на разницу в способе вызова — в случае ex для настоящих объектов у тебя используеся this_call. Попробуй сделать не эмуляцию контрукторов функциями, а сделать init\deinit в самом tobj. Это съэкономит прилично и места, и времени.
Не понял. Приведи код, а то ещё долго будем припираться...
AS>А вообще, проблема на мой взгляд вот в чем. Ты берешь за основу модель исключений, и пытаешься приспособить под нее модель возвращаемых значений. Об этом говорит и использование bool в качестве возвращаемого значения (тогда как используется обычно более информативные типы, возвращающие не только успешность, но и саму ошибку — т.е. информации в разы больше), и построение функции (очередность и кратность вызова), уровень обработки (исключение у тебя обрабатывается на самом высоком уровне, тогда как общая практика — обработка части исключений на различных уровнях вызова, добавление отработки даже на одном уровне — то, что ты называешь ресурсами, уже изменяет ситуацию) и т.д. Неудивительно, что в этих условиях rv почти всегда проигрывает. У тебя полностью сферический конь в полном отсутствии воздуха. Я уже неоднократно говорил, что придумать ситуации, когда ex или rv будет выигрывать проблем не составляет. Нас должно интересовать не это, а реально существующие паттерны кода.
Это вполне реальный паттерн. Нет преобразования исключений. Исключений говорит о неудачности транзакции. Нет никаких частичных восстановлений и фаиловер. У меня лично такого кода очень много и я знаю, что и у других тоже. Я хочу мерить это.
AS>PS Эк тебя зацепило Критику надо воспринимать не в штыки, а пытаться хотя бы слушать оппонента.
Тебя приходится не слушать, а гадать и вытягивать. Код был в первом посте — приведи наконец свой код.
M>Теперь по крайней мере вы знаете, чем обусловленно стремление брать минимальное время.
Теперь? Автор топика сразу сказал, почему он берет минимальное время. Впрочем, обе стратегии в обычных условиях показывают некую тенденцию, а то, что тесты не совсем корректны — думаю, уже убедились. Так что надо смотреть код, как я неоднократно говорил — почему, например, автор не заметил, что вариант с rv используется другую спецификацию вызовов для эмуляции конструкторов\деструкторов? А это по 4 команды на каждый вызов, причем 2 из них — работы с памятью. Нормально так, да?
CC>Бугага! Тест то некорректен... Ну как можно мешать в одном exeшнике два теста которые требуют разных опций компиляции. Ладно б оптимизации так ведь исключений...
Это да. Но все-же имхо это не должно в данном случае... хотя, нет, должно, в смысле, может. Выравнивание.
CC>мои результаты: CC>test_ex: 127 CC>test_rv: 120
А можете привести листинги для функций? Вдруг ic там что проинлайнил? Он может....
Здравствуйте, CreatorCray, Вы писали:
CC>Здравствуйте, remark, Вы писали:
CC>Бугага! Тест то некорректен... Ну как можно мешать в одном exeшнике два теста которые требуют разных опций компиляции. Ладно б оптимизации так ведь исключений...
CC>Раздели на 2 отдельных Exe шника CC>один с /EHsc и кодом основанным на исключениях CC>другой без и с кодом на return value CC>надо это для того, чтоб в rv тесте не было ничего связанного с исключениями.
CC>запускай их РАЗДЕЛЬНО.
CC>мои результаты: CC>test_ex: 127 CC>test_rv: 120
CC>Чуть модифицировал тест с целью посмотреть не на минимум а на полное время выполнения — читай среднее время выполнения
CC>test_ex: 137504231 CC>test_rv: 125761239
CC>в среднем получается:
CC>test_ex: 137 CC>test_rv: 125
CC>Ну как ни крути — у rv время меньше...
CC>Опции компиляции: Intel C++ 9.1 CC>icl /c /O3 /Og /Ob2 /Oi /Ot /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /GF /FD /EHsc /MT /YX"StdAfx.h" /Fp".\Release/test.pch" /FAcs /Fa".\Release/" /Fo".\Release/" /W3 /nologo /Zi /Gd /QxP
Скомпилировал код с возвращаемыми значениями как С-код:
test_ex: 172
test_rv: 184
Если суммировать:
test_ex: 1793656
test_rv: 1853848
Вот поэтому и было бы хорошо сделать "коробочный" тест, который можно было бы легко скачать, скомпилировать, запустить и выложить результаты для своей платформы.
з.ы. а мерить среднее время всё-таки некорректно, если ты запустишь тест несколько раз, то увидишь, что время "плавает", т.е. сказываются третьи факторы, с минимальным временем такого нет.
AS>>3. Где случай, в котором тебе надо вызывать функцию вне зависимости от результата вызова предыдущей? Т.е. вызвали одну, затем другую, затем третью и т.д. — а потом обрабатываем результат в зависимости от вызова. Т.е. не &&, а ||. AS>>4. Где вложенная обработка исключений, тоже непонятно. Впрочем, это боком относится к (3), но все же? Например, некоторые искючения могут позволить продолжить цикл (info\warning), тогда как другие ведут к выходу.
R>А если бы я привёл тот случай, ты бы сказал, а где у тебя этот случай
Так можно сделать оба? Или это будет слишком плохо для исключений, да?
R>Что значит, где другой случай? Вот есть этот случай, я его тестирую. У меня в коде, по крайней мере, такие паттерны есть. Мне этого достаточно. По крайней мере для начала.
Понятно.
AS>>5. Обрати внимание, что в rv варианте используется esp стек фрейм, а в ex — может и ebp. О чем я тебе уже 100 раз говорил.
R>Ни там, ни там не вижу использования ebp. R>Приведи код и листинг. А то сейчас такое ощущение, что ты код не смотрел.
Тот код, что у тебя, слишком прост, чтобы начать пользовать ebp, я это уже тебе написал.
AS>>6. Обрати внимание на разницу в способе вызова — в случае ex для настоящих объектов у тебя используеся this_call. Попробуй сделать не эмуляцию контрукторов функциями, а сделать init\deinit в самом tobj. Это съэкономит прилично и места, и времени.
R>Не понял. Приведи код, а то ещё долго будем припираться...
А что тут понимать? Если бы ты смотрел сгенеренный код:
ex:
lea ecx, DWORD PTR _o2$[esp+20]
push ecx
call ?ctor@@YA_NPAH@Z ; ctor
add esp, 4
test al, al
je SHORT $L105177
Видишь, в одном случае параметр (this) передается в eсx, а во втором — через стек?
Вот как правильно (в смысле, соответствует твоему варианту теста):
struct tobj1
{
bool init();
void release();
int fake;
};
bool tobj1::init() {return true;}
void tobj1::release() {}
static bool test_rv()
{
tobj1 o1, o2, o3, o4;
bool res = false;
if (o1.init())
{
if (o2.init())
{
if (o3.init())
{
if (o4.init())
{
res = test_rv2() && test_rv2() && test_rv2() && test_rv2();
o4.release();
}
o3.release();
}
o2.release();
}
o1.release();
}
return res;
}
lea ecx, DWORD PTR _o3$[esp+20]
call ?init@tobj1@@QAE_NXZ ; tobj1::init
test al, al
je SHORT $L105190
Кроме того, если ты не будешь использовать bool, а нормальный int-based тип, то сравниваться будет eax, что обычно быстрее (т.к. команда другая).
Да, кстати, там у тебя еще одна ошибка — определение tobj использует int fake, а декларация — нет.
R>Это вполне реальный паттерн. Нет преобразования исключений. Исключений говорит о неудачности транзакции. Нет никаких частичных восстановлений и фаиловер. У меня лично такого кода очень много и я знаю, что и у других тоже. Я хочу мерить это.
Т.е. ты всегда выпускаешь все исключения наверх? Вообще, сколько я видел — обычно обрабатывают часть исключений на месте, дабы обеспечить реакцию (например, почему не завершена транзакция, не прочитан файл и т.п.), а затем в зависимости от действий пользователя продолжают выполнение.
В любом случае, то что у тебя — это лишь один из паттернов, и делать на только его основе какие-то выводы — по меньшей мере странно.
R>з.ы. а мерить среднее время всё-таки некорректно, если ты запустишь тест несколько раз, то увидишь, что время "плавает", т.е. сказываются третьи факторы, с минимальным временем такого нет.
Поэтому и надо ставить приоритет реалтайм и привязывать к одном процессору, как тут уже говорили. Еще и рабочую область неплохо бы закоммитить.
Здравствуйте, remark, Вы писали:
R>Здравствуйте, rm822, Вы писали:
R>>Здравствуйте, remark, Вы писали:
R>>>Здравствуйте, rm822, Вы писали:
R>>>>>>Двойка вам за научный подход.
R>>>Тут основная проблема, что бы сделать тесты справедливые для обоих стратегий. R>>>Т.е. что я делаю — беру некую предельно простую абстракцию — ресурс — приписываю ему семантику — его надо выделить, причём выделение может провалится, и его обязательно надо освободить при выходе из функции. Далее я моделирую эту абстракцию на С++ и на С предельно "честно" по отношению к обоим языкам и их возможностям — на С++ — объект с деструктором, на С — функции construct/destroy. И сравниваю именно эти реализации одной и той же задачи разными средствами. Какая реализация быстрее? R>>>Сравнивать тёплое с мягким нет смысла. R>>>Т.е. нет смысла мерить типа "а что если мы в с++ вставим дополнительный new/delete" — ты тогда и в С вставь тоже что-то, что будет решать ту задачу, для решения которой ты в С++ вставил дополнительный new/delete. R>>>Т.е. ты придумай некую абстрактную задачу, которая бы на С++ требовала применения иерархий ошибок/динамического полиморфизма и т.д., потом максимально "честно" вырази её решение на С, причём там, что бы тебе потом не говорили — а я бы это сделал подругому и быстрее. И вот тогда будем мерить, где решение задачи "мега динамической полиморфной обработки ошибок" работает быстрее.
R>>Реальность такова что для обработки исключений используется иерахия классов а не int.(Обычно) Для обработки ошибок error-кодами используется int а не классы. (Обычно) И сравнивать нужно именно теплое с мягким, потому что такова реальность.
R>Обычно и не думают о наносекундах, и что?
Короче, мне этот пустой пи.... надоел — сам все проверю когда время будет
R>>>
... << RSDN@Home 1.1.4 stable SR1 rev. 568>>
Re[18]: [Investigation] Exceptions vs. Return Values
Здравствуйте, Andrew S, Вы писали:
M>>>>Я не хочу ничего объяснять , если вас заинтересовал этот вопрос , почитайте Криса Касперски.
AS>>>Так, давайте, что именно? Или просто "отвяжись" — дык тогда может лучше так и говорить?
M>>начать можно тут M>>http://www.codenet.ru/progr/optimize/kk/
AS>Ничего не напоминает? А рассуждения о том, что минимальное время выполнения зависит только от программного окружения — очень и очень спорно. Особенно при наличии многопроцессорных и неюниформных юнитов. AS>Кстати, там же и про RDTSC есть и про то, что и как он меряет. И почему то, что автора топика, неправильно. И читать это полезно как раз не мне (хотя мне это и интересно, впрочем, статью эту я уже читал), а автору топика и тестов...
Вот и он говорит, что можно брать и минимальное время тоже.
Тут зависит от того что мерить. Если мерить обращения к БД по сети, то возможно лечше брать некое среднее время. А если я хочу замерить время выполнения функции порядка десятков тактов, которая никак не связана ни со свопом, ни с памятью, ни с диском, ни с сетью и т.д., то самая оптимальная, т.к. "чистая", стратегия — это минимальное время. А уже все третьи задержки, будут одинаково добавляться что к одному времени, что к другому. Если будем мерить среднее в данном случае, то всё что получим — ненужную дисперсию.
Здравствуйте, Andrew S, Вы писали:
R>>з.ы. а мерить среднее время всё-таки некорректно, если ты запустишь тест несколько раз, то увидишь, что время "плавает", т.е. сказываются третьи факторы, с минимальным временем такого нет.
AS>Поэтому и надо ставить приоритет реалтайм и привязывать к одном процессору, как тут уже говорили. Еще и рабочую область неплохо бы закоммитить.
Если мерить длинный интервал и запускать функцию _один_ раз, то да. В моём случае всё это учтено, поэтому ничего этого не надо — это никак не повлияет на результат.
Здравствуйте, Andrew S, Вы писали:
AS>А можете привести листинги для функций? Вдруг ic там что проинлайнил? Он может....
если указывать __declspec(noinline) — то не будет гарантировано.
Впрочем вот:
AS>>А можете привести листинги для функций? Вдруг ic там что проинлайнил? Он может.... CC>если указывать __declspec(noinline) — то не будет гарантировано. CC>Впрочем вот:
Cудя по коду для rv, icc убрал вызовы ctor и dtor. Мы договорились этого не делать
R>>>з.ы. а мерить среднее время всё-таки некорректно, если ты запустишь тест несколько раз, то увидишь, что время "плавает", т.е. сказываются третьи факторы, с минимальным временем такого нет.
AS>>Поэтому и надо ставить приоритет реалтайм и привязывать к одном процессору, как тут уже говорили. Еще и рабочую область неплохо бы закоммитить.
R>Если мерить длинный интервал и запускать функцию _один_ раз, то да. В моём случае всё это учтено, поэтому ничего этого не надо — это никак не повлияет на результат.
Ладно, ладно, мы все тупые, а ты один умный. Делай как знаешь
M>>>>>Я не хочу ничего объяснять , если вас заинтересовал этот вопрос , почитайте Криса Касперски.
AS>>>>Так, давайте, что именно? Или просто "отвяжись" — дык тогда может лучше так и говорить?
M>>>начать можно тут M>>>http://www.codenet.ru/progr/optimize/kk/
AS>>Ничего не напоминает? А рассуждения о том, что минимальное время выполнения зависит только от программного окружения — очень и очень спорно. Особенно при наличии многопроцессорных и неюниформных юнитов. AS>>Кстати, там же и про RDTSC есть и про то, что и как он меряет. И почему то, что автора топика, неправильно. И читать это полезно как раз не мне (хотя мне это и интересно, впрочем, статью эту я уже читал), а автору топика и тестов...
R>Вот и он говорит, что можно брать и минимальное время тоже.
Когда он это говорил, про мультиядерность, доступную обычному юзеру, еще не было и речи. Соответственно, не было речи и про NUMA, про синхронизации кешей и прочие вещи, характерные для многопроцессорных систем. Минимальное время выполнения там зависит от аппаратных компонент, на которых по прихоти шедуллера будет выполняться код. Правда, там есть упоминания о беспричинных выпадениях для K7 и KT133. Поэтому выпадения минимума могут быть _НАСТОЛЬКО_ редки и нехарактерны, что ты можешь с приличной вероятностью получить fake (даже программно — например, для одного варианта шедулер может всегда выбирать один юнит, а для другого — например, всегда другой). Гарантировать ты это в твоей реализации не сможешь. Поэтому и надо:
1. Привязывать к юниту.
2. Поднимать приоритеты.
3. Брать средне-минимальное время.
Здравствуйте, Andrew S, Вы писали:
R>>>>з.ы. а мерить среднее время всё-таки некорректно, если ты запустишь тест несколько раз, то увидишь, что время "плавает", т.е. сказываются третьи факторы, с минимальным временем такого нет.
AS>>>Поэтому и надо ставить приоритет реалтайм и привязывать к одном процессору, как тут уже говорили. Еще и рабочую область неплохо бы закоммитить.
R>>Если мерить длинный интервал и запускать функцию _один_ раз, то да. В моём случае всё это учтено, поэтому ничего этого не надо — это никак не повлияет на результат.
AS>Ладно, ладно, мы все тупые, а ты один умный. Делай как знаешь
Сейчас сколько бы раз я не запускал тест у меня получается, допустим 172.
Т.е. ты хочешь сказать, если я запущу свой тест на своей машине с привязкой к ядру, с установкой максимального приоритета, коммитом памяти и т.д., у меня выдастся другое число?
Здравствуйте, remark, Вы писали:
R>Что я понял уже когда я это всё доделал, что надо было код не кустарным способом делать, а сделать генератор кода, которому задаёшь параметр — кол-во "ресурсов, кол-во уровней вызовов и т.д. и что бы он генерировал много красивого кода. И снабдить это ещё скриптами для автоматической сборки и что бы результаты выводились сразу в виде красивых табличек. Тогда бы я это выложил и все желающие запускали бы это на своих платформах и потом выкладывали бы сюда свои результаты... тогда и для ARM'ов бы можно было протестировать и более точно проследить зависимости скорости/размера кода от различных параметров типа кол-ва "ресурсов".
R>К сожалению это не ко мне. Может руки дойдут это оформить в виде набора файлов, который можно запустить на своей платформе и выложить результаты...
Ну ты, например, можешь выложить то, что есть, а руки могут дойти у кого-то ещё
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
R>>>>>з.ы. а мерить среднее время всё-таки некорректно, если ты запустишь тест несколько раз, то увидишь, что время "плавает", т.е. сказываются третьи факторы, с минимальным временем такого нет.
AS>>>>Поэтому и надо ставить приоритет реалтайм и привязывать к одном процессору, как тут уже говорили. Еще и рабочую область неплохо бы закоммитить.
R>>>Если мерить длинный интервал и запускать функцию _один_ раз, то да. В моём случае всё это учтено, поэтому ничего этого не надо — это никак не повлияет на результат.
AS>>Ладно, ладно, мы все тупые, а ты один умный. Делай как знаешь
R>Сейчас сколько бы раз я не запускал тест у меня получается, допустим 172. R>Т.е. ты хочешь сказать, если я запущу свой тест на своей машине с привязкой к ядру, с установкой максимального приоритета, коммитом памяти и т.д., у меня выдастся другое число?
Вполне вероятно. Особенно если ты увеличишь количество итераций. Привязка к ядру — но ты же не только на своей машине планируешь запускать тест, ведь так?
Здравствуйте, Andrew S, Вы писали:
AS>>>А можете привести листинги для функций? Вдруг ic там что проинлайнил? Он может.... CC>>если указывать __declspec(noinline) — то не будет гарантировано. CC>>Впрочем вот:
AS>Cудя по коду для rv, icc убрал вызовы ctor и dtor. Мы договорились этого не делать
Увы, заставить его не убирать совершенно пустые функции не получается... Щас проведу эксперимент — добавлю _asm nop в них
AS>>>>А можете привести листинги для функций? Вдруг ic там что проинлайнил? Он может.... CC>>>если указывать __declspec(noinline) — то не будет гарантировано. CC>>>Впрочем вот:
AS>>Cудя по коду для rv, icc убрал вызовы ctor и dtor. Мы договорились этого не делать CC>Увы, заставить его не убирать совершенно пустые функции не получается... Щас проведу эксперимент — добавлю _asm nop в них
Используй тот код, что приводил я (c init\release). В этом случае не должно убрать — если только линкер не оптимизирует.
Здравствуйте, Andrew S, Вы писали:
AS>>>>>А можете привести листинги для функций? Вдруг ic там что проинлайнил? Он может.... CC>>>>если указывать __declspec(noinline) — то не будет гарантировано. CC>>>>Впрочем вот:
AS>>>Cудя по коду для rv, icc убрал вызовы ctor и dtor. Мы договорились этого не делать CC>>Увы, заставить его не убирать совершенно пустые функции не получается... Щас проведу эксперимент — добавлю _asm nop в них
AS>Используй тот код, что приводил я (c init\release). В этом случае не должно убрать — если только линкер не оптимизирует.
Кинь ссылкой плз, бо в этом топике уже черт ногу сломит.
Скомпилил с nop-ами — получилось одинаково у обоих — 182
AS>>Используй тот код, что приводил я (c init\release). В этом случае не должно убрать — если только линкер не оптимизирует. CC>Кинь ссылкой плз, бо в этом топике уже черт ногу сломит.
AS>>Ладно, ладно, мы все тупые, а ты один умный. Делай как знаешь
M>Должен заметить, что полезную информацию дал пока что только Ремарк. От вас , кроме НЕконструктивной критики пока не было ничего.
А от вас пока ничего, кроме флуда (хотя нет, была ссылка на статью про книгу криса касперски, и то не по адресу). Есть что по делу то сказать?
Здравствуйте, Andrew S, Вы писали:
AS>>>Используй тот код, что приводил я (c init\release). В этом случае не должно убрать — если только линкер не оптимизирует. CC>>Кинь ссылкой плз, бо в этом топике уже черт ногу сломит.
AS>http://www.rsdn.ru/Forum/Message.aspx?mid=2430252&only=1
Здравствуйте, gear nuke, Вы писали:
GN>Здравствуйте, <Аноним>, Вы писали:
А>>1 — передача адреса без чтония памяти. la заместо mov
GN>Имеется ввиду команда lea? Не пойму, как она может быть лучше.
А>>2 — Может E(ABCD)X кончильсь, а указатели остались.
GN>Указатели, как и данные, могут храниться в любом регистре.
Хранение данных в индексных регистрах несколько ограничивает возможные операции, хоть бывает это оправдано. Насчет lea я имел ввиду случай когда можно получить адрес а переменная прямо сейчас не понадобится или например может вообще не понадобится в зависимости от условий. Lea использует например this, который в регистре и константный офсет а реального чтения не происходит изза значений флагов.
В приведенных кодах некоторое сомнение вызывает сочетание __declspec и __fastcall
Кстати тут недавно кто-то volatile использовал для борьбы с компилятором
Re[5]: [Investigation] Exceptions vs. Return Values
Здравствуйте, Programador, Вы писали:
P>В приведенных кодах некоторое сомнение вызывает сочетание __declspec и __fastcall
Чем?
__declspec(noinline) запрещает компилеру инлайнинг данной функции
__fastcall грит компилеру чтоб старался передавать параметры через регистры
все для чего это тут надо — чтоб компилер не выкидывал пустые функции а тратил на их вызов тики
Здравствуйте, Andrew S, Вы писали:
R>>Сейчас сколько бы раз я не запускал тест у меня получается, допустим 172. R>>Т.е. ты хочешь сказать, если я запущу свой тест на своей машине с привязкой к ядру, с установкой максимального приоритета, коммитом памяти и т.д., у меня выдастся другое число?
AS>Вполне вероятно. Особенно если ты увеличишь количество итераций.
Интересно за счёт чего? И ты ещё говорил о компетентности
AS>Привязка к ядру — но ты же не только на своей машине планируешь запускать тест, ведь так?
При чём тут, что я планирую. Я запускал на однопроцессорной машине. Если я запланирую запускать ещё где-то, то тогда я возможно запланирую и изменить методику измерения, если это будет нужно.
R>>>Сейчас сколько бы раз я не запускал тест у меня получается, допустим 172. R>>>Т.е. ты хочешь сказать, если я запущу свой тест на своей машине с привязкой к ядру, с установкой максимального приоритета, коммитом памяти и т.д., у меня выдастся другое число?
AS>>Вполне вероятно. Особенно если ты увеличишь количество итераций.
R>Интересно за счёт чего? И ты ещё говорил о компетентности
За счет того, что шедуллер будет выделять больше времени твоему потоку. Или ты думаешь, в системе один поток и он твой?
Это во-первых. А во-вторых, о компетентности прямо я не говорил (хотя и советовал тебе ознакомиться хотя бы со статьей, приведенной в топике), но теперь могу наверное и начать
AS>>Привязка к ядру — но ты же не только на своей машине планируешь запускать тест, ведь так?
R>При чём тут, что я планирую. Я запускал на однопроцессорной машине. Если я запланирую запускать ещё где-то, то тогда я возможно запланирую и изменить методику измерения, если это будет нужно.
Ууу.. все ясно. Ок, уговорил — больше в таком "интересном" варианте мне общаться не хочется. Конструктива с твоей стороны я не вижу, хотя привел уже несколько ошибок в твоем тесте, ты, похоже, считаешь, что все в порядке. На критику ты реагируешь тоже весьма "интересно". В общем, желаю удачи и доработать тесты хотя бы до вменяемого состояния
Здравствуйте, CreatorCray, Вы писали:
CC>Здравствуйте, Programador, Вы писали:
P>>В приведенных кодах некоторое сомнение вызывает сочетание __declspec и __fastcall CC>Чем? CC>__declspec(noinline) запрещает компилеру инлайнинг данной функции CC>__fastcall грит компилеру чтоб старался передавать параметры через регистры
CC>все для чего это тут надо — чтоб компилер не выкидывал пустые функции а тратил на их вызов тики
там был импортный __declspec и у него баг случился — при оптимизации параметры в стек засунулись, а не в регистры.
У меня такое отношение что __fastcall это типа инлайне. Хотя в принципе экспорт можно подружить с инлайне путем дублирования кода
Re[7]: [Investigation] Exceptions vs. Return Values
P>У меня такое отношение что __fastcall это типа инлайне. Хотя в принципе экспорт можно подружить с инлайне путем дублирования кода
Отчего ж? Из кернела экспортируются функции с конвенцией fastcall — и ничего, все работает Фасткалл — это конвенция вызова и ничего более — к экспорту\импорту она не должна иметь отношения, кроме как для манглинга...
Здравствуйте, Programador, Вы писали:
P>Хранение данных в индексных регистрах несколько ограничивает возможные операции, хоть бывает это оправдано.
В x86-32 нет индексных регистров, все равноправны.
P>Насчет lea я имел ввиду случай когда можно получить адрес а переменная прямо сейчас не понадобится или например может вообще не понадобится в зависимости от условий. Lea использует например this, который в регистре и константный офсет а реального чтения не происходит изза значений флагов.
Если чтения нет — компилятор не справился с оптимизацией, сгенерировав лишние команды. Если чтение есть — нет разницы в скорости.
Компилятор для ссылки довольно часто может обойтись вообще compile-time вычислениями, не генерируя код, но делает так не всегда.
P>В приведенных кодах некоторое сомнение вызывает сочетание __declspec и __fastcall
Это правильно, поскольку функция экспортируется именно так.
People who are more than casually interested in computers should have at least some idea of what the underlying hardware is like. Otherwise the programs they write will be pretty weird (c) D.Knuth
Re[5]: [Investigation] Exceptions vs. Return Values
Здравствуйте, remark, Вы писали:
R>Ну под дебагом-то что код смотреть?
Баги кодогенератора проявились в дебаге, в релизе просто повезло в конкретном случае.
R>Ты приведи пример, где под релизом так.
На коде небольшого размера не получается воспроизвести, а оставить как есть в продакшене и потом ждать багрепорты об ошибке 2го порядка довольно накладно.
R>В msvc помогает "добить" такие места, где не идельная оптимизация, включение link-time code generation — он как раз вот такие "форварды" должен элиминировать.
Я проводил довольно простой тест — сначала заинклудил все cpp в основной и скомпелировал, потом компелировал по-раздельности и линковал с /LTCG — в последнем случае код хуже.
People who are more than casually interested in computers should have at least some idea of what the underlying hardware is like. Otherwise the programs they write will be pretty weird (c) D.Knuth
Re[5]: [Investigation] Exceptions vs. Return Values
Здравствуйте, remark, Вы писали:
R>Ну так исключение всё равно может полететь, и деструкторы есть, значит фрейм всё равно надо устанавливать.
Тем более, эти результаты неверны, это уже с++/rv+ex какой-то.
R>>>А код С++ компилятор даже лучше генерирует — поскольку деструкторы не вызываются явно у него есть некая свобода в оптимизации.
GN>>Не верю Компилятор там один и тот же.
R>В С — там код более явный, поэтому у компилятора меньше свободы, а в С++ менее явный — и у компилятора больше свободы.
Интересная теория, я всегда считал что С и С++ перед оптимизацией трансформируется в одно и тоже ADT.
R>Я глядел сгенерированный код "с++/rv" — я там даже сразу не понял, что он там к чему нагенерил — там он как-то хитро пишет прям в стек, т.е. не push/pop, а mov [sp],x и всякого такого рода вещи.
При оптимизации по скорости так происходит довольно часто.
People who are more than casually interested in computers should have at least some idea of what the underlying hardware is like. Otherwise the programs they write will be pretty weird (c) D.Knuth
Re[6]: [Investigation] Exceptions vs. Return Values
Здравствуйте, gear nuke, Вы писали:
GN>Здравствуйте, Programador, Вы писали:
P>>Хранение данных в индексных регистрах несколько ограничивает возможные операции, хоть бывает это оправдано.
GN>В x86-32 нет индексных регистров, все равноправны.
Равноправны 2 регистра FS и GS это совсем не все
P>>Насчет lea я имел ввиду случай когда можно получить адрес а переменная прямо сейчас не понадобится или например может вообще не понадобится в зависимости от условий. Lea использует например this, который в регистре и константный офсет а реального чтения не происходит изза значений флагов.
GN>Если чтения нет — компилятор не справился с оптимизацией, сгенерировав лишние команды. Если чтение есть — нет разницы в скорости.
GN>Компилятор для ссылки довольно часто может обойтись вообще compile-time вычислениями, не генерируя код, но делает так не всегда.
Я про рунтайм условия. Мы загружаем адрес, а память возможно не читаем, засчет этого ссылка на инт может быть быстрее
Re[7]: [Investigation] Exceptions vs. Return Values
Здравствуйте, Programador, Вы писали:
P>Равноправны 2 регистра FS и GS это совсем не все
Если говорить о сегментных регистрах, то FS и GS далеко не одно и тоже. Но для С++ это не имеет значения, поскольку в Виндовсе плоская модель памяти, компилятор использует только регистры общего назначения (eax, ecx, edx, ebx, ebp, esi, edi; esp традиционно используется как указатель стека).
P>Я про рунтайм условия. Мы загружаем адрес, а память возможно не читаем, засчет этого ссылка на инт может быть быстрее
Зачем загружать адрес, если в дальнейшем не будет чтения? Это лишние действия, т.е. код не оптимален.
People who are more than casually interested in computers should have at least some idea of what the underlying hardware is like. Otherwise the programs they write will be pretty weird (c) D.Knuth
Re[8]: [Investigation] Exceptions vs. Return Values
Здравствуйте, gear nuke, Вы писали:
GN>Если говорить о сегментных регистрах, то FS и GS далеко не одно и тоже. Но для С++ это не имеет значения, поскольку в Виндовсе плоская модель памяти, компилятор использует только регистры общего назначения (eax, ecx, edx, ebx, ebp, esi, edi; esp традиционно используется как указатель стека).
Еще активно используется FS для thread-specific штук типа TLS и исключений
... << RSDN@Home 1.1.4 stable SR1 rev. 568>>
Re[8]: [Investigation] Exceptions vs. Return Values
Здравствуйте, gear nuke, Вы писали:
GN>Здравствуйте, Programador, Вы писали:
P>>Равноправны 2 регистра FS и GS это совсем не все
GN>Если говорить о сегментных регистрах, то FS и GS далеко не одно и тоже. Но для С++ это не имеет значения, поскольку в Виндовсе плоская модель памяти, компилятор использует только регистры общего назначения (eax, ecx, edx, ebx, ebp, esi, edi; esp традиционно используется как указатель стека).
И что нет каких либо названий для первых четырех?
Потом говорить esp указатель стека просто по традиции не совсем корректно. Кстати вопрос — заметил что возможна адресация [esp+ofset] , чего мне кажется в 486 не было. Вроде ebp уже не актуален, тоесть можно пользовать esp, учитывай только что он меняется по мере проталкивания параметров. Тогда дополнительный свободный регистр появляется?
P>>Я про рунтайм условия. Мы загружаем адрес, а память возможно не читаем, засчет этого ссылка на инт может быть быстрее
GN>Зачем загружать адрес, если в дальнейшем не будет чтения? Это лишние действия, т.е. код не оптимален.
Я про
int fun(int &a,int &b)
{ extern int c;
return c?a:b;
}
и
int fun(int a,int b)
{ extern int c;
return c?a:b;
}
Re[9]: [Investigation] Exceptions vs. Return Values
Здравствуйте, gear nuke, Вы писали:
GN>Здравствуйте, remark, Вы писали:
R>>Ну так исключение всё равно может полететь, и деструкторы есть, значит фрейм всё равно надо устанавливать.
GN>Тем более, эти результаты неверны, это уже с++/rv+ex какой-то.
Почему не верны?
Для "чистого" случая я мерял С-код там ничего такого не было — если тебе нужен такой случай, то бери эти результаты.
А тут я намеренно мерил "с++/rv+ex" — т.к. это есть реальный паттерн — некоторые библиотеки используют именно а таком виде, например, ACE.
R>>>>А код С++ компилятор даже лучше генерирует — поскольку деструкторы не вызываются явно у него есть некая свобода в оптимизации.
GN>>>Не верю Компилятор там один и тот же.
R>>В С — там код более явный, поэтому у компилятора меньше свободы, а в С++ менее явный — и у компилятора больше свободы.
GN>Интересная теория, я всегда считал что С и С++ перед оптимизацией трансформируется в одно и тоже ADT.
Я просто верб своим глазам, а машинный код говорит именно это...
Здравствуйте, Programador, Вы писали:
P>И что нет каких либо названий для первых четырех?
Были, и для других то же, в 16ти битных процессорах.
P>Потом говорить esp указатель стека просто по традиции не совсем корректно. Кстати вопрос — заметил что возможна адресация [esp+ofset] , чего мне кажется в 486 не было.
В 16ти битном режиме нельзя было адресовать стек через sp.
P>Вроде ebp уже не актуален, тоесть можно пользовать esp, учитывай только что он меняется по мере проталкивания параметров. Тогда дополнительный свободный регистр появляется?
Andrew S писал тут про это не один раз. В случае ипользования исключений ebp занят под указатель на стековый кадр, иначе свободен.
P>>>Я про рунтайм условия. Мы загружаем адрес, а память возможно не читаем, засчет этого ссылка на инт может быть быстрее
GN>>Зачем загружать адрес, если в дальнейшем не будет чтения? Это лишние действия, т.е. код не оптимален.
P>Я про P>
P>int fun(int a,int b)
P>{ extern int c;
P> return c?a:b;
P>}
Во-первых, пример надуман, в реальном мире такого не будет (без const)
Во-вторых, поскольку размер int не больше, чем const int*, компилятору незачем использовать указатели, в лучшем случае код будет одинаков, в худьшем будут лишние копирования новых переменных (int a,int b).
People who are more than casually interested in computers should have at least some idea of what the underlying hardware is like. Otherwise the programs they write will be pretty weird (c) D.Knuth
Re[7]: [Investigation] Exceptions vs. Return Values
Здравствуйте, remark, Вы писали:
R>Почему не верны?
Потому что написано с++/rv, а там с++/rv+ex (кстати, было и "там инструкции и на проверки возвращаемых значений и на поддержку исключений", но таблица сбила с толку )
R>Для "чистого" случая я мерял С-код там ничего такого не было — если тебе нужен такой случай, то бери эти результаты.
А, ну тогда практика согласуется с теорией — размер кода с исключениями не меньше, плюс еще размер неучтённых дополнительных данных.
R>Я просто верб своим глазам, а машинный код говорит именно это...
Я конечно соглашусь, что по машинному коду можно сказать: "вот это написано на С++". Потому что RAII, ООП и т.п. дают отпечаток даже () на исходном коде (как правило машинный код в этом случае не лучший). Но нельзя сказать уверенно: "это написано на С". Может быть и на С++. Никто же не запретит писать на С++ (практически) как на С. Когда активно используют расширенные возмоности — это видно. Например, по лишнему коду можно отличить std::auto_ptr от голого указателя, автоматические вызовы *структоров в new и delete от "эмуляции" их на С. Хотя на С++ можно получить более компактный результат, поскольку проще оптимизировать на более высоком уровне. А вот лушчего машинного кода у С++ я не только никогда не видел, но даже не могу понять, как большая свобода компилятора этому может способствовать
People who are more than casually interested in computers should have at least some idea of what the underlying hardware is like. Otherwise the programs they write will be pretty weird (c) D.Knuth
Re[9]: [Investigation] Exceptions vs. Return Values
Здравствуйте, CreatorCray, Вы писали:
CC>Еще активно используется FS для thread-specific штук типа TLS и исключений
Не только для этого, но в сгенерированном из чистого С++ коде сегментных регистров не будет.
People who are more than casually interested in computers should have at least some idea of what the underlying hardware is like. Otherwise the programs they write will be pretty weird (c) D.Knuth
R>Попутно я исследовал следующий вопрос – имеет ли смысл передавать умные указатели по ссылке вместо передачи по значению. Мы зачастую (ну я по-крайней мере ) передаю просто boost::shared_ptr без ссылок – по значению. R>Функции аналогичные функциям из предыдущего теста – просто много форвардинга вызовов функций, без выделения ресурсов, но при этом между функцими передавался один boost::intrusive_ptr, в одном случае по значению, в другом по ссылке.
А зачем вообще передавать boost::intrusive_ptr<T> через параметры?
Что дает применение:
void foo( const boost::intrusive_ptr<T>& ptr )
по сравнению с
void foo( const boost::intrusive_ptr<T>& ptr )
Может я чего не понимаю но
boost::intrusive_ptr<T> это средство memory management — т.е. при чем здесь параметры?
Re[2]: [Investigation] Exceptions vs. Return Values
Так и знал Это можно скомпелирвать g++ Формально я конечно не прав и MSVC использует FS (раз уж есть в ОС готовый механизм SEH). В этих случаях необходимость использования FS явно следует из контекста, его не нужно передавать в параметрах функции (как часть указателя). Говоря о плоской модели я всего лишь имел ввиду, что нет разницы, что адресует указатель — стек, хип, секцию данных исполняемого файла, даже адрес TIB — он всегда может храниться в любом из регистров общего назначения. (я бы лучше вместо fs вспомнил про eip, вот что действительно постоянно используется и является строго указателем )
People who are more than casually interested in computers should have at least some idea of what the underlying hardware is like. Otherwise the programs they write will be pretty weird (c) D.Knuth
Re[12]: [Investigation] Exceptions vs. Return Values
Здравствуйте, gear nuke, Вы писали:
GN>Так и знал
Любопытно, что именно знал? GN> Формально я конечно не прав и MSVC использует FS
Я бы сказал "в общем случае не прав".
Да и фраза "но в сгенерированном из чистого С++ коде сегментных регистров не будет" звучит так, как будто любой С++ код будучи откомпилирован не будет содержать сегментных регистров.
А во всем остальном —
... << RSDN@Home 1.1.4 stable SR1 rev. 568>>
Re[13]: [Investigation] Exceptions vs. Return Values
Здравствуйте, CreatorCray, Вы писали:
CC>Любопытно, что именно знал?
Что увижу этот пример
GN>> Формально я конечно не прав и MSVC использует FS CC>Я бы сказал "в общем случае не прав". CC>Да и фраза "но в сгенерированном из чистого С++ коде сегментных регистров не будет" звучит так, как будто любой С++ код будучи откомпилирован не будет содержать сегментных регистров.
Дык там я до этого писал "поскольку в Виндовсе плоская модель памяти" и далее продолжал в этом контексте. Если правильно понимаю, кастить к интегральному типу указатель в DOS — то сегментный регистр будет "добавлен" в результат, и наоборот; я имел ввиду, что в Виндовсе такого никогда не будет.
CC>А во всем остальном —
People who are more than casually interested in computers should have at least some idea of what the underlying hardware is like. Otherwise the programs they write will be pretty weird (c) D.Knuth
Re[3]: [Investigation] Exceptions vs. Return Values
CS>Может я чего не понимаю но CS>boost::intrusive_ptr<T> это средство memory management — т.е. при чем здесь параметры?
Во-первых, так просто зачатую делают, не задумываясь о том, что это под собой подразумевает
Действительно это обычно не надо
Во-вторых, я отталкивался от boost::shared_ptr, который надо передавать, что бы его потом можно было скопировать и сохранить на будущее (иногда это можно сделать имея указатель, но не всегда). Потом я заменил shared_ptr на intrusive_ptr, что бы сделать копирование полегче... в итоге пример получился не очень вразумительным т.к. действительно из голого указателя всегда можно восстановить intrusive_ptr.
Но в целом смысл был даже не в этом, смысл был в том, чтобы оценить разницу, когда у функции есть аргументы с нетривиальным деструктором и когда нет. Т.е. оценить, можно ли чего то добится, если некоторыми усилиями убрать из функции параметры с деструкторами.
Отсюда, в частности, следуют следующие выводы:
— объекты CString и подобные быстрее всё же передавать по сслыке (хоть они и используют COW), особенно если таким образом получится "разрузить" функцию от объектов с деструкторами. Т.к. исчезает установка/снятие фрейма исключений.
— код:
Речь идёт о замере интервалов порядка десятков тактов.
Я предлагаю следующую методику:
Выполняем код много раз подряд.
Разы, в которые произошёл вызов планировщика, переключение контекста, страничное прерывание и т.д. отбрасываем, как выдающие заведомо оооочень большое неправильное время, никак не связанное с нашим измеряемым кодом.
Т.о. получаем "истинное" время выполнения целевого фрагмента кода.
Ты предлагаешь (поправь, где не прав):
Выполняем код много раз подряд.
Разы, в которые произошёл вызов планировщика, переключение контекста, страничное прерывание и т.д. учитываем.
Потом "размазываем" эти ооочень сильно большие времена, усредняя все значения.
Т.о. получаем "истинное" время выполнения целевого фрагмента кода.
Т.е. ты считаешь нормальным, если, например, при замере одного фрагмента кода переключения контекста произошли 1 раз, а во время замера второго фрагмента кода переключения контекста произошли 2 раза. Потом мы усредняем эти времена и сравниваем.
Т.е. ты считаешь нормальным, если произошёл вызов планировщика, потом одно переключение контекста, потом поработал другой поток, потом опять вызов планировщика, потом опять переключение контекста и потом продолжила работать наша функция. Это нормально, это время можно включить в замер функции в 20 тактов. А вот если ко всему этому прибавляется ещё и пенальти, вызванное переключением потока на другое ядро, то вот это ты считаешь не нормальным, и для борьбы с этим предлагаешь делать привязку потока к ядру.
Аналогичная ситуация с поднятием приоритета потока. Ты считаешь, что вызов планировщика совершенно нормально учитывать при замере куска кода.
Ну уж извините, с таким подходом я в корне не согласен. Хоть убейте.
Здравствуйте, gear nuke, Вы писали:
GN>Я конечно соглашусь, что по машинному коду можно сказать: "вот это написано на С++". Потому что RAII, ООП и т.п. дают отпечаток даже () на исходном коде (как правило машинный код в этом случае не лучший). Но нельзя сказать уверенно: "это написано на С". Может быть и на С++. Никто же не запретит писать на С++ (практически) как на С. Когда активно используют расширенные возмоности — это видно. Например, по лишнему коду можно отличить std::auto_ptr от голого указателя, автоматические вызовы *структоров в new и delete от "эмуляции" их на С. Хотя на С++ можно получить более компактный результат, поскольку проще оптимизировать на более высоком уровне. А вот лушчего машинного кода у С++ я не только никогда не видел, но даже не могу понять, как большая свобода компилятора этому может способствовать
В общем случае, если код делает одно и тоже, то и машинный код быдет таким же. Не виже никаких причин для обратного.
Вот набил такой пример:
__declspec(noinline) void auto_ptr()
{
std::auto_ptr<int> i (new int(5));
}
__declspec(noinline) void by_hand()
{
int* i = new int(5);
delete i;
}
int main()
{
auto_ptr();
by_hand();
}
Ты не поверишь, но компилятор сделал следующее:
int main()
{
auto_ptr();
0040109A call auto_ptr (401060h)
by_hand();
0040109F call auto_ptr (401060h)
}
004010A4 xor eax,eax
004010A6 ret
Приведу твой ответ из ветки "Конструктив":
AS>Давай. Навскидку я вижу следующие паттерны: AS>1. Чисто вычислительная функция — когда вызовов апи не производится вообще. Это может быть быть парсер текста, вычисление с использованием матриц и т.п. Желательно распределить их по степени сложности — глубины вызовов и количеству вызовов с проверками в одной функции.
согласен
AS>2. Обертки1. Когда ровно один слой и все сводится к вызову апи функции (или метода) и проверке возвращаемого значения\трансляции в исключение.
согласен — такой тест я делал
AS>3. Обертки1. Тоже ровно один слой, но в этом случае апи использует исключение (пример — MFC). Мы транслируем в свое исключение либо в код ошибки\возвращаемое значение.
в принципе не очень согласен но сделать можно, а потом пусть каждый сам решает насколько ему этот случай интересен
AS>4. Работа с файлами и анализ текста. Микс тест из 1, 2 и 3.
вот тут не очень понятно — ты что предлагаешь делать реальные вызовы ос?
я думаю, что это загубит весь тест, т.к. во-первых, время большое, а во-вторых, оно может сильно колебаться
а если не делать вызовов ос, то не понятно чем это отличается от (1)
AS>5. Смешанные вычисления — когда необходимо в процессе вычислений обращаться к апи. Сложность вычислений,процент обращений и характер распределения мест обработки по уровням можно регулировать (либо определить статистически общие варианты и использовать только их).
не понятно, чем отличается от (4)
что хотелось добавить — имхо тут надо делать всё-таки не очень нагруженные "мясом" тесты, что бы более "чисто" выделить вклад именно стратегий сообщения об ошибках. вызовы ос имхо накорню всё погубят. с вычислениями тоже надо быть осторожно — если тест получится memory bandwidth bound — то пиши пропало. т.е. имхо можно осторожно поделать какие-нибудь вычисления в регистрах, и повызывать заглушки ос. а в остальном оставить только "скелеты" от паттернов и при этом использовать невстраиваемые функции.
какие бы паттерны я добавил:
6. не понятно, почему обёртки в один слой. современные проекты насчитывают как минимум десяток слоёв (если считать в функциях), а то и больше. соответственно предлагаю мерить толстые обёртки. при этом варьировать толшину и ширину обёртки до "функций ос".
7. имхо обязательно надо мерить функции, использующие "ресурсы" — память/файлы/хендлы/соединения и т.д. в принципе это можно включить в предыдущий пункт, только варьировать ещё и кол-во ресурсов.
По поводу ebp я с одной стороны понял, а с другой не понял.
Понял, что при использовании исключений он может заниматься, и т.о. останется меньше регистров для вычислений.
Правда, имхо, такой эффект будет нетривиально отследить — надо сделать какой-то синтетический тест, которому требуется много регистров...
достаточно простенький пример и ebp _не_ занимается.
Почему и под что он будет заниматься в более сложных случаях?
Я бы понял, если бы наоборот — в простых он занимался, а в сложных освобождался каким-либо образом под другие нужны, а наоборот не понимаю...
Заодно по поводу thiscall. Для С случая нельзя использовать thiscall — его там нет. Поэтому всё же придётся мерить с cdecl.
Случай, когда в С++ используются возвращаемые значения, конечно, можно и надо мерить с thiscall.
По поводу сделать для случая С++ с возвращаемыми значениями вместо деструктора функцию deinit() — так не пойдёт. Даже если в С++ и используют функции типа init/create и т.д. вместо конструктора, то деструкторы используют даже самые вшивинькие библиотеки типа MFC/ACE. Поэтому я считаю, что случай с deinit() в С++ просто doesn't make any sense.
R>По поводу ebp я с одной стороны понял, а с другой не понял.
R>Понял, что при использовании исключений он может заниматься, и т.о. останется меньше регистров для вычислений. R>Правда, имхо, такой эффект будет нетривиально отследить — надо сделать какой-то синтетический тест, которому требуется много регистров...
Эффект проявляется уже в простейших нагруженых функциях. Как пример — недавно делал блиттер. Так вот, этот несчастный один дополнительный регистр во внутреннем цикле давал +20% производительности. Ну мало регистров у х86, никуда от этого не уйти... Идиотизм, конечно, но тут все вопросы к Интел.
R>Не понял, когда и под что он занимается. R>здесь
достаточно простенький пример и ebp _не_ занимается. R>Почему и под что он будет заниматься в более сложных случаях?
Я привел код. Посмотри, там явно видно, под что.
R>Я бы понял, если бы наоборот — в простых он занимался, а в сложных освобождался каким-либо образом под другие нужны, а наоборот не понимаю...
Это как это наоборот? В простейших случаях компилятор может организовать вычисления стек-фрейма на esp, а в сложных ему это сделать не судьба — когда стек фрейм может раскручиваться как локальным исключением (фрейм, установленный в самой функции), так и во фрейм, установленный по вызову выше. В общем, ничего удивительного.
R>Заодно по поводу thiscall. Для С случая нельзя использовать thiscall — его там нет. Поэтому всё же придётся мерить с cdecl.
Случай "С" меня как то не очень интересует, откровенно говоря. Надо сравнивать сравнимое.
R>Случай, когда в С++ используются возвращаемые значения, конечно, можно и надо мерить с thiscall.
R>По поводу сделать для случая С++ с возвращаемыми значениями вместо деструктора функцию deinit() — так не пойдёт. Даже если в С++ и используют функции типа init/create и т.д. вместо конструктора, то деструкторы используют даже самые вшивинькие библиотеки типа MFC/ACE. Поэтому я считаю, что случай с deinit() в С++ просто doesn't make any sense.
Нет. Обычно init\destroy используют, когда имеются тривиальные конструкторы\деструкторы (которые инлайнятся), и тяжелые функции первичной\вторичной инициализации. Либо вообще не имеют конструкторов\деструкторов — получаются обычные POD. Поэтому именно то, что я приводил. А назвать MFC или ACE вшивенькой библиотекой — ну это да. Вообще, MFC как раз очень вполне завязана на исключения — посмотреть те же самые CFile.
Здравствуйте, remark, Вы писали:
R>В общем случае, если код делает одно и тоже, то и машинный код быдет таким же. Не виже никаких причин для обратного.
auto_ptr делает несколько больше. Я же не писал "всегда отличается"
R>Ты не поверишь, но компилятор сделал следующее:
R>
Поверю, у меня тела функций тоже получились идентичными, но в разных местах, поэтому хотелось бы узнать компилятор и ключи
Немного модернизировал пример, что бы было лучше видно лишнюю работу:
__declspec(noinline) void auto_ptr()
{
std::auto_ptr<int> i /*(new int(5))*/;
}
>__declspec(noinline) void by_hand()
{
int* i /*= new int(5)*/;
//delete i;
}
int main()
{
auto_ptr();
by_hand();
}
MSVC 2005 + Dinkumware STL:
; Function compile flags: /Ogspy
?by_hand@@YAXXZ PROC ; by_hand
ret 0
?by_hand@@YAXXZ ENDP ; by_hand
?auto_ptr@@YAXXZ PROC ; auto_ptr
push 0
call ??3@YAXPAX@Z ; operator delete
pop ecx
ret 0
?auto_ptr@@YAXXZ ENDP ; auto_ptr
_main PROC
call ?auto_ptr@@YAXXZ ; auto_ptr
xor eax, eax
ret 0
_main ENDP
Конечно вырожденный случай, вот более жизненный:
__declspec(noinline) void auto_ptr()
{
std::auto_ptr<int> i;
int* i2 = new int(5);
i.reset(i2);
}
__declspec(noinline) void by_hand()
{
int* i;
int* i2 = new int(5);
i = i2;
delete i;
}
int main()
{
auto_ptr();
by_hand();
}
MSVC 2005 + Dinkumware STL:
; Function compile flags: /Ogspy
?by_hand@@YAXXZ PROC ; by_hand
push 4
call ??2@YAPAXI@Z ; operator new
test eax, eax
pop ecx
je SHORT $LN3@by_hand
mov DWORD PTR [eax], 5
jmp SHORT $LN4@by_hand
$LN3@by_hand:
xor eax, eax
$LN4@by_hand:
push eax
call ??3@YAXPAX@Z ; operator delete
pop ecx
ret 0
?by_hand@@YAXXZ ENDP ; by_hand
?auto_ptr@@YAXXZ PROC ; auto_ptr
push esi
push 4
call ??2@YAPAXI@Z ; operator new
test eax, eax
pop ecx
je SHORT $LN3@auto_ptr
mov DWORD PTR [eax], 5
mov esi, eax
jmp SHORT $LN4@auto_ptr
$LN3@auto_ptr:
xor esi, esi
$LN4@auto_ptr:
test esi, esi
je SHORT $LN7@auto_ptr
push 0
call ??3@YAXPAX@Z ; operator delete
pop ecx
$LN7@auto_ptr:
push esi
call ??3@YAXPAX@Z ; operator delete
pop ecx
pop esi
ret 0
?auto_ptr@@YAXXZ ENDP; auto_ptr
В STLPort должны получиться похожие результаты.
Cитуацию (точнее, auto_ptr) можно немного исправить.
первый вариант:
; Function compile flags: /Ogspy
?auto_ptr@@YGXXZ PROC ; auto_ptr, COMDAT
ret 0
?auto_ptr2@@YGXXZ ENDP; auto_ptr
второй:
; Function compile flags: /Ogspy
; COMDAT ?by_hand@@YGXXZ
_TEXT SEGMENT
?by_hand@@YGXXZ PROC ; by_hand, COMDAT
; заинлайнен new:
push esi
mov esi, 1280593503 ; 4c544e5fH
push esi
push 4
push 1
call DWORD PTR __imp__ExAllocatePoolWithTag@12test eax, eax
je SHORT $LN3@by_hand
mov DWORD PTR [eax], 5
jmp SHORT $LN4@by_hand
$LN3@by_hand:
xor eax, eax
$LN4@by_hand:
test eax, eax
je SHORT $LN12@by_hand
; заинлайнен delete
push esi
push eax
call DWORD PTR __imp__ExFreePoolWithTag@8
$LN12@by_hand:
pop esi
ret 0
?by_hand@@YGXXZ ENDP ; by_hand
?auto_ptr@@YGXXZ PROC ; auto_ptr, COMDAT
; заинлайнен new:
push esi
mov esi, 1280593503 ; 4c544e5fH
push esi
push 4
push 1
call DWORD PTR __imp__ExAllocatePoolWithTag@12test eax, eax
je SHORT $LN3@auto_ptr
mov DWORD PTR [eax], 5
jmp SHORT $LN23@auto_ptr
$LN3@auto_ptr:
xor eax, eax
$LN23@auto_ptr:
test eax, eax
je SHORT $LN37@auto_ptr
; заинлайнен delete
push esi
push eax
call DWORD PTR __imp__ExFreePoolWithTag@8
$LN37@auto_ptr:
pop esi
ret 0
?auto_ptr@@YGXXZ ENDP; auto_ptr
Текст примеров не изменён, исходный текст auto_ptr приведу следующим постом, этот и так уже большой
R>Или вот ещё:
People who are more than casually interested in computers should have at least some idea of what the underlying hardware is like. Otherwise the programs they write will be pretty weird (c) D.Knuth
People who are more than casually interested in computers should have at least some idea of what the underlying hardware is like. Otherwise the programs they write will be pretty weird (c) D.Knuth
R>Речь идёт о замере интервалов порядка десятков тактов.
Десятков???? Десятков тысяч. Или ты про разницу?
R>Я предлагаю следующую методику: R>Выполняем код много раз подряд. R>Разы, в которые произошёл вызов планировщика, переключение контекста, страничное прерывание и т.д. отбрасываем, как выдающие заведомо оооочень большое неправильное время, никак не связанное с нашим измеряемым кодом. R>Т.о. получаем "истинное" время выполнения целевого фрагмента кода.
Планировщик вызывается раз в ~15мс. Ты действительно думаешь, что он не влияет на результаты замеров? Еще раз — чтобы снизить влияние планировщика, надо поднять приоритеты и увеличить время замеров до нескольких (десятков) минут.
R>Ты предлагаешь (поправь, где не прав): R>Выполняем код много раз подряд.
Много _тысяч_ раз подряд.
R>Разы, в которые произошёл вызов планировщика, переключение контекста, страничное прерывание и т.д. учитываем.
Мы их в любом случае учитываем. Ты в мультизадачной ОС. Прервать выполнение кода может не только планировщик — еще есть аппаратные прерывания, DMA, и многие другие вещи, неявно влияющие на производительность памяти, процессора и т.п.
R>Потом "размазываем" эти ооочень сильно большие времена, усредняя все значения.
Нет. Времена будут примерно одинаковые. Почитай статью по ссылке, там пояснено, как что и почему. Я уже устал говорить, что время выполнения в современных программно-аппаратных системах зависит не только от программной, но и той аппаратной части (юнита), на котором исполняется код. А с учетом всяких "прикольных" чипсетов типа KT133 с его закидонами с шиной — так и вообще от фазы луны. Поэтому надо использовать длительные измерения, а влияние перепланировки уменьшать путем повышения приоритета. Право — это же азы.
R>Т.о. получаем "истинное" время выполнения целевого фрагмента кода.
Не истинное, а реальное. Минимальное время выполнения на современном железе может быть вызвано совершенно безумным сочетанием невоспроизводимых в реальной ситуации факторов (ну или редко воспроизводимых).
R>Т.е. ты считаешь нормальным, если, например, при замере одного фрагмента кода переключения контекста произошли 1 раз, а во время замера второго фрагмента кода переключения контекста произошли 2 раза. Потом мы усредняем эти времена и сравниваем.
Нет. Я считаю нормальным, когда за время замера переключение контекста произошло 100000 или 100010 раз.
R>Т.е. ты считаешь нормальным, если произошёл вызов планировщика, потом одно переключение контекста, потом поработал другой поток, потом опять вызов планировщика, потом опять переключение контекста и потом продолжила работать наша функция. Это нормально, это время можно включить в замер функции в 20 тактов. А вот если ко всему этому прибавляется ещё и пенальти, вызванное переключением потока на другое ядро, то вот это ты считаешь не нормальным, и для борьбы с этим предлагаешь делать привязку потока к ядру.
Ты все в кашу мешаешь... Даже не буду комментировать, но право, рекомендую разобраться, что к чему и зачем. К одному ядру я рекомендую привязывать по совершенно другим причинам. И я о них уже говорил.
Ты, например, даже не учитываешь, что RDTSC замеряет совсем не время выполнения кода, а совершенно другое — пропускную способность декодера команд. Поэтому все твои измерения с точностью до 20 команд — пшик. Конвейер у того же П4 — 18-20 стадий, что ты меряешь? Почитай статью — там хороший пример. ПОэтому надо мерить не 20 тактов, а 200000 тактов. Тогда точность измерения будет приемлемой (инструмент измерений не будет влиять на измеряемый объект). Между вызовами точек отсчета должны проходить десятки, а лучше сотни тысяч команд.
R>Аналогичная ситуация с поднятием приоритета потока. Ты считаешь, что вызов планировщика совершенно нормально учитывать при замере куска кода.
Без этого — никак. Хочешь отключить планировщик? Работай в DOS
R>Ну уж извините, с таким подходом я в корне не согласен. Хоть убейте.
Ну, можно не соглашаться с теорией относительности, или с тем, что в одном рубле 100 копеек. Но от этого она (ТО) не станет более верной или не верной, а рубль не полегчает и тяжелее тоже не станет. Есть испытанные методики измерения времени выполнения кода. Про них тебе уже говорили несколько разных людей, не только я.
Все, эта тема для меня закрыта — нового я тебе ничего не скажу, ты мне — тоже . Право, ходим по кругу — я не вижу смысла продолжать.
Здравствуйте, Аноним, Вы писали:
А>То, что исключения позволяют сократить один if при вызове функции, по-моему, с лихвой компенсируется тем, что исключения вынуждают пользоваться обертками в виде умных указателей.
"Вы поставили знак минус вместо знака плюс"
Выделенное нужно заменить на и бесплатно получаем
Уже ради этих обеих вещей стоит использовать исключения