git-subtree-dir: third_party/abseil_cpp git-subtree-mainline:ffb2ae54begit-subtree-split:768eb2ca28
		
			
				
	
	
		
			172 lines
		
	
	
	
		
			7.5 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			172 lines
		
	
	
	
		
			7.5 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| // Copyright 2017 Google Inc. All Rights Reserved.
 | |
| //
 | |
| // Licensed under the Apache License, Version 2.0 (the "License");
 | |
| // you may not use this file except in compliance with the License.
 | |
| // You may obtain a copy of the License at
 | |
| //
 | |
| //     https://www.apache.org/licenses/LICENSE-2.0
 | |
| //
 | |
| // Unless required by applicable law or agreed to in writing, software
 | |
| // distributed under the License is distributed on an "AS IS" BASIS,
 | |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | |
| // See the License for the specific language governing permissions and
 | |
| // limitations under the License.
 | |
| 
 | |
| #ifndef ABSL_RANDOM_INTERNAL_NANOBENCHMARK_H_
 | |
| #define ABSL_RANDOM_INTERNAL_NANOBENCHMARK_H_
 | |
| 
 | |
| // Benchmarks functions of a single integer argument with realistic branch
 | |
| // prediction hit rates. Uses a robust estimator to summarize the measurements.
 | |
| // The precision is about 0.2%.
 | |
| //
 | |
| // Examples: see nanobenchmark_test.cc.
 | |
| //
 | |
| // Background: Microbenchmarks such as http://github.com/google/benchmark
 | |
| // can measure elapsed times on the order of a microsecond. Shorter functions
 | |
| // are typically measured by repeating them thousands of times and dividing
 | |
| // the total elapsed time by this count. Unfortunately, repetition (especially
 | |
| // with the same input parameter!) influences the runtime. In time-critical
 | |
| // code, it is reasonable to expect warm instruction/data caches and TLBs,
 | |
| // but a perfect record of which branches will be taken is unrealistic.
 | |
| // Unless the application also repeatedly invokes the measured function with
 | |
| // the same parameter, the benchmark is measuring something very different -
 | |
| // a best-case result, almost as if the parameter were made a compile-time
 | |
| // constant. This may lead to erroneous conclusions about branch-heavy
 | |
| // algorithms outperforming branch-free alternatives.
 | |
| //
 | |
| // Our approach differs in three ways. Adding fences to the timer functions
 | |
| // reduces variability due to instruction reordering, improving the timer
 | |
| // resolution to about 40 CPU cycles. However, shorter functions must still
 | |
| // be invoked repeatedly. For more realistic branch prediction performance,
 | |
| // we vary the input parameter according to a user-specified distribution.
 | |
| // Thus, instead of VaryInputs(Measure(Repeat(func))), we change the
 | |
| // loop nesting to Measure(Repeat(VaryInputs(func))). We also estimate the
 | |
| // central tendency of the measurement samples with the "half sample mode",
 | |
| // which is more robust to outliers and skewed data than the mean or median.
 | |
| 
 | |
| // NOTE: for compatibility with multiple translation units compiled with
 | |
| // distinct flags, avoid #including headers that define functions.
 | |
| 
 | |
| #include <stddef.h>
 | |
| #include <stdint.h>
 | |
| 
 | |
| #include "absl/base/config.h"
 | |
| 
 | |
