diff --git a/eval/CMakeLists.txt b/eval/CMakeLists.txt
index d1d59f61321220962ccad99f00829124bc74584c..0d509b0ce0a08f412b88144c4424dda7623228e4 100644
--- a/eval/CMakeLists.txt
+++ b/eval/CMakeLists.txt
@@ -1,2 +1,5 @@
 add_executable(time_to_spawn TimeToSpawn.cpp)
 target_link_libraries(time_to_spawn Threads::Threads emper)
+
+add_executable(spawn_a_lot SpawnALot.cpp)
+target_link_libraries(spawn_a_lot Threads::Threads emper)
diff --git a/eval/SpawnALot.cpp b/eval/SpawnALot.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..1bbe87430c7896055601c38f2c5fb98d541ad5cb
--- /dev/null
+++ b/eval/SpawnALot.cpp
@@ -0,0 +1,168 @@
+#include "Runtime.hpp"
+#include "PrivateSemaphore.hpp"
+#include "BinaryPrivateSemaphore.hpp"
+#include "CountingPrivateSemaphore.hpp"
+#include "DebugUtil.hpp"
+
+#define CACHE_LINE_SIZE 64
+
+static void spawnALotThreadsRecursiveTFun(unsigned int depth, unsigned int width, unsigned int current_depth) {
+		if (current_depth == depth) return;
+
+		std::thread* threads = new std::thread[width];
+		const unsigned int new_depth = current_depth + 1;
+		for (unsigned int i = 0; i < width; ++i) {
+			threads[i] = std::thread(spawnALotThreadsRecursiveTFun, depth, width, new_depth);
+		}
+		for (unsigned int i = 0; i < width; ++i) {
+			threads[i].join();
+		}
+
+		delete[] threads;
+}
+
+static void spawnALotThreadsRecursive(unsigned int depth, unsigned int width) {
+	std::thread thread(spawnALotThreadsRecursiveTFun, depth, width, 0);
+	thread.join();
+}
+
+static void spawnALotThreadsNonRecursive(uint64_t count) {
+	uint8_t* flags = new uint8_t[count * CACHE_LINE_SIZE];
+	std::thread* threads = new std::thread[count];
+	for (uint64_t i = 0; i < count; ++i) {
+		threads[i] = std::thread([&flags, i] {
+				flags[i * CACHE_LINE_SIZE] = 1;
+			});
+	}
+
+	for (uint64_t i = 0; i < count; ++i) {
+		threads[i].join();
+	}
+
+	delete[] flags;
+	delete[] threads;
+}
+
+struct SpawnALotFibersData {
+	Runtime& runtime;
+	const unsigned int depth;
+	const unsigned int width;
+	PS& ps;
+	unsigned int current_depth;
+
+	SpawnALotFibersData(SpawnALotFibersData* oldData, PS& ps)
+		: runtime(oldData->runtime)
+		, depth(oldData->depth)
+		, width(oldData->width)
+		, ps(ps)
+		, current_depth(oldData->current_depth + 1) {
+	}
+
+	SpawnALotFibersData(Runtime& runtime, unsigned int depth, unsigned int width, PS& ps)
+		: runtime(runtime)
+		, depth(depth)
+		, width(width)
+		, ps(ps)
+		, current_depth(0) {
+	}
+
+};
+
+static void spawnALotFibersRecursiveFFun(void* dataPtr) {
+	SpawnALotFibersData* data = (SpawnALotFibersData*) dataPtr;
+	if (data->current_depth < data->depth) {
+		CPS childSem(data->width);
+		SpawnALotFibersData newData(data, childSem);
+
+		for (unsigned int i = 0; i < data->width; ++i) {
+			Fiber* fiber = Fiber::from(spawnALotFibersRecursiveFFun, (void*) &newData);
+			data->runtime.schedule(*fiber);
+		}
+
+		childSem.wait();
+	}
+
+	data->ps.signal();
+}
+
+static void spawnALotFibersRecursive(Runtime& runtime, unsigned int depth, unsigned int width) {
+	BPS bps;
+	SpawnALotFibersData data(runtime, depth, width, bps);
+
+	Fiber* fiber = Fiber::from(spawnALotFibersRecursiveFFun, (void*) &data);
+	runtime.schedule(*fiber);
+
+	bps.wait();
+}
+
+static void spawnALotFibersNonRecursive(Runtime& runtime, uint64_t count) {
+	uint8_t* flags = new uint8_t[count * CACHE_LINE_SIZE];
+	CPS cps(count);
+
+	for (uint64_t i = 0; i < count; ++i) {
+		Fiber* fiber = Fiber::from([i, &cps, flags] {
+				flags[i * CACHE_LINE_SIZE] = 1;
+				cps.signal();
+			});
+		runtime.schedule(*fiber);
+	}
+
+	cps.wait();
+
+	delete[] flags;
+}
+
+int main(UNUSED_ARG int argc, UNUSED_ARG char *argv[]) {
+	enableStacktraceOnAborts();
+	const uint64_t count = 1024;
+
+	const unsigned int depth = 10;
+	const unsigned int width = 2;
+
+	std::chrono::time_point<std::chrono::high_resolution_clock> start, end;
+	std::chrono::nanoseconds diff;
+
+	start = std::chrono::high_resolution_clock::now();
+	spawnALotThreadsRecursive(depth, width);
+	end = std::chrono::high_resolution_clock::now();
+	diff = std::chrono::duration_cast<std::chrono::nanoseconds>(end -start);
+	std::cout << "Spawn a lot of threads recursive (depth="
+			  << depth << ", width="
+			  << width << ") took "
+			  << diff.count() << "us"
+			  << std::endl;
+
+	start = std::chrono::high_resolution_clock::now();
+	spawnALotThreadsNonRecursive(count);
+	end = std::chrono::high_resolution_clock::now();
+	diff = std::chrono::duration_cast<std::chrono::nanoseconds>(end -start);
+	std::cout << "Spawn a lot of threads non-recursive (count="
+			  << count << ") took "
+			  << diff.count() << "us"
+			  << std::endl;
+
+	Runtime runtime;
+
+	runtime.executeAndWait([&] {
+			start = std::chrono::high_resolution_clock::now();
+			spawnALotFibersRecursive(runtime, depth, width);
+			end = std::chrono::high_resolution_clock::now();
+			diff = std::chrono::duration_cast<std::chrono::nanoseconds>(end -start);
+			std::cout << "Spawn a lot of fibers recursive (depth="
+					  << depth << ", width="
+					  << width << ") took "
+					  << diff.count() << "us"
+					  << std::endl;
+
+			start = std::chrono::high_resolution_clock::now();
+			spawnALotFibersNonRecursive(runtime, count);
+			end = std::chrono::high_resolution_clock::now();
+			diff = std::chrono::duration_cast<std::chrono::nanoseconds>(end -start);
+			std::cout << "Spawn a lot of fibers non-recursive (count="
+					  << count << ") took "
+					  << diff.count() << "us"
+					  << std::endl;
+		});
+
+	return EXIT_SUCCESS;
+}