воскресенье, 8 января 2017 г.

C++: Кто быстрее - слон или кит?

Не холивара ради, не нравоучений для, а просто для прикола. Небольшой кусочек сравнительной анатомии из жизни инопланетян-геев :) Ну или геев-инопланетян, кому как больше нравится.

Как вы думаете, мои хитиновые хвостатые, с рекурсивными челюстями, сошедшие с иллюстраций Гигера, друзья: какая реализация главного цикла из приведенных ниже быстрее выполняется? Нет, не трогая компилятор :)

Вариант I:

 // If max threads not specified, set default  
 std::thread threads[v_max_threads];  
 /*-------------Main Loop----------------*/  
      while (!std::cin.eof()) {  
           // Start all processing threads  
           for (auto& t: threads) {  
                t = std::thread([&]() {  
                     processdata();  
                     });  
           }  
           // Finish all threads  
           for (auto& t: threads) {  
                t.join();  
           }  
      }  
 /*-------------Main Loop----------------*/  
   

Вариант II

 // If max threads not specified, set default  
 std::vector<std::thread> threads;  
 threads.reserve(v_max_threads);  
 /*-------------Main Loop----------------*/  
      while (!std::cin.eof()) {  
           // Start all processing threads  
           for (unsigned int i = 0; i < v_max_threads; i++) {  
                threads.emplace_back(processdata);  
           }  
           // Finish all threads  
           for (auto& t: threads) {  
                t.join();  
           }  
           threads.clear();  
      }  
 /*-------------Main Loop----------------*/  
   

Вариант III

 // If max threads not specified, set default  
 std::thread* threads = new std::thread[v_max_threads];  
 /*-------------Main Loop----------------*/  
      while (!std::cin.eof()) {  
           // Start all processing threads  
           for (unsigned int i = 0; i < v_max_threads; i++) {  
                threads[i] = std::thread([&]() {  
                     processdata();  
                     });  
           }  
           // Finish all threads  
           for (unsigned int i = 0; i < v_max_threads; i++) {  
                threads[i].join();  
           }  
      }  
 /*-------------Main Loop----------------*/  
 delete [] threads;  
   

Сдаетесь? :)

Профайлер говорит (и сравнительный бенчмарк подтверждает): "скорость практически одинакова, плюс-минус миллисекунда".

При этом вариант II генерирует примерно на 1 Кб больше кода (в байтах). (Компилятор GCC 5.2 в режиме -std=c++11, разумеется, режим оптимизации -O3). Вариант III совпадает до байта по генерации кода с вариантом I, который в C++ возможен, однако опасен (компилятор не во всяком режиме вообще позволит собрать такое). При этом, вариант III еще небезопасен тем, что возможны утечки памяти, если произойдет исключение до оператора delete [] .

Соответственно, из вышеперечисленных соображений "правила пальца" выбираем предпочтительным вариант II. :)

PS. Предварительная оптимизация - корень всех зол, да. Любим, помним, скорбим. :)