Здравствуйте, 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>Так, давайте, что именно? Или просто "отвяжись" — дык тогда может лучше так и говорить?