PHPのfor文の条件式にcount関数を使用すると・・・ その後・・・ 
初めまして、今月からお世話になりますマローです。
7月にうっち~さんがphpのfor()内でcount()を行うと・・・という話がありましたが、
実際に 速度を測定しました!
test code!!
<?php
define('COUNT_NUM' ,1000);
define('IN_COUNT' ,true);
$ary = array();
for ($i = 0, $j = 1; $i <= constant('COUNT_NUM'); $i++, $j++) $ary[$i] = $j;
if( constant( 'IN_COUNT')) {
$time1 = microtime();
for ($i = 0; $i < count($ary); $i++) {}
$time2 = microtime();
}else{
$time1 = microtime();
$cnt = count( $ary);
for ($i = 0; $i < $cnt; $i++) {}
$time2 = microtime();
}
echo $time2 - $time1."\n";
?>
10回ずつ回して、平均を算出しました。
for()内でcount()する 0.000343
for()内でcount()しない 0.000134
ヤッター!ヽ(`д´)ノ0.000209秒も早い!
しかし、こいつは何をやってるのだろう・・・と思い、さっそくstrace!
straceじゃ、システムコールしかとれないじゃん・・・orz
というわけで、みんなも持っているgdbとphp本体のソースコードです。
php-5.3.x/ext/standard/array.c
この子の中にPHP_FUNCTION(count)が居ます。
PHP_FUNCTION(count)
{
zval *array;
long mode = COUNT_NORMAL;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z|l", &array, &mode) == FAILURE) {
return;
}
switch (Z_TYPE_P(array)) {
case IS_NULL:
RETURN_LONG(0);
break;
case IS_ARRAY:
RETURN_LONG (php_count_recursive (array, mode TSRMLS_CC));
break;
case IS_ARRAY:でRETURN_LONGを返して終了です。
php_count_recursive は同じソース内のすぐ上にありました。
static int php_count_recursive(zval *array, long mode TSRMLS_DC) /* {{{ */
{
long cnt = 0;
zval **element;
if (Z_TYPE_P(array) == IS_ARRAY) {
/*~ 一部省略 ~*/
cnt = zend_hash_num_elements(Z_ARRVAL_P(array));
/*~ 一部省略 ~*/
}
return cnt;
}
php_count_recursive で「cnt = zend_hash_num_elements(Z_ARRVAL_P(array));」
cntにzend_hash_num_elementsの値が入ってます。
zend_hash_num_elementsは別のフォルダの「Zend/zend_hash.c」にありました。
ZEND_API int zend_hash_num_elements(const HashTable *ht)
{
IS_CONSISTENT(ht);
return ht->nNumOfElements;
}
値を返してるだけじゃねーか!!Σ(゜д゜lll)
実はHashTableの構造体を作成するときに、すでに要素数が入ってたのです・・・orz
typedef struct _hashtable {
uint nTableSize;
uint nTableMask;
uint nNumOfElements; /* 要素数 */
ulong nNextFreeElement;
Bucket *pInternalPointer; /* Used for element traversal */
Bucket *pListHead;
Bucket *pListTail;
Bucket **arBuckets;
dtor_func_t pDestructor;
zend_bool persistent;
unsigned char nApplyCount;
zend_bool bApplyProtection;
#if ZEND_DEBUG
int inconsistent;
#endif
} HashTable;
というわけで、count()はこれといって作業をしているわけでは無く、
単純にfunction call の呼び出しコストといわけで・・・。
要は無駄な関数は呼ぶなという事ですね!
あと、二次元配列の場合は再帰的に「php_count_recursive」が呼ばれるよ!
