A sandbox environment for C++ experimentation:
- Run code with
main.cpp
- Test code with
test.cpp
using GoogleTest - Benchmark code with
benchmark.cpp
using GoogleBenchmark - View assembly with
assembly.cpp
Edit main.cpp
file
$ cmake -B build -G Ninja -DCMAKE_BUILD_TYPE=<Debug|Release> # Configure
$ cmake --build build # Build
$ ./build/main # Run
$ lldb ./build/main # Debug
Install GoogleTest
$ sudo pacman -S gtest # Arch Linux
$ sudo apt-get install libgmock-dev libgtest-dev # Debian-based
$ brew install googletest # MacOS
Edit test.cpp
file
$ cmake -B build -G Ninja -DCMAKE_BUILD_TYPE=<Debug|Release> # Configure
$ cmake --build build # Build
$ ./build/test # Run
$ lldb ./build/test # Debug
For running a specific tests, the following commands are useful
$ ./build/test --gtest_list_tests
$ ./build/test --gtest_filter='*.<matcher>'
Example solution for Leetcode 1 problem twoSum
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <unordered_map>
#include <vector>
class Solution {
public:
[[nodiscard]]
static auto twoSum(const std::vector<int>& nums, int target) noexcept
-> std::vector<int> {
auto mp = std::unordered_map<int, int>{}; // (nums[i], i)
for (auto i = 0ZU, n = nums.size(); i < n; ++i) {
auto complement = target - nums[i];
if (mp.contains(complement)) {
return {static_cast<int>(i), mp[complement]};
}
mp[nums[i]] = static_cast<int>(i);
}
return {0, 0};
}
};
TEST(SolutionTest, Test1) {
std::vector<int> nums{2, 7, 11, 15};
EXPECT_THAT(Solution::twoSum(nums, 9), testing::UnorderedElementsAre(0, 1));
}
TEST(SolutionTest, Test2) {
std::vector<int> nums{3, 2, 4};
EXPECT_THAT(Solution::twoSum(nums, 6), testing::UnorderedElementsAre(1, 2));
}
TEST(SolutionTest, Test3) {
std::vector<int> nums{3, 3};
EXPECT_THAT(Solution::twoSum(nums, 6), testing::UnorderedElementsAre(0, 1));
}
Install GoogleBenchmark C++ benchmarking framework on your machine:
$ sudo pacman -S benchmark # Arch Linux
$ sudo apt-get install libbenchmark-dev # Debian-based
$ brew install google-benchmark # MacOS
Edit benchmark.cpp
file
$ cmake -B build -G Ninja -DCMAKE_BUILD_TYPE=Release # Configure
$ cmake --build build # Build
$ ./build/benchmark # Run
$ lldb ./build/benchmark # Debug
Example from https://quick-bench.com/
#include <benchmark/benchmark.h>
static void StringCreation(benchmark::State& state) {
for (auto _ : state) {
auto foo = std::string{"hello"};
benchmark::DoNotOptimize(foo);
}
}
BENCHMARK(StringCreation);
static void StringCopy(benchmark::State& state) {
auto foo = std::string{"hello"};
for (auto _ : state) {
auto bar = std::string{foo};
benchmark::DoNotOptimize(bar);
}
}
BENCHMARK(StringCopy);
That generates the following output
Running build/benchmark
Run on (14 X 4400 MHz CPU s)
CPU Caches:
L1 Data 48 KiB (x7)
L1 Instruction 64 KiB (x7)
L2 Unified 2048 KiB (x7)
L3 Unified 12288 KiB (x1)
Load Average: 0.45, 0.42, 0.63
---------------------------------------------------------
Benchmark Time CPU Iterations
---------------------------------------------------------
StringCreation 0.922 ns 0.921 ns 765882382
StringCopy 1.35 ns 1.35 ns 520319518
Edit assembly.cpp
file
$ cmake -B build -G Ninja -DCMAKE_BUILD_TYPE=Release # Configure
$ cmake --build build # Build
$ ./build/assembly # Run
$ lldb ./build/assembly # Debug
Example
auto add(int a, int b) -> int {
return a + b;
}
auto main() -> int {
auto result = add(3, 5);
}
Generates build/assembly_demangled.s
add(int, int): # @add(int, int)
# %bb.0:
push rbp
mov rbp, rsp
mov dword ptr [rbp - 4], edi
mov dword ptr [rbp - 8], esi
mov eax, dword ptr [rbp - 4]
add eax, dword ptr [rbp - 8]
pop rbp
ret
main: # @main
# %bb.0:
push rbp
mov rbp, rsp
sub rsp, 16
mov edi, 3
mov esi, 5
call add(int, int)
mov dword ptr [rbp - 4], eax
xor eax, eax
add rsp, 16
pop rbp
ret
Suggestions or improvements? Raise a pull request!