Skip to content

Performance, Debugging and Build Tools Interview Questions for C++

24 Performance, Debugging and Build Tools questions with answers for c++ roles — 7 easy, 12 medium, 5 hard.

About Performance, Debugging and Build Tools

Learn the professional toolchain: CMake, static and dynamic libraries, compiler flags, GoogleTest, gdb, sanitizers, Valgrind and perf. Understand cache locality and profiling-driven optimisation, and practise debugging crashes, latency spikes and slow builds the way experienced C++ engineers do in production teams.

Sheet progressStart practicing
3 QUESTIONS FREESign up to unlock the full question bank and track your progress.Unlock every question and answer, then continue practising company-wise and package-wise.
3 questions free Unlock the rest in seconds
01

What is CMake and why is it used for C++ projects?

Save to ▾
Show answer
CMake is a cross-platform build system generator. You describe targets, source files, include paths and dependencies in CMakeLists.txt, and CMake generates Makefiles, Ninja files or Visual Studio projects. Modern CMake is target based, using target_link_libraries and target_include_directories so settings flow to dependents. It integrates with package managers such as vcpkg and Conan and with CTest for running tests.
02

What is the difference between a static library and a dynamic library?

Save to ▾
Show answer
A static library, .a or .lib, is copied into the executable at link time, giving a self-contained binary but larger size and a rebuild for every library update. A dynamic library, .so or .dll, is loaded at run time and shared between processes, saving memory and allowing updates without relinking, but it needs the right version present on the machine and has a small call overhead.
03

Which unit testing frameworks are common in C++?

Save to ▾
Show answer
GoogleTest with GoogleMock is the most widely used; it provides TEST and TEST_F macros, assertions such as EXPECT_EQ and ASSERT_TRUE, fixtures and mocking. Catch2 and doctest are lightweight, header-friendly alternatives with natural syntax. Tests are usually registered with CTest in CMake and run in CI on every merge. Good tests are fast, independent and cover edge cases like empty input and overflow.
04

What are header guards and #pragma once?

Save to ▾
Show answer
Including the same header twice in a translation unit would redefine classes and cause errors. Header guards wrap the file in #ifndef MY_HEADER_H, #define MY_HEADER_H and #endif, so the second inclusion is skipped. #pragma once does the same in one line and is supported by all major compilers, though it is not in the standard. Most modern projects use #pragma once for simplicity.
05

Write a minimal modern CMakeLists.txt for an app with a library and tests.

Save to ▾
Show answer
cmake_minimum_required(VERSION 3.20) project(Inventory LANGUAGES CXX) set(CMAKE_CXX_STANDARD 20) set(CMAKE_CXX_STANDARD_REQUIRED ON) add_library(core src/stock.cpp src/order.cpp) target_include_directories(core PUBLIC include) target_compile_options(core PRIVATE -Wall -Wextra -Wpedantic) add_executable(app src/main.cpp) target_link_libraries(app PRIVATE core) enable_testing() find_package(GTest REQUIRED) add_executable(core_tests tests/stock_test.cpp) target_link_libraries(core_tests PRIVATE core GTest::gtest_main) add_test(NAME core_tests COMMAND core_tests)
06

Measure how long a function takes to run in C++.

Save to ▾
Show answer
#include <chrono> #include <iostream> template <typename F> long long timeMs(F&& f) { auto start = std::chrono::steady_clock::now(); f(); auto end = std::chrono::steady_clock::now(); return std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count(); } // usage // auto ms = timeMs([] { heavyWork(); }); // Use steady_clock, not system_clock, because it never jumps backwards.
07

Show the compiler commands to build with strict warnings and sanitizers enabled.

Save to ▾
Show answer
# Strict warnings, treat them as errors g++ -std=c++20 -Wall -Wextra -Wpedantic -Werror -g main.cpp -o app # Address + undefined behaviour sanitizers g++ -std=c++20 -g -O1 -fno-omit-frame-pointer \ -fsanitize=address,undefined main.cpp -o app_asan # Thread sanitizer (separate build) clang++ -std=c++20 -g -O1 -fsanitize=thread main.cpp -o app_tsan # Static analysis clang-tidy main.cpp -- -std=c++20
08

What do compiler optimisation levels like -O0, -O2, -O3 and -Os mean?

