Skip to main content

Vector of shared_ptr vs vector of objects

· 7 min read
Hreniuc Cristian-Alexandru

How much faster it is to have a vector of objects instead of pointers.

Compile the code from below and run it:

g++ -std=c++17 -O3 -g -o exe main.cpp

Ussage: ./exe iterations entries_per_vector no_tests [shared_ptr|object] [pre-allocated]

☰ dockerfile ⑂master_interactive* ♦ gcc --version
gcc (Ubuntu 9.2.1-9ubuntu2) 9.2.1 20191008

Not prealocated: - Storing a vector of objects is 2 times faster.

☰ shared_ptr_investigation ⑂master_interactive* ♦ ./exe 1000000 20 10 shared_ptr
max_entries_per_vector = 20 iterations = 1000000 no_tests = 10
Shared_ptr not prealocated
The time it took: 507
The time it took: 505
The time it took: 505
The time it took: 505
The time it took: 506
The time it took: 506
The time it took: 509
The time it took: 507
The time it took: 509
The time it took: 506
The size of the vector is: 10
☰ shared_ptr_investigation ⑂master_interactive* ♦ ./exe 1000000 20 10 object
max_entries_per_vector = 20 iterations = 1000000 no_tests = 10
Object not prealocated
The time it took: 243
The time it took: 243
The time it took: 243
The time it took: 243
The time it took: 243
The time it took: 243
The time it took: 243
The time it took: 243
The time it took: 242
The time it took: 243
The size of the vector is: 10

Prealocated vectors: - Storing a vector of objects is 3 times faster.

☰ shared_ptr_investigation ⑂master_interactive* ♦ ./exe 1000000 20 10 shared_ptr preallocated
max_entries_per_vector = 20 iterations = 1000000 no_tests = 10
Shared_ptr prealocated
The time it took: 435
The time it took: 431
The time it took: 432
The time it took: 432
The time it took: 431
The time it took: 431
The time it took: 432
The time it took: 432
The time it took: 432
The time it took: 433
The size of the vector is: 10
☰ shared_ptr_investigation ⑂master_interactive* ♦ ./exe 1000000 20 10 object preallocated
max_entries_per_vector = 20 iterations = 1000000 no_tests = 10
Object prealocated
The time it took: 140
The time it took: 141
The time it took: 141
The time it took: 139
The time it took: 140
The time it took: 140
The time it took: 139
The time it took: 139
The time it took: 141
The time it took: 141
The size of the vector is: 10

Code:

// Compile with this:
// g++ -std=c++17 -O3 -g -o exe main.cpp

#include <iostream>
#include <memory>
#include <vector>
#include <cstring>
#include <ctime>
#include <charconv>
#include <chrono>

using namespace std;

enum type { type1 = 0, type2 = 1 };

enum book_action_type { INSERT = 0, UPDATE = 1, DELETE = 2 };
struct DecimalPrice {
DecimalPrice(int mantisa, int exponent)
: mantisa(mantisa), exponent(exponent) {}
int mantisa;
int exponent;
};

class interf {
public:
virtual type get_type() const = 0;
virtual ~interf() = default;
::std::time_t timestamp{0};
};

class price_quantity : public interf {
type entry_type;
price_quantity() = delete;

public:
price_quantity(const type entry_type, ::std::time_t timestamp,
book_action_type action_type, DecimalPrice price_value,
unsigned int quantity_value)
: entry_type(entry_type), action_type(action_type),
price_value(price_value), quantity_value(quantity_value) {
this->timestamp = timestamp;
}
virtual ~price_quantity() override = default;
book_action_type action_type{book_action_type::UPDATE};
DecimalPrice price_value;
unsigned int quantity_value{0};

type get_type() const override { return entry_type; }

bool operator==(price_quantity const &price_quantity) const {
return quantity_value == price_quantity.quantity_value;
}
};

int shared_ptr_test(const int iterations, const int max_entries_per_vector) {
chrono::milliseconds result_time;
int sum = 0;
auto start_shared_ptr = chrono::high_resolution_clock::now();
for (auto iteration = 0; iteration < iterations; iteration++) {
vector<shared_ptr<price_quantity>> entries_vector;
for (auto entries = 0; entries < max_entries_per_vector; entries++) {
entries_vector.emplace_back(make_shared<price_quantity>(
static_cast<type>(max_entries_per_vector % 2), entries,
static_cast<book_action_type>(entries % 3),
DecimalPrice(entries % 10, entries % 5), iterations % (entries + 1)));
}
for (auto const &element : entries_vector) {
sum += element->quantity_value + element->price_value.mantisa -
element->timestamp - element->price_value.exponent +
static_cast<int>(element->action_type) +
static_cast<int>(element->get_type());
}
}
auto end_shared_ptr = chrono::high_resolution_clock::now();

result_time = chrono::duration_cast<chrono::milliseconds>(end_shared_ptr -
start_shared_ptr);
cout << "The time it took: " << result_time.count() << endl;
return sum;
}

