@@ -19,8 +19,9 @@ namespace {
1919
2020constexpr int DEFAULT_WARMUP_SECONDS = 1 ;
2121constexpr int DEFAULT_INTERVAL_SECONDS = 5 ;
22+ constexpr int DEFAULT_MIN_INFLIGHT = 1 ;
2223constexpr int DEFAULT_MAX_INFLIGHT = 128 ;
23- constexpr int DEFAULT_PERCENTILE = 99.0 ;
24+ const std::vector< double > DEFAULT_PERCENTILES = { 50.0 , 90 , 99.0 } ;
2425
2526constexpr TCommandLatency::EFormat DEFAULT_FORMAT = TCommandLatency::EFormat::Plain;
2627constexpr TCommandPing::EPingKind DEFAULT_RUN_KIND = TCommandPing::EPingKind::AllKinds;
@@ -36,9 +37,19 @@ using TRequestMaker = std::function<bool()>;
3637using TCallableFactory = std::function<TRequestMaker()>;
3738
3839struct TResult {
40+ TResult () = default ;
41+
42+ TResult (TCommandPing::EPingKind kind, int threadCount, NHdr::THistogram&& hist, int throughput)
43+ : Kind(kind)
44+ , ThreadCount(threadCount)
45+ , LatencyHistogramUs(std::move(hist))
46+ , Throughput(throughput)
47+ {
48+ }
49+
3950 TCommandPing::EPingKind Kind;
4051 int ThreadCount = 0 ;
41- int LatencyUs = 0 ;
52+ NHdr::THistogram LatencyHistogramUs ;
4253 int Throughput = 0 ;
4354};
4455
@@ -50,8 +61,6 @@ struct alignas(64) TEvaluateResult {
5061
5162 ui64 OkCount = 0 ;
5263 ui64 ErrorCount = 0 ;
53- int LatencyUs = 0 ;
54-
5564 NHdr::THistogram LatencyHistogramUs;
5665};
5766
@@ -60,8 +69,7 @@ void Evaluate(
6069 ui64 warmupSeconds,
6170 ui64 intervalSeconds,
6271 int threadCount,
63- TCallableFactory factory,
64- double percentile)
72+ TCallableFactory factory)
6573{
6674 std::atomic<bool > startMeasure{false };
6775 std::atomic<bool > stop{false };
@@ -133,19 +141,17 @@ void Evaluate(
133141 total.ErrorCount += result.ErrorCount ;
134142 total.LatencyHistogramUs .Add (result.LatencyHistogramUs );
135143 }
136-
137- total.LatencyUs = total.LatencyHistogramUs .GetValueAtPercentile (percentile);
138144}
139145
140146} // anonymous
141147
142148TCommandLatency::TCommandLatency ()
143149 : TYdbCommand(" latency" , {}, " Check basic latency with variable inflight" )
144150 , IntervalSeconds(DEFAULT_INTERVAL_SECONDS)
151+ , MinInflight(DEFAULT_MIN_INFLIGHT)
145152 , MaxInflight(DEFAULT_MAX_INFLIGHT)
146153 , Format(DEFAULT_FORMAT)
147154 , RunKind(DEFAULT_RUN_KIND)
148- , Percentile(DEFAULT_PERCENTILE)
149155 , ChainConfig(new NDebug::TActorChainPingSettings())
150156{}
151157
@@ -161,12 +167,15 @@ void TCommandLatency::Config(TConfig& config) {
161167 config.Opts ->AddLongOption (
162168 ' i' , " interval" , TStringBuilder () << " Seconds for each latency kind" )
163169 .RequiredArgument (" INT" ).StoreResult (&IntervalSeconds).DefaultValue (DEFAULT_INTERVAL_SECONDS);
170+ config.Opts ->AddLongOption (
171+ " min-inflight" , TStringBuilder () << " Min inflight" )
172+ .RequiredArgument (" INT" ).StoreResult (&MinInflight).DefaultValue (DEFAULT_MIN_INFLIGHT);
164173 config.Opts ->AddLongOption (
165174 ' m' , " max-inflight" , TStringBuilder () << " Max inflight" )
166175 .RequiredArgument (" INT" ).StoreResult (&MaxInflight).DefaultValue (DEFAULT_MAX_INFLIGHT);
167176 config.Opts ->AddLongOption (
168177 ' p' , " percentile" , TStringBuilder () << " Latency percentile" )
169- .RequiredArgument (" DOUBLE" ).StoreResult (&Percentile). DefaultValue (DEFAULT_PERCENTILE );
178+ .RequiredArgument (" DOUBLE" ).AppendTo (&Percentiles );
170179 config.Opts ->AddLongOption (
171180 ' f' , " format" , TStringBuilder () << " Output format. Available options: " << availableFormats)
172181 .OptionalArgument (" STRING" ).StoreResult (&Format).DefaultValue (DEFAULT_FORMAT);
@@ -188,6 +197,10 @@ void TCommandLatency::Config(TConfig& config) {
188197
189198void TCommandLatency::Parse (TConfig& config) {
190199 TClientCommand::Parse (config);
200+
201+ if (Percentiles.empty ()) {
202+ Percentiles = DEFAULT_PERCENTILES;
203+ }
191204}
192205
193206int TCommandLatency::Run (TConfig& config) {
@@ -272,9 +285,9 @@ int TCommandLatency::Run(TConfig& config) {
272285
273286 std::vector<TResult> results;
274287 for (const auto & [taskKind, factory]: runTasks) {
275- for (int threadCount = 1 ; threadCount <= MaxInflight && !IsInterrupted (); ) {
288+ for (int threadCount = MinInflight ; threadCount <= MaxInflight && !IsInterrupted (); ) {
276289 TEvaluateResult result;
277- Evaluate (result, DEFAULT_WARMUP_SECONDS, IntervalSeconds, threadCount, factory, Percentile );
290+ Evaluate (result, DEFAULT_WARMUP_SECONDS, IntervalSeconds, threadCount, factory);
278291
279292 bool skip = false ;
280293 if (result.ErrorCount ) {
@@ -290,18 +303,20 @@ int TCommandLatency::Run(TConfig& config) {
290303 if (!skip) {
291304 ui64 throughput = result.OkCount / IntervalSeconds;
292305 ui64 throughputPerThread = throughput / threadCount;
293- ui64 latencyUsec = result.LatencyUs ;
294-
295- results.emplace_back (taskKind, threadCount, latencyUsec, throughput);
296306
297307 if (Format == EFormat::Plain) {
298308 Cout << taskKind << " threads=" << threadCount
299309 << " , throughput: " << throughput
300- << " , per thread: " << throughputPerThread
301- << " , latency p" << Percentile << " usec: " << latencyUsec
302- << " , ok: " << result.OkCount
303- << " , error: " << result.ErrorCount << Endl;
310+ << " , per thread: " << throughputPerThread;
311+ for (size_t i = 0 ; i < Percentiles.size (); ++i) {
312+ Cout << " , p" << Percentiles[i]
313+ << " usec: " << result.LatencyHistogramUs .GetValueAtPercentile (Percentiles[i]);
314+ }
315+ Cout << " , ok: " << result.OkCount << " , error: " << result.ErrorCount << Endl;
304316 }
317+
318+ // note the move
319+ results.emplace_back (taskKind, threadCount, std::move (result.LatencyHistogramUs ), throughput);
305320 }
306321
307322 if (threadCount < INCREMENT_UNTIL_THREAD_COUNT) {
@@ -316,18 +331,17 @@ int TCommandLatency::Run(TConfig& config) {
316331 return 0 ;
317332 }
318333
319- TMap<TCommandPing::EPingKind, std::vector<int >> latencies;
334+ TMap<TCommandPing::EPingKind, std::vector<const NHdr::THistogram* >> latencies;
320335 TMap<TCommandPing::EPingKind, std::vector<int >> throughputs;
321336 for (const auto & result: results) {
322- latencies[result.Kind ].push_back (result.LatencyUs );
337+ latencies[result.Kind ].push_back (& result.LatencyHistogramUs );
323338 throughputs[result.Kind ].push_back (result.Throughput );
324339 }
325340
326341 if (Format == EFormat::CSV) {
327342 const int maxThreadsMeasured = results.back ().ThreadCount ;
328343
329344 Cout << Endl;
330- Cout << " Latencies" << Endl;
331345
332346 TStringStream ss;
333347 ss << " Kind" ;
@@ -342,17 +356,21 @@ int TCommandLatency::Run(TConfig& config) {
342356 ss << Endl;
343357 TString header = ss.Str ();
344358
345- Cout << header;
346- for (const auto & [kind, vec]: latencies) {
347- Cout << kind;
348- for (auto value: vec) {
349- Cout << " ," << value;
359+ for (auto percentile: Percentiles) {
360+ Cout << " Latencies, p" << percentile << Endl;
361+
362+ Cout << header;
363+ for (const auto & [kind, histVec]: latencies) {
364+ Cout << kind;
365+ for (const auto & hist: histVec) {
366+ Cout << " ," << hist->GetValueAtPercentile (percentile);
367+ }
368+ Cout << Endl;
350369 }
351370 Cout << Endl;
352371 }
353- Cout << Endl;
354372
355- Cout << " Througputs " << Endl;
373+ Cout << " Throughputs " << Endl;
356374 Cout << header;
357375 for (const auto & [kind, vec]: throughputs) {
358376 Cout << kind;
0 commit comments