Patch: Foreach Performance for 4.3
| From: | Derick Rethans | Date: | Tue, 14 Sep 2004 11:14:28 +0000 |
| Subject: | Patch: Foreach Performance for 4.3 | ||
| Groups: | php.internals | ||
| Request: | Send a blank email to internals+get-12792@lists.php.net to get a copy of this message | ||
Hei,
I backported Marcus' performance-improvement-when-not-using-a-key-in-foreach
patch to 4.3 and attached it. I am planning to commit this after 4.3.9
is released. If there are any comments, please let me know.
Derick
--
Derick Rethans
http://derickrethans.nl | http://ez.no | http://xdebug.org
? php_test_results_20040914.txt ? ext/gd/testsbug27582.png ? ext/tokenizer/tests/bug26463.diff ? ext/tokenizer/tests/bug26463.exp ? ext/tokenizer/tests/bug26463.log ? ext/tokenizer/tests/bug26463.out ? ext/tokenizer/tests/bug26463.php Index: Zend/zend_compile.c =================================================================== RCS file: /repository/Zend/Attic/zend_compile.c,v retrieving revision 1.240.2.7 diff -u -p -r1.240.2.7 zend_compile.c --- Zend/zend_compile.c 29 Aug 2004 16:51:09 -0000 1.240.2.7 +++ Zend/zend_compile.c 14 Sep 2004 11:10:58 -0000 @@ -2147,9 +2147,9 @@ void zend_do_foreach_begin(znode *foreac } -void zend_do_foreach_cont(znode *value, znode *key, znode *as_token TSRMLS_DC) +void zend_do_foreach_cont(znode *value, znode *key, znode *as_token, znode *foreach_token TSRMLS_DC) { - zend_op *opline = get_next_op(CG(active_op_array) TSRMLS_CC); + zend_op *opline; znode result_value, result_key, dummy; if (key->op_type != IS_UNUSED) { @@ -2159,18 +2159,10 @@ void zend_do_foreach_cont(znode *value, tmp = key; key = value; value = tmp; - } - opline->opcode = ZEND_FETCH_DIM_TMP_VAR; - opline->result.op_type = IS_VAR; - opline->result.u.EA.type = 0; - opline->result.u.opline_num = get_temporary_variable(CG(active_op_array)); - opline->op1 = *as_token; - opline->op2.op_type = IS_CONST; - opline->op2.u.constant.type = IS_LONG; - opline->op2.u.constant.value.lval = 0; - opline->extended_value = ZEND_FETCH_STANDARD; /* ignored in fetch_dim_tmp_var, but what the hell. */ - result_value = opline->result; + /* Mark extended_value in case both key and value are being used */ + CG(active_op_array)->opcodes[foreach_token->u.opline_num].extended_value |= ZEND_FE_FETCH_WITH_KEY; + } if (key->op_type != IS_UNUSED) { opline = get_next_op(CG(active_op_array) TSRMLS_CC); @@ -2181,9 +2173,23 @@ void zend_do_foreach_cont(znode *value, opline->op1 = *as_token; opline->op2.op_type = IS_CONST; opline->op2.u.constant.type = IS_LONG; + opline->op2.u.constant.value.lval = 0; + opline->extended_value = ZEND_FETCH_STANDARD; /* ignored in fetch_dim_tmp_var, but what the hell. */ + result_value = opline->result; + + opline = get_next_op(CG(active_op_array) TSRMLS_CC); + opline->opcode = ZEND_FETCH_DIM_TMP_VAR; + opline->result.op_type = IS_VAR; + opline->result.u.EA.type = 0; + opline->result.u.opline_num = get_temporary_variable(CG(active_op_array)); + opline->op1 = *as_token; + opline->op2.op_type = IS_CONST; + opline->op2.u.constant.type = IS_LONG; opline->op2.u.constant.value.lval = 1; opline->extended_value = ZEND_FETCH_STANDARD; /* ignored in fetch_dim_tmp_var, but what the hell. */ result_key = opline->result; + } else { + result_value = CG(active_op_array)->opcodes[foreach_token->u.opline_num].result; } zend_do_assign(&dummy, value, &result_value TSRMLS_CC); @@ -2191,8 +2197,8 @@ void zend_do_foreach_cont(znode *value, if (key->op_type != IS_UNUSED) { zend_do_assign(&dummy, key, &result_key TSRMLS_CC); CG(active_op_array)->opcodes[CG(active_op_array)->last-1].result.u.EA.type |= EXT_TYPE_UNUSED; + zend_do_free(as_token TSRMLS_CC); } - zend_do_free(as_token TSRMLS_CC); do_begin_loop(TSRMLS_C); INC_BPC(CG(active_op_array)); Index: Zend/zend_compile.h =================================================================== RCS file: /repository/Zend/Attic/zend_compile.h,v retrieving revision 1.144.4.5 diff -u -p -r1.144.4.5 zend_compile.h --- Zend/zend_compile.h 31 May 2003 01:37:43 -0000 1.144.4.5 +++ Zend/zend_compile.h 14 Sep 2004 11:10:58 -0000 @@ -326,7 +326,7 @@ void zend_do_unset(znode *variable TSRML void zend_do_isset_or_isempty(int type, znode *result, znode *variable TSRMLS_DC); void zend_do_foreach_begin(znode *foreach_token, znode *array, znode *open_brackets_token, znode *as_token, int variable TSRMLS_DC); -void zend_do_foreach_cont(znode *value, znode *key, znode *as_token TSRMLS_DC); +void zend_do_foreach_cont(znode *value, znode *key, znode *as_token, znode *foreach_token TSRMLS_DC); void zend_do_foreach_end(znode *foreach_token, znode *open_brackets_token TSRMLS_DC); void zend_do_declare_begin(TSRMLS_D); @@ -577,6 +577,9 @@ int zendlex(znode *zendlval TSRMLS_DC); #define ZEND_FETCH_STANDARD 0 #define ZEND_FETCH_ADD_LOCK 1 +#define ZEND_FE_FETCH_BYREF 1 +#define ZEND_FE_FETCH_WITH_KEY 2 + #define ZEND_MEMBER_FUNC_CALL 1<<0 #define ZEND_CTOR_CALL 1<<1 Index: Zend/zend_execute.c =================================================================== RCS file: /repository/Zend/Attic/zend_execute.c,v retrieving revision 1.316.2.39 diff -u -p -r1.316.2.39 zend_execute.c --- Zend/zend_execute.c 2 Aug 2004 02:35:05 -0000 1.316.2.39 +++ Zend/zend_execute.c 14 Sep 2004 11:10:58 -0000 @@ -2350,13 +2350,14 @@ send_by_ref: } NEXT_OPCODE(); case ZEND_FE_FETCH: { - zval *array = get_zval_ptr(&EX(opline)->op1, EX(Ts), &EG(free_op1), BP_VAR_R); - zval *result = &EX(Ts)[EX(opline)->result.u.var].tmp_var; + zend_op *opline = EX(opline); + zval *array = get_zval_ptr(&opline->op1, EX(Ts), &EG(free_op1), BP_VAR_R); zval **value, *key; char *str_key; uint str_key_len; ulong int_key; HashTable *fe_ht; + zend_bool use_key = opline->extended_value & ZEND_FE_FETCH_WITH_KEY; PZVAL_LOCK(array); @@ -2370,27 +2371,33 @@ send_by_ref: EX(opline) = op_array->opcodes+EX(opline)->op2.u.opline_num; continue; } - array_init(result); + zval *result = &EX(Ts)[opline->result.u.var].tmp_var; + if (!use_key) { + *result = **value; + zval_copy_ctor(result); + } else { + array_init(result); - (*value)->refcount++; - zend_hash_index_update(result->value.ht, 0, value, sizeof(zval *), NULL); + (*value)->refcount++; + zend_hash_index_update(result->value.ht, 0, value, sizeof(zval *), NULL); - ALLOC_ZVAL(key); - INIT_PZVAL(key); - switch (zend_hash_get_current_key_ex(fe_ht, &str_key, &str_key_len, &int_key, 1, NULL)) { - case HASH_KEY_IS_STRING: - key->value.str.val = str_key; - key->value.str.len = str_key_len-1; - key->type = IS_STRING; - break; - case HASH_KEY_IS_LONG: - key->value.lval = int_key; - key->type = IS_LONG; - break; - EMPTY_SWITCH_DEFAULT_CASE() + ALLOC_ZVAL(key); + INIT_PZVAL(key); + switch (zend_hash_get_current_key_ex(fe_ht, &str_key, &str_key_len, &int_key, 1, NULL)) { + case HASH_KEY_IS_STRING: + key->value.str.val = str_key; + key->value.str.len = str_key_len-1; + key->type = IS_STRING; + break; + case HASH_KEY_IS_LONG: + key->value.lval = int_key; + key->type = IS_LONG; + break; + EMPTY_SWITCH_DEFAULT_CASE() + } + zend_hash_index_update(result->value.ht, 1, &key, sizeof(zval *), NULL); } - zend_hash_index_update(result->value.ht, 1, &key, sizeof(zval *), NULL); zend_hash_move_forward(fe_ht); } NEXT_OPCODE(); Index: Zend/zend_language_parser.y =================================================================== RCS file: /repository/Zend/Attic/zend_language_parser.y,v retrieving revision 1.23 diff -u -p -r1.23 zend_language_parser.y --- Zend/zend_language_parser.y 4 Aug 2002 06:39:44 -0000 1.23 +++ Zend/zend_language_parser.y 14 Sep 2004 11:10:58 -0000 @@ -199,8 +199,8 @@ unticked_statement: | expr ';' { zend_do_free(&$1 TSRMLS_CC); } | T_USE use_filename ';' { zend_error(E_COMPILE_ERROR,"use: Not yet supported. Please use include_once() or require_once()"); zval_dtor(&$2.u.constant); } | T_UNSET '(' unset_variables ')' ';' - | T_FOREACH '(' w_cvar T_AS { zend_do_foreach_begin(&$1, &$3, &$2, &$4, 1 TSRMLS_CC); } w_cvar foreach_optional_arg ')' { zend_do_foreach_cont(&$6, &$7, &$4 TSRMLS_CC); } foreach_statement { zend_do_foreach_end(&$1, &$2 TSRMLS_CC); } - | T_FOREACH '(' expr_without_variable T_AS { zend_do_foreach_begin(&$1, &$3, &$2, &$4, 0 TSRMLS_CC); } w_cvar foreach_optional_arg ')' { zend_do_foreach_cont(&$6, &$7, &$4 TSRMLS_CC); } foreach_statement { zend_do_foreach_end(&$1, &$2 TSRMLS_CC); } + | T_FOREACH '(' w_cvar T_AS { zend_do_foreach_begin(&$1, &$3, &$2, &$4, 1 TSRMLS_CC); } w_cvar foreach_optional_arg ')' { zend_do_foreach_cont(&$6, &$7, &$4, &$1 TSRMLS_CC); } foreach_statement { zend_do_foreach_end(&$1, &$2 TSRMLS_CC); } + | T_FOREACH '(' expr_without_variable T_AS { zend_do_foreach_begin(&$1, &$3, &$2, &$4, 0 TSRMLS_CC); } w_cvar foreach_optional_arg ')' { zend_do_foreach_cont(&$6, &$7, &$4, &$1 TSRMLS_CC); } foreach_statement { zend_do_foreach_end(&$1, &$2 TSRMLS_CC); } | T_DECLARE { $1.u.opline_num = get_next_op_number(CG(active_op_array)); zend_do_declare_begin(TSRMLS_C); } '(' declare_list ')' declare_statement {zend_do_declare_end(&$1 TSRMLS_CC); } | ';' /* empty statement */ ; Index: ext/standard/array.c =================================================================== RCS file: /repository/php-src/ext/standard/array.c,v retrieving revision 1.199.2.36 diff -u -p -r1.199.2.36 array.c --- ext/standard/array.c 10 Aug 2004 06:04:12 -0000 1.199.2.36 +++ ext/standard/array.c 14 Sep 2004 11:10:59 -0000 @@ -1712,7 +1712,7 @@ static void _phpi_pop(INTERNAL_FUNCTION_ zval **stack, /* Input stack */ **val; /* Value to be popped */ char *key = NULL; - int key_len = 0; + uint key_len = 0; ulong index; /* Get the arguments and do error-checking */ Index: ext/standard/file.c =================================================================== RCS file: /repository/php-src/ext/standard/file.c,v retrieving revision 1.279.2.61 diff -u -p -r1.279.2.61 file.c --- ext/standard/file.c 17 Aug 2004 14:10:03 -0000 1.279.2.61 +++ ext/standard/file.c 14 Sep 2004 11:11:00 -0000 @@ -909,7 +909,7 @@ static int parse_context_options(php_str HashPosition pos, opos; zval **wval, **oval; char *wkey, *okey; - int wkey_len, okey_len; + uint wkey_len, okey_len; int ret = SUCCESS; ulong num_key;
? php_test_results_20040914.txt ? ext/gd/testsbug27582.png ? ext/tokenizer/tests/bug26463.diff ? ext/tokenizer/tests/bug26463.exp ? ext/tokenizer/tests/bug26463.log ? ext/tokenizer/tests/bug26463.out ? ext/tokenizer/tests/bug26463.php Index: Zend/zend_compile.c =================================================================== RCS file: /repository/Zend/Attic/zend_compile.c,v retrieving revision 1.240.2.7 diff -u -p -r1.240.2.7 zend_compile.c --- Zend/zend_compile.c 29 Aug 2004 16:51:09 -0000 1.240.2.7 +++ Zend/zend_compile.c 14 Sep 2004 11:10:58 -0000 @@ -2147,9 +2147,9 @@ void zend_do_foreach_begin(znode *foreac } -void zend_do_foreach_cont(znode *value, znode *key, znode *as_token TSRMLS_DC) +void zend_do_foreach_cont(znode *value, znode *key, znode *as_token, znode *foreach_token TSRMLS_DC) { - zend_op *opline = get_next_op(CG(active_op_array) TSRMLS_CC); + zend_op *opline; znode result_value, result_key, dummy; if (key->op_type != IS_UNUSED) { @@ -2159,18 +2159,10 @@ void zend_do_foreach_cont(znode *value, tmp = key; key = value; value = tmp; - } - opline->opcode = ZEND_FETCH_DIM_TMP_VAR; - opline->result.op_type = IS_VAR; - opline->result.u.EA.type = 0; - opline->result.u.opline_num = get_temporary_variable(CG(active_op_array)); - opline->op1 = *as_token; - opline->op2.op_type = IS_CONST; - opline->op2.u.constant.type = IS_LONG; - opline->op2.u.constant.value.lval = 0; - opline->extended_value = ZEND_FETCH_STANDARD; /* ignored in fetch_dim_tmp_var, but what the hell. */ - result_value = opline->result; + /* Mark extended_value in case both key and value are being used */ + CG(active_op_array)->opcodes[foreach_token->u.opline_num].extended_value |= ZEND_FE_FETCH_WITH_KEY; + } if (key->op_type != IS_UNUSED) { opline = get_next_op(CG(active_op_array) TSRMLS_CC); @@ -2181,9 +2173,23 @@ void zend_do_foreach_cont(znode *value, opline->op1 = *as_token; opline->op2.op_type = IS_CONST; opline->op2.u.constant.type = IS_LONG; + opline->op2.u.constant.value.lval = 0; + opline->extended_value = ZEND_FETCH_STANDARD; /* ignored in fetch_dim_tmp_var, but what the hell. */ + result_value = opline->result; + + opline = get_next_op(CG(active_op_array) TSRMLS_CC); + opline->opcode = ZEND_FETCH_DIM_TMP_VAR; + opline->result.op_type = IS_VAR; + opline->result.u.EA.type = 0; + opline->result.u.opline_num = get_temporary_variable(CG(active_op_array)); + opline->op1 = *as_token; + opline->op2.op_type = IS_CONST; + opline->op2.u.constant.type = IS_LONG; opline->op2.u.constant.value.lval = 1; opline->extended_value = ZEND_FETCH_STANDARD; /* ignored in fetch_dim_tmp_var, but what the hell. */ result_key = opline->result; + } else { + result_value = CG(active_op_array)->opcodes[foreach_token->u.opline_num].result; } zend_do_assign(&dummy, value, &result_value TSRMLS_CC); @@ -2191,8 +2197,8 @@ void zend_do_foreach_cont(znode *value, if (key->op_type != IS_UNUSED) { zend_do_assign(&dummy, key, &result_key TSRMLS_CC); CG(active_op_array)->opcodes[CG(active_op_array)->last-1].result.u.EA.type |= EXT_TYPE_UNUSED; + zend_do_free(as_token TSRMLS_CC); } - zend_do_free(as_token TSRMLS_CC); do_begin_loop(TSRMLS_C); INC_BPC(CG(active_op_array)); Index: Zend/zend_compile.h =================================================================== RCS file: /repository/Zend/Attic/zend_compile.h,v retrieving revision 1.144.4.5 diff -u -p -r1.144.4.5 zend_compile.h --- Zend/zend_compile.h 31 May 2003 01:37:43 -0000 1.144.4.5 +++ Zend/zend_compile.h 14 Sep 2004 11:10:58 -0000 @@ -326,7 +326,7 @@ void zend_do_unset(znode *variable TSRML void zend_do_isset_or_isempty(int type, znode *result, znode *variable TSRMLS_DC); void zend_do_foreach_begin(znode *foreach_token, znode *array, znode *open_brackets_token, znode *as_token, int variable TSRMLS_DC); -void zend_do_foreach_cont(znode *value, znode *key, znode *as_token TSRMLS_DC); +void zend_do_foreach_cont(znode *value, znode *key, znode *as_token, znode *foreach_token TSRMLS_DC); void zend_do_foreach_end(znode *foreach_token, znode *open_brackets_token TSRMLS_DC); void zend_do_declare_begin(TSRMLS_D); @@ -577,6 +577,9 @@ int zendlex(znode *zendlval TSRMLS_DC); #define ZEND_FETCH_STANDARD 0 #define ZEND_FETCH_ADD_LOCK 1 +#define ZEND_FE_FETCH_BYREF 1 +#define ZEND_FE_FETCH_WITH_KEY 2 + #define ZEND_MEMBER_FUNC_CALL 1<<0 #define ZEND_CTOR_CALL 1<<1 Index: Zend/zend_execute.c =================================================================== RCS file: /repository/Zend/Attic/zend_execute.c,v retrieving revision 1.316.2.39 diff -u -p -r1.316.2.39 zend_execute.c --- Zend/zend_execute.c 2 Aug 2004 02:35:05 -0000 1.316.2.39 +++ Zend/zend_execute.c 14 Sep 2004 11:10:58 -0000 @@ -2350,13 +2350,14 @@ send_by_ref: } NEXT_OPCODE(); case ZEND_FE_FETCH: { - zval *array = get_zval_ptr(&EX(opline)->op1, EX(Ts), &EG(free_op1), BP_VAR_R); - zval *result = &EX(Ts)[EX(opline)->result.u.var].tmp_var; + zend_op *opline = EX(opline); + zval *array = get_zval_ptr(&opline->op1, EX(Ts), &EG(free_op1), BP_VAR_R); zval **value, *key; char *str_key; uint str_key_len; ulong int_key; HashTable *fe_ht; + zend_bool use_key = opline->extended_value & ZEND_FE_FETCH_WITH_KEY; PZVAL_LOCK(array); @@ -2370,27 +2371,33 @@ send_by_ref: EX(opline) = op_array->opcodes+EX(opline)->op2.u.opline_num; continue; } - array_init(result); + zval *result = &EX(Ts)[opline->result.u.var].tmp_var; + if (!use_key) { + *result = **value; + zval_copy_ctor(result); + } else { + array_init(result); - (*value)->refcount++; - zend_hash_index_update(result->value.ht, 0, value, sizeof(zval *), NULL); + (*value)->refcount++; + zend_hash_index_update(result->value.ht, 0, value, sizeof(zval *), NULL); - ALLOC_ZVAL(key); - INIT_PZVAL(key); - switch (zend_hash_get_current_key_ex(fe_ht, &str_key, &str_key_len, &int_key, 1, NULL)) { - case HASH_KEY_IS_STRING: - key->value.str.val = str_key; - key->value.str.len = str_key_len-1; - key->type = IS_STRING; - break; - case HASH_KEY_IS_LONG: - key->value.lval = int_key; - key->type = IS_LONG; - break; - EMPTY_SWITCH_DEFAULT_CASE() + ALLOC_ZVAL(key); + INIT_PZVAL(key); + switch (zend_hash_get_current_key_ex(fe_ht, &str_key, &str_key_len, &int_key, 1, NULL)) { + case HASH_KEY_IS_STRING: + key->value.str.val = str_key; + key->value.str.len = str_key_len-1; + key->type = IS_STRING; + break; + case HASH_KEY_IS_LONG: + key->value.lval = int_key; + key->type = IS_LONG; + break; + EMPTY_SWITCH_DEFAULT_CASE() + } + zend_hash_index_update(result->value.ht, 1, &key, sizeof(zval *), NULL); } - zend_hash_index_update(result->value.ht, 1, &key, sizeof(zval *), NULL); zend_hash_move_forward(fe_ht); } NEXT_OPCODE(); Index: Zend/zend_language_parser.y =================================================================== RCS file: /repository/Zend/Attic/zend_language_parser.y,v retrieving revision 1.23 diff -u -p -r1.23 zend_language_parser.y --- Zend/zend_language_parser.y 4 Aug 2002 06:39:44 -0000 1.23 +++ Zend/zend_language_parser.y 14 Sep 2004 11:10:58 -0000 @@ -199,8 +199,8 @@ unticked_statement: | expr ';' { zend_do_free(&$1 TSRMLS_CC); } | T_USE use_filename ';' { zend_error(E_COMPILE_ERROR,"use: Not yet supported. Please use include_once() or require_once()"); zval_dtor(&$2.u.constant); } | T_UNSET '(' unset_variables ')' ';' - | T_FOREACH '(' w_cvar T_AS { zend_do_foreach_begin(&$1, &$3, &$2, &$4, 1 TSRMLS_CC); } w_cvar foreach_optional_arg ')' { zend_do_foreach_cont(&$6, &$7, &$4 TSRMLS_CC); } foreach_statement { zend_do_foreach_end(&$1, &$2 TSRMLS_CC); } - | T_FOREACH '(' expr_without_variable T_AS { zend_do_foreach_begin(&$1, &$3, &$2, &$4, 0 TSRMLS_CC); } w_cvar foreach_optional_arg ')' { zend_do_foreach_cont(&$6, &$7, &$4 TSRMLS_CC); } foreach_statement { zend_do_foreach_end(&$1, &$2 TSRMLS_CC); } + | T_FOREACH '(' w_cvar T_AS { zend_do_foreach_begin(&$1, &$3, &$2, &$4, 1 TSRMLS_CC); } w_cvar foreach_optional_arg ')' { zend_do_foreach_cont(&$6, &$7, &$4, &$1 TSRMLS_CC); } foreach_statement { zend_do_foreach_end(&$1, &$2 TSRMLS_CC); } + | T_FOREACH '(' expr_without_variable T_AS { zend_do_foreach_begin(&$1, &$3, &$2, &$4, 0 TSRMLS_CC); } w_cvar foreach_optional_arg ')' { zend_do_foreach_cont(&$6, &$7, &$4, &$1 TSRMLS_CC); } foreach_statement { zend_do_foreach_end(&$1, &$2 TSRMLS_CC); } | T_DECLARE { $1.u.opline_num = get_next_op_number(CG(active_op_array)); zend_do_declare_begin(TSRMLS_C); } '(' declare_list ')' declare_statement {zend_do_declare_end(&$1 TSRMLS_CC); } | ';' /* empty statement */ ; Index: ext/standard/array.c =================================================================== RCS file: /repository/php-src/ext/standard/array.c,v retrieving revision 1.199.2.36 diff -u -p -r1.199.2.36 array.c --- ext/standard/array.c 10 Aug 2004 06:04:12 -0000 1.199.2.36 +++ ext/standard/array.c 14 Sep 2004 11:10:59 -0000 @@ -1712,7 +1712,7 @@ static void _phpi_pop(INTERNAL_FUNCTION_ zval **stack, /* Input stack */ **val; /* Value to be popped */ char *key = NULL; - int key_len = 0; + uint key_len = 0; ulong index; /* Get the arguments and do error-checking */ Index: ext/standard/file.c =================================================================== RCS file: /repository/php-src/ext/standard/file.c,v retrieving revision 1.279.2.61 diff -u -p -r1.279.2.61 file.c --- ext/standard/file.c 17 Aug 2004 14:10:03 -0000 1.279.2.61 +++ ext/standard/file.c 14 Sep 2004 11:11:00 -0000 @@ -909,7 +909,7 @@ static int parse_context_options(php_str HashPosition pos, opos; zval **wval, **oval; char *wkey, *okey; - int wkey_len, okey_len; + uint wkey_len, okey_len; int ret = SUCCESS; ulong num_key;