mirror of
https://github.com/ossrs/srs.git
synced 2025-03-09 15:49:59 +00:00
631 lines
22 KiB
C++
631 lines
22 KiB
C++
// -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*-
|
|
// Copyright (c) 2008, Google Inc.
|
|
// All rights reserved.
|
|
//
|
|
// Redistribution and use in source and binary forms, with or without
|
|
// modification, are permitted provided that the following conditions are
|
|
// met:
|
|
//
|
|
// * Redistributions of source code must retain the above copyright
|
|
// notice, this list of conditions and the following disclaimer.
|
|
// * Redistributions in binary form must reproduce the above
|
|
// copyright notice, this list of conditions and the following disclaimer
|
|
// in the documentation and/or other materials provided with the
|
|
// distribution.
|
|
// * Neither the name of Google Inc. nor the names of its
|
|
// contributors may be used to endorse or promote products derived from
|
|
// this software without specific prior written permission.
|
|
//
|
|
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
|
|
// ---
|
|
// All Rights Reserved.
|
|
//
|
|
// Author: Daniel Ford
|
|
//
|
|
// Checks basic properties of the sampler
|
|
|
|
#include "config_for_unittests.h"
|
|
#include <stdlib.h> // defines posix_memalign
|
|
#include <stdio.h> // for the printf at the end
|
|
#if defined HAVE_STDINT_H
|
|
#include <stdint.h> // to get uintptr_t
|
|
#elif defined HAVE_INTTYPES_H
|
|
#include <inttypes.h> // another place uintptr_t might be defined
|
|
#endif
|
|
#include <sys/types.h>
|
|
#include <iostream>
|
|
#include <algorithm>
|
|
#include <vector>
|
|
#include <string>
|
|
#include <math.h>
|
|
#include "base/logging.h"
|
|
#include "base/commandlineflags.h"
|
|
#include "sampler.h" // The Sampler class being tested
|
|
|
|
using std::sort;
|
|
using std::min;
|
|
using std::max;
|
|
using std::vector;
|
|
using std::abs;
|
|
|
|
vector<void (*)()> g_testlist; // the tests to run
|
|
|
|
#define TEST(a, b) \
|
|
struct Test_##a##_##b { \
|
|
Test_##a##_##b() { g_testlist.push_back(&Run); } \
|
|
static void Run(); \
|
|
}; \
|
|
static Test_##a##_##b g_test_##a##_##b; \
|
|
void Test_##a##_##b::Run()
|
|
|
|
|
|
static int RUN_ALL_TESTS() {
|
|
vector<void (*)()>::const_iterator it;
|
|
for (it = g_testlist.begin(); it != g_testlist.end(); ++it) {
|
|
(*it)(); // The test will error-exit if there's a problem.
|
|
}
|
|
fprintf(stderr, "\nPassed %d tests\n\nPASS\n", (int)g_testlist.size());
|
|
return 0;
|
|
}
|
|
|
|
#undef LOG // defined in base/logging.h
|
|
// Ideally, we'd put the newline at the end, but this hack puts the
|
|
// newline at the end of the previous log message, which is good enough :-)
|
|
#define LOG(level) std::cerr << "\n"
|
|
|
|
static std::string StringPrintf(const char* format, ...) {
|
|
char buf[256]; // should be big enough for all logging
|
|
va_list ap;
|
|
va_start(ap, format);
|
|
perftools_vsnprintf(buf, sizeof(buf), format, ap);
|
|
va_end(ap);
|
|
return buf;
|
|
}
|
|
|
|
namespace {
|
|
template<typename T> class scoped_array {
|
|
public:
|
|
scoped_array(T* p) : p_(p) { }
|
|
~scoped_array() { delete[] p_; }
|
|
const T* get() const { return p_; }
|
|
T* get() { return p_; }
|
|
T& operator[](int i) { return p_[i]; }
|
|
private:
|
|
T* p_;
|
|
};
|
|
}
|
|
|
|
// Note that these tests are stochastic.
|
|
// This mean that the chance of correct code passing the test is,
|
|
// in the case of 5 standard deviations:
|
|
// kSigmas=5: ~99.99994267%
|
|
// in the case of 4 standard deviations:
|
|
// kSigmas=4: ~99.993666%
|
|
static const double kSigmas = 4;
|
|
static const size_t kSamplingInterval = 512*1024;
|
|
|
|
// Tests that GetSamplePeriod returns the expected value
|
|
// which is 1<<19
|
|
TEST(Sampler, TestGetSamplePeriod) {
|
|
tcmalloc::Sampler sampler;
|
|
sampler.Init(1);
|
|
uint64_t sample_period;
|
|
sample_period = sampler.GetSamplePeriod();
|
|
CHECK_GT(sample_period, 0);
|
|
}
|
|
|
|
// Tests of the quality of the random numbers generated
|
|
// This uses the Anderson Darling test for uniformity.
|
|
// See "Evaluating the Anderson-Darling Distribution" by Marsaglia
|
|
// for details.
|
|
|
|
// Short cut version of ADinf(z), z>0 (from Marsaglia)
|
|
// This returns the p-value for Anderson Darling statistic in
|
|
// the limit as n-> infinity. For finite n, apply the error fix below.
|
|
double AndersonDarlingInf(double z) {
|
|
if (z < 2) {
|
|
return exp(-1.2337141 / z) / sqrt(z) * (2.00012 + (0.247105 -
|
|
(0.0649821 - (0.0347962 - (0.011672 - 0.00168691
|
|
* z) * z) * z) * z) * z);
|
|
}
|
|
return exp( - exp(1.0776 - (2.30695 - (0.43424 - (0.082433 -
|
|
(0.008056 - 0.0003146 * z) * z) * z) * z) * z));
|
|
}
|
|
|
|
// Corrects the approximation error in AndersonDarlingInf for small values of n
|
|
// Add this to AndersonDarlingInf to get a better approximation
|
|
// (from Marsaglia)
|
|
double AndersonDarlingErrFix(int n, double x) {
|
|
if (x > 0.8) {
|
|
return (-130.2137 + (745.2337 - (1705.091 - (1950.646 -
|
|
(1116.360 - 255.7844 * x) * x) * x) * x) * x) / n;
|
|
}
|
|
double cutoff = 0.01265 + 0.1757 / n;
|
|
double t;
|
|
if (x < cutoff) {
|
|
t = x / cutoff;
|
|
t = sqrt(t) * (1 - t) * (49 * t - 102);
|
|
return t * (0.0037 / (n * n) + 0.00078 / n + 0.00006) / n;
|
|
} else {
|
|
t = (x - cutoff) / (0.8 - cutoff);
|
|
t = -0.00022633 + (6.54034 - (14.6538 - (14.458 - (8.259 - 1.91864
|
|
* t) * t) * t) * t) * t;
|
|
return t * (0.04213 + 0.01365 / n) / n;
|
|
}
|
|
}
|
|
|
|
// Returns the AndersonDarling p-value given n and the value of the statistic
|
|
double AndersonDarlingPValue(int n, double z) {
|
|
double ad = AndersonDarlingInf(z);
|
|
double errfix = AndersonDarlingErrFix(n, ad);
|
|
return ad + errfix;
|
|
}
|
|
|
|
double AndersonDarlingStatistic(int n, double* random_sample) {
|
|
double ad_sum = 0;
|
|
for (int i = 0; i < n; i++) {
|
|
ad_sum += (2*i + 1) * log(random_sample[i] * (1 - random_sample[n-1-i]));
|
|
}
|
|
double ad_statistic = - n - 1/static_cast<double>(n) * ad_sum;
|
|
return ad_statistic;
|
|
}
|
|
|
|
// Tests if the array of doubles is uniformly distributed.
|
|
// Returns the p-value of the Anderson Darling Statistic
|
|
// for the given set of sorted random doubles
|
|
// See "Evaluating the Anderson-Darling Distribution" by
|
|
// Marsaglia and Marsaglia for details.
|
|
double AndersonDarlingTest(int n, double* random_sample) {
|
|
double ad_statistic = AndersonDarlingStatistic(n, random_sample);
|
|
LOG(INFO) << StringPrintf("AD stat = %f, n=%d\n", ad_statistic, n);
|
|
double p = AndersonDarlingPValue(n, ad_statistic);
|
|
return p;
|
|
}
|
|
|
|
// Test the AD Test. The value of the statistic should go to zero as n->infty
|
|
// Not run as part of regular tests
|
|
void ADTestTest(int n) {
|
|
scoped_array<double> random_sample(new double[n]);
|
|
for (int i = 0; i < n; i++) {
|
|
random_sample[i] = (i+0.01)/n;
|
|
}
|
|
sort(random_sample.get(), random_sample.get() + n);
|
|
double ad_stat = AndersonDarlingStatistic(n, random_sample.get());
|
|
LOG(INFO) << StringPrintf("Testing the AD test. n=%d, ad_stat = %f",
|
|
n, ad_stat);
|
|
}
|
|
|
|
// Print the CDF of the distribution of the Anderson-Darling Statistic
|
|
// Used for checking the Anderson-Darling Test
|
|
// Not run as part of regular tests
|
|
void ADCDF() {
|
|
for (int i = 1; i < 40; i++) {
|
|
double x = i/10.0;
|
|
LOG(INFO) << "x= " << x << " adpv= "
|
|
<< AndersonDarlingPValue(100, x) << ", "
|
|
<< AndersonDarlingPValue(1000, x);
|
|
}
|
|
}
|
|
|
|
// Testing that NextRandom generates uniform
|
|
// random numbers.
|
|
// Applies the Anderson-Darling test for uniformity
|
|
void TestNextRandom(int n) {
|
|
tcmalloc::Sampler sampler;
|
|
sampler.Init(1);
|
|
uint64_t x = 1;
|
|
// This assumes that the prng returns 48 bit numbers
|
|
uint64_t max_prng_value = static_cast<uint64_t>(1)<<48;
|
|
// Initialize
|
|
for (int i = 1; i <= 20; i++) { // 20 mimics sampler.Init()
|
|
x = sampler.NextRandom(x);
|
|
}
|
|
scoped_array<uint64_t> int_random_sample(new uint64_t[n]);
|
|
// Collect samples
|
|
for (int i = 0; i < n; i++) {
|
|
int_random_sample[i] = x;
|
|
x = sampler.NextRandom(x);
|
|
}
|
|
// First sort them...
|
|
sort(int_random_sample.get(), int_random_sample.get() + n);
|
|
scoped_array<double> random_sample(new double[n]);
|
|
// Convert them to uniform randoms (in the range [0,1])
|
|
for (int i = 0; i < n; i++) {
|
|
random_sample[i] = static_cast<double>(int_random_sample[i])/max_prng_value;
|
|
}
|
|
// Now compute the Anderson-Darling statistic
|
|
double ad_pvalue = AndersonDarlingTest(n, random_sample.get());
|
|
LOG(INFO) << StringPrintf("pvalue for AndersonDarlingTest "
|
|
"with n= %d is p= %f\n", n, ad_pvalue);
|
|
CHECK_GT(min(ad_pvalue, 1 - ad_pvalue), 0.0001);
|
|
// << StringPrintf("prng is not uniform, %d\n", n);
|
|
}
|
|
|
|
|
|
TEST(Sampler, TestNextRandom_MultipleValues) {
|
|
TestNextRandom(10); // Check short-range correlation
|
|
TestNextRandom(100);
|
|
TestNextRandom(1000);
|
|
TestNextRandom(10000); // Make sure there's no systematic error
|
|
}
|
|
|
|
// Tests that PickNextSamplePeriod generates
|
|
// geometrically distributed random numbers.
|
|
// First converts to uniforms then applied the
|
|
// Anderson-Darling test for uniformity.
|
|
void TestPickNextSample(int n) {
|
|
tcmalloc::Sampler sampler;
|
|
sampler.Init(1);
|
|
scoped_array<uint64_t> int_random_sample(new uint64_t[n]);
|
|
int sample_period = sampler.GetSamplePeriod();
|
|
int ones_count = 0;
|
|
for (int i = 0; i < n; i++) {
|
|
int_random_sample[i] = sampler.PickNextSamplingPoint();
|
|
CHECK_GE(int_random_sample[i], 1);
|
|
if (int_random_sample[i] == 1) {
|
|
ones_count += 1;
|
|
}
|
|
CHECK_LT(ones_count, 4); // << " out of " << i << " samples.";
|
|
}
|
|
// First sort them...
|
|
sort(int_random_sample.get(), int_random_sample.get() + n);
|
|
scoped_array<double> random_sample(new double[n]);
|
|
// Convert them to uniform random numbers
|
|
// by applying the geometric CDF
|
|
for (int i = 0; i < n; i++) {
|
|
random_sample[i] = 1 - exp(-static_cast<double>(int_random_sample[i])
|
|
/ sample_period);
|
|
}
|
|
// Now compute the Anderson-Darling statistic
|
|
double geom_ad_pvalue = AndersonDarlingTest(n, random_sample.get());
|
|
LOG(INFO) << StringPrintf("pvalue for geometric AndersonDarlingTest "
|
|
"with n= %d is p= %f\n", n, geom_ad_pvalue);
|
|
CHECK_GT(min(geom_ad_pvalue, 1 - geom_ad_pvalue), 0.0001);
|
|
// << "PickNextSamplingPoint does not produce good "
|
|
// "geometric/exponential random numbers\n";
|
|
}
|
|
|
|
TEST(Sampler, TestPickNextSample_MultipleValues) {
|
|
TestPickNextSample(10); // Make sure the first few are good (enough)
|
|
TestPickNextSample(100);
|
|
TestPickNextSample(1000);
|
|
TestPickNextSample(10000); // Make sure there's no systematic error
|
|
}
|
|
|
|
|
|
// This is superceeded by the Anderson-Darling Test
|
|
// and it not run now.
|
|
// Tests how fast nearby values are spread out with LRand64
|
|
// The purpose of this code is to determine how many
|
|
// steps to apply to the seed during initialization
|
|
void TestLRand64Spread() {
|
|
tcmalloc::Sampler sampler;
|
|
sampler.Init(1);
|
|
uint64_t current_value;
|
|
printf("Testing LRand64 Spread\n");
|
|
for (int i = 1; i < 10; i++) {
|
|
printf("%d ", i);
|
|
current_value = i;
|
|
for (int j = 1; j < 100; j++) {
|
|
current_value = sampler.NextRandom(current_value);
|
|
}
|
|
LOG(INFO) << current_value;
|
|
}
|
|
}
|
|
|
|
|
|
// Futher tests
|
|
|
|
bool CheckMean(size_t mean, int num_samples) {
|
|
tcmalloc::Sampler sampler;
|
|
sampler.Init(1);
|
|
size_t total = 0;
|
|
for (int i = 0; i < num_samples; i++) {
|
|
total += sampler.PickNextSamplingPoint();
|
|
}
|
|
double empirical_mean = total / static_cast<double>(num_samples);
|
|
double expected_sd = mean / pow(num_samples * 1.0, 0.5);
|
|
return(fabs(mean-empirical_mean) < expected_sd * kSigmas);
|
|
}
|
|
|
|
// Prints a sequence so you can look at the distribution
|
|
void OutputSequence(int sequence_length) {
|
|
tcmalloc::Sampler sampler;
|
|
sampler.Init(1);
|
|
size_t next_step;
|
|
for (int i = 0; i< sequence_length; i++) {
|
|
next_step = sampler.PickNextSamplingPoint();
|
|
LOG(INFO) << next_step;
|
|
}
|
|
}
|
|
|
|
|
|
double StandardDeviationsErrorInSample(
|
|
int total_samples, int picked_samples,
|
|
int alloc_size, int sampling_interval) {
|
|
double p = 1 - exp(-(static_cast<double>(alloc_size) / sampling_interval));
|
|
double expected_samples = total_samples * p;
|
|
double sd = pow(p*(1-p)*total_samples, 0.5);
|
|
return((picked_samples - expected_samples) / sd);
|
|
}
|
|
|
|
TEST(Sampler, LargeAndSmallAllocs_CombinedTest) {
|
|
tcmalloc::Sampler sampler;
|
|
sampler.Init(1);
|
|
int counter_big = 0;
|
|
int counter_small = 0;
|
|
int size_big = 129*8*1024+1;
|
|
int size_small = 1024*8;
|
|
int num_iters = 128*4*8;
|
|
// Allocate in mixed chunks
|
|
for (int i = 0; i < num_iters; i++) {
|
|
if (!sampler.RecordAllocation(size_big)) {
|
|
counter_big += 1;
|
|
}
|
|
for (int i = 0; i < 129; i++) {
|
|
if (!sampler.RecordAllocation(size_small)) {
|
|
counter_small += 1;
|
|
}
|
|
}
|
|
}
|
|
// Now test that there are the right number of each
|
|
double large_allocs_sds =
|
|
StandardDeviationsErrorInSample(num_iters, counter_big,
|
|
size_big, kSamplingInterval);
|
|
double small_allocs_sds =
|
|
StandardDeviationsErrorInSample(num_iters*129, counter_small,
|
|
size_small, kSamplingInterval);
|
|
LOG(INFO) << StringPrintf("large_allocs_sds = %f\n", large_allocs_sds);
|
|
LOG(INFO) << StringPrintf("small_allocs_sds = %f\n", small_allocs_sds);
|
|
CHECK_LE(fabs(large_allocs_sds), kSigmas);
|
|
CHECK_LE(fabs(small_allocs_sds), kSigmas);
|
|
}
|
|
|
|
// Tests whether the mean is about right over 1000 samples
|
|
TEST(Sampler, IsMeanRight) {
|
|
CHECK(CheckMean(kSamplingInterval, 1000));
|
|
}
|
|
|
|
// This flag is for the OldSampler class to use
|
|
const int64 FLAGS_mock_tcmalloc_sample_parameter = 1<<19;
|
|
|
|
// A cut down and slightly refactored version of the old Sampler
|
|
class OldSampler {
|
|
public:
|
|
void Init(uint32_t seed);
|
|
void Cleanup() {}
|
|
|
|
// Record allocation of "k" bytes. Return true iff allocation
|
|
// should be sampled
|
|
bool SampleAllocation(size_t k);
|
|
|
|
// Generate a geometric with mean 1M (or FLAG value)
|
|
void PickNextSample(size_t k);
|
|
|
|
// Initialize the statics for the Sample class
|
|
static void InitStatics() {
|
|
sample_period = 1048583;
|
|
}
|
|
size_t bytes_until_sample_;
|
|
|
|
private:
|
|
uint32_t rnd_; // Cheap random number generator
|
|
static uint64_t sample_period;
|
|
// Should be a prime just above a power of 2:
|
|
// 2, 5, 11, 17, 37, 67, 131, 257,
|
|
// 521, 1031, 2053, 4099, 8209, 16411,
|
|
// 32771, 65537, 131101, 262147, 524309, 1048583,
|
|
// 2097169, 4194319, 8388617, 16777259, 33554467
|
|
};
|
|
|
|
// Statics for OldSampler
|
|
uint64_t OldSampler::sample_period;
|
|
|
|
void OldSampler::Init(uint32_t seed) {
|
|
// Initialize PRNG -- run it for a bit to get to good values
|
|
if (seed != 0) {
|
|
rnd_ = seed;
|
|
} else {
|
|
rnd_ = 12345;
|
|
}
|
|
bytes_until_sample_ = 0;
|
|
for (int i = 0; i < 100; i++) {
|
|
PickNextSample(sample_period * 2);
|
|
}
|
|
};
|
|
|
|
// A cut-down version of the old PickNextSampleRoutine
|
|
void OldSampler::PickNextSample(size_t k) {
|
|
// Make next "random" number
|
|
// x^32+x^22+x^2+x^1+1 is a primitive polynomial for random numbers
|
|
static const uint32_t kPoly = (1 << 22) | (1 << 2) | (1 << 1) | (1 << 0);
|
|
uint32_t r = rnd_;
|
|
rnd_ = (r << 1) ^ ((static_cast<int32_t>(r) >> 31) & kPoly);
|
|
|
|
// Next point is "rnd_ % (sample_period)". I.e., average
|
|
// increment is "sample_period/2".
|
|
const int flag_value = FLAGS_mock_tcmalloc_sample_parameter;
|
|
static int last_flag_value = -1;
|
|
|
|
if (flag_value != last_flag_value) {
|
|
// There should be a spinlock here, but this code is
|
|
// for benchmarking only.
|
|
sample_period = 1048583;
|
|
last_flag_value = flag_value;
|
|
}
|
|
|
|
bytes_until_sample_ += rnd_ % sample_period;
|
|
|
|
if (k > (static_cast<size_t>(-1) >> 2)) {
|
|
// If the user has asked for a huge allocation then it is possible
|
|
// for the code below to loop infinitely. Just return (note that
|
|
// this throws off the sampling accuracy somewhat, but a user who
|
|
// is allocating more than 1G of memory at a time can live with a
|
|
// minor inaccuracy in profiling of small allocations, and also
|
|
// would rather not wait for the loop below to terminate).
|
|
return;
|
|
}
|
|
|
|
while (bytes_until_sample_ < k) {
|
|
// Increase bytes_until_sample_ by enough average sampling periods
|
|
// (sample_period >> 1) to allow us to sample past the current
|
|
// allocation.
|
|
bytes_until_sample_ += (sample_period >> 1);
|
|
}
|
|
|
|
bytes_until_sample_ -= k;
|
|
}
|
|
|
|
inline bool OldSampler::SampleAllocation(size_t k) {
|
|
if (bytes_until_sample_ < k) {
|
|
PickNextSample(k);
|
|
return true;
|
|
} else {
|
|
bytes_until_sample_ -= k;
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// This checks that the stated maximum value for the
|
|
// tcmalloc_sample_parameter flag never overflows bytes_until_sample_
|
|
TEST(Sampler, bytes_until_sample_Overflow_Underflow) {
|
|
tcmalloc::Sampler sampler;
|
|
sampler.Init(1);
|
|
uint64_t one = 1;
|
|
// sample_parameter = 0; // To test the edge case
|
|
uint64_t sample_parameter_array[4] = {0, 1, one<<19, one<<58};
|
|
for (int i = 0; i < 4; i++) {
|
|
uint64_t sample_parameter = sample_parameter_array[i];
|
|
LOG(INFO) << "sample_parameter = " << sample_parameter;
|
|
double sample_scaling = - log(2.0) * sample_parameter;
|
|
// Take the top 26 bits as the random number
|
|
// (This plus the 1<<26 sampling bound give a max step possible of
|
|
// 1209424308 bytes.)
|
|
const uint64_t prng_mod_power = 48; // Number of bits in prng
|
|
|
|
// First, check the largest_prng value
|
|
uint64_t largest_prng_value = (static_cast<uint64_t>(1)<<48) - 1;
|
|
double q = (largest_prng_value >> (prng_mod_power - 26)) + 1.0;
|
|
LOG(INFO) << StringPrintf("q = %f\n", q);
|
|
LOG(INFO) << StringPrintf("log2(q) = %f\n", log(q)/log(2.0));
|
|
uint64_t smallest_sample_step
|
|
= static_cast<uint64_t>(min(log2(q) - 26, 0.0)
|
|
* sample_scaling + 1);
|
|
LOG(INFO) << "Smallest sample step is " << smallest_sample_step;
|
|
uint64_t cutoff = static_cast<uint64_t>(10)
|
|
* (sample_parameter/(one<<24) + 1);
|
|
LOG(INFO) << "Acceptable value is < " << cutoff;
|
|
// This checks that the answer is "small" and positive
|
|
CHECK_LE(smallest_sample_step, cutoff);
|
|
|
|
// Next, check with the smallest prng value
|
|
uint64_t smallest_prng_value = 0;
|
|
q = (smallest_prng_value >> (prng_mod_power - 26)) + 1.0;
|
|
LOG(INFO) << StringPrintf("q = %f\n", q);
|
|
uint64_t largest_sample_step
|
|
= static_cast<uint64_t>(min(log2(q) - 26, 0.0)
|
|
* sample_scaling + 1);
|
|
LOG(INFO) << "Largest sample step is " << largest_sample_step;
|
|
CHECK_LE(largest_sample_step, one<<63);
|
|
CHECK_GE(largest_sample_step, smallest_sample_step);
|
|
}
|
|
}
|
|
|
|
|
|
// Test that NextRand is in the right range. Unfortunately, this is a
|
|
// stochastic test which could miss problems.
|
|
TEST(Sampler, NextRand_range) {
|
|
tcmalloc::Sampler sampler;
|
|
sampler.Init(1);
|
|
uint64_t one = 1;
|
|
// The next number should be (one << 48) - 1
|
|
uint64_t max_value = (one << 48) - 1;
|
|
uint64_t x = (one << 55);
|
|
int n = 22; // 27;
|
|
LOG(INFO) << "Running sampler.NextRandom 1<<" << n << " times";
|
|
for (int i = 1; i <= (1<<n); i++) { // 20 mimics sampler.Init()
|
|
x = sampler.NextRandom(x);
|
|
CHECK_LE(x, max_value);
|
|
}
|
|
}
|
|
|
|
// Tests certain arithmetic operations to make sure they compute what we
|
|
// expect them too (for testing across different platforms)
|
|
TEST(Sampler, arithmetic_1) {
|
|
tcmalloc::Sampler sampler;
|
|
sampler.Init(1);
|
|
uint64_t rnd; // our 48 bit random number, which we don't trust
|
|
const uint64_t prng_mod_power = 48;
|
|
uint64_t one = 1;
|
|
rnd = one;
|
|
uint64_t max_value = (one << 48) - 1;
|
|
for (int i = 1; i <= (1>>27); i++) { // 20 mimics sampler.Init()
|
|
rnd = sampler.NextRandom(rnd);
|
|
CHECK_LE(rnd, max_value);
|
|
double q = (rnd >> (prng_mod_power - 26)) + 1.0;
|
|
CHECK_GE(q, 0); // << rnd << " " << prng_mod_power;
|
|
}
|
|
// Test some potentially out of bounds value for rnd
|
|
for (int i = 1; i <= 63; i++) {
|
|
rnd = one << i;
|
|
double q = (rnd >> (prng_mod_power - 26)) + 1.0;
|
|
LOG(INFO) << "rnd = " << rnd << " i=" << i << " q=" << q;
|
|
CHECK_GE(q, 0);
|
|
// << " rnd=" << rnd << " i=" << i << " prng_mod_power" << prng_mod_power;
|
|
}
|
|
}
|
|
|
|
void test_arithmetic(uint64_t rnd) {
|
|
const uint64_t prng_mod_power = 48; // Number of bits in prng
|
|
uint64_t shifted_rnd = rnd >> (prng_mod_power - 26);
|
|
CHECK_GE(shifted_rnd, 0);
|
|
CHECK_LT(shifted_rnd, (1<<26));
|
|
LOG(INFO) << shifted_rnd;
|
|
LOG(INFO) << static_cast<double>(shifted_rnd);
|
|
CHECK_GE(static_cast<double>(static_cast<uint32_t>(shifted_rnd)), 0);
|
|
// << " rnd=" << rnd << " srnd=" << shifted_rnd;
|
|
CHECK_GE(static_cast<double>(shifted_rnd), 0);
|
|
// << " rnd=" << rnd << " srnd=" << shifted_rnd;
|
|
double q = static_cast<double>(shifted_rnd) + 1.0;
|
|
CHECK_GT(q, 0);
|
|
}
|
|
|
|
// Tests certain arithmetic operations to make sure they compute what we
|
|
// expect them too (for testing across different platforms)
|
|
// know bad values under with -c dbg --cpu piii for _some_ binaries:
|
|
// rnd=227453640600554
|
|
// shifted_rnd=54229173
|
|
// (hard to reproduce)
|
|
TEST(Sampler, arithmetic_2) {
|
|
uint64_t rnd = 227453640600554LL;
|
|
test_arithmetic(rnd);
|
|
}
|
|
|
|
|
|
// It's not really a test, but it's good to know
|
|
TEST(Sample, size_of_class) {
|
|
tcmalloc::Sampler sampler;
|
|
sampler.Init(1);
|
|
LOG(INFO) << "Size of Sampler class is: " << sizeof(tcmalloc::Sampler);
|
|
LOG(INFO) << "Size of Sampler object is: " << sizeof(sampler);
|
|
}
|
|
|
|
// Make sure sampling is enabled, or the tests won't work right.
|
|
DECLARE_int64(tcmalloc_sample_parameter);
|
|
|
|
int main(int argc, char **argv) {
|
|
if (FLAGS_tcmalloc_sample_parameter == 0)
|
|
FLAGS_tcmalloc_sample_parameter = 524288;
|
|
return RUN_ALL_TESTS();
|
|
}
|