Skip to content

Commit

Permalink
eliminate sampling and record all data
Browse files Browse the repository at this point in the history
  • Loading branch information
wg committed Feb 21, 2015
1 parent 9b84d3e commit 39af03f
Show file tree
Hide file tree
Showing 12 changed files with 92 additions and 484 deletions.
2 changes: 2 additions & 0 deletions CHANGES
Original file line number Diff line number Diff line change
Expand Up @@ -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.
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
35 changes: 0 additions & 35 deletions NOTICE
Original file line number Diff line number Diff line change
Expand Up @@ -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.
4 changes: 2 additions & 2 deletions README
Original file line number Diff line number Diff line change
Expand Up @@ -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.
8 changes: 4 additions & 4 deletions SCRIPTING
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
32 changes: 19 additions & 13 deletions src/script.c
Original file line number Diff line number Diff line change
Expand Up @@ -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 *);
Expand All @@ -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 }
Expand Down Expand Up @@ -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;
}

Expand Down
107 changes: 49 additions & 58 deletions src/stats.c
Original file line number Diff line number Diff line change
Expand Up @@ -7,97 +7,88 @@
#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;
}

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) {
long double upper = mean + (stdev * n);
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];
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;
}

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 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 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_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;
}
12 changes: 4 additions & 8 deletions src/stats.h
Original file line number Diff line number Diff line change
Expand Up @@ -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))
Expand All @@ -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;
Expand All @@ -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 */
Loading

0 comments on commit 39af03f

Please sign in to comment.