2016-11-24 4 views
4

Это своего рода на вопрос расширения от Efficient way to return a std::vector in c++эффективный способ вернуть СТЛ вектор по значению из функции

#include <cstdio> 
#include <vector> 
#include <chrono> 

std::vector<int> func1() { 
    std::vector<int> v; 
    v.reserve(1e6); 
    for (int i = 0; i < 1e6; i++) v.emplace_back(i); 
    return v; 
} 

std::vector<int> func2() { 
    std::vector<int> v; 
    v.reserve(1e6); 
    for (int i = 0; i < 1e6; i++) v.emplace_back(i); 
    return std::move(v); 
} 

int main() { 
    auto start1 = std::chrono::steady_clock::now(); 
    std::vector<int> v1 = func1(); 
    auto end1 = std::chrono::steady_clock::now(); 
    printf("%d\n", std::chrono::duration_cast<std::chrono::nanoseconds>(end1-start1).count()); 

    auto start2 = std::chrono::steady_clock::now(); 
    std::vector<int> v2 = func2(); 
    auto end2 = std::chrono::steady_clock::now(); 
    printf("%d\n", std::chrono::duration_cast<std::chrono::nanoseconds>(end2-start2).count()); 

/* 
    auto start3 = std::chrono::steady_clock::now(); 
    std::vector<int> v3 = v2; 
    auto end3 = std::chrono::steady_clock::now(); 
    printf("%d\n", std::chrono::duration_cast<std::chrono::nanoseconds>(end3-start3).count()); 
*/ 

    return 0; 
} 

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

Метод 3 является последовательно лучшим. Как эмулировать метод 3, когда я должен вернуться из функции? (Нет, я не могу пройти по ссылке)

Использование GCC 6.1.0

+0

а) Предотвратить даже мельчайшие количества нежелательных оптимизации компилятора. Как компилятор, я бы удалить, например. v3, поэтому третий из них самый быстрый - это не то, что вы хотите знать. – deviantfan

+0

Обратите внимание, что ваш тест ошибочен: вы также учитываете вставку в вектор, включая * перераспределение *. –

+0

И компилятор также может использовать движение для 'func1', его не нужно копировать. –

ответ

9

Метод 1 - вы используете Named Return Value Optimization (NRVO). На самом деле это самое лучшее, поскольку в оптимизированном коде не создаются никакие временные объекты. Если компилятор не может выполнить NRVO, он будет использовать семантику перемещения, как и в методе 2.

Метод 2 - вы эффективно закрываете NRVO и принудительно устанавливаете конструктор перемещения для назначения std :: vector. Таким образом, это не хорошо, как метод 1.

Метод 3 - вы, собственно, копируете вектор здесь, это на сегодняшний день - НАИБОЛЕЕ возможное исполнение. Но, поскольку вы копируете вектор за один проход (один большой кусок памяти, а не много emplaces) - вы получаете максимальную производительность, но это не тиражируется в вашем случае использования в методе 1 или 2.

Как работает NRVO ? Когда у вас есть только одно возвращаемое значение: в этом случае это std::vector<int> v, компилятор вообще не создает эту функцию внутри. Он создает неназванный вектор rvalue, который вы вернете, и передает ссылку на него на вашу функцию.

Нечто подобное будет происходить в оптимизированном коде:

std::vector<int> func1(std::vector<int>& hidden) { 
    hidden.emplace_back(stuff); 
    return; 
} 
+0

Я вижу это. Отбросить комментарий. Я согласен. Этот тест не имеет никакого дела в этой скамье, поскольку он буквально сравнивает яблоки с апельсинами. – WhozCraig