Pull to refresh

Comments 25

Не нашел, какая использовалась версия Qt и какой компилятор C++?
Ага немаловажным было бы сравнить скорости на разных компиляторах…
Сейчас попробую протестить gcc 4.6 и clang 3.1 из под linux x86_64
Плюс еще clang может юзать libcxx вместо libstdc++
gcc 4-6 linux x86-64
Run test for type:F7QVectorIiEvE                                                                                                                                    
"New for" results(ms):avg:189 min:188 max:192                                                                                                                       
"Accumulate" results(ms):avg:242 min:240 max:253                                                                                                                    
"Qt foreach" results(ms):avg:237 min:237 max:238                                                                                                                    
"STL for" results(ms):avg:381 min:381 max:382                                                                                                                       
"STL for 2" results(ms):avg:240 min:240 max:241                                                                                                                     
Run test for type:FSt6vectorIiSaIiEEvE                                                                                                                              
"New for" results(ms):avg:186 min:185 max:187                                                                                                                       
"Accumulate" results(ms):avg:186 min:185 max:188                                                                                                                    
"Qt foreach" results(ms):avg:484 min:482 max:499                                                                                                                    
"STL for" results(ms):avg:187 min:186 max:188                                                                                                                       
"STL for 2" results(ms):avg:186 min:186 max:187                                                                                                                     
Run test for type:F5QListIiEvE                                                                                                                                      
"New for" results(ms):avg:299 min:298 max:316                                                                                                                       
"Accumulate" results(ms):avg:298 min:297 max:301                                                                                                                    
"Qt foreach" results(ms):avg:356 min:356 max:359                                                                                                                    
"STL for" results(ms):avg:360 min:359 max:368                                                                                                                       
"STL for 2" results(ms):avg:298 min:297 max:300

clang 3.1+libcxx
Run test for type:F7QVectorIiEvE
"New for" results(ms):avg:186 min:185 max:189
"Accumulate" results(ms):avg:187 min:186 max:190
"Qt foreach" results(ms):avg:245 min:244 max:247
"STL for" results(ms):avg:331 min:330 max:343
"STL for 2" results(ms):avg:187 min:186 max:190
Run test for type:FNSt3__16vectorIiNS_9allocatorIiEEEEvE
"New for" results(ms):avg:186 min:186 max:188
"Accumulate" results(ms):avg:186 min:186 max:189
"Qt foreach" results(ms):avg:813 min:525 max:849
"STL for" results(ms):avg:187 min:185 max:204
"STL for 2" results(ms):avg:187 min:186 max:189
Run test for type:F5QListIiEvE
"New for" results(ms):avg:298 min:298 max:301
"Accumulate" results(ms):avg:299 min:297 max:316
"Qt foreach" results(ms):avg:343 min:342 max:355
"STL for" results(ms):avg:408 min:408 max:410
"STL for 2" results(ms):avg:298 min:297 max:302

Qt5 gcc 4.6
Run test for type:F7QVectorIiEvE
"New for" results(ms):avg:241 min:240 max:243
"Accumulate" results(ms):avg:187 min:186 max:189
"Qt foreach" results(ms):avg:195 min:193 max:197
"STL for" results(ms):avg:312 min:311 max:314
"STL for 2" results(ms):avg:190 min:189 max:202
Run test for type:FSt6vectorIiSaIiEEvE
"New for" results(ms):avg:186 min:186 max:188
"Accumulate" results(ms):avg:187 min:186 max:197
"Qt foreach" results(ms):avg:485 min:483 max:502
"STL for" results(ms):avg:186 min:186 max:189
"STL for 2" results(ms):avg:187 min:186 max:201
Run test for type:F5QListIiEvE
"New for" results(ms):avg:299 min:297 max:318
"Accumulate" results(ms):avg:299 min:297 max:303
"Qt foreach" results(ms):avg:350 min:348 max:357
"STL for" results(ms):avg:385 min:378 max:398
"STL for 2" results(ms):avg:305 min:299 max:326

А как же BOOST_FOREACH? Он копирования не делает и вообще устроен очень круто и правильно. Там дичайшая шаблонная магия с приенением sizeof и тернарного оператора, но учтены все возможные мелочи и работать он должен максимально быстро. За потробностями обращаться сюда: www.artima.com/cppsource/foreach.html
Добавил в тест BOOST_FOREACH.

Linux 32bit, gcc 4.6.3, Qt 4.8.1

"New for" results(ms): avg:100 min:97 max:108
"Accumulate" results(ms): avg:100 min:97 max:108
"Qt foreach" results(ms): avg:103 min:100 max:112
"Boost foreach" results(ms): avg:101 min:97 max:106
"STL for" results(ms): avg:130 min:126 max:151
"STL for 2" results(ms): avg:99 min:97 max:104

Run test for type: FSt6vectorIiSaIiEEvE
"New for" results(ms): avg:100 min:97 max:106
"Accumulate" results(ms): avg:98 min:95 max:104
"Qt foreach" results(ms): avg:258 min:249 max:290
"Boost foreach" results(ms): avg:100 min:97 max:107
"STL for" results(ms): avg:100 min:98 max:107
"STL for 2" results(ms): avg:99 min:97 max:108

