Performance Guidelines

Writing efficient modern C++ without sacrificing readability

Memory Management

// BAD: Frequent allocations
for (int i = 0; i < n; ++i) {
    auto obj = std::make_unique<ExpensiveObject>();  // n allocations!
    // use obj...
}

// GOOD: Reuse memory
auto obj = std::make_unique<ExpensiveObject>();
for (int i = 0; i < n; ++i) {
    obj->reset();  // Reuse allocated memory
    // use obj...
}

// BAD: Resizing vector in loop
std::vector<int> v;
for (int i = 0; i < 1000; ++i) {
    v.push_back(i);  // Multiple reallocations
}

// GOOD: Reserve capacity
std::vector<int> v;
v.reserve(1000);  // Single allocation
for (int i = 0; i < 1000; ++i) {
    v.push_back(i);
}

Move vs Copy

// BAD: Unnecessary copy
std::vector<BigObject> process(const std::vector<BigObject>& input) {
    std::vector<BigObject> result = input;  // Copy!
    // modify result...
    return result;
}

// GOOD: Pass by value + move
std::vector<BigObject> process(std::vector<BigObject> input) {
    // modify input in place...
    return input;  // NRVO or move
}

// Usage
auto result = process(std::move(data));  // Move, no copy

// BAD: Not moving when appropriate
auto ptr = std::make_unique<Resource>();
consume(ptr);  // Copy (if passed by value) or doesn't compile

// GOOD: Explicit move
consume(std::move(ptr));  // Transfer ownership

Algorithm Selection

// BAD: O(n^2) lookup in loop
for (const auto& item : items) {
    if (std::find(large_vector.begin(), large_vector.end(), item) != large_vector.end()) {
        // Found!
    }
}

// GOOD: Use unordered_set for O(1) lookup
std::unordered_set<Item> item_set(large_vector.begin(), large_vector.end());
for (const auto& item : items) {
    if (item_set.contains(item)) {
        // Found! O(1)
    }
}

// BAD: Sorting already sorted data
std::sort(data.begin(), data.end());  // O(n log n)

// GOOD: Check if sorted first
if (!std::is_sorted(data.begin(), data.end())) {
    std::sort(data.begin(), data.end());
}

Cache Efficiency

// BAD: Column-major access (cache unfriendly)
for (size_t col = 0; col < cols; ++col) {
    for (size_t row = 0; row < rows; ++row) {
        matrix[row][col] += 1;  // Jumps in memory
    }
}

// GOOD: Row-major access (cache friendly)
for (size_t row = 0; row < rows; ++row) {
    for (size_t col = 0; col < cols; ++col) {
        matrix[row][col] += 1;  // Sequential access
    }
}

// BAD: Vector of pointers (scattered memory)
std::vector<std::unique_ptr<Object>> objects;

// GOOD: Contiguous storage
std::vector<Object> objects;

// If polymorphism needed, consider:
std::vector<std::variant<Type1, Type2, Type3>> objects;