-
Notifications
You must be signed in to change notification settings - Fork 567
Add stress testing framework, with basic metrics example to demonstrate. #3241
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Draft
lalitb
wants to merge
20
commits into
open-telemetry:main
Choose a base branch
from
lalitb:stress-test
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Draft
Changes from all commits
Commits
Show all changes
20 commits
Select commit
Hold shift + click to select a range
2564cc6
Merge pull request #308 from open-telemetry/main
lalitb 9433197
Merge branch 'main' of github.com:lalitb/opentelemetry-cpp into main
lalitb 11bd32c
initial commit
lalitb 22a178b
formar
lalitb 449f360
add docs
lalitb a385503
Merge branch 'main' into stress-test
lalitb f9b0814
remove extra endif
lalitb 5f9d0da
maintainer mode build
lalitb 32d06ff
fix copyright
lalitb 57d99c3
copyright
lalitb 5a222fd
fix format
lalitb 03ffa54
Add copyright and license information
lalitb ab07553
fix msvc error
lalitb a2f17b1
Merge branch 'stress-test' of github.com:lalitb/opentelemetry-cpp int…
lalitb b56f996
add newline
lalitb eead3a0
Merge branch 'main' into stress-test
lalitb 4bfadb5
Merge branch 'main' into stress-test
lalitb 17fcc54
Merge branch 'main' into stress-test
ThomsonTan 08217e2
Merge branch 'main' into stress-test
lalitb a757366
Clean up stress test harness
lalitb File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,6 @@ | ||
| # Copyright The OpenTelemetry Authors | ||
| # SPDX-License-Identifier: Apache-2.0 | ||
|
|
||
| # Add subdirectories for common and metrics components | ||
| add_subdirectory(common) | ||
| add_subdirectory(metrics) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,14 @@ | ||
| # Copyright The OpenTelemetry Authors | ||
| # SPDX-License-Identifier: Apache-2.0 | ||
|
|
||
| add_library(stress STATIC stress.cc) | ||
|
|
||
| # Include directory for the throughput library | ||
| target_include_directories(stress PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) | ||
|
|
||
| # Set C++ standard | ||
| set_target_properties( | ||
| stress | ||
| PROPERTIES CXX_STANDARD 17 | ||
| CXX_STANDARD_REQUIRED YES | ||
| CXX_EXTENSIONS NO) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change | ||||||
|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,153 @@ | ||||||||
| // Copyright The OpenTelemetry Authors | ||||||||
| // SPDX-License-Identifier: Apache-2.0 | ||||||||
|
|
||||||||
| #include "stress.h" | ||||||||
|
|
||||||||
| #include <algorithm> | ||||||||
| #include <limits> | ||||||||
| #include <locale> | ||||||||
| #include <utility> | ||||||||
|
|
||||||||
| #ifdef __linux__ | ||||||||
| # include <pthread.h> | ||||||||
| # include <sched.h> | ||||||||
| #endif | ||||||||
|
|
||||||||
| // StressTest constructor | ||||||||
| Stress::Stress(std::function<void()> func, size_t numThreads) | ||||||||
| : func_(std::move(func)), stats_(numThreads), numThreads_(numThreads) | ||||||||
| {} | ||||||||
|
|
||||||||
| // Main function to start the stress test | ||||||||
| void Stress::run() | ||||||||
| { | ||||||||
| std::cout << "Starting stress test with " << numThreads_ << " threads...\n"; | ||||||||
| stopFlag_.store(false, std::memory_order_release); | ||||||||
| readyFlag_.store(false, std::memory_order_release); | ||||||||
|
|
||||||||
| auto startTime = std::chrono::steady_clock::now(); | ||||||||
|
|
||||||||
| std::thread controllerThread(&Stress::monitorThroughput, this); | ||||||||
|
|
||||||||
| threads_.reserve(numThreads_); | ||||||||
| for (size_t i = 0; i < numThreads_; ++i) | ||||||||
| { | ||||||||
| threads_.emplace_back(&Stress::workerThread, this, i); | ||||||||
| } | ||||||||
|
|
||||||||
| readyFlag_.store(true, std::memory_order_release); | ||||||||
|
|
||||||||
| for (auto &thread : threads_) | ||||||||
| { | ||||||||
| if (thread.joinable()) | ||||||||
| { | ||||||||
| thread.join(); | ||||||||
| } | ||||||||
| } | ||||||||
|
|
||||||||
| if (controllerThread.joinable()) | ||||||||
| { | ||||||||
| controllerThread.join(); | ||||||||
| } | ||||||||
|
|
||||||||
| auto endTime = std::chrono::steady_clock::now(); | ||||||||
| auto duration = std::chrono::duration_cast<std::chrono::seconds>(endTime - startTime); | ||||||||
|
|
||||||||
| uint64_t totalCount = 0; | ||||||||
| for (const auto &stat : stats_) | ||||||||
| { | ||||||||
| totalCount += stat.count.load(std::memory_order_relaxed); | ||||||||
| } | ||||||||
|
|
||||||||
| const auto duration_seconds = duration.count(); | ||||||||
| const auto average_throughput = | ||||||||
| duration_seconds > 0 ? totalCount / static_cast<uint64_t>(duration_seconds) : 0; | ||||||||
|
|
||||||||
| std::cout << "\nTest completed:\n" | ||||||||
| << "Total iterations: " << formatNumber(totalCount) << "\n" | ||||||||
| << "Duration: " << duration_seconds << " seconds\n" | ||||||||
| << "Average throughput: " << formatNumber(average_throughput) << " iterations/sec\n"; | ||||||||
| } | ||||||||
|
|
||||||||
| // Worker thread function | ||||||||
| void Stress::workerThread(size_t threadIndex) | ||||||||
| { | ||||||||
| #ifdef __linux__ | ||||||||
| cpu_set_t cpuset; | ||||||||
| CPU_ZERO(&cpuset); | ||||||||
| CPU_SET(threadIndex % std::thread::hardware_concurrency(), &cpuset); | ||||||||
| pthread_setaffinity_np(pthread_self(), sizeof(cpuset), &cpuset); | ||||||||
| #endif | ||||||||
|
|
||||||||
| while (!readyFlag_.load(std::memory_order_acquire)) | ||||||||
| { | ||||||||
| std::this_thread::yield(); | ||||||||
| } | ||||||||
|
|
||||||||
| while (!stopFlag_.load(std::memory_order_acquire)) | ||||||||
| { | ||||||||
| func_(); | ||||||||
| stats_[threadIndex].count.fetch_add(1, std::memory_order_relaxed); | ||||||||
| } | ||||||||
| } | ||||||||
|
|
||||||||
| // Monitoring thread function | ||||||||
| void Stress::monitorThroughput() | ||||||||
| { | ||||||||
| uint64_t lastTotalCount = 0; | ||||||||
| auto lastTime = std::chrono::steady_clock::now(); | ||||||||
|
||||||||
| auto lastTime = std::chrono::steady_clock::now(); | |
| auto lastTime = std::chrono::steady_clock::now(); | |
| const size_t MAX_HISTORY_SIZE = 100; // Maximum number of entries in throughputHistory |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,100 @@ | ||
| // Copyright The OpenTelemetry Authors | ||
| // SPDX-License-Identifier: Apache-2.0 | ||
|
|
||
| /** | ||
| * A multi-threaded stress test framework to measure throughput and performance of a given workload. | ||
| * | ||
| * ## Overview | ||
| * Multi-threaded stress test framework designed to execute a specified function | ||
| * in parallel across multiple threads and measure its throughput. The results are displayed | ||
| * dynamically, including current throughput, average throughput, and minimum/maximum throughput | ||
| * during the test. | ||
| * | ||
| * ## Key Features | ||
| * - **Multi-threading**: Uses std::thread to execute the workload in parallel across a user-defined | ||
| * number of threads. | ||
| * - **Thread Safety**: Tracks iteration counts per thread using an aligned and padded structure | ||
| * (WorkerStats) to avoid false sharing and ensure efficient thread-safe updates. | ||
| * - **Dynamic Metrics**: Continuously calculates and displays throughput (iterations/sec) over | ||
| * sliding time windows. | ||
| * - **Graceful Termination**: Captures signals (e.g., Ctrl+C) to cleanly stop all threads and | ||
| * summarize the results. | ||
| * - **Thread Affinity (Linux-only)**: Optionally binds threads to specific CPU cores for consistent | ||
| * performance. | ||
| * | ||
| * ## Implementation Details | ||
| * - **Worker Threads**: | ||
| * - Each worker thread executes the workload function (func) in a loop until a global STOP flag | ||
| * is set. | ||
| * - Each thread maintains its own iteration count to minimize contention. | ||
| * | ||
| * - **Throughput Monitoring**: | ||
| * - A separate controller thread monitors throughput by periodically summing up iteration counts | ||
| * across threads. | ||
| * - Throughput is calculated over a sliding window (SLIDING_WINDOW_SIZE) and displayed | ||
| * dynamically. | ||
| * | ||
| * - **Thread Synchronization**: | ||
| * - The STOP flag, an std::atomic<bool>, ensures all threads stop gracefully when signaled. | ||
| * - Memory ordering (e.g., std::memory_order_relaxed, std::memory_order_acquire/release) is used | ||
| * to optimize performance while maintaining correctness. | ||
| * | ||
| * - **Final Summary**: | ||
| * - At the end of the test, the program calculates and prints the total iterations, duration, and | ||
| * average throughput. | ||
| */ | ||
|
|
||
| #pragma once | ||
|
|
||
| #include <atomic> | ||
| #include <chrono> | ||
| #include <csignal> | ||
| #include <cstdint> | ||
| #include <functional> | ||
| #include <iostream> | ||
| #include <sstream> | ||
| #include <thread> | ||
| #include <vector> | ||
|
|
||
| // Configuration constants | ||
| constexpr uint64_t SLIDING_WINDOW_SIZE = 2; // Time window for throughput calculation (in seconds) | ||
| constexpr size_t CACHE_LINE_SIZE = 64; // Typical CPU cache line size for alignment | ||
|
|
||
| // WorkerStats structure for tracking iteration counts per thread | ||
| struct alignas(CACHE_LINE_SIZE) WorkerStats | ||
| { | ||
| std::atomic<uint64_t> count{0}; // Count of iterations for a specific thread | ||
| char padding[CACHE_LINE_SIZE - | ||
| sizeof(std::atomic<uint64_t>)]; // Padding to ensure proper alignment | ||
| }; | ||
|
|
||
| // StressTest class | ||
| class Stress | ||
| { | ||
| public: | ||
| // Constructor | ||
| Stress(std::function<void()> func, size_t numThreads = std::thread::hardware_concurrency()); | ||
|
|
||
| // Main function to start the stress test | ||
| void run(); | ||
|
|
||
| // function to stop the test | ||
| void stop(); | ||
|
|
||
| private: | ||
| std::function<void()> func_; // Function to be executed by each thread | ||
| std::vector<std::thread> threads_; // Vector to hold worker threads | ||
| std::vector<WorkerStats> stats_; // Vector to hold statistics for each thread | ||
| const size_t numThreads_; // Number of threads to run | ||
| std::atomic<bool> stopFlag_{false}; // signal to stop the test | ||
| std::atomic<bool> readyFlag_{false}; // signal to start worker loops | ||
|
|
||
| // Worker thread function | ||
| void workerThread(size_t threadIndex); | ||
|
|
||
| // Monitoring thread function to calculate and display throughput | ||
| void monitorThroughput(); | ||
|
|
||
| // Helper function to format numbers with commas for readability | ||
| static std::string formatNumber(uint64_t num); | ||
| }; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,30 @@ | ||
| # Copyright The OpenTelemetry Authors | ||
| # SPDX-License-Identifier: Apache-2.0 | ||
|
|
||
| # Define the metrics executable | ||
| add_executable(stress_metrics metrics.cc) | ||
|
|
||
| # Link throughput library and OpenTelemetry Metrics API | ||
| target_link_libraries( | ||
| stress_metrics PRIVATE stress opentelemetry_metrics # OpenTelemetry Metrics | ||
| # SDK | ||
| ) | ||
|
|
||
| # Include directories for throughput | ||
| target_include_directories(stress_metrics | ||
| PRIVATE ${CMAKE_SOURCE_DIR}/stress/common) | ||
|
|
||
| # Set properties | ||
| set_target_properties( | ||
| stress_metrics | ||
| PROPERTIES CXX_STANDARD 17 | ||
| CXX_STANDARD_REQUIRED YES | ||
| CXX_EXTENSIONS NO) | ||
|
|
||
| # Optional: Installation | ||
| if(OPENTELEMETRY_INSTALL) | ||
| install( | ||
| TARGETS stress_metrics | ||
| EXPORT "${PROJECT_NAME}-target" | ||
| RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) | ||
| endif() |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
is the READY flag meant to start workers at the same time? Or is it superfluous as copilot mentioned?