Skip to content

Commit 9d9be0e

Browse files
committed
Initial implementation of redis-cli --latency-dist.
1 parent d4047f7 commit 9d9be0e

File tree

1 file changed

+153
-0
lines changed

1 file changed

+153
-0
lines changed

src/redis-cli.c

Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@
4444
#include <assert.h>
4545
#include <fcntl.h>
4646
#include <limits.h>
47+
#include <math.h>
4748

4849
#include "hiredis.h"
4950
#include "sds.h"
@@ -74,6 +75,7 @@ static struct config {
7475
int monitor_mode;
7576
int pubsub_mode;
7677
int latency_mode;
78+
int latency_dist_mode;
7779
int latency_history;
7880
int cluster_mode;
7981
int cluster_reissue_command;
@@ -741,6 +743,8 @@ static int parseOptions(int argc, char **argv) {
741743
config.output = OUTPUT_CSV;
742744
} else if (!strcmp(argv[i],"--latency")) {
743745
config.latency_mode = 1;
746+
} else if (!strcmp(argv[i],"--latency-dist")) {
747+
config.latency_dist_mode = 1;
744748
} else if (!strcmp(argv[i],"--latency-history")) {
745749
config.latency_mode = 1;
746750
config.latency_history = 1;
@@ -833,6 +837,8 @@ static void usage(void) {
833837
" --latency Enter a special mode continuously sampling latency.\n"
834838
" --latency-history Like --latency but tracking latency changes over time.\n"
835839
" Default time interval is 15 sec. Change it using -i.\n"
840+
" --latency-dist Shows latency as a spectrum, requires xterm 256 colors.\n"
841+
" Default time interval is 1 sec. Change it using -i.\n"
836842
" --slave Simulate a slave showing commands received from the master.\n"
837843
" --rdb <filename> Transfer an RDB dump from remote server to local file.\n"
838844
" --pipe Transfer raw Redis protocol from stdin to server.\n"
@@ -1071,6 +1077,146 @@ static void latencyMode(void) {
10711077
}
10721078
}
10731079

1080+
/*------------------------------------------------------------------------------
1081+
* Latency distribution mode -- requires 256 colors xterm
1082+
*--------------------------------------------------------------------------- */
1083+
1084+
#define LATENCY_DIST_DEFAULT_INTERVAL 1000 /* milliseconds. */
1085+
#define LATENCY_DIST_MIN_GRAY 233 /* Less than that is too hard to see gray. */
1086+
#define LATENCY_DIST_MAX_GRAY 255
1087+
#define LATENCY_DIST_GRAYS (LATENCY_DIST_MAX_GRAY-LATENCY_DIST_MIN_GRAY+1)
1088+
1089+
/* Structure to store samples distribution. */
1090+
struct distsamples {
1091+
long long max; /* Max latency to fit into this interval (usec). */
1092+
long long count; /* Number of samples in this interval. */
1093+
int character; /* Associated character in visualization. */
1094+
};
1095+
1096+
/* Helper function for latencyDistMode(). Performs the spectrum visualization
1097+
* of the collected samples targeting an xterm 256 terminal.
1098+
*
1099+
* Takes an array of distsamples structures, ordered from smaller to bigger
1100+
* 'max' value. Last sample max must be 0, to mean that it olds all the
1101+
* samples greater than the previous one, and is also the stop sentinel.
1102+
*
1103+
* "tot' is the total number of samples in the different buckets, so it
1104+
* is the SUM(samples[i].conut) for i to 0 up to the max sample.
1105+
*
1106+
* As a side effect the function sets all the buckets count to 0. */
1107+
void showLatencyDistSamples(struct distsamples *samples, long long tot) {
1108+
int j;
1109+
1110+
/* We convert samples into a number between 0 and DIST_GRAYS,
1111+
* proportional to the percentage a given bucket represents.
1112+
* This way intensity of the different parts of the spectrum
1113+
* don't change relative to the number of requests, which avoids to
1114+
* pollute the visualization with non-latency related info. */
1115+
printf("\033[38;5;0m"); /* Set foreground color to black. */
1116+
for (j = 0; ; j++) {
1117+
float color = (float) samples[j].count / tot * LATENCY_DIST_GRAYS;
1118+
color = ceil(color) + (LATENCY_DIST_MIN_GRAY-1);
1119+
if (color == LATENCY_DIST_MIN_GRAY-1) {
1120+
printf("\033[48;5;0m ");
1121+
} else {
1122+
printf("\033[48;5;%dm%c", (int)color, samples[j].character);
1123+
}
1124+
samples[j].count = 0;
1125+
if (samples[j].max == 0) break; /* Last sample. */
1126+
}
1127+
printf("\033[0m\n");
1128+
fflush(stdout);
1129+
}
1130+
1131+
/* Show the legend: different buckets values and colors meaning, so
1132+
* that the spectrum is more easily readable. */
1133+
void showLatencyDistLegend(void) {
1134+
printf(". - * 0.01 0.125 0.5 milliseconds\n");
1135+
printf("1,2,3,...,9 from 1 to 9 milliseconds\n");
1136+
printf("A,B,C,D,E 10,20,30,40,50 milliseconds\n");
1137+
printf("F,G,H,I,J .1,.2,.3,.4,.5 seconds\n");
1138+
printf("K,L,M,N,O,P,Q,? 1,2,4,8,16,30,60,>60 seconds\n");
1139+
printf("---------------------------------------------\n");
1140+
}
1141+
1142+
static void latencyDistMode(void) {
1143+
redisReply *reply;
1144+
long long start, latency, count = 0;
1145+
long long history_interval =
1146+
config.interval ? config.interval/1000 :
1147+
LATENCY_DIST_DEFAULT_INTERVAL;
1148+
long long history_start = ustime();
1149+
int j, outputs = 0;
1150+
1151+
struct distsamples samples[] = {
1152+
/* We use a mostly logarithmic scale, with certain linear intervals
1153+
* which are more interesting than others, like 1-10 milliseconds
1154+
* range. */
1155+
{10,0,'.'}, /* 0.01 ms */
1156+
{125,0,'-'}, /* 0.125 ms */
1157+
{250,0,'*'}, /* 0.25 ms */
1158+
{500,0,'#'}, /* 0.5 ms */
1159+
{1000,0,'1'}, /* 1 ms */
1160+
{2000,0,'2'}, /* 2 ms */
1161+
{3000,0,'3'}, /* 3 ms */
1162+
{4000,0,'4'}, /* 4 ms */
1163+
{5000,0,'5'}, /* 5 ms */
1164+
{6000,0,'6'}, /* 6 ms */
1165+
{7000,0,'7'}, /* 7 ms */
1166+
{8000,0,'8'}, /* 8 ms */
1167+
{9000,0,'9'}, /* 9 ms */
1168+
{10000,0,'A'}, /* 10 ms */
1169+
{20000,0,'B'}, /* 20 ms */
1170+
{30000,0,'C'}, /* 30 ms */
1171+
{40000,0,'D'}, /* 40 ms */
1172+
{50000,0,'E'}, /* 50 ms */
1173+
{100000,0,'F'}, /* 0.1 s */
1174+
{200000,0,'G'}, /* 0.2 s */
1175+
{300000,0,'H'}, /* 0.3 s */
1176+
{400000,0,'I'}, /* 0.4 s */
1177+
{500000,0,'J'}, /* 0.5 s */
1178+
{1000000,0,'K'}, /* 1 s */
1179+
{2000000,0,'L'}, /* 2 s */
1180+
{4000000,0,'M'}, /* 4 s */
1181+
{8000000,0,'N'}, /* 8 s */
1182+
{16000000,0,'O'}, /* 16 s */
1183+
{30000000,0,'P'}, /* 30 s */
1184+
{60000000,0,'Q'}, /* 1 minute */
1185+
{0,0,'?'}, /* > 1 minute */
1186+
};
1187+
1188+
if (!context) exit(1);
1189+
while(1) {
1190+
start = ustime();
1191+
reply = redisCommand(context,"PING");
1192+
if (reply == NULL) {
1193+
fprintf(stderr,"\nI/O error\n");
1194+
exit(1);
1195+
}
1196+
latency = ustime()-start;
1197+
freeReplyObject(reply);
1198+
count++;
1199+
1200+
/* Populate the relevant bucket. */
1201+
for (j = 0; ; j++) {
1202+
if (samples[j].max == 0 || latency <= samples[j].max) {
1203+
samples[j].count++;
1204+
break;
1205+
}
1206+
}
1207+
1208+
/* From time to time show the spectrum. */
1209+
if (count && (ustime()-history_start)/1000 > history_interval) {
1210+
if ((outputs++ % 20) == 0)
1211+
showLatencyDistLegend();
1212+
showLatencyDistSamples(samples,count);
1213+
history_start = ustime();
1214+
count = 0;
1215+
}
1216+
usleep(LATENCY_SAMPLE_RATE * 1000);
1217+
}
1218+
}
1219+
10741220
/*------------------------------------------------------------------------------
10751221
* Slave mode
10761222
*--------------------------------------------------------------------------- */
@@ -1887,6 +2033,7 @@ int main(int argc, char **argv) {
18872033
config.monitor_mode = 0;
18882034
config.pubsub_mode = 0;
18892035
config.latency_mode = 0;
2036+
config.latency_dist_mode = 0;
18902037
config.latency_history = 0;
18912038
config.cluster_mode = 0;
18922039
config.slave_mode = 0;
@@ -1921,6 +2068,12 @@ int main(int argc, char **argv) {
19212068
latencyMode();
19222069
}
19232070

2071+
/* Latency distribution mode */
2072+
if (config.latency_dist_mode) {
2073+
if (cliConnect(0) == REDIS_ERR) exit(1);
2074+
latencyDistMode();
2075+
}
2076+
19242077
/* Slave mode */
19252078
if (config.slave_mode) {
19262079
if (cliConnect(0) == REDIS_ERR) exit(1);

0 commit comments

Comments
 (0)