Веселые старты или C++ и STL: кто быстрее?

Постановка задачи


Нас интересует скорость различных стандартных инструментов C++ для выполнения однотипных операций над большим количеством элементов (циклы, алгоритмы STL, итераторы, указатели и т.д.). Для упрощения будем считать исходной задачу вычисления суммы большого количества целых чисел. (Ссылка для тех, кто не любит читать, а любит смотреть.)

Prepare


Для начала напишем код, который будет оценивать быстродействие цикла:

#include <iostream>
#include <sys/time.h>
#include <iomanip>

using namespace std;

const int COUNT = 1000 * 1000 * 1000;

int main () {
       struct timespec tm1, tm2;
       clock_gettime(CLOCK_REALTIME, &tm1);

 // Our code to estimate

       clock_gettime(CLOCK_REALTIME, &tm2);

       double t1 = 1000.0 * tm1.tv_sec + tm1.tv_nsec / (1000.0 * 1000);
       double t2 = 1000.0 * tm2.tv_sec + tm2.tv_nsec / (1000.0 * 1000);
       cout << "t=" << setprecision(5) << t2 -t1 << " ms" << endl;
       return 0;
};


Не очень точно, но зато просто и понятно.

При последовательном переборе элементов в STL самым быстрым контейнером является вектор, так как его элементы располагаются последовательно. Так что создадим вектор размером COUNT и заполним его (почти) случайными целыми значениями:


