Php 阵列键的微优化

Php 阵列键的微优化,php,arrays,php-internals,Php,Arrays,Php Internals,我有一个数组,我正在使用一些项来构造更多的数组,下面是一个粗略的例子 $rows = [ [1, 2, 3, 'a', 'b', 'c'], [4, 5, 6, 'd', 'e', 'f'], [4, 5, 6, 'g', 'h', 'i'], ]; $derivedData = []; foreach ($rows as $data) { $key = $data[0] . '-' . $data[1] . '-' . $data[2]; $de

我有一个数组,我正在使用一些项来构造更多的数组,下面是一个粗略的例子

$rows = [
    [1, 2, 3, 'a', 'b', 'c'],
    [4, 5, 6, 'd', 'e', 'f'],
    [4, 5, 6, 'g', 'h', 'i'],
];

$derivedData = [];

foreach ($rows as $data) {

    $key = $data[0] . '-' . $data[1] . '-' . $data[2];

    $derivedData['itemName']['count'] ++;
    $derivedData['itemName']['items'][$key]['a'] = $data[3];
    $derivedData['itemName']['items'][$key]['count'] ++;
}
现在,如果我转储数组,它将看起来像

derivedData: [
    itemName: [
        count: 3
        items: [
            1-2-3: [
                a: a,
                count: 1
            ],
            4-5-6: [
                a: g,
                count: 2
            ],
        ]
    ]
]
如您所见,
derivedData.itemName.count.items
中的键是字符串。如果我做这样的事情,我会得到任何好处吗

$uniqueId = 0;
$uniqueArray = [];

$rows = [
    [1, 2, 3, 'a', 'b', 'c'],
    [4, 5, 6, 'd', 'e', 'f'],
    [4, 5, 6, 'g', 'h', 'i'],
];

$derivedData = [];

foreach ($rows as $data) {

    $uniqueArrayKey = $data[0] . '-' . $data[1] . '-' . $data[2];

    if (!isset($uniqueArray[$uniqueArrayKey])) {
        $uniqueArray[$uniqueArrayKey] = $uniqueId++;
    }

    $uniqueKey = $uniqueArray[$uniqueArrayKey];

    $derivedData['itemName']['count'] ++;
    $derivedData['itemName']['items'][$uniqueKey ]['a'] = $data[3];
    $derivedData['itemName']['items'][$uniqueKey ]['count'] ++;
}
现在我将有一个索引数组和实际的数据数组

uniqueArray: [
    1-2-3: 0,
    4-5-6: 1
]

derivedData: [
    itemName: [
        count: 3
        items: [
            0: [
                a: a,
                count: 1
            ],
            1: [
                a: g,
                count: 2
            ],
        ]
    ]
]
我问自己的问题是,当使用字符串键时,PHP是否在内部为我这样做,即将它们保存在某个地方,并将它们作为键的指针引用,而不是每次都复制它们


换句话说,假设我有一个变量
$a
,如果我在不同的数组中使用它作为键,每个数组的
$a
的值会被用作键(并被复制),还是会使用内存中的指针,这基本上是我的问题?

当我假设内部有一段时间没有改变时,声明它们基本上是哈希表,有一些细微差别以避免键冲突。因此,在某种程度上,它确实做到了你在引擎盖下所说的

换句话说,假设我有一个变量$a,如果我在不同的数组中使用它作为键,$a的值会被每个数组作为键使用(并复制),还是会使用内存中的指针,这基本上是我的问题

下面是PHP>=5.4和PHP 7之间的区别,这取决于您的环境。我不是PHP专家,我的回答可能是错误的,但是我已经为PHP编程了很长一段时间了,我试图根据我的观察回答你的问题

在PHP 5.6.26的源代码
zend_hash.c
中,我们可以找到以下函数:

ZEND_API int _zend_hash_add_or_update(HashTable *ht, const char *arKey, uint nKeyLength, void *pData, uint nDataSize, void **pDest, int flag ZEND_FILE_LINE_DC)
{
// omitted
        if (IS_INTERNED(arKey)) {
                p = (Bucket *) pemalloc(sizeof(Bucket), ht->persistent);
                p->arKey = arKey;
        } else {
                p = (Bucket *) pemalloc(sizeof(Bucket) + nKeyLength, ht->persistent);
                p->arKey = (const char*)(p + 1);
                memcpy((char*)p->arKey, arKey, nKeyLength);
        }
// omitted
}
似乎是否复制字符串取决于
is\u INTERNED()
的值,那么它在哪里呢?首先,在
ZendAccelerator.h
中,我们可以找到:

#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO
// omitted
#else
# define IS_INTERNED(s)             0
// omitted
#endif

因此,“内部字符串”的概念从PHP5.4开始就存在了。字符串将始终在PHP5.3之前和中复制。但是由于PHP,我对示例代码感到困惑——在循环中引用
$data['a']
,但是在任何rows@ChrisOKelly抱歉,修正了。我没有逐行阅读链接的文章,但我已经阅读了。它说PHP中的一切都是哈希表,但当我为PHP编写扩展时,有两个不同的函数,
array_init()
ALLOC_HASHTABLE()
&
zend_hash_init()
,将
zval
初始化为数组或哈希表,“所以他们显然是不同的。也许我误解了这个问题,因为这是一个相当开放的问题,”弗雷德里克·张说。然而,使用提供的代码,PHP将通过创建一组可能的散列键并对其进行映射(基于链接文章),自己尝试并优化事情。但是我可能是错的。我不太确定这个问题本身,但要澄清的是,现在ZE并没有使用完全相同的数据结构来处理数组和哈希表。啊,是的,它不会使用相同的数据结构,我更要指出的是,正在进行的优化在某种程度上已经在幕后完成了(使用key=>index的散列来更快地搜索散列中的值)。我理解这一点,但我怀疑它是否适用于不同数组的键?假设我有变量
$a
,如果我将其用作不同数组中的键,
$a
的值会被使用(并复制)对于每个数组,作为键或内存中的指针将被使用,这基本上是我的问题?这是我在这个网站多年来得到的最好答案。我喜欢你的“哦,哦,等等,另一个宏,它又在哪里?”评论。Zend有大量的宏。为了澄清,在这种情况下,
$uniqueKey
字符串将不会被使用interned,因为它是在运行时创建的。interned仅适用于编译时已知(文字)字符串。@NikiC谢谢,我在回答中引用了它:)
#ifndef ZTS