| namespace absl {
 | |
| ABSL_NAMESPACE_BEGIN
 | |
| namespace random_internal_nanobenchmark {
 | |
| 
 | |
| // Input influencing the function being measured (e.g. number of bytes to copy).
 | |
| using FuncInput = size_t;
 | |
| 
 | |
| // "Proof of work" returned by Func to ensure the compiler does not elide it.
 | |
| using FuncOutput = uint64_t;
 | |
| 
 | |
| // Function to measure: either 1) a captureless lambda or function with two
 | |
| // arguments or 2) a lambda with capture, in which case the first argument
 | |
| // is reserved for use by MeasureClosure.
 | |
| using Func = FuncOutput (*)(const void*, FuncInput);
 | |
| 
 | |
| // Internal parameters that determine precision/resolution/measuring time.
 | |
| struct Params {
 | |
|   // For measuring timer overhead/resolution. Used in a nested loop =>
 | |
|   // quadratic time, acceptable because we know timer overhead is "low".
 | |
|   // constexpr because this is used to define array bounds.
 | |
|   static constexpr size_t kTimerSamples = 256;
 | |
| 
 | |
|   // Best-case precision, expressed as a divisor of the timer resolution.
 | |
|   // Larger => more calls to Func and higher precision.
 | |
|   size_t precision_divisor = 1024;
 | |
| 
 | |
|   // Ratio between full and subset input distribution sizes. Cannot be less
 | |
|   // than 2; larger values increase measurement time but more faithfully
 | |
|   // model the given input distribution.
 | |
|   size_t subset_ratio = 2;
 | |
| 
 | |
|   // Together with the estimated Func duration, determines how many times to
 | |
|   // call Func before checking the sample variability. Larger values increase
 | |
|   // measurement time, memory/cache use and precision.
 | |
|   double seconds_per_eval = 4E-3;
 | |
| 
 | |
|   // The minimum number of samples before estimating the central tendency.
 | |
|   size_t min_samples_per_eval = 7;
 | |
| 
 | |
|   // The mode is better than median for estimating the central tendency of
 | |
|   // skewed/fat-tailed distributions, but it requires sufficient samples
 | |
|   // relative to the width of half-ranges.
 | |
|   size_t min_mode_samples = 64;
 | |
| 
 | |
|   // Maximum permissible variability (= median absolute deviation / center).
 | |
|   double target_rel_mad = 0.002;
 | |
| 
 | |
|   // Abort after this many evals without reaching target_rel_mad. This
 | |
|   // prevents infinite loops.
 | |
|   size_t max_evals = 9;
 | |
| 
 | |
|   // Retry the measure loop up to this many times.
 | |
|   size_t max_measure_retries = 2;
 | |
| 
 | |
|   // Whether to print additional statistics to stdout.
 | |
|   bool verbose = true;
 | |
| };
 | |
| 
 | |
| // Measurement result for each unique input.
 | |
| struct Result {
 | |
|   FuncInput input;
 | |
| 
 | |
|   // Robust estimate (mode or median) of duration.
 | |
|   float ticks;
 | |
| 
 | |
|   // Measure of variability (median absolute deviation relative to "ticks").
 | |
|   float variability;
 | |
| };
 | |
| 
 | |
| // Ensures the thread is running on the specified cpu, and no others.
 | |
| // Reduces noise due to desynchronized socket RDTSC and context switches.
 | |
| // If "cpu" is negative, pin to the currently running core.
 | |
| void PinThreadToCPU(const int cpu = -1);
 | |
| 
 | |
| // Returns tick rate, useful for converting measurements to seconds. Invariant
 | |
| // means the tick counter frequency is independent of CPU throttling or sleep.
 | |
| // This call may be expensive, callers should cache the result.
 | |
| double InvariantTicksPerSecond();
 | |
| 
 | |
| // Precisely measures the number of ticks elapsed when calling "func" with the
 | |
| // given inputs, shuffled to ensure realistic branch prediction hit rates.
 | |
| //
 | |
| // "func" returns a 'proof of work' to ensure its computations are not elided.
 | |
| // "arg" is passed to Func, or reserved for internal use by MeasureClosure.
 | |
| // "inputs" is an array of "num_inputs" (not necessarily unique) arguments to
 | |
| //   "func". The values should be chosen to maximize coverage of "func". This
 | |
| //   represents a distribution, so a value's frequency should reflect its
 | |
| //   probability in the real application. Order does not matter; for example, a
 | |
| //   uniform distribution over [0, 4) could be represented as {3,0,2,1}.
 | |
| // Returns how many Result were written to "results": one per unique input, or
 | |
| //   zero if the measurement failed (an error message goes to stderr).
 | |
| size_t Measure(const Func func, const void* arg, const FuncInput* inputs,
 | |
|                const size_t num_inputs, Result* results,
 | |
|                const Params& p = Params());
 | |
| 
 | |
| // Calls operator() of the given closure (lambda function).
 | |
| template <class Closure>
 | |
| static FuncOutput CallClosure(const void* f, const FuncInput input) {
 | |
|   return (*reinterpret_cast<const Closure*>(f))(input);
 | |
| }
 | |
| 
 | |
| // Same as Measure, except "closure" is typically a lambda function of
 | |
| // FuncInput -> FuncOutput with a capture list.
 | |
| template <class Closure>
 | |
| static inline size_t MeasureClosure(const Closure& closure,
 | |
|                                     const FuncInput* inputs,
 | |
|                                     const size_t num_inputs, Result* results,
 | |
|                                     const Params& p = Params()) {
 | |
|   return Measure(reinterpret_cast<Func>(&CallClosure<Closure>),
 | |
|                  reinterpret_cast<const void*>(&closure), inputs, num_inputs,
 | |
|                  results, p);
 | |
| }
 | |
| 
 | |
| }  // namespace random_internal_nanobenchmark
 | |
| ABSL_NAMESPACE_END
 | |
| }  // namespace absl
 | |
| 
 | |
| #endif  // ABSL_RANDOM_INTERNAL_NANOBENCHMARK_H_
 |