Как стать автором
Обновить

Комментарии 36

Имхо, намного проще будет написать:
return a1.1 == a2.a && a1.b == a2.b && a1.d == a2.d;
минимум оверхеда по памяти и максимум скорости.
для проверки на равенство — да. а вот сравнение на «больше-меньше» сложнее
Да, но набор логических операций, насколько бы сложными они не были, всё равно быстрее, чем те же операции и несколько циклов в придачу. И не факт, что писанины меньше.
Для структур — memcmp, prbbly?
Ну это очевидный способ, но там могут быть проблемы с выравниванием, да и еще может чего.
#pragma pack(push, 1)

Либо, учесть, что по умолчанию все выравнивается по двойному слову, а промежутки заполняются нулями, то есть при сравнении через memcpy они не учитываются.

Просто этот «очевидный способ» занимает одну строку, пишется легко и не имеет проблем с производительностью.
memcmp, конечно.
#pragma pack не стандартизировано, и не факт что будет поддерживаться везде и на всех платформах.
Это не важно. Выравнивание структур не мешает memcmp корректно их сравнивать, так как промежутки заполняются нулями.
точно заполняются? Мне кажется, в промежутках может лежать любой неинициализированный мусор.
Если вас смущает этот момент, ничего не мешает сделать ZeroMemory(this, sizeof(yourtypename)) в конструкторе перед инициализацией.
Ну, это лишняя морока. И не всегда целесообразно определять конструктор. Его наличие, например, не позволит писать так:
data a = { 1, 2, 3, 4 } ;
Да, писать этот баян на бусте — не морока, а написать в конструкторе без параметров ZeroMemory — морока.

Круто.
Я считаю, что лучше написать один раз как следует, чем постоянно тащить за собой кучу boilerplate-кода.
Вы быстродействие способа замеряли?

Просто спортивный интерес.
Да, в комментариях ниже написал.
НЛО прилетело и опубликовало эту надпись здесь
Это не принципиально.
return !(a2.a_ > a1.a_ || a2.b_ > a2.b_ || a2.d_ > a2.d_ || (a1.a_ == a2.a_ && a1.b_ == a2.b_ && a1.d_ == a2.d_));
Еще как вариант, можно упростить, определив оператор == и упростить выражение заменой правой части (a1 == a2)
Оу, дико извиняюсь, неправ.
Правильно так:
return (a1.a_ < a2.a_) || ((a1.a_ == a2.a_) && ((a1.b_ < a2.b_) || ((a1.b_ == a2.b_) && (a1.d_ < a2.d_))));
А этот способ, кстати, по скорости эквивалентен «хорошему» и «безумному», время около 3.7с. Быстрее всех работает c if'ами, как в начале статьи.
хм, обычно если в структуре много полей то имеет смысл создать что-то вроде
std::map<std::string, Something>
а тогда такие вопросы решаются сами собой.

И кроме того редко встречал ситуации когда стравнение шло по многим параметрам — обычно достаточно сравнить по одному ( ну двум ) ключевому чтобы точно сказать равно, больше или меньше
разъясните, пожалуйста, как все решается с помощью std::map
хм, ну вместо создания отдельных переменных мы храним данные в мэпе. Таким образом сравнение, обнуление — все это делается простейшими циклами
std::for_each
std::equal

если данные однородные — то можно хранить в мэпе сразу нужный тип данных. Если разных типов — то либо через базовый класс, либо можно хранить все в строках, и автоматически преобразовывать
скажем так
bool get(const std::string& key, T& t)
{
istringstream istr(map[key]);
istr >> t;
return istr.good();
}
как это подружить с производительностью?
не хранить таким образном переменные циклов, а хранить лишь обычные данные.

вообще из привычного — если нужно хранить множество однородных данных — то это
— данные конфига ( нужно лишь изредка читать — производительность не страдает даже если каждый раз читать из файла )
— сохраненный файл данных — раз вычитал и забыл — или раз записал и забыл

как бы то ни было — не те вещи с которыми нужно постоянно работать. Производительность вообще странная штука — иногда думаешь как бы оптимизировать а потом передаешь файл по сети пару секунд — и все остальные оптимизации делаются ненужными =)
Ну, если Вам интересно порыться в boost и поизучать разные программерские финты — то это хороший пример. Но упаси Вас Бог так писать в реальной программе. На это есть как минимум 2 причины:

1. Никто, кроме Вас, в жизни не поймет что тут написано. Нет, ну если человек порассматривает код, подумает пару минут, поднимет мануал по незнакомым ему частям буста, пройдетя в дебаггере — то да. Но на практике быстрее и проще выгнать из команды человека, пишущего такой код, чем на каждые его 2 строчки тратить по 10 минут время на попытки понять в чем суть.

2. Сделайте массив на 10 000 рандомных структур и заюзайте для него какую нибудь сортировку, вызывающую оператор<. Замеряйте время при классической реализации и при Вашей. Мне кажется, разница будет существенна (конечно же, в пользу классической).

Вывод: если можно написать понятный и быстро работающий код, то не нужно писать непонятный и медленно работающий, даже если он будет на пару строк короче.
Проверил.
Классический способ быстрее, как и ожидалось, время около 2.8с. «Хороший» и «безумный» способы примерно равноценны, время около 3.7с, но «безумный» обычно быстрее на 0.01с.
От количества полей (2, 3, 4) зависит незначительно.
Структура из интов, размер массива 10 000 000 элементов, сортировка std::sort, данные в каждом подходе одинаковые, компилятор VS2008, Release.
Это не совсем корректная проверка, потому как похоже что основную часть времени будет занимать не код сравнения, а код сортировки, вызова функций, и так далее.

Попробуйте просто в цикле на несколько миллионов раз вызвать для разных структур оператор.
Вполне корректная, я считаю. Доля прочего кода при одних и тех же данных постоянна, меняется лишь время сравнения. Точное отношение скоростей сравнения проверка не даст, но выявить победителя позволит точно.
И потом, где еще используется такое массовое сравнение? Обычно при сортировке и поиске.

Времена 10 000 000 сравнений, с:
0.168, 0.232, 0.233
Что подтверждает.
А memcmp?
А memcmp нельзя избирательно сделать по полям a_, b_ и d_, игнорируя c_, или вообще в другом порядке старшинства полей.
Хотя, в принципе, и так проигрыш в полтора раза по быстродействию ради абстрактной «легкости сравнения» — не лучшее решение.
Плюс еще шаблоны буста за собой тащить.
Кодогенерацию попробовать.

Она в итоге три этажа наделает, человек не ошибется и ничего лишнего не будет тягать (внешняя библиотека + затраты памяти лишние на лишние создания).

Единственный минус — трехэтажность + генерация.
Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации

Истории