diff options
-rw-r--r-- | src/core/lib/debug/stats.c | 110 | ||||
-rw-r--r-- | src/core/lib/debug/stats.h | 17 | ||||
-rw-r--r-- | src/core/lib/debug/stats_data.c | 150 | ||||
-rw-r--r-- | src/core/lib/debug/stats_data.h | 102 | ||||
-rw-r--r-- | src/core/lib/debug/stats_data.yaml | 14 | ||||
-rw-r--r-- | src/core/lib/iomgr/tcp_posix.c | 4 | ||||
-rw-r--r-- | test/cpp/microbenchmarks/helpers.cc | 13 | ||||
-rwxr-xr-x | tools/codegen/core/gen_stats_data.py | 199 |
8 files changed, 585 insertions, 24 deletions
diff --git a/src/core/lib/debug/stats.c b/src/core/lib/debug/stats.c index 4dbd94c724..5079ed2ffa 100644 --- a/src/core/lib/debug/stats.c +++ b/src/core/lib/debug/stats.c @@ -45,7 +45,98 @@ void grpc_stats_collect(grpc_stats_data *output) { output->counters[i] += gpr_atm_no_barrier_load( &grpc_stats_per_cpu_storage[core].counters[i]); } + for (size_t i = 0; i < GRPC_STATS_HISTOGRAM_BUCKETS; i++) { + output->histograms[i] += gpr_atm_no_barrier_load( + &grpc_stats_per_cpu_storage[core].histograms[i]); + } + } +} + +void grpc_stats_diff(const grpc_stats_data *b, const grpc_stats_data *a, + grpc_stats_data *c) { + for (size_t i = 0; i < GRPC_STATS_COUNTER_COUNT; i++) { + c->counters[i] = b->counters[i] - a->counters[i]; + } + for (size_t i = 0; i < GRPC_STATS_HISTOGRAM_BUCKETS; i++) { + c->histograms[i] = b->histograms[i] - a->histograms[i]; + } +} + +int grpc_stats_histo_find_bucket_slow(grpc_exec_ctx *exec_ctx, double value, + const double *table, int table_size) { + GRPC_STATS_INC_HISTOGRAM_SLOW_LOOKUPS(exec_ctx); + if (value < 0.0) return 0; + if (value >= table[table_size - 1]) return table_size - 1; + int a = 0; + int b = table_size - 1; + while (a < b) { + int c = a + ((b - a) / 2); + if (value < table[c]) { + b = c - 1; + } else if (value > table[c]) { + a = c + 1; + } else { + return c; + } + } + return a; +} + +size_t grpc_stats_histo_count(const grpc_stats_data *stats, + grpc_stats_histograms histogram) { + size_t sum = 0; + for (int i = 0; i < grpc_stats_histo_buckets[histogram]; i++) { + sum += (size_t)stats->histograms[grpc_stats_histo_start[histogram] + i]; + } + return sum; +} + +static double threshold_for_count_below(const gpr_atm *bucket_counts, + const double *bucket_boundaries, + int num_buckets, double count_below) { + double count_so_far; + double lower_bound; + double upper_bound; + int lower_idx; + int upper_idx; + + /* find the lowest bucket that gets us above count_below */ + count_so_far = 0.0; + for (lower_idx = 0; lower_idx < num_buckets; lower_idx++) { + count_so_far += (double)bucket_counts[lower_idx]; + if (count_so_far >= count_below) { + break; + } } + if (count_so_far == count_below) { + /* this bucket hits the threshold exactly... we should be midway through + any run of zero values following the bucket */ + for (upper_idx = lower_idx + 1; upper_idx < num_buckets; upper_idx++) { + if (bucket_counts[upper_idx]) { + break; + } + } + return (bucket_boundaries[lower_idx] + bucket_boundaries[upper_idx]) / 2.0; + } else { + /* treat values as uniform throughout the bucket, and find where this value + should lie */ + lower_bound = bucket_boundaries[lower_idx]; + upper_bound = bucket_boundaries[lower_idx + 1]; + return upper_bound - + (upper_bound - lower_bound) * (count_so_far - count_below) / + (double)bucket_counts[lower_idx]; + } +} + +double grpc_stats_histo_percentile(const grpc_stats_data *stats, + grpc_stats_histograms histogram, + double percentile) { + size_t count = grpc_stats_histo_count(stats, histogram); + if (count == 0) return 0.0; + return threshold_for_count_below( + stats->histograms + grpc_stats_histo_start[histogram], + grpc_stats_histo_bucket_boundaries[histogram], + grpc_stats_histo_buckets[histogram], (double)count * percentile / 100.0); } char *grpc_stats_data_as_json(const grpc_stats_data *data) { @@ -60,6 +151,25 @@ char *grpc_stats_data_as_json(const grpc_stats_data *data) { gpr_strvec_add(&v, tmp); is_first = false; } + for (size_t i = 0; i < GRPC_STATS_HISTOGRAM_COUNT; i++) { + gpr_asprintf(&tmp, "%s\"%s\": [", is_first ? "" : ", ", + grpc_stats_histogram_name[i]); + gpr_strvec_add(&v, tmp); + for (int j = 0; j < grpc_stats_histo_buckets[i]; j++) { + gpr_asprintf(&tmp, "%s%" PRIdPTR, j == 0 ? "" : ",", + data->histograms[grpc_stats_histo_start[i] + j]); + gpr_strvec_add(&v, tmp); + } + gpr_asprintf(&tmp, "], \"%s_bkt\": [", grpc_stats_histogram_name[i]); + gpr_strvec_add(&v, tmp); + for (int j = 0; j < grpc_stats_histo_buckets[i]; j++) { + gpr_asprintf(&tmp, "%s%lf", j == 0 ? "" : ",", + grpc_stats_histo_bucket_boundaries[i][j]); + gpr_strvec_add(&v, tmp); + } + gpr_strvec_add(&v, gpr_strdup("]")); + is_first = false; + } gpr_strvec_add(&v, gpr_strdup("}")); tmp = gpr_strvec_flatten(&v, NULL); gpr_strvec_destroy(&v); diff --git a/src/core/lib/debug/stats.h b/src/core/lib/debug/stats.h index 563b108dff..c440ab3b66 100644 --- a/src/core/lib/debug/stats.h +++ b/src/core/lib/debug/stats.h @@ -25,6 +25,7 @@ typedef struct grpc_stats_data { gpr_atm counters[GRPC_STATS_COUNTER_COUNT]; + gpr_atm histograms[GRPC_STATS_HISTOGRAM_BUCKETS]; } grpc_stats_data; extern grpc_stats_data *grpc_stats_per_cpu_storage; @@ -36,9 +37,25 @@ extern grpc_stats_data *grpc_stats_per_cpu_storage; (gpr_atm_no_barrier_fetch_add( \ &GRPC_THREAD_STATS_DATA((exec_ctx))->counters[(ctr)], 1)) +#define GRPC_STATS_INC_HISTOGRAM(exec_ctx, histogram, index) \ + (gpr_atm_no_barrier_fetch_add( \ + &GRPC_THREAD_STATS_DATA((exec_ctx)) \ + ->histograms[histogram##_FIRST_SLOT + (index)], \ + 1)) + void grpc_stats_init(void); void grpc_stats_shutdown(void); void grpc_stats_collect(grpc_stats_data *output); +// c = b-a +void grpc_stats_diff(const grpc_stats_data *b, const grpc_stats_data *a, + grpc_stats_data *c); char *grpc_stats_data_as_json(const grpc_stats_data *data); +int grpc_stats_histo_find_bucket_slow(grpc_exec_ctx *exec_ctx, double value, + const double *table, int table_size); +double grpc_stats_histo_percentile(const grpc_stats_data *data, + grpc_stats_histograms histogram, + double percentile); +size_t grpc_stats_histo_count(const grpc_stats_data *data, + grpc_stats_histograms histogram); #endif diff --git a/src/core/lib/debug/stats_data.c b/src/core/lib/debug/stats_data.c index 2203358a7e..177234ab5b 100644 --- a/src/core/lib/debug/stats_data.c +++ b/src/core/lib/debug/stats_data.c @@ -20,6 +20,152 @@ #include "src/core/lib/debug/stats_data.h" const char *grpc_stats_counter_name[GRPC_STATS_COUNTER_COUNT] = { - "client_calls_created", "server_calls_created", "syscall_write", - "syscall_read", "syscall_poll", "syscall_wait", + "client_calls_created", "server_calls_created", "syscall_write", + "syscall_read", "syscall_poll", "syscall_wait", + "histogram_slow_lookups", }; +const char *grpc_stats_histogram_name[GRPC_STATS_HISTOGRAM_COUNT] = { + "tcp_write_size", "tcp_write_iov_size", "tcp_read_size", +}; +const double grpc_stats_table_0[64] = {0, + 1, + 2, + 3, + 4, + 5.17974600698, + 6.70744217421, + 8.68571170472, + 11.2474451301, + 14.5647272503, + 18.8603969544, + 24.4230164536, + 31.6262554885, + 40.9539926456, + 53.032819969, + 68.6741343683, + 88.9286433193, + 115.156946285, + 149.120933174, + 193.102139541, + 250.055009057, + 323.805358672, + 419.307378404, + 542.976429747, + 703.119998467, + 910.495751121, + 1179.03418281, + 1526.77440013, + 1977.07590065, + 2560.18775048, + 3315.28056941, + 4293.07782286, + 5559.26317765, + 7198.89281155, + 9322.10907382, + 12071.5393129, + 15631.8768886, + 20242.2879738, + 26212.4775761, + 33943.4940145, + 43954.6693961, + 56918.5058232, + 73705.8508152, + 95444.3966128, + 123594.433061, + 160046.942783, + 207250.628202, + 268376.403469, + 347530.401059, + 450029.801797, + 582760.01722, + 754637.218056, + 977207.279236, + 1265421.37565, + 1638640.32942, + 2121935.1758, + 2747771.31348, + 3558189.37227, + 4607629.29828, + 5966587.36485, + 7726351.7696, + 10005134.9318, + 12956014.428, + 16777216.0}; +const uint8_t grpc_stats_table_1[87] = { + 0, 1, 3, 3, 4, 6, 6, 7, 9, 9, 10, 12, 12, 13, 15, 15, 16, 18, + 18, 19, 21, 21, 22, 24, 24, 25, 27, 27, 28, 30, 30, 31, 32, 34, 34, 36, + 36, 37, 39, 39, 40, 42, 42, 43, 44, 46, 46, 47, 49, 49, 51, 51, 52, 53, + 55, 55, 56, 58, 58, 59, 61, 61, 63, 63, 64, 65, 67, 67, 68, 70, 70, 71, + 73, 73, 75, 75, 76, 77, 79, 79, 80, 82, 82, 83, 85, 85, 87}; +const double grpc_stats_table_2[64] = {0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12.0020736244, + 13.0954337532, + 14.2883963681, + 15.5900350167, + 17.0102498252, + 18.5598427974, + 20.2505999737, + 22.0953810747, + 24.1082173107, + 26.3044181014, + 28.7006875181, + 31.315251333, + 34.1679956422, + 37.2806181177, + 40.6767930374, + 44.3823513489, + 48.4254771375, + 52.8369219909, + 57.6502388927, + 62.902037423, + 68.6322622068, + 74.8844967285, + 81.7062948236, + 89.1495423679, + 97.2708519163, + 106.131993291, + 115.800363399, + 126.34949884, + 137.859635225, + 150.418317437, + 164.121065485, + 179.072101023, + 195.38514005, + 213.184257818, + 232.604832535, + 253.794575043, + 276.914652285, + 302.140913126, + 329.665225843, + 359.696937452, + 392.464465978, + 428.217037783, + 467.226583154, + 509.78980457, + 556.230433401, + 606.901692163, + 662.1889811, + 722.512809492, + 788.331994007, + 860.147148411, + 938.504491184, + 1024.0}; +const uint8_t grpc_stats_table_3[52] = { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, + 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, + 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 52}; +const int grpc_stats_histo_buckets[3] = {64, 64, 64}; +const int grpc_stats_histo_start[3] = {0, 64, 128}; +const double *const grpc_stats_histo_bucket_boundaries[3] = { + grpc_stats_table_0, grpc_stats_table_2, grpc_stats_table_0}; diff --git a/src/core/lib/debug/stats_data.h b/src/core/lib/debug/stats_data.h index c9c2f65c30..9a4ba68655 100644 --- a/src/core/lib/debug/stats_data.h +++ b/src/core/lib/debug/stats_data.h @@ -21,6 +21,8 @@ #ifndef GRPC_CORE_LIB_DEBUG_STATS_DATA_H #define GRPC_CORE_LIB_DEBUG_STATS_DATA_H +#include <inttypes.h> + typedef enum { GRPC_STATS_COUNTER_CLIENT_CALLS_CREATED, GRPC_STATS_COUNTER_SERVER_CALLS_CREATED, @@ -28,8 +30,26 @@ typedef enum { GRPC_STATS_COUNTER_SYSCALL_READ, GRPC_STATS_COUNTER_SYSCALL_POLL, GRPC_STATS_COUNTER_SYSCALL_WAIT, + GRPC_STATS_COUNTER_HISTOGRAM_SLOW_LOOKUPS, GRPC_STATS_COUNTER_COUNT } grpc_stats_counters; +extern const char *grpc_stats_counter_name[GRPC_STATS_COUNTER_COUNT]; +typedef enum { + GRPC_STATS_HISTOGRAM_TCP_WRITE_SIZE, + GRPC_STATS_HISTOGRAM_TCP_WRITE_IOV_SIZE, + GRPC_STATS_HISTOGRAM_TCP_READ_SIZE, + GRPC_STATS_HISTOGRAM_COUNT +} grpc_stats_histograms; +extern const char *grpc_stats_histogram_name[GRPC_STATS_HISTOGRAM_COUNT]; +typedef enum { + GRPC_STATS_HISTOGRAM_TCP_WRITE_SIZE_FIRST_SLOT = 0, + GRPC_STATS_HISTOGRAM_TCP_WRITE_SIZE_BUCKETS = 64, + GRPC_STATS_HISTOGRAM_TCP_WRITE_IOV_SIZE_FIRST_SLOT = 64, + GRPC_STATS_HISTOGRAM_TCP_WRITE_IOV_SIZE_BUCKETS = 64, + GRPC_STATS_HISTOGRAM_TCP_READ_SIZE_FIRST_SLOT = 128, + GRPC_STATS_HISTOGRAM_TCP_READ_SIZE_BUCKETS = 64, + GRPC_STATS_HISTOGRAM_BUCKETS = 192 +} grpc_stats_histogram_constants; #define GRPC_STATS_INC_CLIENT_CALLS_CREATED(exec_ctx) \ GRPC_STATS_INC_COUNTER((exec_ctx), GRPC_STATS_COUNTER_CLIENT_CALLS_CREATED) #define GRPC_STATS_INC_SERVER_CALLS_CREATED(exec_ctx) \ @@ -42,6 +62,86 @@ typedef enum { GRPC_STATS_INC_COUNTER((exec_ctx), GRPC_STATS_COUNTER_SYSCALL_POLL) #define GRPC_STATS_INC_SYSCALL_WAIT(exec_ctx) \ GRPC_STATS_INC_COUNTER((exec_ctx), GRPC_STATS_COUNTER_SYSCALL_WAIT) -extern const char *grpc_stats_counter_name[GRPC_STATS_COUNTER_COUNT]; +#define GRPC_STATS_INC_HISTOGRAM_SLOW_LOOKUPS(exec_ctx) \ + GRPC_STATS_INC_COUNTER((exec_ctx), GRPC_STATS_COUNTER_HISTOGRAM_SLOW_LOOKUPS) +#define GRPC_STATS_INC_TCP_WRITE_SIZE(exec_ctx, value) \ + do { \ + union { \ + double dbl; \ + uint64_t uint; \ + } _val; \ + _val.dbl = (double)(value); \ + if (_val.dbl < 0) _val.dbl = 0; \ + if (_val.dbl < 5.000000) { \ + GRPC_STATS_INC_HISTOGRAM( \ + (exec_ctx), GRPC_STATS_HISTOGRAM_TCP_WRITE_SIZE, (int)_val.dbl); \ + } else { \ + if (_val.uint < 4715268809856909312ull) { \ + GRPC_STATS_INC_HISTOGRAM( \ + (exec_ctx), GRPC_STATS_HISTOGRAM_TCP_WRITE_SIZE, \ + grpc_stats_table_1[((_val.uint - 4617315517961601024ull) >> 50)]); \ + } else { \ + GRPC_STATS_INC_HISTOGRAM( \ + (exec_ctx), GRPC_STATS_HISTOGRAM_TCP_WRITE_SIZE, \ + grpc_stats_histo_find_bucket_slow((exec_ctx), _val.dbl, \ + grpc_stats_table_0, 64)); \ + } \ + } \ + } while (false) +#define GRPC_STATS_INC_TCP_WRITE_IOV_SIZE(exec_ctx, value) \ + do { \ + union { \ + double dbl; \ + uint64_t uint; \ + } _val; \ + _val.dbl = (double)(value); \ + if (_val.dbl < 0) _val.dbl = 0; \ + if (_val.dbl < 12.000000) { \ + GRPC_STATS_INC_HISTOGRAM( \ + (exec_ctx), GRPC_STATS_HISTOGRAM_TCP_WRITE_IOV_SIZE, (int)_val.dbl); \ + } else { \ + if (_val.uint < 4652218415073722368ull) { \ + GRPC_STATS_INC_HISTOGRAM( \ + (exec_ctx), GRPC_STATS_HISTOGRAM_TCP_WRITE_IOV_SIZE, \ + grpc_stats_table_3[((_val.uint - 4622945017495814144ull) >> 49)]); \ + } else { \ + GRPC_STATS_INC_HISTOGRAM( \ + (exec_ctx), GRPC_STATS_HISTOGRAM_TCP_WRITE_IOV_SIZE, \ + grpc_stats_histo_find_bucket_slow((exec_ctx), _val.dbl, \ + grpc_stats_table_2, 64)); \ + } \ + } \ + } while (false) +#define GRPC_STATS_INC_TCP_READ_SIZE(exec_ctx, value) \ + do { \ + union { \ + double dbl; \ + uint64_t uint; \ + } _val; \ + _val.dbl = (double)(value); \ + if (_val.dbl < 0) _val.dbl = 0; \ + if (_val.dbl < 5.000000) { \ + GRPC_STATS_INC_HISTOGRAM((exec_ctx), GRPC_STATS_HISTOGRAM_TCP_READ_SIZE, \ + (int)_val.dbl); \ + } else { \ + if (_val.uint < 4715268809856909312ull) { \ + GRPC_STATS_INC_HISTOGRAM( \ + (exec_ctx), GRPC_STATS_HISTOGRAM_TCP_READ_SIZE, \ + grpc_stats_table_1[((_val.uint - 4617315517961601024ull) >> 50)]); \ + } else { \ + GRPC_STATS_INC_HISTOGRAM( \ + (exec_ctx), GRPC_STATS_HISTOGRAM_TCP_READ_SIZE, \ + grpc_stats_histo_find_bucket_slow((exec_ctx), _val.dbl, \ + grpc_stats_table_0, 64)); \ + } \ + } \ + } while (false) +extern const double grpc_stats_table_0[64]; +extern const uint8_t grpc_stats_table_1[87]; +extern const double grpc_stats_table_2[64]; +extern const uint8_t grpc_stats_table_3[52]; +extern const int grpc_stats_histo_buckets[3]; +extern const int grpc_stats_histo_start[3]; +extern const double *const grpc_stats_histo_bucket_boundaries[3]; #endif /* GRPC_CORE_LIB_DEBUG_STATS_DATA_H */ diff --git a/src/core/lib/debug/stats_data.yaml b/src/core/lib/debug/stats_data.yaml index 8afe48f5cd..b8d5f71991 100644 --- a/src/core/lib/debug/stats_data.yaml +++ b/src/core/lib/debug/stats_data.yaml @@ -1,5 +1,5 @@ -# Stats data declaration -# use tools/codegen/core/gen_stats_data.py to turn this into stats_data.h +#Stats data declaration +#use tools / codegen / core / gen_stats_data.py to turn this into stats_data.h - counter: client_calls_created - counter: server_calls_created @@ -7,3 +7,13 @@ - counter: syscall_read - counter: syscall_poll - counter: syscall_wait +- counter: histogram_slow_lookups +- histogram: tcp_write_size + max: 16777216 # 16 meg max write tracked + buckets: 64 +- histogram: tcp_write_iov_size + max: 1024 + buckets: 64 +- histogram: tcp_read_size + max: 16777216 + buckets: 64 diff --git a/src/core/lib/iomgr/tcp_posix.c b/src/core/lib/iomgr/tcp_posix.c index 7e789cc5b5..ccf328ec1c 100644 --- a/src/core/lib/iomgr/tcp_posix.c +++ b/src/core/lib/iomgr/tcp_posix.c @@ -287,6 +287,7 @@ static void tcp_do_read(grpc_exec_ctx *exec_ctx, grpc_tcp *tcp) { GRPC_ERROR_CREATE_FROM_STATIC_STRING("Socket closed"), tcp)); TCP_UNREF(exec_ctx, tcp, "read"); } else { + GRPC_STATS_INC_TCP_READ_SIZE(exec_ctx, read_bytes); add_to_estimate(tcp, (size_t)read_bytes); GPR_ASSERT((size_t)read_bytes <= tcp->incoming_buffer->length); if ((size_t)read_bytes < tcp->incoming_buffer->length) { @@ -403,6 +404,9 @@ static bool tcp_flush(grpc_exec_ctx *exec_ctx, grpc_tcp *tcp, msg.msg_controllen = 0; msg.msg_flags = 0; + GRPC_STATS_INC_TCP_WRITE_SIZE(exec_ctx, sending_length); + GRPC_STATS_INC_TCP_WRITE_IOV_SIZE(exec_ctx, iov_size); + GPR_TIMER_BEGIN("sendmsg", 1); do { /* TODO(klempner): Cork if this is a partial write */ diff --git a/test/cpp/microbenchmarks/helpers.cc b/test/cpp/microbenchmarks/helpers.cc index 415d27445f..b0caa48cd0 100644 --- a/test/cpp/microbenchmarks/helpers.cc +++ b/test/cpp/microbenchmarks/helpers.cc @@ -31,10 +31,17 @@ void TrackCounters::Finish(benchmark::State &state) { void TrackCounters::AddToLabel(std::ostream &out, benchmark::State &state) { grpc_stats_data stats_end; grpc_stats_collect(&stats_end); + grpc_stats_data stats; + grpc_stats_diff(&stats_end, &stats_begin_, &stats); for (int i = 0; i < GRPC_STATS_COUNTER_COUNT; i++) { - out << " " << grpc_stats_counter_name[i] << "/iter:" - << ((double)(stats_end.counters[i] - stats_begin_.counters[i]) / - (double)state.iterations()); + out << " " << grpc_stats_counter_name[i] + << "/iter:" << ((double)stats.counters[i] / (double)state.iterations()); + } + for (int i = 0; i < GRPC_STATS_HISTOGRAM_COUNT; i++) { + out << " " << grpc_stats_histogram_name[i] << "-median:" + << grpc_stats_histo_percentile(&stats, (grpc_stats_histograms)i, 50.0) + << " " << grpc_stats_histogram_name[i] << "-99p:" + << grpc_stats_histo_percentile(&stats, (grpc_stats_histograms)i, 99.0); } #ifdef GPR_LOW_LEVEL_COUNTERS grpc_memory_counters counters_at_end = grpc_memory_counters_snapshot(); diff --git a/tools/codegen/core/gen_stats_data.py b/tools/codegen/core/gen_stats_data.py index bc601a89a7..85489eb7dc 100755 --- a/tools/codegen/core/gen_stats_data.py +++ b/tools/codegen/core/gen_stats_data.py @@ -15,21 +15,143 @@ # limitations under the License. import collections +import ctypes +import math import sys import yaml with open('src/core/lib/debug/stats_data.yaml') as f: attrs = yaml.load(f.read()) -Counter = collections.namedtuple('Counter', 'name') +types = ( + (collections.namedtuple('Counter', 'name'), []), + (collections.namedtuple('Histogram', 'name max buckets'), []), +) -counters = [] +inst_map = dict((t[0].__name__, t[1]) for t in types) + +stats = [] for attr in attrs: - if 'counter' in attr: - counters.append(Counter(name=attr['counter'])) + found = False + for t, lst in types: + t_name = t.__name__.lower() + if t_name in attr: + name = attr[t_name] + del attr[t_name] + lst.append(t(name=name, **attr)) + found = True + break + assert found, "Bad decl: %s" % attr + +def dbl2u64(d): + return ctypes.c_ulonglong.from_buffer(ctypes.c_double(d)).value + +def shift_works_until(mapped_bounds, shift_bits): + for i, ab in enumerate(zip(mapped_bounds, mapped_bounds[1:])): + a, b = ab + if (a >> shift_bits) == (b >> shift_bits): + return i + return len(mapped_bounds) + +def find_ideal_shift(mapped_bounds, max_size): + best = None + for shift_bits in reversed(range(0,64)): + n = shift_works_until(mapped_bounds, shift_bits) + if n == 0: continue + table_size = mapped_bounds[n-1] >> shift_bits + if table_size > max_size: continue + if table_size > 65535: continue + if best is None: + best = (shift_bits, n, table_size) + elif best[1] < n: + best = (shift_bits, n, table_size) + print best + return best + +def gen_map_table(mapped_bounds, shift_data): + tbl = [] + cur = 0 + mapped_bounds = [x >> shift_data[0] for x in mapped_bounds] + for i in range(0, mapped_bounds[shift_data[1]-1]): + while i > mapped_bounds[cur]: + cur += 1 + tbl.append(mapped_bounds[cur]) + return tbl + +static_tables = [] + +def decl_static_table(values, type): + global static_tables + v = (type, values) + for i, vp in enumerate(static_tables): + if v == vp: return i + print "ADD TABLE: %s %r" % (type, values) + r = len(static_tables) + static_tables.append(v) + return r + +def type_for_uint_table(table): + mv = max(table) + if mv < 2**8: + return 'uint8_t' + elif mv < 2**16: + return 'uint16_t' + elif mv < 2**32: + return 'uint32_t' else: - print 'Error: bad attr %r' % attr + return 'uint64_t' + +def gen_bucket_code(histogram): + bounds = [0, 1] + done_trivial = False + done_unmapped = False + first_nontrivial = None + first_unmapped = None + while len(bounds) < histogram.buckets: + mul = math.pow(float(histogram.max) / bounds[-1], + 1.0 / (histogram.buckets - len(bounds))) + nextb = bounds[-1] * mul + if nextb < bounds[-1] + 1: + nextb = bounds[-1] + 1 + elif not done_trivial: + done_trivial = True + first_nontrivial = len(bounds) + bounds.append(nextb) + bounds_idx = decl_static_table(bounds, 'double') + if done_trivial: + first_nontrivial_code = dbl2u64(first_nontrivial) + code_bounds = [dbl2u64(x) - first_nontrivial_code for x in bounds] + shift_data = find_ideal_shift(code_bounds[first_nontrivial:], 256 * histogram.buckets) + print first_nontrivial, shift_data, bounds + if shift_data is not None: print [hex(x >> shift_data[0]) for x in code_bounds[first_nontrivial:]] + code = 'do {\\\n' + code += ' union { double dbl; uint64_t uint; } _val;\\\n' + code += '_val.dbl = (double)(value);\\\n' + code += 'if (_val.dbl < 0) _val.dbl = 0;\\\n' + map_table = gen_map_table(code_bounds[first_nontrivial:], shift_data) + if first_nontrivial is None: + code += ('GRPC_STATS_INC_HISTOGRAM((exec_ctx), GRPC_STATS_HISTOGRAM_%s, (int)_val.dbl);\\\n' + % histogram.name.upper()) + else: + code += 'if (_val.dbl < %f) {\\\n' % first_nontrivial + code += ('GRPC_STATS_INC_HISTOGRAM((exec_ctx), GRPC_STATS_HISTOGRAM_%s, (int)_val.dbl);\\\n' + % histogram.name.upper()) + code += '} else {' + first_nontrivial_code = dbl2u64(first_nontrivial) + if shift_data is not None: + map_table_idx = decl_static_table(map_table, type_for_uint_table(map_table)) + code += 'if (_val.uint < %dull) {\\\n' % ((map_table[-1] << shift_data[0]) + first_nontrivial_code) + code += 'GRPC_STATS_INC_HISTOGRAM((exec_ctx), GRPC_STATS_HISTOGRAM_%s, ' % histogram.name.upper() + code += 'grpc_stats_table_%d[((_val.uint - %dull) >> %d)]);\\\n' % (map_table_idx, first_nontrivial_code, shift_data[0]) + code += '} else {\\\n' + code += 'GRPC_STATS_INC_HISTOGRAM((exec_ctx), GRPC_STATS_HISTOGRAM_%s, '% histogram.name.upper() + code += 'grpc_stats_histo_find_bucket_slow((exec_ctx), _val.dbl, grpc_stats_table_%d, %d));\\\n' % (bounds_idx, len(bounds)) + if shift_data is not None: + code += '}' + code += '}' + code += '} while (false)' + return (code, bounds_idx) # utility: print a big comment block into a set of files def put_banner(files, banner): @@ -61,17 +183,50 @@ with open('src/core/lib/debug/stats_data.h', 'w') as H: print >>H, "#ifndef GRPC_CORE_LIB_DEBUG_STATS_DATA_H" print >>H, "#define GRPC_CORE_LIB_DEBUG_STATS_DATA_H" print >>H + print >>H, "#include <inttypes.h>" + print >>H + + for typename, instances in sorted(inst_map.items()): + print >>H, "typedef enum {" + for inst in instances: + print >>H, " GRPC_STATS_%s_%s," % (typename.upper(), inst.name.upper()) + print >>H, " GRPC_STATS_%s_COUNT" % (typename.upper()) + print >>H, "} grpc_stats_%ss;" % (typename.lower()) + print >>H, "extern const char *grpc_stats_%s_name[GRPC_STATS_%s_COUNT];" % ( + typename.lower(), typename.upper()) + + histo_start = [] + histo_buckets = [] + histo_bucket_boundaries = [] print >>H, "typedef enum {" - for ctr in counters: - print >>H, " GRPC_STATS_COUNTER_%s," % ctr.name.upper() - print >>H, " GRPC_STATS_COUNTER_COUNT" - print >>H, "} grpc_stats_counters;" + first_slot = 0 + for histogram in inst_map['Histogram']: + histo_start.append(first_slot) + histo_buckets.append(histogram.buckets) + print >>H, " GRPC_STATS_HISTOGRAM_%s_FIRST_SLOT = %d," % (histogram.name.upper(), first_slot) + print >>H, " GRPC_STATS_HISTOGRAM_%s_BUCKETS = %d," % (histogram.name.upper(), histogram.buckets) + first_slot += histogram.buckets + print >>H, " GRPC_STATS_HISTOGRAM_BUCKETS = %d" % first_slot + print >>H, "} grpc_stats_histogram_constants;" + + for ctr in inst_map['Counter']: + print >>H, ("#define GRPC_STATS_INC_%s(exec_ctx) " + + "GRPC_STATS_INC_COUNTER((exec_ctx), GRPC_STATS_COUNTER_%s)") % ( + ctr.name.upper(), ctr.name.upper()) + for histogram in inst_map['Histogram']: + code, bounds_idx = gen_bucket_code(histogram) + histo_bucket_boundaries.append(bounds_idx) + print >>H, ("#define GRPC_STATS_INC_%s(exec_ctx, value) %s") % ( + histogram.name.upper(), + code) - for ctr in counters: - print >>H, "#define GRPC_STATS_INC_%s(exec_ctx) GRPC_STATS_INC_COUNTER((exec_ctx), GRPC_STATS_COUNTER_%s)" % (ctr.name.upper(), ctr.name.upper()) + for i, tbl in enumerate(static_tables): + print >>H, "extern const %s grpc_stats_table_%d[%d];" % (tbl[0], i, len(tbl[1])) - print >>H, "extern const char *grpc_stats_counter_name[GRPC_STATS_COUNTER_COUNT];" + print >>H, "extern const int grpc_stats_histo_buckets[%d];" % len(inst_map['Histogram']) + print >>H, "extern const int grpc_stats_histo_start[%d];" % len(inst_map['Histogram']) + print >>H, "extern const double *const grpc_stats_histo_bucket_boundaries[%d];" % len(inst_map['Histogram']) print >>H print >>H, "#endif /* GRPC_CORE_LIB_DEBUG_STATS_DATA_H */" @@ -96,7 +251,19 @@ with open('src/core/lib/debug/stats_data.c', 'w') as C: print >>C, "#include \"src/core/lib/debug/stats_data.h\"" - print >>C, "const char *grpc_stats_counter_name[GRPC_STATS_COUNTER_COUNT] = {"; - for ctr in counters: - print >>C, " \"%s\"," % ctr.name - print >>C, "};" + for typename, instances in sorted(inst_map.items()): + print >>C, "const char *grpc_stats_%s_name[GRPC_STATS_%s_COUNT] = {" % ( + typename.lower(), typename.upper()) + for inst in instances: + print >>C, " \"%s\"," % inst.name + print >>C, "};" + for i, tbl in enumerate(static_tables): + print >>C, "const %s grpc_stats_table_%d[%d] = {%s};" % ( + tbl[0], i, len(tbl[1]), ','.join('%s' % x for x in tbl[1])) + + print >>C, "const int grpc_stats_histo_buckets[%d] = {%s};" % ( + len(inst_map['Histogram']), ','.join('%s' % x for x in histo_buckets)) + print >>C, "const int grpc_stats_histo_start[%d] = {%s};" % ( + len(inst_map['Histogram']), ','.join('%s' % x for x in histo_start)) + print >>C, "const double *const grpc_stats_histo_bucket_boundaries[%d] = {%s};" % ( + len(inst_map['Histogram']), ','.join('grpc_stats_table_%d' % x for x in histo_bucket_boundaries)) |