Save to ▾
Show answer
-O0 disables optimisation and is best for debugging. -O1 and -O2 enable progressively more optimisations such as inlining, constant propagation and loop optimisations; -O2 is the common release default. -O3 adds aggressive vectorisation and inlining, which may increase code size. -Os optimises for size. Link-time optimisation with -flto optimises across translation units, and -march=native tunes for the current CPU.
09

What is cache locality and why does it matter for performance?

Save to ▾
Show answer
CPUs load memory in cache lines, usually 64 bytes, and access to L1 cache takes a few cycles while main memory takes hundreds. Code that walks memory sequentially, like iterating a std::vector, uses every byte loaded and lets the prefetcher help. Pointer-heavy structures like linked lists jump around memory and cause cache misses. That is why a vector often beats a list even for insert-heavy workloads.
10

Which profiling tools do you use for C++ and what does each show?

Save to ▾
Show answer
On Linux, perf samples the CPU and shows hot functions and call graphs, often visualised as flame graphs. Valgrind callgrind counts instructions precisely but runs slowly, and massif or heaptrack profile memory. gprof is older and needs instrumented builds. Intel VTune and AMD uProf show cache misses and pipeline stalls. On Windows, the Visual Studio profiler covers CPU and memory.
11

What are AddressSanitizer, UndefinedBehaviorSanitizer and ThreadSanitizer?

Save to ▾
Show answer
They are compiler instrumentation tools in GCC and Clang. ASan, -fsanitize=address, detects out-of-bounds access, use-after-free, double free and leaks with about 2x slowdown. UBSan, -fsanitize=undefined, catches signed overflow, invalid shifts and null dereferences. TSan, -fsanitize=thread, detects data races but cannot be combined with ASan. Running tests under sanitizers in CI catches bugs long before production.
12

A C++ server crashes with a segmentation fault in production. How do you debug it?

Save to ▾
Show answer
Make sure core dumps are enabled with ulimit -c unlimited or systemd-coredump, and keep debug symbols for release builds. Load the core with gdb binary core, run bt to see the stack trace, then inspect frames and variables. If the stack looks corrupted, reproduce in staging with AddressSanitizer. I would also check logs around the crash time and recent deployments, then add a regression test with the fix.
13

A full build of your C++ project takes 40 minutes. How would you reduce it?

Save to ▾
Show answer
I would measure with Clang -ftime-trace or build tooling to find the slowest files and headers. Then reduce header dependencies using forward declarations and the pimpl idiom, use Ninja for parallel builds, enable ccache or sccache, add precompiled headers for stable heavy headers, and split huge translation units. Unity builds and distributed compilation also help, and CI can cache build outputs.
14

Your application is slow and you are asked to optimise it. Where do you start?

Save to ▾
Show answer
I start by measuring, not guessing. I define a target, such as p99 under 50 ms, create a repeatable benchmark, and profile with perf to find where time actually goes. Then I fix the biggest hotspot first, usually algorithmic complexity, unnecessary copies, allocations or I/O, and re-measure after each change. I keep correctness tests running and stop when the target is met.
15

A binary runs on your laptop but fails on the server with error while loading shared libraries. What is wrong?

Save to ▾
Show answer
The program depends on a shared library that is missing or in a different version on the server, or the loader cannot find it. I would run ldd on the binary to see unresolved libraries, then install the right package, set the rpath at link time, or ship the library alongside. A newer glibc or libstdc++ on the build machine is a common cause; build inside a container matching production, or link statically where allowed.
16

List the gdb commands you would use to debug a crash.

Save to ▾
Show answer
g++ -g -O0 main.cpp -o app # build with debug symbols gdb ./app # or: gdb ./app core (gdb) run arg1 arg2 # start the program (gdb) bt # backtrace after the crash (gdb) frame 2 # select a stack frame (gdb) info locals # show local variables (gdb) print ptr->size # inspect an expression (gdb) break order.cpp:42 # set a breakpoint (gdb) watch total # stop when a variable changes (gdb) next / step / continue # step over, step into, resume
17

Write a GoogleTest unit test for a function, including an edge case.

Save to ▾
Show answer
#include <gtest/gtest.h> #include <stdexcept> #include <vector> double average(const std::vector<int>& v) { if (v.empty()) throw std::invalid_argument("empty"); long long s = 0; for (int x : v) s += x; return static_cast<double>(s) / v.size(); } TEST(AverageTest, ComputesMean) { EXPECT_DOUBLE_EQ(average({2, 4, 6}), 4.0); } TEST(AverageTest, ThrowsOnEmpty) { EXPECT_THROW(average({}), std::invalid_argument); }
18

