сравнение производительности между вектором и необработанным массивом c-style - PullRequest
0 голосов
/ 21 мая 2018

Я профилировал производительность между вектором c ++ и массивом c-style.Результат немного неожиданный, поскольку в литературе говорится, что производительность вектора должна быть очень близка к массиву необработанных данных, но это не так.Я сделал что-то не так в своем профилировании?

void getVector1(int n)
{
    if (n < 0)
    {
        throw std::invalid_argument(std::string("negative argument n:") + std::to_string(n));
    }

    auto tp1 = std::chrono::steady_clock::now();
    std::vector<int> ivec(n);
    int i = 0;
    for (auto& x : ivec)
    {
        x = ++i;
    }

    auto tp2 = std::chrono::steady_clock::now();
    std::chrono::duration<double, std::micro> dd = tp2 - tp1;

    printf("spend %6.2f us time to create: %d elements vector inside %s() at %s:%d \n", dd.count(), n, __func__, __FILE__, __LINE__);
}


void getVector2(int n)
{
    if (n < 0)
    {
        throw std::invalid_argument(std::string("negative argument n:") + std::to_string(n));
    }

    auto tp1 = std::chrono::steady_clock::now();
    auto pvec = new int[n];

    for (int i = 0; i < n; ++i)
    {
        pvec[i] = i;
    }

    auto tp2 = std::chrono::steady_clock::now();
    std::chrono::duration<double, std::micro> dd = tp2 - tp1;

    delete[] pvec;
    printf("spend %6.2f us time to create: %d elements vector inside %s() at %s:%d \n", dd.count(), n, __func__, __FILE__, __LINE__);
}



int main()
{
    int n = 10000000;
    getVector1(n);
    getVector2(n);

    return 0;
}

Код был скомпилирован с использованием g ++ с параметром -O3.

потратили 11946,38 т. Времени на создание: вектор 10000000 элементов внутри getVector1 () в testVectorSpeed.cpp

потратить 7298,66 т. времени на создание: вектор 10000000 элементов внутри getVector2 () в testVectorSpeed.cpp

1 Ответ

0 голосов
/ 21 мая 2018

Эта стоимость сводится к векторному обнулению памяти через ее распределитель.


Во-первых, всегда полезно использовать библиотеку сравнения, такую ​​как Google Benchmark , а не скользящую.ваш собственный бенчмаркинг.Мы можем использовать quick-bench.com , чтобы быстро использовать библиотеку.Перепишите код, чтобы использовать это:

// Just the benchmark code:
void getVector1(benchmark::State& state)
{
    int n = state.range(0);

    for (auto _ : state) {
      std::vector<int> ivec(n);

      // This is the same operation that you are doing
      std::iota(ivec.begin(), ivec.end(), 1);

      // We don't want the compiler to see that we aren't
      // using `ivec` and thus optimize away the entire
      // loop body
      benchmark::DoNotOptimize(ivec);
    }
}

void getArray1(benchmark::State& state)
{
    int n = state.range(0);

    for (auto _ : state) {
      auto pvec = new int[n];

      std::iota(pvec, pvec + n, 1);

      benchmark::DoNotOptimize(pvec);

      delete[] pvec;
    }
}

// Smaller number still reproduces it
BENCHMARK(getVector1)->Arg(10000);
BENCHMARK(getArray1)->Arg(10000);

benchmark OP's code

Нажмите на изображение для ссылки быстрого стенда

Путем небольшой игры мы можем обнаружить, что разница в стоимости - это просто стоимость обнуления памяти с помощью std::uninitialized_fill ( на быстром бенче ).

Действительно, если вместо этого мы используем распределитель, который оставляет неинициализированной память , то между этими двумя значениями нет ощутимой разницы:

// Allocator from https://stackoverflow.com/a/41049640
template <typename T, typename A = std::allocator<T>>
class default_init_allocator : public A {
    typedef std::allocator_traits<A> a_t;
public:
    // http://en.cppreference.com/w/cpp/language/using_declaration
    using A::A; // Inherit constructors from A

    template <typename U> struct rebind {
        using other =
            default_init_allocator
            <  U, typename a_t::template rebind_alloc<U>  >;
    };

    template <typename U>
    void construct(U* ptr)
        noexcept(std::is_nothrow_default_constructible<U>::value) {
        ::new(static_cast<void*>(ptr)) U;
    }

    template <typename U, typename...Args>
    void construct(U* ptr, Args&&... args) {
        a_t::construct(static_cast<A&>(*this),
            ptr, std::forward<Args>(args)...);
    }
};

void getVector1(benchmark::State& state)
{
    int n = state.range(0);

    for (auto _ : state) {
      std::vector<int, default_init_allocator<int>> ivec(n);

      std::iota(ivec.begin(), ivec.end(), 1);

      benchmark::DoNotOptimize(ivec);
    }
}

void getArray1(benchmark::State& state)
{
    int n = state.range(0);

    for (auto _ : state) {
      auto pvec = new int[n];

      std::iota(pvec, pvec + n, 1);

      benchmark::DoNotOptimize(pvec);

      delete[] pvec;
    }
}

BENCHMARK(getVector1)->Arg(10000);
BENCHMARK(getArray1)->Arg(10000);

benchmark uninitialized allocator

Нажмите на изображение для быстрой ссылки на стенд

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...