use embedded string object and more efficient ll2string for long long value convert to string (#12250)
A value of type long long is always less than 21 bytes when convert to a string, so always meets the conditions for using embedded string object which can always get memory reduction and performance gain (less calls to the heap allocator). Additionally, for the conversion of longlong type to sds, we also use a faster algorithm (the one in util.c instead of the one that used to be in sds.c). For the DECR command on 32-bit Redis, we get about a 5.7% performance improvement. There will also be some performance gains for some commands that heavily use sdscatfmt to convert numbers, such as INFO. Co-authored-by: Oran Agra <oran@redislabs.com>
This commit is contained in:
parent
d9c2ef8a72
commit
93708c7f6a
@ -490,7 +490,7 @@ void geoaddCommand(client *c) {
|
||||
GeoHashBits hash;
|
||||
geohashEncodeWGS84(xy[0], xy[1], GEO_STEP_MAX, &hash);
|
||||
GeoHashFix52Bits bits = geohashAlign52Bits(hash);
|
||||
robj *score = createObject(OBJ_STRING, sdsfromlonglong(bits));
|
||||
robj *score = createStringObjectFromLongLongWithSds(bits);
|
||||
robj *val = c->argv[longidx + i * 3 + 2];
|
||||
argv[longidx+i*2] = score;
|
||||
argv[longidx+1+i*2] = val;
|
||||
|
@ -6037,7 +6037,7 @@ robj **moduleCreateArgvFromUserFormat(const char *cmdname, const char *fmt, int
|
||||
argv[argc++] = createStringObject(buf,len);
|
||||
} else if (*p == 'l') {
|
||||
long long ll = va_arg(ap,long long);
|
||||
argv[argc++] = createObject(OBJ_STRING,sdsfromlonglong(ll));
|
||||
argv[argc++] = createStringObjectFromLongLongWithSds(ll);
|
||||
} else if (*p == 'v') {
|
||||
/* A vector of strings */
|
||||
robj **v = va_arg(ap, void*);
|
||||
@ -11049,12 +11049,12 @@ int RM_ScanKey(RedisModuleKey *key, RedisModuleScanCursor *cursor, RedisModuleSc
|
||||
vstr = lpGetValue(p,&vlen,&vll);
|
||||
robj *field = (vstr != NULL) ?
|
||||
createStringObject((char*)vstr,vlen) :
|
||||
createObject(OBJ_STRING,sdsfromlonglong(vll));
|
||||
createStringObjectFromLongLongWithSds(vll);
|
||||
p = lpNext(o->ptr,p);
|
||||
vstr = lpGetValue(p,&vlen,&vll);
|
||||
robj *value = (vstr != NULL) ?
|
||||
createStringObject((char*)vstr,vlen) :
|
||||
createObject(OBJ_STRING,sdsfromlonglong(vll));
|
||||
createStringObjectFromLongLongWithSds(vll);
|
||||
fn(key, field, value, privdata);
|
||||
p = lpNext(o->ptr,p);
|
||||
decrRefCount(field);
|
||||
|
53
src/object.c
53
src/object.c
@ -142,33 +142,24 @@ robj *tryCreateStringObject(const char *ptr, size_t len) {
|
||||
return tryCreateRawStringObject(ptr,len);
|
||||
}
|
||||
|
||||
/* Create a string object from a long long value. When possible returns a
|
||||
* shared integer object, or at least an integer encoded one.
|
||||
*
|
||||
* If valueobj is non zero, the function avoids returning a shared
|
||||
* integer, because the object is going to be used as value in the Redis key
|
||||
* space (for instance when the INCR command is used), so we want LFU/LRU
|
||||
* values specific for each key. */
|
||||
robj *createStringObjectFromLongLongWithOptions(long long value, int valueobj) {
|
||||
/* Create a string object from a long long value according to the specified flag. */
|
||||
#define LL2STROBJ_AUTO 0 /* automatically create the optimal string object */
|
||||
#define LL2STROBJ_NO_SHARED 1 /* disallow shared objects */
|
||||
#define LL2STROBJ_NO_INT_ENC 2 /* disallow integer encoded objects. */
|
||||
robj *createStringObjectFromLongLongWithOptions(long long value, int flag) {
|
||||
robj *o;
|
||||
|
||||
if (server.maxmemory == 0 ||
|
||||
!(server.maxmemory_policy & MAXMEMORY_FLAG_NO_SHARED_INTEGERS))
|
||||
{
|
||||
/* If the maxmemory policy permits, we can still return shared integers
|
||||
* even if valueobj is true. */
|
||||
valueobj = 0;
|
||||
}
|
||||
|
||||
if (value >= 0 && value < OBJ_SHARED_INTEGERS && valueobj == 0) {
|
||||
if (value >= 0 && value < OBJ_SHARED_INTEGERS && flag == LL2STROBJ_AUTO) {
|
||||
o = shared.integers[value];
|
||||
} else {
|
||||
if (value >= LONG_MIN && value <= LONG_MAX) {
|
||||
if ((value >= LONG_MIN && value <= LONG_MAX) && flag != LL2STROBJ_NO_INT_ENC) {
|
||||
o = createObject(OBJ_STRING, NULL);
|
||||
o->encoding = OBJ_ENCODING_INT;
|
||||
o->ptr = (void*)((long)value);
|
||||
} else {
|
||||
o = createObject(OBJ_STRING,sdsfromlonglong(value));
|
||||
char buf[LONG_STR_SIZE];
|
||||
int len = ll2string(buf, sizeof(buf), value);
|
||||
o = createStringObject(buf, len);
|
||||
}
|
||||
}
|
||||
return o;
|
||||
@ -177,15 +168,27 @@ robj *createStringObjectFromLongLongWithOptions(long long value, int valueobj) {
|
||||
/* Wrapper for createStringObjectFromLongLongWithOptions() always demanding
|
||||
* to create a shared object if possible. */
|
||||
robj *createStringObjectFromLongLong(long long value) {
|
||||
return createStringObjectFromLongLongWithOptions(value,0);
|
||||
return createStringObjectFromLongLongWithOptions(value, LL2STROBJ_AUTO);
|
||||
}
|
||||
|
||||
/* Wrapper for createStringObjectFromLongLongWithOptions() avoiding a shared
|
||||
* object when LFU/LRU info are needed, that is, when the object is used
|
||||
* as a value in the key space, and Redis is configured to evict based on
|
||||
* LFU/LRU. */
|
||||
/* The function avoids returning a shared integer when LFU/LRU info
|
||||
* are needed, that is, when the object is used as a value in the key
|
||||
* space(for instance when the INCR command is used), and Redis is
|
||||
* configured to evict based on LFU/LRU, so we want LFU/LRU values
|
||||
* specific for each key. */
|
||||
robj *createStringObjectFromLongLongForValue(long long value) {
|
||||
return createStringObjectFromLongLongWithOptions(value,1);
|
||||
if (server.maxmemory == 0 || !(server.maxmemory_policy & MAXMEMORY_FLAG_NO_SHARED_INTEGERS)) {
|
||||
/* If the maxmemory policy permits, we can still return shared integers */
|
||||
return createStringObjectFromLongLongWithOptions(value, LL2STROBJ_AUTO);
|
||||
} else {
|
||||
return createStringObjectFromLongLongWithOptions(value, LL2STROBJ_NO_SHARED);
|
||||
}
|
||||
}
|
||||
|
||||
/* Create a string object that contains an sds inside it. That means it can't be
|
||||
* integer encoded (OBJ_ENCODING_INT), and it'll always be an EMBSTR type. */
|
||||
robj *createStringObjectFromLongLongWithSds(long long value) {
|
||||
return createStringObjectFromLongLongWithOptions(value, LL2STROBJ_NO_INT_ENC);
|
||||
}
|
||||
|
||||
/* Create a string object from a long double. If humanfriendly is non-zero
|
||||
|
@ -326,7 +326,7 @@ void *rdbLoadIntegerObject(rio *rdb, int enctype, int flags, size_t *lenptr) {
|
||||
} else if (encode) {
|
||||
return createStringObjectFromLongLongForValue(val);
|
||||
} else {
|
||||
return createObject(OBJ_STRING,sdsfromlonglong(val));
|
||||
return createStringObjectFromLongLongWithSds(val);
|
||||
}
|
||||
}
|
||||
|
||||
|
90
src/sds.c
90
src/sds.c
@ -38,6 +38,7 @@
|
||||
#include <limits.h>
|
||||
#include "sds.h"
|
||||
#include "sdsalloc.h"
|
||||
#include "util.h"
|
||||
|
||||
const char *SDS_NOINIT = "SDS_NOINIT";
|
||||
|
||||
@ -526,90 +527,13 @@ sds sdscpy(sds s, const char *t) {
|
||||
return sdscpylen(s, t, strlen(t));
|
||||
}
|
||||
|
||||
/* Helper for sdscatlonglong() doing the actual number -> string
|
||||
* conversion. 's' must point to a string with room for at least
|
||||
* SDS_LLSTR_SIZE bytes.
|
||||
*
|
||||
* The function returns the length of the null-terminated string
|
||||
* representation stored at 's'. */
|
||||
#define SDS_LLSTR_SIZE 21
|
||||
int sdsll2str(char *s, long long value) {
|
||||
char *p, aux;
|
||||
unsigned long long v;
|
||||
size_t l;
|
||||
|
||||
/* Generate the string representation, this method produces
|
||||
* a reversed string. */
|
||||
if (value < 0) {
|
||||
/* Since v is unsigned, if value==LLONG_MIN, -LLONG_MIN will overflow. */
|
||||
if (value != LLONG_MIN) {
|
||||
v = -value;
|
||||
} else {
|
||||
v = ((unsigned long long)LLONG_MAX) + 1;
|
||||
}
|
||||
} else {
|
||||
v = value;
|
||||
}
|
||||
|
||||
p = s;
|
||||
do {
|
||||
*p++ = '0'+(v%10);
|
||||
v /= 10;
|
||||
} while(v);
|
||||
if (value < 0) *p++ = '-';
|
||||
|
||||
/* Compute length and add null term. */
|
||||
l = p-s;
|
||||
*p = '\0';
|
||||
|
||||
/* Reverse the string. */
|
||||
p--;
|
||||
while(s < p) {
|
||||
aux = *s;
|
||||
*s = *p;
|
||||
*p = aux;
|
||||
s++;
|
||||
p--;
|
||||
}
|
||||
return l;
|
||||
}
|
||||
|
||||
/* Identical sdsll2str(), but for unsigned long long type. */
|
||||
int sdsull2str(char *s, unsigned long long v) {
|
||||
char *p, aux;
|
||||
size_t l;
|
||||
|
||||
/* Generate the string representation, this method produces
|
||||
* a reversed string. */
|
||||
p = s;
|
||||
do {
|
||||
*p++ = '0'+(v%10);
|
||||
v /= 10;
|
||||
} while(v);
|
||||
|
||||
/* Compute length and add null term. */
|
||||
l = p-s;
|
||||
*p = '\0';
|
||||
|
||||
/* Reverse the string. */
|
||||
p--;
|
||||
while(s < p) {
|
||||
aux = *s;
|
||||
*s = *p;
|
||||
*p = aux;
|
||||
s++;
|
||||
p--;
|
||||
}
|
||||
return l;
|
||||
}
|
||||
|
||||
/* Create an sds string from a long long value. It is much faster than:
|
||||
*
|
||||
* sdscatprintf(sdsempty(),"%lld\n", value);
|
||||
*/
|
||||
sds sdsfromlonglong(long long value) {
|
||||
char buf[SDS_LLSTR_SIZE];
|
||||
int len = sdsll2str(buf,value);
|
||||
char buf[LONG_STR_SIZE];
|
||||
int len = ll2string(buf,sizeof(buf),value);
|
||||
|
||||
return sdsnewlen(buf,len);
|
||||
}
|
||||
@ -745,8 +669,8 @@ sds sdscatfmt(sds s, char const *fmt, ...) {
|
||||
else
|
||||
num = va_arg(ap,long long);
|
||||
{
|
||||
char buf[SDS_LLSTR_SIZE];
|
||||
l = sdsll2str(buf,num);
|
||||
char buf[LONG_STR_SIZE];
|
||||
l = ll2string(buf,sizeof(buf),num);
|
||||
if (sdsavail(s) < l) {
|
||||
s = sdsMakeRoomFor(s,l);
|
||||
}
|
||||
@ -762,8 +686,8 @@ sds sdscatfmt(sds s, char const *fmt, ...) {
|
||||
else
|
||||
unum = va_arg(ap,unsigned long long);
|
||||
{
|
||||
char buf[SDS_LLSTR_SIZE];
|
||||
l = sdsull2str(buf,unum);
|
||||
char buf[LONG_STR_SIZE];
|
||||
l = ull2string(buf,sizeof(buf),unum);
|
||||
if (sdsavail(s) < l) {
|
||||
s = sdsMakeRoomFor(s,l);
|
||||
}
|
||||
|
@ -2744,6 +2744,7 @@ robj *getDecodedObject(robj *o);
|
||||
size_t stringObjectLen(robj *o);
|
||||
robj *createStringObjectFromLongLong(long long value);
|
||||
robj *createStringObjectFromLongLongForValue(long long value);
|
||||
robj *createStringObjectFromLongLongWithSds(long long value);
|
||||
robj *createStringObjectFromLongDouble(long double value, int humanfriendly);
|
||||
robj *createQuicklistObject(void);
|
||||
robj *createListListpackObject(void);
|
||||
|
Loading…
x
Reference in New Issue
Block a user