#define IS_INTERNED(s) \
        (((s) >= CG(interned_strings_start)) && ((s) < CG(interned_strings_end)))

#else

#define IS_INTERNED(s) \
        (0)

#endif
#ifdef ZTS
# define CG(v) TSRMG(compiler_globals_id, zend_compiler_globals *, v)
int zendparse(void *compiler_globals);
#else
# define CG(v) (compiler_globals.v)
extern ZEND_API struct _zend_compiler_globals compiler_globals;
int zendparse(void);
#endif
static zend_always_inline zval *_zend_hash_add_or_update_i(HashTable *ht, zend_string *key, zval *pData, uint32_t flag ZEND_FILE_LINE_DC)
{
        zend_ulong h;
        uint32_t nIndex;
        uint32_t idx;
        Bucket *p;

        IS_CONSISTENT(ht);
        HT_ASSERT(GC_REFCOUNT(ht) == 1);

        if (UNEXPECTED(!(ht->u.flags & HASH_FLAG_INITIALIZED))) {
                CHECK_INIT(ht, 0);
                goto add_to_hash;
        } else if (ht->u.flags & HASH_FLAG_PACKED) {
                zend_hash_packed_to_hash(ht);
        } else if ((flag & HASH_ADD_NEW) == 0) {
                p = zend_hash_find_bucket(ht, key);

                if (p) {
// omitted
                }
        }

        ZEND_HASH_IF_FULL_DO_RESIZE(ht);        /* If the Hash table is full, resize it */

add_to_hash:
        HANDLE_BLOCK_INTERRUPTIONS();
        idx = ht->nNumUsed++;
        ht->nNumOfElements++;
        if (ht->nInternalPointer == HT_INVALID_IDX) {
                ht->nInternalPointer = idx;
        }
        zend_hash_iterators_update(ht, HT_INVALID_IDX, idx);
        p = ht->arData + idx;
        p->key = key;
        if (!ZSTR_IS_INTERNED(key)) {
                zend_string_addref(key);
                ht->u.flags &= ~HASH_FLAG_STATIC_KEYS;
                zend_string_hash_val(key);
        }
// omitted
}

ZEND_API zval* ZEND_FASTCALL _zend_hash_str_add(HashTable *ht, const char *str, size_t len, zval *pData ZEND_FILE_LINE_DC)
{
        zend_string *key = zend_string_init(str, len, ht->u.flags & HASH_FLAG_PERSISTENT);
        zval *ret = _zend_hash_add_or_update_i(ht, key, pData, HASH_ADD ZEND_FILE_LINE_RELAY_CC);
        zend_string_release(key);
        return ret;
}
#define ZSTR_IS_INTERNED(s)                 (GC_FLAGS(s) & IS_STR_INTERNED)