mirror of
https://github.com/wg/wrk
synced 2026-05-13 17:05:29 +08:00
eliminate sampling and record all data
This commit is contained in:
@@ -6,3 +6,5 @@ wrk next
|
||||
* Add setup phase that calls the global setup() for each thread.
|
||||
* Allow assignment to thread.addr to specify the server address.
|
||||
* Add thread:set(key, value), thread:get(key), and thread:stop().
|
||||
* Record latency for every request instead of random samples.
|
||||
* Latency and requests in done() are now callable, not indexable.
|
||||
|
||||
@@ -18,7 +18,7 @@ else ifeq ($(TARGET), freebsd)
|
||||
endif
|
||||
|
||||
SRC := wrk.c net.c ssl.c aprintf.c stats.c script.c units.c \
|
||||
ae.c zmalloc.c http_parser.c tinymt64.c
|
||||
ae.c zmalloc.c http_parser.c
|
||||
BIN := wrk
|
||||
|
||||
ODIR := obj
|
||||
|
||||
@@ -106,38 +106,3 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
||||
=========================================================================
|
||||
== Tiny Mersenne Twister (TinyMT) Notice ==
|
||||
=========================================================================
|
||||
|
||||
Copyright (c) 2011 Mutsuo Saito, Makoto Matsumoto, Hiroshima University
|
||||
and The University of Tokyo. 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 the Hiroshima University 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.
|
||||
|
||||
@@ -42,5 +42,5 @@ Acknowledgements
|
||||
|
||||
wrk contains code from a number of open source projects including the
|
||||
'ae' event loop from redis, the nginx/joyent/node.js 'http-parser',
|
||||
Mike Pall's LuaJIT, and the Tiny Mersenne Twister PRNG. Please consult
|
||||
the NOTICE file for licensing details.
|
||||
and Mike Pall's LuaJIT. Please consult the NOTICE file for licensing
|
||||
details.
|
||||
|
||||
@@ -87,16 +87,16 @@ Done
|
||||
function done(summary, latency, requests)
|
||||
|
||||
The done() function receives a table containing result data, and two
|
||||
statistics objects representing the sampled per-request latency and
|
||||
per-thread request rate. Duration and latency are microsecond values
|
||||
and rate is measured in requests per second.
|
||||
statistics objects representing the per-request latency and per-thread
|
||||
request rate. Duration and latency are microsecond values and rate is
|
||||
measured in requests per second.
|
||||
|
||||
latency.min -- minimum value seen
|
||||
latency.max -- maximum value seen
|
||||
latency.mean -- average value seen
|
||||
latency.stdev -- standard deviation
|
||||
latency:percentile(99.0) -- 99th percentile value
|
||||
latency[i] -- raw sample value
|
||||
latency(i) -- raw value and count
|
||||
|
||||
summary = {
|
||||
duration = N, -- run duration in microseconds
|
||||
|
||||
+19
-13
@@ -14,6 +14,7 @@ typedef struct {
|
||||
|
||||
static int script_addr_tostring(lua_State *);
|
||||
static int script_addr_gc(lua_State *);
|
||||
static int script_stats_call(lua_State *);
|
||||
static int script_stats_len(lua_State *);
|
||||
static int script_stats_index(lua_State *);
|
||||
static int script_thread_index(lua_State *);
|
||||
@@ -30,6 +31,7 @@ static const struct luaL_reg addrlib[] = {
|
||||
};
|
||||
|
||||
static const struct luaL_reg statslib[] = {
|
||||
{ "__call", script_stats_call },
|
||||
{ "__index", script_stats_index },
|
||||
{ "__len", script_stats_len },
|
||||
{ NULL, NULL }
|
||||
@@ -337,27 +339,31 @@ static int script_stats_percentile(lua_State *L) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int script_stats_call(lua_State *L) {
|
||||
stats *s = checkstats(L);
|
||||
uint64_t index = lua_tonumber(L, 2);
|
||||
uint64_t count;
|
||||
lua_pushnumber(L, stats_value_at(s, index - 1, &count));
|
||||
lua_pushnumber(L, count);
|
||||
return 2;
|
||||
}
|
||||
|
||||
static int script_stats_index(lua_State *L) {
|
||||
stats *s = checkstats(L);
|
||||
if (lua_isnumber(L, 2)) {
|
||||
int index = luaL_checkint(L, 2);
|
||||
lua_pushnumber(L, s->data[index - 1]);
|
||||
} else if (lua_isstring(L, 2)) {
|
||||
const char *method = lua_tostring(L, 2);
|
||||
if (!strcmp("min", method)) lua_pushnumber(L, s->min);
|
||||
if (!strcmp("max", method)) lua_pushnumber(L, s->max);
|
||||
if (!strcmp("mean", method)) lua_pushnumber(L, stats_mean(s));
|
||||
if (!strcmp("stdev", method)) lua_pushnumber(L, stats_stdev(s, stats_mean(s)));
|
||||
if (!strcmp("percentile", method)) {
|
||||
lua_pushcfunction(L, script_stats_percentile);
|
||||
}
|
||||
const char *method = lua_tostring(L, 2);
|
||||
if (!strcmp("min", method)) lua_pushnumber(L, s->min);
|
||||
if (!strcmp("max", method)) lua_pushnumber(L, s->max);
|
||||
if (!strcmp("mean", method)) lua_pushnumber(L, stats_mean(s));
|
||||
if (!strcmp("stdev", method)) lua_pushnumber(L, stats_stdev(s, stats_mean(s)));
|
||||
if (!strcmp("percentile", method)) {
|
||||
lua_pushcfunction(L, script_stats_percentile);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int script_stats_len(lua_State *L) {
|
||||
stats *s = checkstats(L);
|
||||
lua_pushinteger(L, s->limit);
|
||||
lua_pushinteger(L, stats_popcount(s));
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
+51
-60
@@ -7,10 +7,11 @@
|
||||
#include "stats.h"
|
||||
#include "zmalloc.h"
|
||||
|
||||
stats *stats_alloc(uint64_t samples) {
|
||||
stats *s = zcalloc(sizeof(stats) + sizeof(uint64_t) * samples);
|
||||
s->samples = samples;
|
||||
s->min = UINT64_MAX;
|
||||
stats *stats_alloc(uint64_t max) {
|
||||
uint64_t limit = max + 1;
|
||||
stats *s = zcalloc(sizeof(stats) + sizeof(uint64_t) * limit);
|
||||
s->limit = limit;
|
||||
s->min = UINT64_MAX;
|
||||
return s;
|
||||
}
|
||||
|
||||
@@ -18,54 +19,35 @@ void stats_free(stats *stats) {
|
||||
zfree(stats);
|
||||
}
|
||||
|
||||
void stats_reset(stats *stats) {
|
||||
stats->limit = 0;
|
||||
stats->index = 0;
|
||||
stats->min = UINT64_MAX;
|
||||
stats->max = 0;
|
||||
}
|
||||
|
||||
void stats_rewind(stats *stats) {
|
||||
stats->limit = 0;
|
||||
stats->index = 0;
|
||||
}
|
||||
|
||||
void stats_record(stats *stats, uint64_t x) {
|
||||
stats->data[stats->index++] = x;
|
||||
if (x < stats->min) stats->min = x;
|
||||
if (x > stats->max) stats->max = x;
|
||||
if (stats->limit < stats->samples) stats->limit++;
|
||||
if (stats->index == stats->samples) stats->index = 0;
|
||||
}
|
||||
|
||||
static int stats_compare(const void *a, const void *b) {
|
||||
uint64_t *x = (uint64_t *) a;
|
||||
uint64_t *y = (uint64_t *) b;
|
||||
return *x - *y;
|
||||
}
|
||||
|
||||
long double stats_summarize(stats *stats) {
|
||||
qsort(stats->data, stats->limit, sizeof(uint64_t), &stats_compare);
|
||||
return stats_mean(stats);
|
||||
void stats_record(stats *stats, uint64_t n) {
|
||||
if (n >= stats->limit) return;
|
||||
__sync_fetch_and_add(&stats->data[n], 1);
|
||||
__sync_fetch_and_add(&stats->count, 1);
|
||||
uint64_t min = stats->min;
|
||||
uint64_t max = stats->max;
|
||||
while (n < min) min = __sync_val_compare_and_swap(&stats->min, min, n);
|
||||
while (n > max) max = __sync_val_compare_and_swap(&stats->max, max, n);
|
||||
}
|
||||
|
||||
long double stats_mean(stats *stats) {
|
||||
if (stats->limit == 0) return 0.0;
|
||||
if (stats->count == 0) return 0.0;
|
||||
|
||||
uint64_t sum = 0;
|
||||
for (uint64_t i = 0; i < stats->limit; i++) {
|
||||
sum += stats->data[i];
|
||||
for (uint64_t i = stats->min; i <= stats->max; i++) {
|
||||
sum += stats->data[i] * i;
|
||||
}
|
||||
return sum / (long double) stats->limit;
|
||||
return sum / (long double) stats->count;
|
||||
}
|
||||
|
||||
long double stats_stdev(stats *stats, long double mean) {
|
||||
long double sum = 0.0;
|
||||
if (stats->limit < 2) return 0.0;
|
||||
for (uint64_t i = 0; i < stats->limit; i++) {
|
||||
sum += powl(stats->data[i] - mean, 2);
|
||||
if (stats->count < 2) return 0.0;
|
||||
for (uint64_t i = stats->min; i <= stats->max; i++) {
|
||||
if (stats->data[i]) {
|
||||
sum += powl(i - mean, 2) * stats->data[i];
|
||||
}
|
||||
}
|
||||
return sqrtl(sum / (stats->limit - 1));
|
||||
return sqrtl(sum / (stats->count - 1));
|
||||
}
|
||||
|
||||
long double stats_within_stdev(stats *stats, long double mean, long double stdev, uint64_t n) {
|
||||
@@ -73,31 +55,40 @@ long double stats_within_stdev(stats *stats, long double mean, long double stdev
|
||||
long double lower = mean - (stdev * n);
|
||||
uint64_t sum = 0;
|
||||
|
||||
for (uint64_t i = 0; i < stats->limit; i++) {
|
||||
uint64_t x = stats->data[i];
|
||||
if (x >= lower && x <= upper) sum++;
|
||||
for (uint64_t i = stats->min; i <= stats->max; i++) {
|
||||
if (i >= lower && i <= upper) {
|
||||
sum += stats->data[i];
|
||||
}
|
||||
}
|
||||
|
||||
return (sum / (long double) stats->limit) * 100;
|
||||
return (sum / (long double) stats->count) * 100;
|
||||
}
|
||||
|
||||
uint64_t stats_percentile(stats *stats, long double p) {
|
||||
uint64_t rank = round((p / 100.0) * stats->limit + 0.5);
|
||||
return stats->data[rank - 1];
|
||||
}
|
||||
|
||||
void stats_sample(stats *dst, tinymt64_t *state, uint64_t count, stats *src) {
|
||||
for (uint64_t i = 0; i < count; i++) {
|
||||
uint64_t n = rand64(state, src->limit);
|
||||
stats_record(dst, src->data[n]);
|
||||
uint64_t rank = round((p / 100.0) * stats->count + 0.5);
|
||||
uint64_t total = 0;
|
||||
for (uint64_t i = stats->min; i <= stats->max; i++) {
|
||||
total += stats->data[i];
|
||||
if (total >= rank) return i;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint64_t rand64(tinymt64_t *state, uint64_t n) {
|
||||
uint64_t x, max = ~UINT64_C(0);
|
||||
max -= max % n;
|
||||
do {
|
||||
x = tinymt64_generate_uint64(state);
|
||||
} while (x >= max);
|
||||
return x % n;
|
||||
uint64_t stats_popcount(stats *stats) {
|
||||
uint64_t count = 0;
|
||||
for (uint64_t i = stats->min; i <= stats->max; i++) {
|
||||
if (stats->data[i]) count++;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
uint64_t stats_value_at(stats *stats, uint64_t index, uint64_t *count) {
|
||||
*count = 0;
|
||||
for (uint64_t i = stats->min; i <= stats->max; i++) {
|
||||
if (stats->data[i] && (*count)++ == index) {
|
||||
*count = stats->data[i];
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
+4
-8
@@ -2,7 +2,7 @@
|
||||
#define STATS_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include "tinymt64.h"
|
||||
#include <stdint.h>
|
||||
|
||||
#define MAX(X, Y) ((X) > (Y) ? (X) : (Y))
|
||||
#define MIN(X, Y) ((X) < (Y) ? (X) : (Y))
|
||||
@@ -16,8 +16,7 @@ typedef struct {
|
||||
} errors;
|
||||
|
||||
typedef struct {
|
||||
uint64_t samples;
|
||||
uint64_t index;
|
||||
uint64_t count;
|
||||
uint64_t limit;
|
||||
uint64_t min;
|
||||
uint64_t max;
|
||||
@@ -26,18 +25,15 @@ typedef struct {
|
||||
|
||||
stats *stats_alloc(uint64_t);
|
||||
void stats_free(stats *);
|
||||
void stats_reset(stats *);
|
||||
void stats_rewind(stats *);
|
||||
|
||||
void stats_record(stats *, uint64_t);
|
||||
|
||||
long double stats_summarize(stats *);
|
||||
long double stats_mean(stats *);
|
||||
long double stats_stdev(stats *stats, long double);
|
||||
long double stats_within_stdev(stats *, long double, long double, uint64_t);
|
||||
uint64_t stats_percentile(stats *, long double);
|
||||
|
||||
void stats_sample(stats *, tinymt64_t *, uint64_t, stats *);
|
||||
uint64_t rand64(tinymt64_t *, uint64_t);
|
||||
uint64_t stats_popcount(stats *);
|
||||
uint64_t stats_value_at(stats *stats, uint64_t, uint64_t *);
|
||||
|
||||
#endif /* STATS_H */
|
||||
|
||||
-129
@@ -1,129 +0,0 @@
|
||||
/**
|
||||
* @file tinymt64.c
|
||||
*
|
||||
* @brief 64-bit Tiny Mersenne Twister only 127 bit internal state
|
||||
*
|
||||
* @author Mutsuo Saito (Hiroshima University)
|
||||
* @author Makoto Matsumoto (The University of Tokyo)
|
||||
*
|
||||
* Copyright (C) 2011 Mutsuo Saito, Makoto Matsumoto,
|
||||
* Hiroshima University and The University of Tokyo.
|
||||
* All rights reserved.
|
||||
*
|
||||
* The 3-clause BSD License is applied to this software, see
|
||||
* LICENSE.txt
|
||||
*/
|
||||
#include "tinymt64.h"
|
||||
|
||||
#define MIN_LOOP 8
|
||||
|
||||
/**
|
||||
* This function represents a function used in the initialization
|
||||
* by init_by_array
|
||||
* @param[in] x 64-bit integer
|
||||
* @return 64-bit integer
|
||||
*/
|
||||
static uint64_t ini_func1(uint64_t x) {
|
||||
return (x ^ (x >> 59)) * UINT64_C(2173292883993);
|
||||
}
|
||||
|
||||
/**
|
||||
* This function represents a function used in the initialization
|
||||
* by init_by_array
|
||||
* @param[in] x 64-bit integer
|
||||
* @return 64-bit integer
|
||||
*/
|
||||
static uint64_t ini_func2(uint64_t x) {
|
||||
return (x ^ (x >> 59)) * UINT64_C(58885565329898161);
|
||||
}
|
||||
|
||||
/**
|
||||
* This function certificate the period of 2^127-1.
|
||||
* @param random tinymt state vector.
|
||||
*/
|
||||
static void period_certification(tinymt64_t * random) {
|
||||
if ((random->status[0] & TINYMT64_MASK) == 0 &&
|
||||
random->status[1] == 0) {
|
||||
random->status[0] = 'T';
|
||||
random->status[1] = 'M';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This function initializes the internal state array with a 64-bit
|
||||
* unsigned integer seed.
|
||||
* @param random tinymt state vector.
|
||||
* @param seed a 64-bit unsigned integer used as a seed.
|
||||
*/
|
||||
void tinymt64_init(tinymt64_t * random, uint64_t seed) {
|
||||
random->status[0] = seed ^ ((uint64_t)random->mat1 << 32);
|
||||
random->status[1] = random->mat2 ^ random->tmat;
|
||||
for (int i = 1; i < MIN_LOOP; i++) {
|
||||
random->status[i & 1] ^= i + UINT64_C(6364136223846793005)
|
||||
* (random->status[(i - 1) & 1]
|
||||
^ (random->status[(i - 1) & 1] >> 62));
|
||||
}
|
||||
period_certification(random);
|
||||
}
|
||||
|
||||
/**
|
||||
* This function initializes the internal state array,
|
||||
* with an array of 64-bit unsigned integers used as seeds
|
||||
* @param random tinymt state vector.
|
||||
* @param init_key the array of 64-bit integers, used as a seed.
|
||||
* @param key_length the length of init_key.
|
||||
*/
|
||||
void tinymt64_init_by_array(tinymt64_t * random, const uint64_t init_key[],
|
||||
int key_length) {
|
||||
const int lag = 1;
|
||||
const int mid = 1;
|
||||
const int size = 4;
|
||||
int i, j;
|
||||
int count;
|
||||
uint64_t r;
|
||||
uint64_t st[4];
|
||||
|
||||
st[0] = 0;
|
||||
st[1] = random->mat1;
|
||||
st[2] = random->mat2;
|
||||
st[3] = random->tmat;
|
||||
if (key_length + 1 > MIN_LOOP) {
|
||||
count = key_length + 1;
|
||||
} else {
|
||||
count = MIN_LOOP;
|
||||
}
|
||||
r = ini_func1(st[0] ^ st[mid % size]
|
||||
^ st[(size - 1) % size]);
|
||||
st[mid % size] += r;
|
||||
r += key_length;
|
||||
st[(mid + lag) % size] += r;
|
||||
st[0] = r;
|
||||
count--;
|
||||
for (i = 1, j = 0; (j < count) && (j < key_length); j++) {
|
||||
r = ini_func1(st[i] ^ st[(i + mid) % size] ^ st[(i + size - 1) % size]);
|
||||
st[(i + mid) % size] += r;
|
||||
r += init_key[j] + i;
|
||||
st[(i + mid + lag) % size] += r;
|
||||
st[i] = r;
|
||||
i = (i + 1) % size;
|
||||
}
|
||||
for (; j < count; j++) {
|
||||
r = ini_func1(st[i] ^ st[(i + mid) % size] ^ st[(i + size - 1) % size]);
|
||||
st[(i + mid) % size] += r;
|
||||
r += i;
|
||||
st[(i + mid + lag) % size] += r;
|
||||
st[i] = r;
|
||||
i = (i + 1) % size;
|
||||
}
|
||||
for (j = 0; j < size; j++) {
|
||||
r = ini_func2(st[i] + st[(i + mid) % size] + st[(i + size - 1) % size]);
|
||||
st[(i + mid) % size] ^= r;
|
||||
r -= i;
|
||||
st[(i + mid + lag) % size] ^= r;
|
||||
st[i] = r;
|
||||
i = (i + 1) % size;
|
||||
}
|
||||
random->status[0] = st[0] ^ st[1];
|
||||
random->status[1] = st[2] ^ st[3];
|
||||
period_certification(random);
|
||||
}
|
||||
-210
@@ -1,210 +0,0 @@
|
||||
#ifndef TINYMT64_H
|
||||
#define TINYMT64_H
|
||||
/**
|
||||
* @file tinymt64.h
|
||||
*
|
||||
* @brief Tiny Mersenne Twister only 127 bit internal state
|
||||
*
|
||||
* @author Mutsuo Saito (Hiroshima University)
|
||||
* @author Makoto Matsumoto (The University of Tokyo)
|
||||
*
|
||||
* Copyright (C) 2011 Mutsuo Saito, Makoto Matsumoto,
|
||||
* Hiroshima University and The University of Tokyo.
|
||||
* All rights reserved.
|
||||
*
|
||||
* The 3-clause BSD License is applied to this software, see
|
||||
* LICENSE.txt
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <inttypes.h>
|
||||
|
||||
#define TINYMT64_MEXP 127
|
||||
#define TINYMT64_SH0 12
|
||||
#define TINYMT64_SH1 11
|
||||
#define TINYMT64_SH8 8
|
||||
#define TINYMT64_MASK UINT64_C(0x7fffffffffffffff)
|
||||
#define TINYMT64_MUL (1.0 / 18446744073709551616.0)
|
||||
|
||||
/*
|
||||
* tinymt64 internal state vector and parameters
|
||||
*/
|
||||
struct TINYMT64_T {
|
||||
uint64_t status[2];
|
||||
uint32_t mat1;
|
||||
uint32_t mat2;
|
||||
uint64_t tmat;
|
||||
};
|
||||
|
||||
typedef struct TINYMT64_T tinymt64_t;
|
||||
|
||||
void tinymt64_init(tinymt64_t * random, uint64_t seed);
|
||||
void tinymt64_init_by_array(tinymt64_t * random, const uint64_t init_key[],
|
||||
int key_length);
|
||||
|
||||
#if defined(__GNUC__)
|
||||
/**
|
||||
* This function always returns 127
|
||||
* @param random not used
|
||||
* @return always 127
|
||||
*/
|
||||
inline static int tinymt64_get_mexp(
|
||||
tinymt64_t * random __attribute__((unused))) {
|
||||
return TINYMT64_MEXP;
|
||||
}
|
||||
#else
|
||||
inline static int tinymt64_get_mexp(tinymt64_t * random) {
|
||||
return TINYMT64_MEXP;
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* This function changes internal state of tinymt64.
|
||||
* Users should not call this function directly.
|
||||
* @param random tinymt internal status
|
||||
*/
|
||||
inline static void tinymt64_next_state(tinymt64_t * random) {
|
||||
uint64_t x;
|
||||
|
||||
random->status[0] &= TINYMT64_MASK;
|
||||
x = random->status[0] ^ random->status[1];
|
||||
x ^= x << TINYMT64_SH0;
|
||||
x ^= x >> 32;
|
||||
x ^= x << 32;
|
||||
x ^= x << TINYMT64_SH1;
|
||||
random->status[0] = random->status[1];
|
||||
random->status[1] = x;
|
||||
random->status[0] ^= -((int64_t)(x & 1)) & random->mat1;
|
||||
random->status[1] ^= -((int64_t)(x & 1)) & (((uint64_t)random->mat2) << 32);
|
||||
}
|
||||
|
||||
/**
|
||||
* This function outputs 64-bit unsigned integer from internal state.
|
||||
* Users should not call this function directly.
|
||||
* @param random tinymt internal status
|
||||
* @return 64-bit unsigned pseudorandom number
|
||||
*/
|
||||
inline static uint64_t tinymt64_temper(tinymt64_t * random) {
|
||||
uint64_t x;
|
||||
#if defined(LINEARITY_CHECK)
|
||||
x = random->status[0] ^ random->status[1];
|
||||
#else
|
||||
x = random->status[0] + random->status[1];
|
||||
#endif
|
||||
x ^= random->status[0] >> TINYMT64_SH8;
|
||||
x ^= -((int64_t)(x & 1)) & random->tmat;
|
||||
return x;
|
||||
}
|
||||
|
||||
/**
|
||||
* This function outputs floating point number from internal state.
|
||||
* Users should not call this function directly.
|
||||
* @param random tinymt internal status
|
||||
* @return floating point number r (1.0 <= r < 2.0)
|
||||
*/
|
||||
inline static double tinymt64_temper_conv(tinymt64_t * random) {
|
||||
uint64_t x;
|
||||
union {
|
||||
uint64_t u;
|
||||
double d;
|
||||
} conv;
|
||||
#if defined(LINEARITY_CHECK)
|
||||
x = random->status[0] ^ random->status[1];
|
||||
#else
|
||||
x = random->status[0] + random->status[1];
|
||||
#endif
|
||||
x ^= random->status[0] >> TINYMT64_SH8;
|
||||
conv.u = ((x ^ (-((int64_t)(x & 1)) & random->tmat)) >> 12)
|
||||
| UINT64_C(0x3ff0000000000000);
|
||||
return conv.d;
|
||||
}
|
||||
|
||||
/**
|
||||
* This function outputs floating point number from internal state.
|
||||
* Users should not call this function directly.
|
||||
* @param random tinymt internal status
|
||||
* @return floating point number r (1.0 < r < 2.0)
|
||||
*/
|
||||
inline static double tinymt64_temper_conv_open(tinymt64_t * random) {
|
||||
uint64_t x;
|
||||
union {
|
||||
uint64_t u;
|
||||
double d;
|
||||
} conv;
|
||||
#if defined(LINEARITY_CHECK)
|
||||
x = random->status[0] ^ random->status[1];
|
||||
#else
|
||||
x = random->status[0] + random->status[1];
|
||||
#endif
|
||||
x ^= random->status[0] >> TINYMT64_SH8;
|
||||
conv.u = ((x ^ (-((int64_t)(x & 1)) & random->tmat)) >> 12)
|
||||
| UINT64_C(0x3ff0000000000001);
|
||||
return conv.d;
|
||||
}
|
||||
|
||||
/**
|
||||
* This function outputs 64-bit unsigned integer from internal state.
|
||||
* @param random tinymt internal status
|
||||
* @return 64-bit unsigned integer r (0 <= r < 2^64)
|
||||
*/
|
||||
inline static uint64_t tinymt64_generate_uint64(tinymt64_t * random) {
|
||||
tinymt64_next_state(random);
|
||||
return tinymt64_temper(random);
|
||||
}
|
||||
|
||||
/**
|
||||
* This function outputs floating point number from internal state.
|
||||
* This function is implemented using multiplying by 1 / 2^64.
|
||||
* @param random tinymt internal status
|
||||
* @return floating point number r (0.0 <= r < 1.0)
|
||||
*/
|
||||
inline static double tinymt64_generate_double(tinymt64_t * random) {
|
||||
tinymt64_next_state(random);
|
||||
return tinymt64_temper(random) * TINYMT64_MUL;
|
||||
}
|
||||
|
||||
/**
|
||||
* This function outputs floating point number from internal state.
|
||||
* This function is implemented using union trick.
|
||||
* @param random tinymt internal status
|
||||
* @return floating point number r (0.0 <= r < 1.0)
|
||||
*/
|
||||
inline static double tinymt64_generate_double01(tinymt64_t * random) {
|
||||
tinymt64_next_state(random);
|
||||
return tinymt64_temper_conv(random) - 1.0;
|
||||
}
|
||||
|
||||
/**
|
||||
* This function outputs floating point number from internal state.
|
||||
* This function is implemented using union trick.
|
||||
* @param random tinymt internal status
|
||||
* @return floating point number r (1.0 <= r < 2.0)
|
||||
*/
|
||||
inline static double tinymt64_generate_double12(tinymt64_t * random) {
|
||||
tinymt64_next_state(random);
|
||||
return tinymt64_temper_conv(random);
|
||||
}
|
||||
|
||||
/**
|
||||
* This function outputs floating point number from internal state.
|
||||
* This function is implemented using union trick.
|
||||
* @param random tinymt internal status
|
||||
* @return floating point number r (0.0 < r <= 1.0)
|
||||
*/
|
||||
inline static double tinymt64_generate_doubleOC(tinymt64_t * random) {
|
||||
tinymt64_next_state(random);
|
||||
return 2.0 - tinymt64_temper_conv(random);
|
||||
}
|
||||
|
||||
/**
|
||||
* This function outputs floating point number from internal state.
|
||||
* This function is implemented using union trick.
|
||||
* @param random tinymt internal status
|
||||
* @return floating point number r (0.0 < r < 1.0)
|
||||
*/
|
||||
inline static double tinymt64_generate_doubleOO(tinymt64_t * random) {
|
||||
tinymt64_next_state(random);
|
||||
return tinymt64_temper_conv_open(random) - 1.0;
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -19,7 +19,6 @@ static struct config {
|
||||
static struct {
|
||||
stats *latency;
|
||||
stats *requests;
|
||||
pthread_mutex_t mutex;
|
||||
} statistics;
|
||||
|
||||
static struct sock sock = {
|
||||
@@ -99,9 +98,8 @@ int main(int argc, char **argv) {
|
||||
signal(SIGPIPE, SIG_IGN);
|
||||
signal(SIGINT, SIG_IGN);
|
||||
|
||||
pthread_mutex_init(&statistics.mutex, NULL);
|
||||
statistics.latency = stats_alloc(SAMPLES);
|
||||
statistics.requests = stats_alloc(SAMPLES);
|
||||
statistics.latency = stats_alloc(cfg.timeout * 1000);
|
||||
statistics.requests = stats_alloc(MAX_THREAD_RATE_S);
|
||||
|
||||
thread *threads = zcalloc(cfg.threads * sizeof(thread));
|
||||
uint64_t stop_at = time_us() + (cfg.duration * 1000000);
|
||||
@@ -117,6 +115,7 @@ int main(int argc, char **argv) {
|
||||
thread *t = &threads[i];
|
||||
t->loop = aeCreateEventLoop(10 + cfg.connections * 3);
|
||||
t->connections = cfg.connections / cfg.threads;
|
||||
t->latency = stats_alloc(cfg.timeout * 1000);
|
||||
t->stop_at = stop_at;
|
||||
|
||||
t->L = script_create(cfg.script, schema, host, port, path);
|
||||
@@ -207,11 +206,6 @@ int main(int argc, char **argv) {
|
||||
|
||||
void *thread_main(void *arg) {
|
||||
thread *thread = arg;
|
||||
aeEventLoop *loop = thread->loop;
|
||||
|
||||
thread->cs = zcalloc(thread->connections * sizeof(connection));
|
||||
tinymt64_init(&thread->rand, time_us());
|
||||
thread->latency = stats_alloc(100000);
|
||||
|
||||
char *request = NULL;
|
||||
size_t length = 0;
|
||||
@@ -220,6 +214,7 @@ void *thread_main(void *arg) {
|
||||
script_request(thread->L, &request, &length);
|
||||
}
|
||||
|
||||
thread->cs = zcalloc(thread->connections * sizeof(connection));
|
||||
connection *c = thread->cs;
|
||||
|
||||
for (uint64_t i = 0; i < thread->connections; i++, c++) {
|
||||
@@ -230,6 +225,7 @@ void *thread_main(void *arg) {
|
||||
connect_socket(thread, c);
|
||||
}
|
||||
|
||||
aeEventLoop *loop = thread->loop;
|
||||
aeCreateTimeEvent(loop, CALIBRATE_DELAY_MS, calibrate, thread, NULL);
|
||||
aeCreateTimeEvent(loop, TIMEOUT_INTERVAL_MS, check_timeouts, thread, NULL);
|
||||
|
||||
@@ -240,13 +236,9 @@ void *thread_main(void *arg) {
|
||||
zfree(thread->cs);
|
||||
|
||||
uint64_t max = thread->latency->max;
|
||||
stats_free(thread->latency);
|
||||
|
||||
pthread_mutex_lock(&statistics.mutex);
|
||||
for (uint64_t i = 0; i < thread->missed; i++) {
|
||||
stats_record(statistics.latency, max);
|
||||
}
|
||||
pthread_mutex_unlock(&statistics.mutex);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
@@ -291,18 +283,19 @@ static int reconnect_socket(thread *thread, connection *c) {
|
||||
static int calibrate(aeEventLoop *loop, long long id, void *data) {
|
||||
thread *thread = data;
|
||||
|
||||
(void) stats_summarize(thread->latency);
|
||||
long double latency = stats_percentile(thread->latency, 90.0) / 1000.0L;
|
||||
long double interval = MAX(latency * 2, 10);
|
||||
long double rate = (interval / latency) * thread->connections;
|
||||
|
||||
if (latency == 0) return CALIBRATE_DELAY_MS;
|
||||
|
||||
stats_free(thread->latency);
|
||||
|
||||
thread->interval = interval;
|
||||
thread->rate = ceil(rate / 10);
|
||||
thread->start = time_us();
|
||||
thread->requests = 0;
|
||||
stats_reset(thread->latency);
|
||||
thread->latency = statistics.latency;
|
||||
|
||||
aeCreateTimeEvent(loop, thread->interval, sample_rate, thread, NULL);
|
||||
|
||||
@@ -334,18 +327,13 @@ static int sample_rate(aeEventLoop *loop, long long id, void *data) {
|
||||
|
||||
uint64_t elapsed_ms = (time_us() - thread->start) / 1000;
|
||||
uint64_t requests = (thread->requests / (double) elapsed_ms) * 1000;
|
||||
uint64_t missed = thread->rate - MIN(thread->rate, thread->latency->limit);
|
||||
uint64_t count = thread->rate - missed;
|
||||
uint64_t missed = thread->rate - MIN(thread->rate, thread->latency->count);
|
||||
|
||||
pthread_mutex_lock(&statistics.mutex);
|
||||
stats_sample(statistics.latency, &thread->rand, count, thread->latency);
|
||||
stats_record(statistics.requests, requests);
|
||||
pthread_mutex_unlock(&statistics.mutex);
|
||||
|
||||
thread->missed += missed;
|
||||
thread->requests = 0;
|
||||
thread->start = time_us();
|
||||
stats_rewind(thread->latency);
|
||||
|
||||
return thread->interval;
|
||||
}
|
||||
@@ -605,7 +593,7 @@ static void print_units(long double n, char *(*fmt)(long double), int width) {
|
||||
|
||||
static void print_stats(char *name, stats *stats, char *(*fmt)(long double)) {
|
||||
uint64_t max = stats->max;
|
||||
long double mean = stats_summarize(stats);
|
||||
long double mean = stats_mean(stats);
|
||||
long double stdev = stats_stdev(stats, mean);
|
||||
|
||||
printf(" %-10s", name);
|
||||
|
||||
@@ -18,8 +18,8 @@
|
||||
|
||||
#define VERSION "3.1.2"
|
||||
#define RECVBUF 8192
|
||||
#define SAMPLES 100000000
|
||||
|
||||
#define MAX_THREAD_RATE_S 10000000
|
||||
#define SOCKET_TIMEOUT_MS 2000
|
||||
#define CALIBRATE_DELAY_MS 500
|
||||
#define TIMEOUT_INTERVAL_MS 2000
|
||||
@@ -38,7 +38,6 @@ typedef struct {
|
||||
uint64_t rate;
|
||||
uint64_t missed;
|
||||
stats *latency;
|
||||
tinymt64_t rand;
|
||||
lua_State *L;
|
||||
errors errors;
|
||||
struct connection *cs;
|
||||
|
||||
Reference in New Issue
Block a user