From 29380ff77d5da51d3065b810a9f93171f8724c1a Mon Sep 17 00:00:00 2001 From: filipe oliveira Date: Sat, 15 Oct 2022 10:17:41 +0100 Subject: optimizing d2string() and addReplyDouble() with grisu2: double to string conversion based on Florian Loitsch's Grisu-algorithm (#10587) All commands / use cases that heavily rely on double to a string representation conversion, (e.g. meaning take a double-precision floating-point number like 1.5 and return a string like "1.5" ), could benefit from a performance boost by swapping snprintf(buf,len,"%.17g",value) by the equivalent [fpconv_dtoa](https://github.com/night-shift/fpconv) or any other algorithm that ensures 100% coverage of conversion. This is a well-studied topic and Projects like MongoDB. RedPanda, PyTorch leverage libraries ( fmtlib ) that use the optimized double to string conversion underneath. The positive impact can be substantial. This PR uses the grisu2 approach ( grisu explained on https://www.cs.tufts.edu/~nr/cs257/archive/florian-loitsch/printf.pdf section 5 ). test suite changes: Despite being compatible, in some cases it produces a different result from printf, and some tests had to be adjusted. one case is that `%.17g` (which means %e or %f which ever is shorter), chose to use `5000000000` instead of 5e+9, which sounds like a bug? In other cases, we changed TCL to compare numbers instead of strings to ignore minor rounding issues (`expr 0.8 == 0.79999999999999999`) --- src/networking.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) (limited to 'src/networking.c') diff --git a/src/networking.c b/src/networking.c index 3cdbe16ce..180782584 100644 --- a/src/networking.c +++ b/src/networking.c @@ -31,6 +31,7 @@ #include "atomicvar.h" #include "cluster.h" #include "script.h" +#include "fpconv_dtoa.h" #include #include #include @@ -855,7 +856,7 @@ void addReplyDouble(client *c, double d) { * but still avoid an extra memcpy of the whole number, we reserve space * for maximum header `$0000\r\n`, print double, add the resp header in * front of it, and then send the buffer with the right `start` offset. */ - int dlen = snprintf(dbuf+7,sizeof(dbuf) - 7,"%.17g",d); + dlen = fpconv_dtoa(d, dbuf+7); int digits = digits10(dlen); int start = 4 - digits; dbuf[start] = '$'; @@ -870,10 +871,15 @@ void addReplyDouble(client *c, double d) { dbuf[6] = '\n'; dbuf[dlen+7] = '\r'; dbuf[dlen+8] = '\n'; + dbuf[dlen+9] = '\0'; addReplyProto(c,dbuf+start,dlen+9-start); } else { - dlen = snprintf(dbuf,sizeof(dbuf),",%.17g\r\n",d); - addReplyProto(c,dbuf,dlen); + dbuf[0] = ','; + dlen = fpconv_dtoa(d, dbuf+1); + dbuf[dlen+1] = '\r'; + dbuf[dlen+2] = '\n'; + dbuf[dlen+3] = '\0'; + addReplyProto(c,dbuf,dlen+3); } } } -- cgit v1.2.1