Кто-нибудь, объясните.
Есть у меня цикл. Думаю, как бы его заставить быстрее работать.
Вижу, что в одной из функции есть такая строка
if(!key)
throw NullException(1);
По логике она там не шибко нужна (вызывающая функция уже проверила объект на нуль), я её удаляю (цикл у меня большой и любое действие, вплоть до простого приравнивания к нулю, имеет большие последствия).
И самый прикол, что после удаления мой цикл стал отрабатывать ещё медленнее (!) на 40 мс. Возвращую строку на место - цикл снова ускоряется ровно на 40 мс. При чём с тем же сталкивался я при обнулении объектов (там вплоть до 200 мс). Если их обнулять, то скорость без внятных причин поднимается, хотя
1) обнуление это лишний запись в память
2) логически оно мне это не надо.
Ничо не понимаю. Компилер g++, меряю через GetTickCount. Что не так?
Может быть, с отсутствием кода gcc выравниваниет некоторые места через nop'ы, и, типа, процессор простаивает? Не втыкаю ваще :( Кто здесь мудрит: я, процессор или компилер?
А ексепшн все-таки хоть раз бросается или нет?
Попробуй if (!key) return; и замерь время.
Скорей всего, компилятор, в случае программного отсутствия проверки исключений, сам подставляет в код таковую. Поройтесь в настройках компилятора, обычно там такие вещи настраиваются.
Цитата: RawonaM от июля 8, 2009, 11:22
А ексепшн все-таки хоть раз бросается или нет?
Не выкидываются. Если бы выкидывались — я бы видел сообщение о внезапном завершении, потому что они нигде не ловятся.
А свой код можете выложить где-нибудь? Боюсь, без конкретного примера шансы выяснить причину замедления стремятся к нулю.
Цитата: Gerbarius от июля 8, 2009, 11:43
А свой код можете выложить где-нибудь? Боюсь, без конкретного примера шансы выяснить причину замедления стремятся к нулю.
Да там ничего такого серьёзного.
Цитировать
template <class K, class V>
typename Map<K, V>::Node* Map<K, V>::add(K key, V value)
{
if(!key)
throw NullException(1);
if(pageCount == 0)
{
firstPage = lastPage = new Page();
pageCount++;
lastPageRatio = 0;
}
else if(lastPageRatio >= NEDO2_CORE_MAP_PAGE_SIZE)
{
Page* newPage = new Page();
lastPage->next = newPage;
lastPage = newPage;
pageCount++;
lastPageRatio = 0;
}
Node* node = &(lastPage->nodes[lastPageRatio++]);
node->key = key;
node->hash = key->hash();
node->value = value;
itemCount++;
return node;
}
Этот кусок ну совершенно никак не влияет на логику... Тест сделан так, что key никогда не бывает нулём. Да даже если и будет - вылетит эксепшн и программа закончится с сообщением а ля "This application has requested the Runtime to terminate it in an unusual way" etc. Но такого нетути.
Ничо не понимаю.
Я так понимаю, что key - это объект какого-то класса на самом деле? В таком случае стоит глянуть, что для него делает оператор !. Там нет случайно никаких побочных эффектов?
Цитата: Gerbarius от июля 8, 2009, 12:28
В таком случае стоит глянуть, что для него делает оператор !
В тесте шаблонная переменная K параметризирована указателем на класс, т.е. действует логика указателя (отрицание адреса в ОЗУ). Да у того класса и operator! не перегружен вовсе.
то бишь там core::Map<core::String*, int> map;
При стандартных настройках проверка деления на нуль всегда неявно вставляется, скажем, при любых опреациях деления.
Какие-то компиляторы позволяют отключать такие приколы, какие-то - нет.
Откуда там указатель?
Почему тогда там написано
Цитировать
template <class K, class V>
typename Map<K, V>::Node* Map<K, V>::add(K key, V value)
а не
Цитировать
template <class K, class V>
typename Map<K, V>::Node* Map<K, V>::add(K *key, V value)
?
Вместо K подставляется String*
ЦитироватьMap<String*, int> map;
В итоге вызов функции будет такой:
Цитироватьadd(String* key, int value);
Дело в том, что я не так шарю в С++, я всё больше ANSI C или Objective-C пользую. Но тут вот надо. Могу ошибаться в выборе интерфейса. Может быть, так делать не стоит. Не знаю. Язык перему́женный излишне. Его модель ООП и реализация ужасны, давлюсь но полтзуюсь сугубо из сооюражений его мейнстримности.
Чем грозит, если я ставлю так, как есть? Если подставить String или String& разницы или не будет, или не скомпилируется?
P.S. Да, глупость какая-то. Перепишу. :) Потмо сверю скорость. Но что-т не верится, что поможет.
Цитата: Алексей Гринь от июля 8, 2009, 11:12
Может быть, с отсутствием кода gcc выравниваниет некоторые места через nop'ы, и, типа, процессор простаивает? Не втыкаю ваще :( Кто здесь мудрит: я, процессор или компилер?
Так дезасемблируйте и посмотрите, как изменяется код если выкинуть ту строчку.
Оптимизация компилятором. Когда проверка на нуль, она проводится ПАРАЛЛЕЛЬНО. Если компилятор сам проверку делает то это он делает сразу перед использованием, т.е. не параллельно.
Try this and report:
template <class K, class V>
typename Map<K, V>::Node* Map<K, V>::add(K key, V value)
{
if(pageCount == 0)
{
firstPage = lastPage = new Page();
pageCount++;
lastPageRatio = 0;
}
else if(lastPageRatio >= NEDO2_CORE_MAP_PAGE_SIZE)
{
Page* newPage = new Page();
lastPage->next = newPage;
lastPage = newPage;
pageCount++;
lastPageRatio = 0;
}
Node* node = &(lastPage->nodes[lastPageRatio++]);
if(!key)
throw NullException(1);
node->key = key;
node->hash = key->hash();
node->value = value;
itemCount++;
return node;
}
Угу, надо в ассемблер смотреть (например, можно файл скомпилировать с параметром -s и всеми остальными параметрами как обычно).
Если компилятор не шибко мудрит, то скорее всего в дело вступают всякие процессорные штучки: кэши, предсказания переходов и т.п. Но такие вещи очень трудно исследовать.
Цитата: iopq от июля 8, 2009, 13:47
Если компилятор сам проверку делает
Откуда у всех такая уверенность, что компилятор непременно занимается самодеятельностью? Ему больше заняться нечем? :)
Цитата: Алексей Гринь от июля 8, 2009, 11:12
Может быть, с отсутствием кода gcc выравниваниет некоторые места через nop'ы, и, типа, процессор простаивает? Не втыкаю ваще :( Кто здесь мудрит: я, процессор или компилер?
А посмотреть ассемблерный код, ты не догадался? :)
Цитата: jvarg от июля 8, 2009, 12:42
При стандартных настройках проверка деления на нуль всегда неявно вставляется, скажем, при любых опреациях деления.
Вы про какой компилятор говорите?
Цитата: Алексей Гринь от июля 8, 2009, 11:12
И самый прикол, что после удаления мой цикл стал отрабатывать ещё медленнее (!) на 40 мс.
Тебе прям кровь из носу надо 40 мс сэкономить? :)
Цитата: Gerbarius от июля 8, 2009, 14:00
Цитата: iopq от июля 8, 2009, 13:47
Если компилятор сам проверку делает
Откуда у всех такая уверенность, что компилятор непременно занимается самодеятельностью? Ему больше заняться нечем? :)
Это уверенность от знания общих тенденций создания компиляторов.
Подавляющее большинство их пользователей - это прикладные программисты. Их волнует не скорость работы, а надежность и удобство разработки. Соответестенно, все настройки по умолчанию расчитаны на них.
Если вы пытаетесь делать что-то нестандартное - изменяйте настройки по умолчанию, рассчитанные на большинство.
У си/си++ другая идеология. Эти языки позволяют программисту очень многое, но и вся ответственность за качество программы ложится на него. Кстати, gcc не вставляет никаких проверок даже для деления на ноль. Только в том случае, когда ты в коде запишешь деление на ноль как на константу, компилятор выдаст предупреждение, но не ошибку! И уж точно он не проверяет указатели.
Цитата: jvarg от июля 8, 2009, 17:16
Это уверенность от знания общих тенденций создания компиляторов.
То есть это было чисто умозрительное суждение? :o
Цитата: Gerbarius от июля 8, 2009, 17:37
Кстати, gcc не вставляет никаких проверок даже для деления на ноль.
И не только gcc. Кстати, gcc ещё довольно предупредителен. :)
Цитировать
Кстати, gcc не вставляет никаких проверок даже для деления на ноль.
Не верю. Это стандартная настройка по умолчанию ЛЮБОГО компилятора, любого языка. Только в некоторых эту опцию отключить нельзя, а в некоторых можно.
Конкретно с gсс я не работал (ибо давно забросил программерство), но во всех предыдущих инкарнациях С++ был в настройках раздел типа "exeption handling", где вся эта фигня настраивалась, и по умолчанию были включены все проверочные подстановки кода...
Кстати, движок форума не дает мне вставить "g плюс плюс", где вместо "плюс" стоит "+".
Выдает:
Forbidden
You don't have permission to access /index.php on this server.
Additionally, a 404 Not Found error was encountered while trying to use an ErrorDocument to handle the request.
________________________________________
Apache/1.3.41 Server at www.lingvoforum.net Port 80
Цитата: jvarg от июля 8, 2009, 19:00
Не верю. Это стандартная настройка по умолчанию ЛЮБОГО компилятора, любого языка. Только в некоторых эту опцию отключить нельзя, а в некоторых можно.
:o No comments. Зачем верить? Возьмите компилятор и убедитесь сами. VC, например, даже предупреждения не выдаёт, поскольку стандарт не обязывает. Вы стандарт обсуждаемого языка читали? Некоторые компиляторы C++ поддерживают кое-какие проверки, в том числе и во время выполнения, но это абсолютно факультативные вещи, и по умолчанию они естественно выключены.
Цитата: jvarg от июля 8, 2009, 19:00
Конкретно с gсс я не работал (ибо давно забросил программерство), но во всех предыдущих инкарнациях С++ был в настройках раздел типа "exeption handling", где вся эта фигня настраивалась, и по умолчанию были включены все проверочные подстановки кода...
Обработка исключений — это совсем другая история.
Та-а-ак. Сегодня цифры по скорости другие :) С удалением этого места ускоряется. Хотя ничего не менял в теле цикла. Хе-хе.
Цитата: myst от июля 8, 2009, 16:44
А посмотреть ассемблерный код, ты не догадался?
Ну блин, при работе с Plain C никогда не приходилось. Всё быстро, всё как надо, без всякой самодеятельности. Сказал — сделает. Не сказал — не сделает.
Цитата: myst от июля 8, 2009, 18:00
Кстати, gcc ещё довольно предупредителен
Ага. У меня там возвращается неполный тип (не очень вкуриваю, чем он неполон), и поэтому приходится писать
typename Map<K, V>::Node вместо
Map<K, V>::Node — g+
+ ругается (мол, синтаксический парсер не может явно определить, что это тип, а не, скажем, глобальная переменная). А MC++ (2003 года выпуска at least), я слышал, пытается сам определить, что это такое по контексту. Во-первых, не факт, что он определит верно. Во-вторых, такая программа не переносима (т.е. при переносе под gcc будешь долго пытаться вкурить, чо такое случилось). В-третьих, это вроде бы не по стандарту.
Цитата: jvarg от июля 8, 2009, 19:04
Кстати, движок форума не дает мне вставить "g плюс плюс", где вместо "плюс" стоит "+".
Выдает:
Forbidden
You don't have permission to access /index.php on this server.
Additionally, a 404 Not Found error was encountered while trying to use an ErrorDocument to handle the request.
Ага :( Это всё козни микрософтцев :)
Чего делаешь-то хоть, расскажи. :)
А сколько итераций в цикле и сколько времени он работает? Если >= секунде, то флуктуации в ~40 мс - обычное дело.
Цитата: GaLL от июля 9, 2009, 05:42
Если >= секунде, то флуктуации в ~40 мс - обычное дело.
Не, одно дело когда эти флуктуации случайны, по вине Винды, и каждый раз то +40 мс, то -40мс, то +100мс — не определить.
А тут было стойкое правило: удаляешь — умедляется, добавляешь — ускоряется. Каждый раз повторялось. Значит это где-то компилятор что-то без моего ведома вставил...
Цитата: myst от июля 9, 2009, 05:39
Чего делаешь-то хоть, расскажи. :)
Да так... :) Экспериментирую со скоростью алгоритмов... Вчера каждый час выигрывал по 100 мс, по капельке то там, то сям подчищая... А тут — нате.
Чувствую, вообщем, ничего лучше Plain C нету. Он просто идеальнейший по простоте и чистоте язык :) Щас думаю накатать себе диздок для правильной имплементации ООП под ним (костыль, зато надёжный).
Может быть дело в том, что при удалении части кода адреса джампов так же смещаются, а джамп на начало параграфа (т. е. на адрес, кратный 16) часто более эффективен (по крайней мере, это верно для джапмов, образующих циклы; кроме того, не зря же нормальные компиляторы обычно выравниваниют адреса подпрограмм по 16 байт)?
Пошарился сейчас в яндексе. Все-таки, нужно явно отключать скрытые проверки, служебные и отладочные коды при помощи флагов компилятора.
Что-то вроде: "-fno-rtti -fno-exceptions"
Поройтесь в описании флагов.
Цитата: jvarg от июля 9, 2009, 08:26
Пошарился сейчас в яндексе. Все-таки, нужно явно отключать скрытые проверки, служебные и отладочные коды при помощи флагов компилятора.
Чиво? :o В gcc? Линки — студию!
Цитата: jvarg от июля 9, 2009, 08:26
Что-то вроде: "-fno-rtti -fno-exceptions"
Это не в тему.
Цитата: GaLL от июля 9, 2009, 07:12
Может быть дело в том, что при удалении части кода адреса джампов так же смещаются, а джамп на начало параграфа (т. е. на адрес, кратный 16) часто более эффективен (по крайней мере, это верно для джапмов, образующих циклы; кроме того, не зря же нормальные компиляторы обычно выравниваниют адреса подпрограмм по 16 байт)?
Да чё гадать? Надо посмотреть ассемблерный выхлоп, и всё станет ясно. Делов-то.
Цитата: Алексей Гринь от июля 8, 2009, 11:12
GetTickCount
Ты в курсе, что её разрешение зависит от разрешения системного таймера? Какое оно в твоей системе?
Цитата: myst от июля 10, 2009, 01:29
ЦитироватьGetTickCount
Ты в курсе, что её разрешение зависит от разрешения системного таймера? Какое оно в твоей системе?
Ёпрст. Если тест срабатывает при одном условии при запуске 100 раз *каждый раз* 5600 мс (плюс минут 3 мс), а при других условиях при запуске 100 раз — стабильно 6200 мс (плюсминус 3 мс)— это значит, что таймер тут ни при чём.
P.S. И вообще я тему блокировал, ты как суперадмин не замечаешь этого даже...
Цитата: Алексей Гринь от июля 10, 2009, 02:38
P.S. И вообще я тему блокировал, ты как суперадмин не замечаешь этого даже...
Упс... :-[