How do you prioritise when a critical bug and a feature deadline arrive at the same time?

Save to ▾
Show answer
Situation: a customer-facing crash was reported two days before a feature demo. Task: handle both without dropping quality. Action: I informed my lead immediately, estimated both tasks, and we agreed the crash came first because it affected users. I fixed and released it in half a day, then cut one optional part of the feature after agreeing with the product owner. Result: the crash was resolved and the demo went ahead on time.
19

Why should we hire you for this C++ developer role?

Save to ▾
Show answer
Situation: I have prepared specifically for systems-level roles. Task: show I can contribute quickly. Action: I have built projects such as a multithreaded log analyser and a small key-value store in modern C++, used CMake, GoogleTest and sanitizers, and practised data structures and debugging with gdb. Result: I can write safe, measurable, well-tested code from the first sprint, and I learn fast from code reviews and feedback.
20

A low-latency service shows occasional latency spikes of several milliseconds. How would you investigate?

Save to ▾
Show answer
I would first capture latency percentiles and correlate spikes with events. Common causes are heap allocation in the hot path, page faults, lock contention, logging that blocks on I/O, CPU frequency changes and the OS scheduling other work on the same core. Tools include perf, off-CPU flame graphs and tracing. Typical fixes are preallocating memory, pinning threads to isolated cores, async logging and avoiding syscalls in hot paths.
21

You are building a particle simulation with millions of particles. Would you store them as an array of structs or a struct of arrays?

Save to ▾
Show answer
If each update loop touches only a few fields, such as position and velocity, a struct of arrays keeps those fields contiguous, so every cache line is fully used and the compiler can vectorise with SIMD. An array of structs is simpler and better when most fields of one particle are used together. I would benchmark both, and often a hybrid that groups hot fields together works best.
22

Use perf to find the hotspots in a C++ program.

Save to ▾
Show answer
# Build optimised but with symbols and frame pointers g++ -O2 -g -fno-omit-frame-pointer app.cpp -o app perf stat ./app # cycles, instructions, cache misses perf record -g ./app # sample with call graphs perf report # browse hottest functions # Flame graph (with FlameGraph scripts on PATH) perf script | stackcollapse-perf.pl | flamegraph.pl > flame.svg perf stat -e cache-misses,branch-misses ./app # specific counters
23

Write a micro-benchmark with Google Benchmark comparing two approaches.

Save to ▾
Show answer
#include <benchmark/benchmark.h> #include <list> #include <numeric> #include <vector> static void BM_VectorSum(benchmark::State& state) { std::vector<int> v(state.range(0), 1); for (auto _ : state) benchmark::DoNotOptimize(std::accumulate(v.begin(), v.end(), 0)); } BENCHMARK(BM_VectorSum)->Range(1 << 10, 1 << 20); static void BM_ListSum(benchmark::State& state) { std::list<int> l(state.range(0), 1); for (auto _ : state) benchmark::DoNotOptimize(std::accumulate(l.begin(), l.end(), 0)); } BENCHMARK(BM_ListSum)->Range(1 << 10, 1 << 20); BENCHMARK_MAIN();
24

Tell me about a performance improvement you delivered and how you measured it.

Save to ▾
Show answer
Situation: our risk calculation batch took 90 minutes and blocked morning reports. Task: bring it under 30 minutes. Action: I profiled with perf and found most time in allocating small objects and a map lookup inside the inner loop. I preallocated buffers, replaced the map with a flat sorted vector and parallelised independent portfolios with a thread pool, validating outputs against the old version. Result: 22 minutes, with identical numbers.

Frequently asked questions

How important is Performance, Debugging and Build Tools in C++ interviews?

Learn the professional toolchain: CMake, static and dynamic libraries, compiler flags, GoogleTest, gdb, sanitizers, Valgrind and perf. Understand cache locality and profiling-driven optimisation, and practise debugging crashes, latency spikes and slow builds the way experienced C++ engineers do in production teams.

How should I use the Performance, Debugging and Build Tools sheet?

Go top to bottom — easy to hard. Answer before opening each answer, tick what you solved and save misses to Last-Minute Revision.