int shared_ptr_preallocated_test(const int iterations,
const int max_entries_per_vector) {
chrono::milliseconds result_time;
int sum = 0;
auto start_shared_ptr = chrono::high_resolution_clock::now();
for (auto iteration = 0; iteration < iterations; iteration++) {
vector<shared_ptr<price_quantity>> entries_vector;
entries_vector.reserve(max_entries_per_vector);
for (auto entries = 0; entries < max_entries_per_vector; entries++) {
entries_vector.emplace_back(make_shared<price_quantity>(
static_cast<type>(max_entries_per_vector % 2), entries,
static_cast<book_action_type>(entries % 3),
DecimalPrice(entries % 10, entries % 5), iterations % (entries + 1)));
}
for (auto const &element : entries_vector) {
sum += element->quantity_value + element->price_value.mantisa -
element->timestamp - element->price_value.exponent +
static_cast<int>(element->action_type) +
static_cast<int>(element->get_type());
}
}
auto end_shared_ptr = chrono::high_resolution_clock::now();

result_time = chrono::duration_cast<chrono::milliseconds>(end_shared_ptr -
start_shared_ptr);
cout << "The time it took: " << result_time.count() << endl;
return sum;
}

int object_test(const int iterations, const int max_entries_per_vector) {
chrono::milliseconds result_time;
int sum = 0;
auto start_shared_ptr = chrono::high_resolution_clock::now();
for (auto iteration = 0; iteration < iterations; iteration++) {
vector<price_quantity> entries_vector;
for (auto entries = 0; entries < max_entries_per_vector; entries++) {
entries_vector.emplace_back(price_quantity(
static_cast<type>(max_entries_per_vector % 2), entries,
static_cast<book_action_type>(entries % 3),
DecimalPrice(entries % 10, entries % 5), iterations % (entries + 1)));
}
for (auto const &element : entries_vector) {
sum += element.quantity_value + element.price_value.mantisa -
element.timestamp - element.price_value.exponent +
static_cast<int>(element.action_type) +
static_cast<int>(element.get_type());
}
}
auto end_shared_ptr = chrono::high_resolution_clock::now();

result_time = chrono::duration_cast<chrono::milliseconds>(end_shared_ptr -
start_shared_ptr);
cout << "The time it took: " << result_time.count() << endl;
return sum;
}

int object_preallocated_test(const int iterations,
const int max_entries_per_vector) {
chrono::milliseconds result_time;
int sum = 0;
auto start_shared_ptr = chrono::high_resolution_clock::now();
for (auto iteration = 0; iteration < iterations; iteration++) {
vector<price_quantity> entries_vector;
entries_vector.reserve(max_entries_per_vector);
for (auto entries = 0; entries < max_entries_per_vector; entries++) {
entries_vector.emplace_back(price_quantity(
static_cast<type>(max_entries_per_vector % 2), entries,
static_cast<book_action_type>(entries % 3),
DecimalPrice(entries % 10, entries % 5), iterations % (entries + 1)));
}
for (auto const &element : entries_vector) {
sum += element.quantity_value + element.price_value.mantisa -
element.timestamp - element.price_value.exponent +
static_cast<int>(element.action_type) +
static_cast<int>(element.get_type());
}
}
auto end_shared_ptr = chrono::high_resolution_clock::now();

result_time = chrono::duration_cast<chrono::milliseconds>(end_shared_ptr -
start_shared_ptr);
cout << "The time it took: " << result_time.count() << endl;
return sum;
}

int main(int argc, char *argv[]) {
if (argc < 5) {
cout << "Ussage: ./exe iterations entries_per_vector no_tests "
"[shared_ptr|object] [pre-allocated]"
<< endl;
exit(1);
}
int iterations{0};

auto const & [ ptr, ec ] =
std::from_chars(argv[1], argv[1] + strlen(argv[1]), iterations);
if (ec != std::errc() || ptr != argv[1] + strlen(argv[1])) {
cout << "Couldn't convert to number." << endl;
exit(1);
}

int max_entries_per_vector{0};

auto const & [ ptr2, ec2 ] = std::from_chars(
argv[2], argv[1] + strlen(argv[2]), max_entries_per_vector);
if (ec2 != std::errc() || ptr2 != argv[2] + strlen(argv[2])) {
cout << "Couldn't convert to number." << endl;
exit(1);
}

int no_tests{0};

auto const & [ ptr3, ec3 ] =
std::from_chars(argv[3], argv[1] + strlen(argv[3]), no_tests);
if (ec3 != std::errc() || ptr3 != argv[3] + strlen(argv[3])) {
cout << "Couldn't convert to number." << endl;
exit(1);
}
cout << "max_entries_per_vector = " << max_entries_per_vector
<< " iterations = " << iterations << " no_tests = " << no_tests << endl;

bool pre_allocated{false};
if (argc == 6) {
pre_allocated = true;
}
bool use_shared_ptr{false};
if (string(argv[4]) == "shared_ptr") {
use_shared_ptr = true;
}

vector<int> sums;

if (!use_shared_ptr && !pre_allocated) {
cout << "Object not prealocated" << endl;
for (auto i = 0; i < no_tests; i++) {
sums.push_back(object_test(iterations, max_entries_per_vector));
}
}

if (!use_shared_ptr && pre_allocated) {
cout << "Object prealocated" << endl;
for (auto i = 0; i < no_tests; i++) {
sums.push_back(
object_preallocated_test(iterations, max_entries_per_vector));
}
}

if (use_shared_ptr && !pre_allocated) {
cout << "Shared_ptr not prealocated" << endl;
for (auto i = 0; i < no_tests; i++) {
sums.push_back(shared_ptr_test(iterations, max_entries_per_vector));
}
}
if (use_shared_ptr && pre_allocated) {
cout << "Shared_ptr prealocated" << endl;
for (auto i = 0; i < no_tests; i++) {
sums.push_back(
shared_ptr_preallocated_test(iterations, max_entries_per_vector));
}
}

cout << "The size of the vector is: " << sums.size() << endl;
return 0;
}