我有一个数组,我正在使用一些项来构造更多的数组,下面是一个粗略的例子。
$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值会作为键使用(并复制),还是使用内存中的指针,这基本上就是我的问题?
发布于 2016-10-07 11:14:25
换句话说,假设我有变量$a,如果我在不同的数组中使用它作为键,那么每个数组的$a值会作为键使用(并复制),还是使用内存中的指针,这基本上就是我的问题?
下面是PHP与>=5.4 7之间的区别,这取决于您的环境。我不是PHP专家,我的回答可能是错误的,但我已经为PHP编程了很长一段时间,并且我试图根据我的观察来回答您的问题。
在PHP5.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_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确实过时了,我想把它从这个答案中删除。那么PHP5.4-5.6呢?在zend_string.h中
#ifndef ZTS
#define IS_INTERNED(s) \
(((s) >= CG(interned_strings_start)) && ((s) < CG(interned_strings_end)))
#else
#define IS_INTERNED(s) \
(0)
#endif哦,哦,等等,又一个宏,又在哪里?在zend_globals_macros.h中
#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因此,在没有Zend线程安全的PHP5.4-5.6中,如果字符串已经在此特定进程的内存中,则将使用引用;但是,对于ZTS,它将始终被复制。(FYI,我们很少需要Linux中的ZTS )。
为了澄清,本例中的
$uniqueKey字符串将不会被实习生,因为它是在运行时创建的。实习只适用于编译时已知(文字)字符串.@NikiC感谢您的澄清
PHP 7呢?在zend_hash.c中,PHP7.0.11的源代码,
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;
}FYI
#define ZSTR_IS_INTERNED(s) (GC_FLAGS(s) & IS_STR_INTERNED)哇,所以PHP 7实际上引入了一个新的,惊人的zend_string结构,它与RC和垃圾收集有关!这比PHP5.6中的效果要好得多!
简而言之,如果您在哈希表中使用一个已存在的字符串作为键,当然,在PHP中,它很可能被复制;在PHP5.4中,如果没有ZTS引用;在PHP5.4中,使用ZTS复制;在PHP5.4中,引用它;在PHP5.4中,引用它。
此外,我还找到了一篇很好的文章供您阅读(稍后我也会阅读它):http://jpauli.github.io/2015/09/18/php-string-management.html
发布于 2016-10-06 05:26:23
虽然我假设内部元素已经有一段时间没有改变,这篇文章说它们基本上是带有一些细微差别的哈希表,以避免关键冲突。所以在某种程度上,它确实做了你在引擎盖下面说的话。
https://stackoverflow.com/questions/39886480
复制相似问题