'What's the difference between std::vector and dynamic allocated array?

I have wrote two functions to compare the time cost of std::vector and dynamic allocated array

#include <iostream>
#include <vector>
#include <chrono>

void A() {
  auto t1 = std::chrono::high_resolution_clock::now();
  std::vector<float> data(5000000);
  auto t2 = std::chrono::high_resolution_clock::now();

  float *p = data.data();
  for (int i = 0; i < 5000000; ++i) {
    p[i] = 0.0f;
  }
  auto t3 = std::chrono::high_resolution_clock::now();
  std::cout << std::chrono::duration_cast<std::chrono::microseconds>(t2 - t1).count() << " us\n";
  std::cout << std::chrono::duration_cast<std::chrono::microseconds>(t3 - t2).count() << " us\n";
}

void B() {
  auto t1 = std::chrono::high_resolution_clock::now();
  auto* data = new float [5000000];
  auto t2 = std::chrono::high_resolution_clock::now();
  float *ptr = data;
  for (int i = 0; i < 5000000; ++i) {
    ptr[i] = 0.0f;
  }
  auto t3 = std::chrono::high_resolution_clock::now();
  std::cout << std::chrono::duration_cast<std::chrono::microseconds>(t2 - t1).count() << " us\n";
  std::cout << std::chrono::duration_cast<std::chrono::microseconds>(t3 - t2).count() << " us\n";
}

int main(int argc, char** argv) {
  A();
  B();
  return 0;
}

A() cost about 6000 us to initialize the vector, then 1400 us to fill zeros.

B() cost less than 10 us to allocate memory, then 5800 us to fill zeros.

Why their time costs have such a large difference?

compiler: g++=9.3.0

flags: -O3 -DNDEBUG

c++


Solution 1:[1]

QuickBench is a nice tool to compare different ways doing the same thing. https://quick-bench.com/q/p4ThYlVCa7VbO6vy6LEVVZ_0CVs

Your array example leaves a huge memory leak and QuickBench gives an error because of that.

The code I used (added two more variants):

static void Vector(benchmark::State& state) {
  // Code inside this loop is measured repeatedly
  for (auto _ : state) {
    std::vector<float> data(500000);
    float *p = data.data();
    for (int i = 0; i < 500000; ++i) {
      p[i] = 0.0f;
    }
    // Make sure the variable is not optimized away by compiler
    benchmark::DoNotOptimize(data);
  }
}
// Register the function as a benchmark
BENCHMARK(Vector);

static void VectorPushBack(benchmark::State& state) {
  for (auto _ : state) {
    std::vector<float> data;
    for (int i = 0; i < 500000; ++i) {
      data.push_back(0.0f);
    }
    benchmark::DoNotOptimize(data);
  }
}
BENCHMARK(VectorPushBack);

static void VectorInit(benchmark::State& state) {
  for (auto _ : state) {
    std::vector<float> data(500000, 0.0f);
    benchmark::DoNotOptimize(data);
  }
}
BENCHMARK(VectorInit);

static void Array(benchmark::State& state) {
  for (auto _ : state) {
    auto* data = new float [500000];
    float *ptr = data;
    for (int i = 0; i < 500000; ++i) {
      ptr[i] = 0.0f;
    }
    benchmark::DoNotOptimize(data);
    delete[] data;
  }
}
BENCHMARK(Array);

static void ArrayInit(benchmark::State& state) {
  for (auto _ : state) {
    auto* data = new float [500000]();
    benchmark::DoNotOptimize(data);
    delete[] data;
  }
}
BENCHMARK(ArrayInit);


static void ArrayMemoryLeak(benchmark::State& state) {
  for (auto _ : state) {
    auto* data = new float [500000];
    float *ptr = data;
    for (int i = 0; i < 500000; ++i) {
      ptr[i] = 0.0f;
    }
    benchmark::DoNotOptimize(data);
  }
}
//BENCHMARK(ArrayMemoryLeak);

Results: QuickBench results

All variants but the push_back one are almost the same in runtime. But the vector is much safer. It's very easy to forget to free the memory (as you demonstrated yourself).

EDIT: Fixed the mistake in the push_back variant. Thanks to t.niese and Scheff's Cat for pointing it out and fixing it.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1