Skip to content

Commit 5117641

Browse files
Add files via upload
1 parent da84537 commit 5117641

File tree

5 files changed

+242
-0
lines changed

5 files changed

+242
-0
lines changed

CMakeLists.txt

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
cmake_minimum_required(VERSION 3.22)
2+
set(CMAKE_C_COMPILER /usr/bin/gcc-12)
3+
set(CMAKE_CXX_COMPILER /usr/bin/g++-12)
4+
5+
project(cppcon2023)
6+
7+
8+
# TODO fix this
9+
set(CMAKE_BUILD_TYPE Debug)
10+
11+
set(CMAKE_CXX_STANDARD 20)
12+
set(CMAKE_CXX_STANDARD_REQUIRED true)
13+
set(CMAKE_CXX_EXTENSIONS OFF)
14+
add_compile_options(-Wall -Werror -Wextra -Wconversion)
15+
add_compile_options(-Wno-unused-parameter)
16+
17+
add_executable(benchmark benchmark.cpp)
18+
target_link_libraries(benchmark pthread)
19+
20+
# add_executable(benchmark.tsan benchmark.cpp)
21+
# target_compile_options(benchmark.tsan -fsanitize=thread)
22+
# target_link_libraries(benchmark.tsan pthread)
23+
24+
include(GoogleTest)
25+
enable_testing()
26+
27+
add_executable(tests sanitizers.cpp Fifo1_ut.cpp)
28+
target_compile_options(tests PRIVATE -fsanitize=undefined -fsanitize=address)
29+
target_link_libraries(tests PRIVATE asan ubsan gtest gtest_main)
30+
target_link_options(tests PRIVATE -fsanitize=undefined -fsanitize=address)
31+
gtest_add_tests(TARGET tests)

Fifo1.hpp

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
#pragma once
2+
3+
#include <cstdlib>
4+
#include <memory>
5+
6+
7+
/// Non-threadsafe circular FIFO
8+
template<typename T>
9+
class Fifo1
10+
{
11+
public:
12+
using ValueType = T;
13+
14+
explicit Fifo1(std::size_t size)
15+
: size_{size}
16+
, ring_{
17+
static_cast<ValueType*>(std::aligned_alloc(alignof(T), size * sizeof(T))),
18+
&std::free}
19+
{}
20+
21+
22+
std::size_t size() const { return size_; }
23+
bool empty() const { return popCursor_ == pushCursor_; }
24+
bool full() const { return (pushCursor_ - popCursor_) == size_; }
25+
26+
bool push(T const& value) {
27+
if (full()) {
28+
return false;
29+
}
30+
new (ring_.get() + pushCursor_ % size_) T(value);
31+
++pushCursor_;
32+
33+
return true;
34+
}
35+
36+
/// Pop one object from the fifo.
37+
/// @return `true` if the pop operation is successful; `false` if fifo was empty.
38+
bool pop(T& value) {
39+
if (empty()) {
40+
return false;
41+
}
42+
value = ring_.get()[popCursor_ % size_];
43+
ring_.get()[popCursor_ % size_].~T();
44+
++popCursor_;
45+
46+
return true;
47+
}
48+
49+
private:
50+
std::size_t size_;
51+
52+
using RingType = std::unique_ptr<ValueType, decltype(&std::free)>;
53+
RingType ring_;
54+
55+
std::size_t pushCursor_{};
56+
std::size_t popCursor_{};
57+
};

