diff --git a/ChangeLog b/ChangeLog index ce8b0c9c9..c5247b5cd 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,10 +1,11 @@ -Changes in version 11.3, 11/02/2024 +Changes in version 11.3, 13/02/2024 =================================== * stressTest.cpp: New --stress-test[=MODE] command-line option. * RiemannR.cpp: Faster Riemann R function implementation #144. * CmdOptions.cpp: New -R && --RiemannR command line options. * CmdOptions.cpp: New --R-inverse command line option. +* CmdOptions.cpp: Add new --timeout option for stress testing. * main.cpp: Improve command-line option handling. Changes in version 11.2, 08/01/2024 diff --git a/README.md b/README.md index 6709c3ab1..3328ef428 100644 --- a/README.md +++ b/README.md @@ -94,11 +94,9 @@ Options: count prime triplets: -c3 or --count=3, ... --cpu-info Print CPU information (cache sizes). -d, --dist=DIST Sieve the interval [START, START + DIST]. - -h, --help Print this help menu. -n, --nth-prime Find the nth prime. primesieve 100 -n: finds the 100th prime, primesieve 2 100 -n: finds the 2nd prime > 100. - --no-status Turn off the progressing status. -p, --print[=NUM] Print primes or prime k-tuplets, NUM <= 6. Print primes: -p or --print, print twin primes: -p2 or --print=2, @@ -108,13 +106,15 @@ Options: By default primesieve uses a sieve size that matches your CPU's L1 cache size (per core) or is slightly smaller than your CPU's L2 cache size. - --stress-test[=MODE] Run stress testing. The MODE can be either - CPU (default) or RAM. Keeps on running forever. + --stress-test[=MODE] Run a stress test. The MODE can be either + CPU (default) or RAM. The default timeout is 24h. --test Run various correctness tests (< 1 minute). -t, --threads=NUM Set the number of threads, NUM <= CPU cores. Default setting: use all available CPU cores. --time Print the time elapsed in seconds. - -v, --version Print version and license information. + --timeout=SEC Set the stress test timeout in seconds. Supported + units of time suffixes: s, m, h, d or y. + 30 minutes timeout: --timeout 30m ``` ## Build instructions diff --git a/doc/primesieve.1 b/doc/primesieve.1 index 3908ed626..e1bf13d03 100644 --- a/doc/primesieve.1 +++ b/doc/primesieve.1 @@ -2,12 +2,12 @@ .\" Title: primesieve .\" Author: [see the "AUTHOR" section] .\" Generator: DocBook XSL Stylesheets vsnapshot -.\" Date: 02/12/2024 +.\" Date: 02/13/2024 .\" Manual: \ \& .\" Source: \ \& .\" Language: English .\" -.TH "PRIMESIEVE" "1" "02/12/2024" "\ \&" "\ \&" +.TH "PRIMESIEVE" "1" "02/13/2024" "\ \&" "\ \&" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- @@ -127,7 +127,7 @@ Set the size of the sieve array in KiB, 16 <= .PP \fB\-\-stress\-test\fR[=\fIMODE\fR] .RS 4 -Run stress testing\&. +Run a stress test\&. The \fIMODE\fR can be either CPU (default) or RAM\&. The CPU \fIMODE\fR @@ -136,7 +136,11 @@ uses little memory (on average about 225 MiB per thread) and puts the highest lo uses much more memory than the CPU \fIMODE\fR (each thread uses about 1\&.16 GiB), but the CPU usually won\(cqt get as hot as in the CPU -\fIMODE\fR\&. Stress testing keeps on running until either a miscalculation occurs (due to a hardware issue) or the user cancels it using Ctrl+C\&. +\fIMODE\fR\&. Stress testing keeps on running until either a miscalculation occurs (due to a hardware issue) or the timeout expires\&. The default timeout is 24 hours, the timeout can be changed using the +\fB\-\-timeout=SECS\fR +option\&. By default the stress test uses a number of threads that matches the number of CPU cores, the number of threads can be changed using the +\fB\-\-threads=NUM\fR +option\&. .RE .PP \fB\-\-test\fR @@ -156,6 +160,13 @@ Set the number of threads, 1 <= Print the time elapsed in seconds\&. .RE .PP +\fB\-\-timeout\fR=\fISECS\fR +.RS 4 +Set the stress test timeout in seconds\&. Units of time for seconds, minutes, hours, days and years are also supported with the suffix s, m, h, d or y\&. E\&.g\&. +\fB\-\-timeout 10m\fR +sets a timeout of 10 minutes\&. The default stress test timeout is 24 hours\&. +.RE +.PP \fB\-v, \-\-version\fR .RS 4 Print version and license information\&. diff --git a/doc/primesieve.txt b/doc/primesieve.txt index 8a83ab19c..fa149f378 100644 --- a/doc/primesieve.txt +++ b/doc/primesieve.txt @@ -78,13 +78,16 @@ OPTIONS size to your CPU's L1 or L2 cache size (per core). *--stress-test*[='MODE']:: - Run stress testing. 'MODE' can be either CPU (default) or RAM. The CPU + Run a stress test. The 'MODE' can be either CPU (default) or RAM. The CPU 'MODE' uses little memory (on average about 225 MiB per thread) and puts the highest load on the CPU. The RAM 'MODE' uses much more memory than the CPU 'MODE' (each thread uses about 1.16 GiB), but the CPU usually won't get as hot as in the CPU 'MODE'. Stress testing keeps on running until either a - miscalculation occurs (due to a hardware issue) or the user cancels it - using Ctrl+C. + miscalculation occurs (due to a hardware issue) or the timeout expires. The + default timeout is 24 hours, the timeout can be changed using the + *--timeout=SECS* option. By default the stress test uses a number of threads + that matches the number of CPU cores, the number of threads can be changed + using the *--threads=NUM* option. *--test*:: Run various sieving tests. @@ -95,7 +98,13 @@ OPTIONS prime. *--time*:: -Print the time elapsed in seconds. + Print the time elapsed in seconds. + +*--timeout*='SECS':: + Set the stress test timeout in seconds. Units of time for seconds, minutes, + hours, days and years are also supported with the suffix s, m, h, d or y. + E.g. *--timeout 10m* sets a timeout of 10 minutes. The default stress test + timeout is 24 hours. *-v, --version*:: Print version and license information. diff --git a/src/app/CmdOptions.cpp b/src/app/CmdOptions.cpp index 3cafef06a..23ab2a153 100644 --- a/src/app/CmdOptions.cpp +++ b/src/app/CmdOptions.cpp @@ -283,6 +283,28 @@ void optionStressTest(Option& opt, opts.stressTestMode = "CPU"; } +/// Stress test timeout +void optionTimeout(Option& opt, + CmdOptions& opts) +{ + std::transform(opt.val.begin(), opt.val.end(), opt.val.begin(), + [](unsigned char c){ return std::tolower(c); }); + + // We support the same options as stress-ng. + // https://manpages.debian.org/unstable/stress-ng/stress-ng.1.en.html + switch (opt.val.back()) + { + case 's': opt.val.pop_back(); opts.timeout = opt.getValue(); break; + case 'm': opt.val.pop_back(); opts.timeout = opt.getValue() * 60; break; + case 'h': opt.val.pop_back(); opts.timeout = opt.getValue() * 3600; break; + case 'd': opt.val.pop_back(); opts.timeout = opt.getValue() * 24 * 3600; break; + case 'y': opt.val.pop_back(); opts.timeout = opt.getValue() * 365 * 24 * 3600; break; + + // By default assume seconds like stress-ng + default: opts.timeout = opt.getValue(); + } +} + } // namespace CmdOptions parseOptions(int argc, char* argv[]) @@ -320,6 +342,7 @@ CmdOptions parseOptions(int argc, char* argv[]) { "-t", std::make_pair(OPTION_THREADS, REQUIRED_PARAM) }, { "--threads", std::make_pair(OPTION_THREADS, REQUIRED_PARAM) }, { "--time", std::make_pair(OPTION_TIME, NO_PARAM) }, + { "--timeout", std::make_pair(OPTION_TIMEOUT, REQUIRED_PARAM) }, { "-v", std::make_pair(OPTION_VERSION, NO_PARAM) }, { "--version", std::make_pair(OPTION_VERSION, NO_PARAM) } }; @@ -337,6 +360,7 @@ CmdOptions parseOptions(int argc, char* argv[]) case OPTION_DISTANCE: optionDistance(opt, opts); break; case OPTION_PRINT: optionPrint(opt, opts); break; case OPTION_STRESS_TEST: optionStressTest(opt, opts); break; + case OPTION_TIMEOUT: optionTimeout(opt, opts); break; case OPTION_SIZE: opts.sieveSize = opt.getValue(); break; case OPTION_THREADS: opts.threads = opt.getValue(); break; case OPTION_QUIET: opts.quiet = true; break; diff --git a/src/app/CmdOptions.hpp b/src/app/CmdOptions.hpp index 3d0847c6a..bf2b7bb0d 100644 --- a/src/app/CmdOptions.hpp +++ b/src/app/CmdOptions.hpp @@ -32,6 +32,7 @@ enum OptionID OPTION_TEST, OPTION_THREADS, OPTION_TIME, + OPTION_TIMEOUT, OPTION_VERSION }; @@ -43,6 +44,9 @@ struct CmdOptions int flags = 0; int sieveSize = 0; int threads = 0; + // Stress test timeout in seconds. + // The default timeout is 24 hours (same as stress-ng). + int64_t timeout = 24 * 3600; bool quiet = false; bool status = true; bool time = false; diff --git a/src/app/help.cpp b/src/app/help.cpp index abce93354..ecacf8b95 100644 --- a/src/app/help.cpp +++ b/src/app/help.cpp @@ -47,12 +47,15 @@ void help(int exitCode) " By default primesieve uses a sieve size that\n" " matches your CPU's L1 cache size (per core) or is\n" " slightly smaller than your CPU's L2 cache size.\n" - " --stress-test[=MODE] Run stress testing. The MODE can be either\n" - " CPU (default) or RAM. Keeps on running forever.\n" + " --stress-test[=MODE] Run a stress test. The MODE can be either\n" + " CPU (default) or RAM. The default timeout is 24h.\n" " --test Run various correctness tests (< 1 minute).\n" " -t, --threads=NUM Set the number of threads, NUM <= CPU cores.\n" " Default setting: use all available CPU cores.\n" " --time Print the time elapsed in seconds.\n" + " --timeout=SEC Set the stress test timeout in seconds. Supported\n" + " units of time suffixes: s, m, h, d or y.\n" + " 30 minutes timeout: --timeout 30m\n" " -v, --version Print version and license information."; std::cout << helpMenu << std::endl; diff --git a/src/app/stressTest.cpp b/src/app/stressTest.cpp index 7c07eaea9..4d7f11eab 100644 --- a/src/app/stressTest.cpp +++ b/src/app/stressTest.cpp @@ -1,13 +1,10 @@ /// /// @file stressTest.cpp /// @brief Run a stress test (--stress-test[=MODE] command-line -/// option) that puts maximum load on the CPU (default) or RAM -/// and verify that there are no miscalculations due to -/// hardware issues. -/// -/// Stress testing keeps on running until a miscalculation -/// occurs (which shouldn't happen on most PCs) or until the -/// user cancels it using Ctrl+C. +/// option) that puts maximum load on the CPU (default) or RAM. +/// The stress test keeps on running until either a +/// miscalculation occurs (due to a hardware issue) or the +/// timeout (--timeout=SECS option) expires. /// /// Copyright (C) 2024 Kim Walisch, /// @@ -117,32 +114,54 @@ const Array primeCounts_1e19 = 2285748648ull, 2285751228ull, 2285725270ull, 2285701010ull }; -void stressTestInfo(const std::string& stressTestMode, +std::string getTimeout(int64_t secs) +{ + // Seconds per: year, day, hour, minute, second + Array time = { 365 * 24 * 3600, 24 * 3600, 3600, 60, 1 }; + Array suffix = { "y", "d", "h", "m", "s" }; + std::string timeout; + + for (std::size_t i = 0; i < time.size(); i++) + { + if (secs > time[i]) + { + timeout += (timeout.empty()) ? "" : " "; + timeout += std::to_string(secs / time[i]) + suffix[i]; + secs %= time[i]; + } + } + + return timeout; +} + +void stressTestInfo(const CmdOptions& opts, int threads) { ASSERT(stressTestMode == "CPU" || stressTestMode == "RAM"); - std::cout << "Started " << stressTestMode << " stress testing using " << threads << " threads.\n"; + std::cout << "Started " << opts.stressTestMode << " stress testing using " << threads << " threads.\n"; std::cout << "The expected memory usage is: " << threads << " threads * "; - if (stressTestMode == "CPU") + if (opts.stressTestMode == "CPU") { int threads_1e19 = threads / 5; int threads_1e13 = threads - threads_1e19; - int avgMiB = (threads_1e13 * 3 + threads_1e19 * 1160) / threads; + double avgMiB = (threads_1e13 * 3.0 + threads_1e19 * 1160.0) / threads; double avgGiB = avgMiB / 1024.0; if (threads * avgMiB < 1024) - std::cout << avgMiB << " MiB = " << threads * avgMiB << " MiB.\n"; + std::cout << std::fixed << std::setprecision(2) << avgMiB << " MiB = " + << std::fixed << std::setprecision(2) << threads * avgGiB << " MiB.\n"; else - std::cout << avgMiB << " MiB = " << std::fixed - << std::setprecision(2) << threads * avgGiB << " GiB.\n"; + std::cout << std::fixed << std::setprecision(2) << avgMiB << " MiB = " + << std::fixed << std::setprecision(2) << threads * avgGiB << " GiB.\n"; } else // stressTestMode == "RAM" std::cout << "1.16 GiB = " << std::fixed << std::setprecision(2) << threads * 1.16 << " GiB.\n"; - std::cout << "Stress testing keeps on running until either a miscalculation occurs\n"; - std::cout << "(due to a hardware issue) or you cancel it using Ctrl+C.\n"; + std::cout << "The stress test keeps on running until either a miscalculation occurs\n"; + std::cout << "(due to a hardware issue) or the timeout of " << getTimeout(opts.timeout) << " expires.\n"; + std::cout << "You may cancel the stress test at any time using Ctrl+C.\n"; std::cout << std::endl; } @@ -246,7 +265,8 @@ void stressTest(const CmdOptions& opts) int maxThreads = std::thread::hardware_concurrency(); int threads = (opts.threads > 0) ? opts.threads : maxThreads; threads = inBetween(1, threads, maxThreads); - auto lastStatusOutput = std::chrono::system_clock::now(); + auto timeBeginning = std::chrono::system_clock::now(); + auto lastStatusOutput = timeBeginning; int statusOutputDelay = 0; std::mutex mutex; @@ -296,31 +316,41 @@ void stressTest(const CmdOptions& opts) } else { + // --timeout option + if (opts.timeout) + { + std::chrono::duration secsBeginning = t2 - timeBeginning; + if (secsBeginning.count() >= (double) opts.timeout) + return; + } + + // --quiet option, no status output + if (opts.quiet) + continue; + // We don't wait here. Keeping the CPU busy is more // important then printing status output. std::unique_lock lock(mutex, std::try_to_lock); - - if (lock.owns_lock()) + if (!lock.owns_lock()) + continue; + + // We slowly increase the status output delay (in seconds) + // until it reaches 10 minutes. This way, long running + // computations don't produce excessive logs. + std::chrono::duration secsStatus = t2 - lastStatusOutput; + if (secsStatus.count() >= statusOutputDelay) { - std::chrono::duration secsStatus = t2 - lastStatusOutput; - - // We slowly increase the status output delay (in seconds) - // until it reaches 10 minutes. This way, long running - // computations don't produce excessive logs. - if (secsStatus.count() >= statusOutputDelay) - { - lastStatusOutput = t2; - statusOutputDelay += 5; - statusOutputDelay = std::min(statusOutputDelay, 600); - printResult(threadId, threads, i, count, secsThread, primeCounts); - } + lastStatusOutput = t2; + statusOutputDelay += 5; + statusOutputDelay = std::min(statusOutputDelay, 600); + printResult(threadId, threads, i, count, secsThread, primeCounts); } } } } }; - stressTestInfo(opts.stressTestMode, threads); + stressTestInfo(opts, threads); using primesieve::Vector; Vector> futures; @@ -343,4 +373,10 @@ void stressTest(const CmdOptions& opts) for (auto& future : futures) future.wait(); + + // Add new line if test results have been printed + if (statusOutputDelay > 0) + std::cout << std::endl; + + std::cout << "All tests passed successfully!" << std::endl; }