Skip to content

Commit 8e81c49

Browse files
Performance increase for MGET
This commit changes the way we construct the MGET command such that we avoid allocating and reallocating the command string pointer for every new key that is added. This means that MGET performance scales in a linear way rather than exponentially.
1 parent b3a6414 commit 8e81c49

File tree

3 files changed

+60
-47
lines changed

3 files changed

+60
-47
lines changed

library.c

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -454,6 +454,21 @@ int redis_cmd_append_str(char **cmd, int cmd_len, char *append, int append_len)
454454
return buf.len;
455455
}
456456

457+
/*
458+
* Given a smart string, number of arguments, a keyword, and the length of the keyword
459+
* initialize our smart string with the proper Redis header for the command to follow
460+
*/
461+
int redis_cmd_init_sstr(smart_str *str, int num_args, char *keyword, int keyword_len) {
462+
smart_str_appendc(str, '*');
463+
smart_str_append_long(str, num_args + 1);
464+
smart_str_appendl(str, _NL, sizeof(_NL) -1);
465+
smart_str_appendc(str, '$');
466+
smart_str_append_long(str, keyword_len);
467+
smart_str_appendl(str, _NL, sizeof(_NL) - 1);
468+
smart_str_appendl(str, keyword, keyword_len);
469+
smart_str_appendl(str, _NL, sizeof(_NL) - 1);
470+
}
471+
457472
/*
458473
* Append a command sequence to a smart_str
459474
*/

library.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ int redis_cmd_format(char **ret, char *format, ...);
44
int redis_cmd_format_static(char **ret, char *keyword, char *format, ...);
55
int redis_cmd_format_header(char **ret, char *keyword, int arg_count);
66
int redis_cmd_append_str(char **cmd, int cmd_len, char *append, int append_len);
7+
int redis_cmd_init_sstr(smart_str *str, int num_args, char *keyword, int keyword_len);
78
int redis_cmd_append_sstr(smart_str *str, char *append, int append_len);
89
int redis_cmd_append_int(char **cmd, int cmd_len, int append);
910

redis.c

Lines changed: 44 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -1325,88 +1325,85 @@ PHP_METHOD(Redis, decrBy){
13251325
*/
13261326
PHP_METHOD(Redis, getMultiple)
13271327
{
1328-
zval *object, *array, **data;
1329-
HashTable *arr_hash;
1330-
HashPosition pointer;
1328+
zval *object, *z_args, **z_ele;
1329+
HashTable *hash;
1330+
HashPosition ptr;
13311331
RedisSock *redis_sock;
1332-
char *cmd = "", *old_cmd = NULL;
1333-
int cmd_len = 0, array_count, elements = 1;
1332+
smart_str cmd = {0};
1333+
int arg_count;
13341334

1335-
if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Oa",
1336-
&object, redis_ce, &array) == FAILURE) {
1335+
// Make sure we have proper arguments
1336+
if(zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Oa",
1337+
&object, redis_ce, &z_args) == FAILURE) {
13371338
RETURN_FALSE;
13381339
}
13391340

1340-
if (redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) {
1341+
// We'll need the socket
1342+
if(redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) {
13411343
RETURN_FALSE;
13421344
}
13431345

1344-
arr_hash = Z_ARRVAL_P(array);
1345-
array_count = zend_hash_num_elements(arr_hash);
1346+
// Grab our array
1347+
hash = Z_ARRVAL_P(z_args);
13461348

1347-
if (array_count == 0) {
1349+
// We don't need to do anything if there aren't any keys
1350+
if((arg_count = zend_hash_num_elements(hash)) == 0) {
13481351
RETURN_FALSE;
13491352
}
13501353

1351-
for (zend_hash_internal_pointer_reset_ex(arr_hash, &pointer);
1352-
zend_hash_get_current_data_ex(arr_hash, (void**) &data,
1353-
&pointer) == SUCCESS;
1354-
zend_hash_move_forward_ex(arr_hash, &pointer)) {
1354+
// Build our command header
1355+
redis_cmd_init_sstr(&cmd, arg_count, "MGET", 4);
13551356

1357+
// Iterate through and grab our keys
1358+
for(zend_hash_internal_pointer_reset_ex(hash, &ptr);
1359+
zend_hash_get_current_data_ex(hash, (void**)&z_ele, &ptr) == SUCCESS;
1360+
zend_hash_move_forward_ex(hash, &ptr))
1361+
{
13561362
char *key;
1357-
int key_len;
1363+
int key_len, key_free;
13581364
zval *z_tmp = NULL;
1359-
char *old_cmd;
1360-
int key_free;
13611365

1362-
if (Z_TYPE_PP(data) == IS_STRING) {
1363-
key = Z_STRVAL_PP(data);
1364-
key_len = Z_STRLEN_PP(data);
1365-
} else { /* not a string, copy and convert. */
1366+
// If the key isn't a string, turn it into one
1367+
if(Z_TYPE_PP(z_ele) == IS_STRING) {
1368+
key = Z_STRVAL_PP(z_ele);
1369+
key_len = Z_STRLEN_PP(z_ele);
1370+
} else {
13661371
MAKE_STD_ZVAL(z_tmp);
1367-
*z_tmp = **data;
1372+
*z_tmp = **z_ele;
13681373
zval_copy_ctor(z_tmp);
13691374
convert_to_string(z_tmp);
13701375

13711376
key = Z_STRVAL_P(z_tmp);
13721377
key_len = Z_STRLEN_P(z_tmp);
13731378
}
1374-
old_cmd = NULL;
1375-
if(*cmd) {
1376-
old_cmd = cmd;
1377-
}
1378-
key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC);
1379-
cmd_len = redis_cmd_format(&cmd, "%s$%d" _NL "%s" _NL
1380-
, cmd, cmd_len
1381-
, key_len, key, key_len);
13821379

1383-
if(key_free) efree(key);
1380+
// Apply key prefix if necissary
1381+
key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC);
13841382

1385-
if(old_cmd) {
1386-
efree(old_cmd);
1387-
}
1388-
elements++;
1383+
// Append this key to our command
1384+
redis_cmd_append_sstr(&cmd, key, key_len);
1385+
1386+
// Free our key if it was prefixed
1387+
if(key_free) efree(key);
1388+
1389+
// Free oour temporary ZVAL if we converted from a non-string
13891390
if(z_tmp) {
13901391
zval_dtor(z_tmp);
13911392
efree(z_tmp);
1393+
z_tmp = NULL;
13921394
}
13931395
}
13941396

1395-
old_cmd = cmd;
1396-
cmd_len = redis_cmd_format(&cmd, "*%d" _NL "$4" _NL "MGET" _NL "%s", elements, cmd, cmd_len);
1397-
efree(old_cmd);
1398-
1399-
REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len);
1397+
// Kick off our command
1398+
REDIS_PROCESS_REQUEST(redis_sock, cmd.c, cmd.len);
14001399
IF_ATOMIC() {
1401-
if (redis_sock_read_multibulk_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU,
1402-
redis_sock, NULL, NULL) < 0) {
1403-
RETURN_FALSE;
1404-
}
1400+
if(redis_sock_read_multibulk_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU,
1401+
redis_sock, NULL, NULL) < 0) {
1402+
RETURN_FALSE;
1403+
}
14051404
}
14061405
REDIS_PROCESS_RESPONSE(redis_sock_read_multibulk_reply);
1407-
14081406
}
1409-
/* }}} */
14101407

14111408
/* {{{ proto boolean Redis::exists(string key)
14121409
*/

0 commit comments

Comments
 (0)