Fifo1_ut.cpp

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
#include "Fifo1.hpp"
2+
3+
#include <gtest/gtest.h>
4+
5+
#include <type_traits>
6+
7+
8+
using TestFifo = Fifo1<int>;
9+
10+
TEST(Fifo1, initialConditions) {
11+
auto fifo = TestFifo(1234);
12+
13+
EXPECT_EQ(1234u, fifo.size());
14+
EXPECT_TRUE(fifo.empty());
15+
EXPECT_FALSE(fifo.full());
16+
}
17+
18+
TEST(Fifo1, push) {
19+
auto fifo = TestFifo(4);
20+
ASSERT_EQ(4u, fifo.size());
21+
22+
EXPECT_TRUE(fifo.push(42));
23+
EXPECT_FALSE(fifo.empty());
24+
EXPECT_FALSE(fifo.full());
25+
26+
EXPECT_TRUE(fifo.push(42));
27+
EXPECT_FALSE(fifo.empty());
28+
EXPECT_FALSE(fifo.full());
29+
30+
EXPECT_TRUE(fifo.push(42));
31+
EXPECT_FALSE(fifo.empty());
32+
EXPECT_FALSE(fifo.full());
33+
34+
EXPECT_TRUE(fifo.push(42));
35+
EXPECT_FALSE(fifo.empty());
36+
EXPECT_TRUE(fifo.full());
37+
38+
EXPECT_FALSE(fifo.push(42));
39+
EXPECT_FALSE(fifo.empty());
40+
EXPECT_TRUE(fifo.full());
41+
}
42+
43+
TEST(Fifo1, pop) {
44+
auto fifo = TestFifo(4);
45+
46+
auto value = TestFifo::ValueType{};
47+
EXPECT_FALSE(fifo.pop(value));
48+
49+
for (auto i = 0u; i < fifo.size(); ++i) {
50+
fifo.push(42 + i);
51+
}
52+
53+
for (auto i = 0u; i < fifo.size(); ++i) {
54+
auto value = TestFifo::ValueType{};
55+
EXPECT_TRUE(fifo.pop(value));
56+
EXPECT_EQ(42+i, value);
57+
}
58+
EXPECT_FALSE(fifo.pop(value));
59+
}

benchmark.cpp

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
#include "Fifo1.hpp"
2+
3+
#include <array>
4+
#include <chrono>
5+
#include <cstdint>
6+
#include <cstdio>
7+
#include <cstdlib>
8+
#include <iostream>
9+
#include <stdexcept>
10+
#include <thread>
11+
12+
#include <pthread.h>
13+
14+
15+
void pinThread(int cpu) {
16+
if (cpu < 0) {
17+
return;
18+
}
19+
::cpu_set_t cpuset;
20+
CPU_ZERO(&cpuset);
21+
CPU_SET(cpu, &cpuset);
22+
if (::pthread_setaffinity_np(pthread_self(), sizeof(cpu_set_t), &cpuset) == -1) {
23+
std::perror("pthread_setaffinity_rp");
24+
std::exit(EXIT_FAILURE);
25+
}
26+
}
27+
28+
template<typename T>
29+
void bench(char const* name, int cpu1, int cpu2) {
30+
using ValueType = typename T::ValueType;
31+
using AryValueType = typename ValueType::value_type;
32+
33+
constexpr auto queueSize = 131072;
34+
constexpr auto iters = 1'000'000'000l;
35+
36+
T q(queueSize);
37+
auto t = std::thread([&] {
38+
pinThread(cpu1);
39+
for (auto i = AryValueType{}; i < iters; ++i) {
40+
ValueType val;
41+
while (not q.pop(val)) {
42+
;
43+
}
44+
if (val[0] != i) {
45+
std::cerr << "#### throw " << val[0] << " != " << i <<"\n";
46+
throw std::runtime_error("foo");
47+
}
48+
}
49+
});
50+
51+
pinThread(cpu2);
52+
auto start = std::chrono::steady_clock::now();
53+
for (auto i = AryValueType{}; i < iters; ++i) {
54+
while (not q.push(ValueType{i})) {
55+
;
56+
}
57+
}
58+
while (not q.empty()) {
59+
;
60+
}
61+
auto stop = std::chrono::steady_clock::now();
62+
t.join();
63+
std::cout << name << " " << iters * 1'000'000'000 /
64+
std::chrono::duration_cast<std::chrono::nanoseconds>(stop - start).count()
65+
<< " ops/s\n";
66+
}
67+
68+
int main(int argc, char* argv[]) {
69+
int cpu1 = -1;
70+
int cpu2 = -1;
71+
if (argc == 3) {
72+
cpu1 = std::atoi(argv[1]);
73+
cpu2 = std::atoi(argv[2]);
74+
}
75+
76+
using TestType = std::array<long, 25>;
77+
bench<Fifo1<TestType>>("Fifo1", cpu1, cpu2);
78+
}
79+
80+
81+
82+

sanitizers.cpp

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
#include <gtest/gtest.h>
2+
3+
extern "C" {
4+
void __ubsan_on_report() {
5+
FAIL() << "Encountered an undefined behavior sanitizer error";
6+
}
7+
void __asan_on_error() {
8+
FAIL() << "Encountered an address sanitizer error";
9+
}
10+
void __tsan_on_report() {
11+
FAIL() << "Encountered a thread sanitizer error";
12+
}
13+
} // extern "C"

0 commit comments

Comments
 (0)