@@ -380,21 +380,34 @@ void RedisReplyBuilder::SendScoredArray(const std::vector<std::pair<std::string,
380
380
bool with_scores) {
381
381
ReplyAggregator agg (this );
382
382
if (!with_scores) {
383
- StartArray (arr.size ());
384
- for (const auto & p : arr) {
385
- SendBulkString (p.first );
386
- }
383
+ auto cb = [&](size_t indx) -> string_view { return arr[indx].first ; };
384
+
385
+ SendStringArrInternal (arr.size (), std::move (cb), CollectionType::ARRAY);
387
386
return ;
388
387
}
388
+
389
+ // DoubleToStringConverter::kBase10MaximalLength is 17.
390
+ char buf[64 ];
391
+
389
392
if (!is_resp3_) { // RESP2 formats withscores as a flat array.
390
- StartArray (arr.size () * 2 );
391
- for (const auto & p : arr) {
392
- SendBulkString (p.first );
393
- SendDouble (p.second );
394
- }
393
+ auto cb = [&](size_t indx) -> string_view {
394
+ if (indx % 2 == 0 )
395
+ return arr[indx / 2 ].first ;
396
+
397
+ // NOTE: we reuse the same buffer, assuming that SendStringArrInternal does not reference
398
+ // previous string_views. The assumption holds for small strings like
399
+ // doubles because SendStringArrInternal employs small string optimization.
400
+ // It's a bit hacky but saves allocations.
401
+ return FormatDouble (arr[indx / 2 ].second , buf, sizeof (buf));
402
+ };
403
+
404
+ SendStringArrInternal (arr.size () * 2 , std::move (cb), CollectionType::ARRAY);
395
405
return ;
396
406
}
407
+
397
408
// Resp3 formats withscores as array of (key, score) pairs.
409
+ // TODO: to implement efficient serializing by extending SendStringArrInternal to support
410
+ // 2-level arrays.
398
411
StartArray (arr.size ());
399
412
for (const auto & p : arr) {
400
413
StartArray (2 );
@@ -406,15 +419,13 @@ void RedisReplyBuilder::SendScoredArray(const std::vector<std::pair<std::string,
406
419
void RedisReplyBuilder::SendDouble (double val) {
407
420
char buf[64 ];
408
421
409
- StringBuilder sb (buf, sizeof (buf));
410
- CHECK (dfly_conv.ToShortest (val, &sb));
422
+ char * start = FormatDouble (val, buf, sizeof (buf));
411
423
412
424
if (!is_resp3_) {
413
- SendBulkString (sb. Finalize () );
425
+ SendBulkString (start );
414
426
} else {
415
427
// RESP3
416
- string str = absl::StrCat (" ," , sb.Finalize (), kCRLF );
417
- SendRaw (str);
428
+ SendRaw (absl::StrCat (" ," , start, kCRLF ));
418
429
}
419
430
}
420
431
@@ -576,10 +587,11 @@ void RedisReplyBuilder::SendStringArrInternal(
576
587
}
577
588
578
589
// When vector length is too long, Send returns EMSGSIZE.
579
- size_t vec_len = std::min<size_t >(128u , size);
590
+ size_t vec_len = std::min<size_t >(124u , size);
580
591
581
592
absl::FixedArray<iovec, 16 > vec (vec_len * 2 + 2 );
582
- absl::FixedArray<char , 64 > meta ((vec_len + 1 ) * 16 ); // 16 bytes per length.
593
+ absl::FixedArray<char , 128 > meta (vec_len * 32 + 64 ); // 32 bytes per element + spare space
594
+
583
595
char * next = meta.data ();
584
596
585
597
auto serialize_len = [&](char prefix, size_t len) {
@@ -598,18 +610,27 @@ void RedisReplyBuilder::SendStringArrInternal(
598
610
for (unsigned i = 0 ; i < size; ++i) {
599
611
src = producer (i);
600
612
serialize_len (' $' , src.size ());
613
+
601
614
// add serialized len blob
602
615
vec[vec_indx++] = IoVec (string_view{start, size_t (next - start)});
603
616
DCHECK_GT (next - start, 0 );
604
617
605
618
start = next;
606
619
607
- vec[vec_indx++] = IoVec (src);
608
-
620
+ // copy data either by referencing via an iovec or copying inline into meta buf.
621
+ if (src.size () >= 30 ) {
622
+ vec[vec_indx++] = IoVec (src);
623
+ } else if (src.size () > 0 ) {
624
+ memcpy (next, src.data (), src.size ());
625
+ vec[vec_indx - 1 ].iov_len += src.size (); // extend the reference
626
+ next += src.size ();
627
+ start = next;
628
+ }
609
629
*next++ = ' \r ' ;
610
630
*next++ = ' \n ' ;
611
631
612
- if (vec_indx + 1 >= vec.size ()) {
632
+ // we keep at least 40 bytes to have enough place for a small string as well as its length.
633
+ if (vec_indx + 1 >= vec.size () || (meta.end () - next < 40 )) {
613
634
// Flush the iovec array.
614
635
if (i < size - 1 || vec_indx == vec.size ()) {
615
636
Send (vec.data (), vec_indx);
0 commit comments