1
0
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:
Will
2015-02-11 11:31:55 +09:00
Unverified
parent 9b84d3e1a4
commit 39af03f7ba
12 changed files with 94 additions and 486 deletions
+2
View File
@@ -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.
+1 -1
View File
@@ -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
-35
View File
@@ -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.
+2 -2
View File
@@ -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.
+4 -4
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
+10 -22
View File
@@ -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);
+1 -2
View File
@@ -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;