#include <vector>
#include <algorithm>
…
int RandomNumber () {
       static int s = 0;
       return (s++ % 100);
}
int main () {
       vector<int> vec(COUNT);
       generate(vec.begin(), vec.end(), RandomNumber);
       ...
       // Our code to estimate


Accumulate


Самым кошерным способом решения нашей задачи является использование функции accumulate:

#include <numeric>
       …
       // Our code to estimate
       accumulate(vec.begin(), vec.end(), 0, [](int sum, int i) {
               return sum + i;
       });


Компилируем и запускаем:

$ g++ -std=c++11 main.cpp -o test && ./test 
$ duration 349.29 mseconds

Не мало…

for_each


Попробуем for_each?

// Our code to estimate
int sum = 0;
for_each(vec.begin(), vec.end(), [&sum](int i) {
       sum += i;
});

Смотрим:

$ g++ -std=c++11 main.cpp -o test && ./test 
$ duration 417.64 mseconds

Интересно, что если использовать объект с состоянием, получится чуть быстрее: передача по ссылке в лямбду чуток нас тормозит:

class Sum {
       int sum;
public:
       Sum() : sum(0) {}
       inline void operator()(int i) {
               sum += i;
       }
       inline operator int() {
               return sum;
       }
};
…
       // Our code to estimate
       for_each(vec.begin(), vec.end(), Sum());


Барабанная дробь…

$ g++ -std=c++11 main.cpp -o test && ./test 
$ duration 371.64 mseconds

Еще можно сделать лямбду с состоянием и получить результат, сравнимый с accumulate, но игра не стоит свеч: результат все равно нас не устроит.

for


Вернемся к старым добрым циклам:

// Our code to estimate
int sum = 0;
for(auto i = vec.begin(); i != vec.end(); i++) {
       sum += *i;
};

Ну и что?

$ g++ -std=c++11 main.cpp -o test && ./test 
$ duration 520.66 mseconds

Ну, это понятно, почему, меняем немного код:

auto end = vec.end();
for(auto i = vec.begin(); i != end; i++) {

Снова компилируем и запускаем:

$ g++ -std=c++11 main.cpp -o test && ./test 
$ duration 403.68 mseconds

Уже лучше! Еще мы знаем, что префиксный инкремент чуть быстрее чем постфиксный:

for(auto i = vec.begin(); i != end; ++i) {

Это потому, что в операции постфиксного инткремента используется временный объект + копирование.

Компилируем и запускаем:

$ g++ -std=c++11 main.cpp -o test && ./test 
$ duration 327.05 mseconds

Лучший результат на сегодня! Но еще не вечер…
Почему цикл оказался чуть быстрее? В основном потому, что тут нет вызова функций, а также нет лишних копирований объекта (в нашем случае — целочисленного) суммы.

iterator or not


Откажемся от итераторов вообще:

for(int i = 0; i < COUNT; ++i) {
   sum += vec[i];
};

Компилируем и запускаем:

$ g++ -std=c++11 main.cpp -o test && ./test 
$ duration 110.52 mseconds

Вот это уже — результат.

Тут мы выиграли сразу на двух операциях — инкремент инта быстрее, чем оператор инкремента у итератора, а оператор разыменовывания медленнее, чем доступ по индексу.

Arrays


А что будет, если мы используем обычный массив? Точнее, будем брать элементы по смещению указателя, а не по индексу (замена вектора на массив бессмыслена — вектор хранит свои данные в массиве):

int *array = &vec[0];
for(int i = 0; i < COUNT; ++i) {
       sum += array[i];
};

И оно того стоило:

$ g++ -std=c++11 main.cpp -o test && ./test 
$ duration 81.219 mseconds

Доступ по указателю намного быстрее!
А если итерировать сразу по указателю, без индексов, выйдет значительно быстрее:

auto end = &vec[vec.size () - 1];
for(int *i = &vec[0]; i != end; ++i) {
   sum += *i;
};

$ g++ -std=c++11 main.cpp -o test && ./test 
$ duration 24.494 mseconds


Хардкор


Что еще мы можем сделать с циклом? Точно — «зарегистрировать»:

register int *array = &vec[0];
register int sum = 0;
for(register int i = 0; i != COUNT; ++i) {
       sum += array[i];
};

И что мы получаем?

$ g++ -std=c++11 main.cpp -o test && ./test 
$ duration 10.456 mseconds

Результат радует, однако следует помнить, что злоупотребление модификатором register сводит его на нет: компилятор перестает обращать на него внимание когда регистров не хватает, а их всегда не хватает) Так что вышеприведенный результат представляет скорее академический интерес, чем практический.

Можно по-эксперементировать с алгоритмами, например, такое изменение:

sum += array[i] + array[++i];

Ускорит наш код еще в двое:

$ g++ -std=c++11 main.cpp -o test && ./test 
$ duration 4.8562 mseconds

UPDATE: и приведет к неопределенному поведению! Спасибо alexac за замечание.
За рамки статьи также вышли оптимизации компилятора, мы их не используем. Но это — тема отдельной статьи

Вывод


Скорость работы нашего цикла увеличилась на порядок! При этом мы даже не думали о многопоточных алгоритмах (и алгоритмах вообще). Мы также не использовали ничего, кроме родного C++: действовали строго по стандарту и не использовали никаких сторонних библиотек.

image

Итак, если нам важна скорость нашего цикла — алгоритмы STL не подходят. Итераторы гибки, но не слишком быстры — быстрее всего доступ осуществляется через обычные указатели.
AdBlock has stolen the banner, but banners are not teeth — they will be back

More
Ads

Comments 43

    +33
    Сравнивать быстродействие кода без оптимизаций? Ну ок.
      +1
      +1. Тема интересна, результаты бесполезны. Пожалуй, повторю эксперимент сам. Один раз столкнулся при оптимизации хотспота с тем, что обход вектора с помощью итератора заметно медленнее, чем по индексу (в релизе со вмеми оптимизациями).
        0
        обход вектора с помощью итератора заметно медленнее, чем по индексу

        Скорее всего это связано с тем, что Вы использовали постинкремент, а надо — преинкремент. Пруф.
          0
          Не-а, всегда пишу прединкремент. После проделанного вчера эксперимента (мой пост ниже) я понял, что дело всего-навсего было в том, что я не догадался предвычислить end() до начала цикла, а size() — догадался. Это единственный фактор, насколько я могу судить по результатам исследования.

          А вот разницы между +it и it++ как раз и нет в этом конкретном тесте.
        0
        Ага, автор узнает много нового когда скомпилит все варианты с -O2 и посмотрит на дизасм :)
        +1
        Повторите пожалуйста те же тесты с _SECURE_SCL=0 и _HAS_ITERATOR_DEBUGGING=0
          +4
          А какое отношение к gcc имеют VC'шные костыли?
            +3
            Вы правы, никакого. Просто автоматическая реакция на «STL тормозит».
          +20
          Только вот во всех этих сравнениях есть одна большая проблема: gcc по умолчанию не включает оптимизации, а если собирать с -O2, то всё становится совсем иначе:

          Берем уровень -O3 и сравниваем accumulate с самым хардкорным вариантом:
          aleksey@aleksey ~/d/p/bench> ./test 
          t=0.00024414 ms
          aleksey@aleksey ~/d/p/bench> ./test2 
          t=0.00024414 ms
          


          А если не видно разницы, то зачем платить больше?
            +1
            Даже при использовании -O1 у меня первый вариант работает с той же скоростью что и последний.
              0
              Кстати, clang тоже дают точно такую же скорость, хотя на -O1 он уже сливает.
              +1
              что-то подозрительно, у меня такое же значение выдало с пустым кодом…
                0
                Теоретически возможно, что, с такой агрессивной оптимизацией, оно просто заполнило массив и посчитало сумму ещё на этапе компиляции.
                  +5
                  а сумма где-нибудь используется? скорее она и не считалась, оптимизатор просто удалил ненужный цикл
              –11
              Все равно хорошая статья, хоть и автор не включил оптимизации в компиляторе. Наглядность улучшений от элементарных изменений просто на шикарном уровне! Как будто «Жемчужины программирования» перечитал, где автор упорно «срезает жирок» с циклов.
                +1
                По-моему, все, что здесь описано, и так более-менее очевидно, без подробностей, конечно. На цифры взглянуть всегда интересно — за это плюс статье.
                +4
                Можно еще префетча немного добавить и суммировать «рядком» в несколько переменных — быть может векторизация прилетит.
                В свое время (ну лет 15 назад, когда DDR память появилась) AMD с Интелом выпускали километры документации о том как же быстро пройтись по массиву и получить весь заявленный в рекламе bandwidth. Ой не простое это было дело.
                С тех пор подросло целое поколение…
                  –6
                  Мне кажется, для хорошего программиста должно быть очевидно, что за любой «сахар» (улучшение читабельности, возможность повторого использования, универсальность, дополнительную абстракцию и т.п.) требуется заплатить как раз-таки скоростью выполнения.
                  Хочешь скорости работы программы — пиши в машинных кодах, а если хочешь скорости своей работы (и тех, кто будет потом править твой код), то пиши на максимально высокоуровневом языке =)

                  И это все, как и в случае со статьей, не учитывает оптимизацию компилятора.
                    +14
                    Отсутствие учета оптимизации компилятора вообще, на мой взгляд, сводит всю пользу статьи на нет.

                    Некоторый «сахар» C++ достается именно что бесплатно. При условии, что компилятор разберется, как перевести этот сахар в эффективный машинный код. Таких случаев немало, и особенно их немало с STL. В крайнем случае «платой за сахар» будет некоторое замедление компиляции.

                    И написание программы на ассемблере ничуть не уступает (по скорости ее работы) написанию ее в машинных кодах. Поэтому в машинных кодах никто не пишет еще с 50х-60х гг прошлого века.
                      –5
                      польза статьи, скорее всего, в том, чтобы показать необходимость знать детали «работы» языка. Ведь не всегда есть возможность использовать оптимизацию компилятора (иначе эта опция был бы включена по умолчанию), да и не всегда стоит доверять компилятору (любое доверие потом может стать боком).
                        +10
                        польза статьи, скорее всего, в том, чтобы показать необходимость знать детали «работы» языка.

                        Вероятно, я невнимательно прочитал, потому что я не заметил описания каких-то деталей работы языка.

                        иначе эта опция был бы включена по умолчанию

                        Я думал, что оптимизации не включены по-умолчанию, чтобы сделать отладку проще и компиляцию быстрее (gcc docs), или другими словами сделать процесс разработки более удобным, разве нет?
                    +4
                    Без включенной оптимизации компилятора все эти результаты несут информацию о скорости работы экзотически скомпилированных (без оптимизации) программ, то есть для практического применения пользы ноль.

                    Поначалу я удивился, почему помогает ключевое слово «register», ведь даже в VC5.0 компилятор разобрался бы, как оптимизировать такой простой цикл, и без «register». Сам бы на регистрах что надо разместил. Тем более регистров процессора более чем достаточно для такого простого цикла.

                    А еще автор не учел поведение кэша процессора. Измеренное быстродействие цикла может зависеть от того, что исполнял процессор перед этим циклом.
                      +1
                      Тот же тест, винда, MSVC 2013 (toolset v120) x86, vector(200 * 1000 * 1000). Релиз + /O2 (дефолтные релизные настройки):

                      accumulate: 46 ms
                      for_each: 118 ms
                      for(int i: vec): 118 ms // Обидно, да?
                      for (auto i = vec.begin(); i != vec.end(); i++): 47 ms
                      for (auto i = vec.begin(); i != end; ++i): 46 ms
                      for (size_t i = 0; i < vec.size(); ++i) sum += vec[i];: 118 ms
                      for (size_t i = 0; i < size; ++i) sum += vec[i];: 47 ms // Ага, понятно, почему range-based for медленный: компилятор, как и в этом случае, не кэширует" vec.size() и вычисляет его каждый раз.
                      int *array = &vec[0]; for(size_t i = 0; i < size; ++i) sum += array[i];: 47 ms
                      auto end = &vec[vec.size() — 1]; for (int *i = &vec[0]; i != end; ++i) sum += *i;: 47 ms
                      register: 47 ms // Ч. т. д.
                      sum += array[i] + array[++i]: 65 ms // Интересно…
                        +1
                        P. S. Я расстроен, что компилятор не умеет оптимизировать вызов const-метода std::vector::size (ну или end). Неужели это так сложно — определить, что на векторе после generate не вызываются никакие не-const методы? И почему бы в векторе не сделать размер переменной, чтоб его просто вернуть, а не вычислять каждый раз?
                          –1
                          Я думаю, тут в первую очередь косяк разработчиков STL (P.J. Plauger). Реализация vector::size вычисляет разность между двумя переменными (this->_Mylast — this->Myfirst). Вероятно, время тратится на доступ к этим указателям в памяти.

                          ассемблерный код получается следующим (VS2013, Release, x86)
                          mov eax,dword ptr[esp+0Ch]
                          mov esi,dword ptr[esp+8]
                          sub eax,esi
                          sar eax,2


                          Во как. 4 команды, 2 доступа к памяти, и кроме вычитания еще имеем сдвиг. Сейчас попробую сделать свой «вектор» и посмотреть, что накомпилирует компилятор при простом возврате члена данных класса.
                            +1
                            Впрочем, к кэшированию все это не относится. Только что посмотрел, как компилятор компилирует цикл for(size_t i=0; i<v.size(); i++) a+=v[i]. Там что-то невообразимое получилось! Очень сложные конструкции, использование SSE и регистров xmm, явные попытки частично размотать цикл. Еще надо поразбираться.
                            –1
                            Только что попробовал сделать свой «вектор», у которого размер хранился бы в переменной-члене класса. Компилятор прекрасно кэширует значение этой переменной на регистре. Вот пример кода:
                            #include <iostream>
                            #include <vector>
                            
                            class kektor
                            {
                            public:
                                kektor(size_t nsize) : m_size(nsize) {}
                            
                                size_t size(void) const throw() { return m_size; }
                            private:
                                size_t m_size;
                            };
                            
                            void print_kek(const kektor& v)
                            {
                                for (size_t i = 0; i < v.size(); i++)
                                {
                                    std::cout << i<<'\n';
                                }
                            }
                            
                            
                            void main(void)
                            {
                                std::cout << "Hello World!\n";
                                size_t sz;
                                std::cin >> sz;
                                kektor v(sz);
                                print_kek(v);
                            }
                            

                            Ассемблерный код для функции print_kek:
                            xor         edi,edi
                            test        esi,esi
                            je          endloop
                            loop:
                            push        edi 
                            call        std::basic_ostream<char,std::char_traits<char> >::operator<<
                            mov         ecx,eax
                            call        std::operator<<<std::char_traits<char> >
                            inc         edi
                            cmp         edi,esi
                            jb          loop
                            endloop: ...
                            

                            Так что да. Неприятно. Недосмотр авторов STL. Оптимизации компилятора доверяй, но проверяй.
                              +1
                              А почему вы уверены, что в строке sum += vec[i] вызывается const метод? Кажется, если сам vec не const, то из const и не const метода будет выбран как раз последний.
                                0
                                Это не так просто: нужно знать, что значение size() не меняется по ходу выполнения цикла, что далеко не факт, независимо от const, может size() текущее время возвращает — откуда компилятору знать. Короче значение size() может косвенно зависеть от операций в цикле и компилятору не так то просто убедиться в обратном.
                                  0
                                  Да, вы правы, const тут не помогает.
                                    0
                                    Вообще, по идее, компилятору реализация size() должна быть видна, и компилятор вывести чистоту этого метода может.

                                    Ждём атрибут [[pure]] или рассчитываем на сознательность библиотек, поставляемых с компилятором.
                                +1
                                Поиграем в игру «найдите 5 отличий»

                                Возьмем код
                                #include <vector>
                                #include <algorithm>
                                using namespace std;
                                
                                int foo (vector<int> vec) {
                                       return accumulate(vec.begin(), vec.end(), 0, [](int sum, int i) {
                                               return sum + i;
                                       });
                                }
                                
                                int bar (vector<int> vec) {
                                       const int COUNT = vec.size();
                                       register int *array = &vec[0];
                                       register int sum = 0;
                                       for(register int i = 0; i != COUNT; ++i) {
                                           sum += array[i];
                                       };
                                       return sum;
                                }
                                
                                Скомпилируем
                                Сравним результат
                                foo(std::vector<int, std::allocator<int> >):
                                	mov	rcx, QWORD PTR [rdi+8]
                                	mov	rdx, QWORD PTR [rdi]
                                	xor	eax, eax
                                	cmp	rdx, rcx
                                	je	.L4
                                .L3:
                                	add	eax, DWORD PTR [rdx]
                                	add	rdx, 4
                                	cmp	rcx, rdx
                                	jne	.L3
                                	rep ret
                                .L4:
                                	rep ret
                                bar(std::vector<int, std::allocator<int> >):
                                	mov	rcx, QWORD PTR [rdi]
                                	mov	rax, QWORD PTR [rdi+8]
                                	sub	rax, rcx
                                	sar	rax, 2
                                	test	eax, eax
                                	mov	esi, eax
                                	je	.L10
                                	xor	edx, edx
                                	xor	eax, eax
                                .L9:
                                	add	eax, DWORD PTR [rcx+rdx*4]
                                	add	rdx, 1
                                	cmp	esi, edx
                                	jne	.L9
                                	rep ret
                                .L10:
                                	xor	eax, eax
                                	ret
                                
                                  +2
                                  Странно, что вас ругали за отсутствие оптимизаций, но пропустили CLOCK_REALTIME. Эти часы нельзя использовать для любых измерений производительности: документация явно говорит, что они могут изменяться. Специально для измерения производительности есть CLOCK_MONOTONIC_RAW (CLOCK_MONOTONIC для не‐linux) и CLOCK_PROCESS_CPUTIME_ID. На простоту кода изменение константы на правильную не влияет никак.
                                    0
                                    Отличное замечание, вы безусловно правы
                                    +6
                                    Мне одному кажется, что последний пример

                                    sum += array[i] + array[++i];
                                    


                                    Это явное UB? Я не удивляюсь, что там прирост скорости вдвое, там компилятор имел право вообще пол системы снести во время трансляции.

                                    Упрощенный пример и предупреждение: codepad.org/U2jK5eIo
                                    • UFO just landed and posted this here
                                        +5
                                        Нет, имеется ввиду, что i изменяется и читается в одном выражении. Например, порядок вычисления array[i] и array[++i] не определен, например, он может сначала посчитать array[++i] и тогда программа дважды обратится к одному и тому же элементу массива, что не правильно.

                                        А вообще вот, что пишет стандарт:
                                        If a side effect on a scalar object is unsequenced relative to either another side effect on the same scalar object or a value computation using the value of the same scalar object, the behavior is undefined.

                                        В данном случае, как я понимаю, сайд эффект от ++i «unsequenced» с использованием i в array[i].
                                          +1
                                          Да, вы совершенно правы, так как (§1.9/15)
                                          Except where noted, evaluations of operands of individual operators and of subexpressions of individual expressions are unsequenced
                                        0
                                        Да, вы правы, позор на мои седины,
                                        добавил в статью UPDATE, спасибо!
                                        +1
                                        auto end = &vec[vec.size () — 1];
                                        for(int *i = &vec[0]; i != end; ++i) {
                                        sum += *i;
                                        };

                                        А последний элемент не суммируем?
                                          –2
                                          да ну его
                                          0
                                          Решил проверить сам и заодно узнать, насколько хорошо работают оптимизации. Во-первых не сошлись времена даже близко — у меня «регистровая» версия выдаёт 660 ms (а не 10 как у автора). Во-вторых оптимизации всё уравнивают, что бы оптимизации цикл совсем не выкинули — добавил вывод суммы в консоль.
                                          g++ --version
                                          cat /proc/cpuinfo | grep "model name" | head -n1
                                          
                                          g++ -std=c++11 test_for.cpp -o test_for
                                          g++ -std=c++11 -O3 test_for.cpp -o test_forO3
                                          
                                          g++ -std=c++11 test_acc.cpp -o test_acc
                                          g++ -std=c++11 -O3 test_acc.cpp -o test_accO3
                                          
                                          g++ -std=c++11 test_reg.cpp -o test_reg
                                          g++ -std=c++11 -O3 test_reg.cpp -o test_regO3
                                          
                                          echo "No O; for,acc,reg:"
                                          ./test_for
                                          ./test_acc
                                          ./test_reg
                                          
                                          echo "-O3; for,acc,reg:"
                                          ./test_forO3
                                          ./test_accO3
                                          ./test_regO3
                                          

                                          Результат:
                                          g++ (Ubuntu 4.9.2-0ubuntu1~14.04) 4.9.2
                                          Copyright (C) 2014 Free Software Foundation, Inc.
                                          This is free software; see the source for copying conditions.  There is NO
                                          warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
                                          
                                          model name      : Intel(R) Core(TM) i7-4930K CPU @ 3.40GHz
                                          No O; for,acc,reg:
                                          t=11341 ms, sum=-2039607552
                                          t=8141.1 ms, sum=-2039607552
                                          t=660.89 ms, sum=-2039607552
                                          -O3; for,acc,reg:
                                          t=231.37 ms, sum=-2039607552
                                          t=231.31 ms, sum=-2039607552
                                          t=232.29 ms, sum=-2039607552
                                          

                                          test_reg.cpp
                                          #include <iostream>
                                          #include <sys/time.h>
                                          #include <iomanip>
                                          #include <vector>
                                          #include <algorithm>
                                          #include <numeric>
                                          
                                          using namespace std;
                                          
                                          const int COUNT = 1000 * 1000 * 1000;
                                          int RandomNumber () {
                                              static int s = 0;
                                              return (s++ % 100);
                                          }
                                          
                                          int main () {
                                              vector<int> vec(COUNT);
                                              generate(vec.begin(), vec.end(), RandomNumber);
                                              
                                              struct timespec tm1, tm2;
                                              clock_gettime(CLOCK_REALTIME, &tm1);
                                              
                                              register int *array = &vec[0];
                                              register int sum = 0;
                                              for(register int i = 0; i != COUNT; ++i) {
                                          	sum += array[i];
                                              };
                                              
                                              clock_gettime(CLOCK_REALTIME, &tm2);
                                              
                                              double t1 = 1000.0 * tm1.tv_sec + tm1.tv_nsec / (1000.0 * 1000);
                                              double t2 = 1000.0 * tm2.tv_sec + tm2.tv_nsec / (1000.0 * 1000);
                                              cout << "t=" << setprecision(5) << t2 -t1 << " ms, sum=" <<sum<< endl;
                                              return 0;
                                          };
                                          



                                          test_acc.cpp
                                          #include <iostream>
                                          #include <sys/time.h>
                                          #include <iomanip>
                                          #include <vector>
                                          #include <algorithm>
                                          #include <numeric>
                                          
                                          using namespace std;
                                          
                                          const int COUNT = 1000 * 1000 * 1000;
                                          int RandomNumber () {
                                              static int s = 0;
                                              return (s++ % 100);
                                          }
                                          
                                          int main () {
                                              vector<int> vec(COUNT);
                                              generate(vec.begin(), vec.end(), RandomNumber);
                                              
                                              struct timespec tm1, tm2;
                                              clock_gettime(CLOCK_REALTIME, &tm1);
                                          
                                              int sum;    
                                              sum=accumulate(vec.begin(), vec.end(), 0, [](int sum, int i) {
                                          	return sum + i;
                                              });
                                              
                                              clock_gettime(CLOCK_REALTIME, &tm2);
                                              
                                              double t1 = 1000.0 * tm1.tv_sec + tm1.tv_nsec / (1000.0 * 1000);
                                              double t2 = 1000.0 * tm2.tv_sec + tm2.tv_nsec / (1000.0 * 1000);
                                              cout << "t=" << setprecision(5) << t2 -t1 << " ms, sum=" <<sum<< endl;
                                              return 0;
                                          };
                                          



                                          test_for.cpp
                                          #include <iostream>
                                          #include <sys/time.h>
                                          #include <iomanip>
                                          #include <vector>
                                          #include <algorithm>
                                          #include <numeric>
                                          
                                          using namespace std;
                                          
                                          const int COUNT = 1000 * 1000 * 1000;
                                          int RandomNumber () {
                                              static int s = 0;
                                              return (s++ % 100);
                                          }
                                          
                                          int main () {
                                              vector<int> vec(COUNT);
                                              generate(vec.begin(), vec.end(), RandomNumber);
                                          
                                              struct timespec tm1, tm2;
                                              clock_gettime(CLOCK_REALTIME, &tm1);
                                          
                                              int sum = 0;
                                              for(auto i = vec.begin(); i != vec.end(); ++i) {
                                          	sum += *i;
                                              };
                                          
                                              clock_gettime(CLOCK_REALTIME, &tm2);
                                          
                                              double t1 = 1000.0 * tm1.tv_sec + tm1.tv_nsec / (1000.0 * 1000);
                                              double t2 = 1000.0 * tm2.tv_sec + tm2.tv_nsec / (1000.0 * 1000);
                                              cout << "t=" << setprecision(5) << t2 -t1 << " ms, sum=" <<sum<< endl;
                                              return 0;
                                          };
                                          
                                          

                                        Only users with full accounts can post comments. Log in, please.