Run test for type: F5QListIiEvE
"New for" results(ms): avg:100 min:98 max:110
"Accumulate" results(ms): avg:101 min:98 max:106
"Qt foreach" results(ms): avg:103 min:101 max:111
"Boost foreach" results(ms): avg:100 min:98 max:108
"STL for" results(ms): avg:131 min:126 max:146
"STL for 2" results(ms): avg:101 min:98 max:110
Ну вот, ничем не уступает range-based for. ЧТД
Как это — он поддерживается любым компилятором, включая очень древние версии gcc. А в студии до сих пор нет range based for, хотя на Going Native они заявили, что написали его реализацию.

Причем, она достаточно проста:
for ( TYPE x: range_expr ) {… }

заменяется на
auto&& range = range_expr; // perfect forwarding
for ( auto i = begin©, e = end©; i != e; ++i ) { // begin и end добавлены в c++11
TYPE x = *i;

}

По сути BOOST_FOREACH делает то же самое и поэтому выигрыша никакого быть и не может.
Да никто и не сомневался. Range-based for loop, вида:
for (T obj: range) { ... }

раскрывается компилятором в:
for (auto it = std::begin(range); it != std::end(range); ++it) {
    T obj = *it;
    ...
}

Это примерно то же самое, что делает BOOST_FOREACH.
Но из-за того, что BOOST_FOREACH является макросом, нельзя написать например так:
BOOST_FOREACH(std::pair<int, int> p, range) { ... }

Компилятор посчитает, что мы передаем макросу 3 аргумента, вместо 2-x.
One way to fix this is with a typedef.

std::map<int,int> m;
typedef std::pair<int,int> pair_t;

BOOST_FOREACH(pair_t p, m) // ...


Another way to fix it is to predeclare the loop variable:

std::map<int,int> m;
std::pair<int,int> p;

BOOST_FOREACH(p, m) // ...

Из документации буста.
Это понятно. Но такой способ сводит на нет то, ради чего BOOST_FOREACH и делался – лаконичность записи.
А я обычно так делаю: я никогда не использую неименованные типы, например, map, у меня всегда идет typedef: FileID2StreamID.

И в форыче я пишу так: BOOST_FOREACH(const FileID2StreamID::value_type& v, m) {… }
Вы не совсем точно описали. У меня выше более правильно, хотя у вас более аккурано отформатировано.

Претензии две:
1) скорее всего end вычисляется все же 1 раз, хотя да, если у контейнера end выполняется не за O(1) — сам дурак
2) вы не рассматриваете случай, когда range это не переменная, а выражение. Поэтому оно предварительно сохраняется в переменную, через perfect forwarding, чтобы не городить копирование там, где выражение возвращает, например, константную ссылку.
Спасибо за уточнение.
Вообще, интересующиеся могут посмотреть на пункт 6.5.4 стандарта. Там много всяких нюансов.
Ну в свете нового стандарта, Qt foreach может быть и действительно не нужен,
но следует не забывать что, написан он был задолго до того.

А так кроме очевидного вылета:
std::vector на foreach, ничего шокирующего не увидел,
реализация Qt foreach никогда и не претендовала на скорость,
она просто облегчала написание не критического итеративного кода.
Если мне нужно пройтись по контейнеру, в котором гарантированно не больше сотни-другой элементов,
неужели стоит пожертвовать чистотой кода ради, нескольких микросекунд?

Другое дело, в том что надо понимать когда не стоит этим злоупотребять.

В итоге считаю, ваш вывод крайне категоричным, и не вижу ничего страшного в умеренном использовании foreach по ситуации.
Вы какого рода софт пишете? Чтобы метод прохода циклом имел значение, нужно И чтобы элементов было дофига, И чтобы тело цикла было тривиальным. Во всех остальных случаях разница пренебрежимо мала, поэтому оптимизации типа «никогда не использовать qt foreach» являются преждевременными.
Это, скорее, не столько Саттера рекомендация, сколько Лававея (мейнтейнера STL в Microsoft). Он на GoingNative 2012 рассказывал, что применил там оптимизацию, избавившись от 1 лишней аллокации.
А по стандарту поведение двух этих вариантов не обязано отличаться.
Ну понятное дело, что будут отличаться. Это хорошая оптимизация, простая в реализации и использовании. К тому же ее уже вкатили в буст, так что оно распространится и нужно привыкать к ней. Она еще и безопаснее в плане ислкючений (что крайне важно).
> Это, скорее, не столько Саттера рекомендация, сколько Лававея (мейнтейнера STL в Microsoft).

Да, STL — клёвый чувак (здесь «STL» — инициалы упомянутого мейнтейнера STL'я). Было бы неплохо, если бы он книжку накатал.
разный временной масштаб графиков вводит в заблуждение.
Qt foreach удобен но внутри сделан странно. Зачем-то делается копия контейнера вместо обхода по итераторам. Что приводит к забавным курьезам: например у меня был кастомный QList указателей с удалением объектов в деструкторе. Так вот из-за копирования в foreach всё это естественно падало по выходе из цикла (по указателям объекты убивались).
Ну так же в современном программировании на С++ прямо сказано, что наследоваться от неполиморфных классов — это та ещё глупость.
Согласен, наследование в данном случае было ошибкой. Тем не менее поведение foreach нельзя назвать предсказуемым.
Sign up to leave a comment.

Articles