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. Брать средне-минимальное время.