Skip to content

Commit 3325dd7

Browse files
committed
Reduce the minimum size for packed arrays from 8 to 2
There are probably ways this could be optimized. For now, I'm aiming to see if this will work correctly.
1 parent ecb1e31 commit 3325dd7

File tree

4 files changed

+26
-9
lines changed

4 files changed

+26
-9
lines changed

Zend/zend_hash.c

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -144,10 +144,18 @@ static zend_always_inline void zend_hash_real_init_mixed_ex(HashTable *ht)
144144
uint32_t nSize = ht->nTableSize;
145145

146146
if (UNEXPECTED(GC_FLAGS(ht) & IS_ARRAY_PERSISTENT)) {
147+
if (nSize < HT_MIN_SIZE_UNPACKED) {
148+
nSize = HT_MIN_SIZE_UNPACKED;
149+
ht->nTableSize = HT_MIN_SIZE_UNPACKED;
150+
}
147151
data = pemalloc(HT_SIZE_EX(nSize, HT_SIZE_TO_MASK(nSize)), 1);
148-
} else if (EXPECTED(nSize == HT_MIN_SIZE)) {
149-
data = emalloc(HT_SIZE_EX(HT_MIN_SIZE, HT_SIZE_TO_MASK(HT_MIN_SIZE)));
150-
ht->nTableMask = HT_SIZE_TO_MASK(HT_MIN_SIZE);
152+
} else if (EXPECTED(nSize <= HT_MIN_SIZE_UNPACKED)) {
153+
if (nSize < HT_MIN_SIZE_UNPACKED) {
154+
nSize = HT_MIN_SIZE_UNPACKED;
155+
ht->nTableSize = HT_MIN_SIZE_UNPACKED;
156+
}
157+
data = emalloc(HT_SIZE_EX(HT_MIN_SIZE_UNPACKED, HT_SIZE_TO_MASK(HT_MIN_SIZE_UNPACKED)));
158+
ht->nTableMask = HT_SIZE_TO_MASK(HT_MIN_SIZE_UNPACKED);
151159
HT_SET_DATA_ADDR(ht, data);
152160
/* Don't overwrite iterator count. */
153161
ht->u.v.flags = HASH_FLAG_STATIC_KEYS;
@@ -210,6 +218,7 @@ static zend_always_inline void zend_hash_real_init_ex(HashTable *ht, int packed)
210218
static const uint32_t uninitialized_bucket[-HT_MIN_MASK] =
211219
{HT_INVALID_IDX, HT_INVALID_IDX};
212220

221+
// XXX: Is MIN_SIZE_UNPACKED the best choice vs this (8 vs 2)? Haven't benchmarked it.
213222
ZEND_API const HashTable zend_empty_array = {
214223
.gc.refcount = 2,
215224
.gc.u.type_info = IS_ARRAY | (GC_IMMUTABLE << GC_FLAGS_SHIFT),
@@ -218,7 +227,7 @@ ZEND_API const HashTable zend_empty_array = {
218227
.arData = (Bucket*)&uninitialized_bucket[2],
219228
.nNumUsed = 0,
220229
.nNumOfElements = 0,
221-
.nTableSize = HT_MIN_SIZE,
230+
.nTableSize = HT_MIN_SIZE_UNPACKED,
222231
.nInternalPointer = 0,
223232
.nNextFreeElement = 0,
224233
.pDestructor = ZVAL_PTR_DTOR
@@ -244,6 +253,7 @@ ZEND_API void ZEND_FASTCALL _zend_hash_init(HashTable *ht, uint32_t nSize, dtor_
244253
_zend_hash_init_int(ht, nSize, pDestructor, persistent);
245254
}
246255

256+
// TODO should this be 2 or 8? also see zend_new_array
247257
ZEND_API HashTable* ZEND_FASTCALL _zend_new_array_0(void)
248258
{
249259
HashTable *ht = emalloc(sizeof(HashTable));
@@ -262,6 +272,7 @@ ZEND_API HashTable* ZEND_FASTCALL zend_new_pair(zval *val1, zval *val2)
262272
{
263273
Bucket *p;
264274
HashTable *ht = emalloc(sizeof(HashTable));
275+
// XXX will need to adjust all calls like this if HT_MIN_SIZE goes below 2
265276
_zend_hash_init_int(ht, HT_MIN_SIZE, ZVAL_PTR_DTOR, 0);
266277
ht->nNumUsed = ht->nNumOfElements = ht->nNextFreeElement = 2;
267278
zend_hash_real_init_packed_ex(ht);
@@ -317,11 +328,12 @@ ZEND_API void ZEND_FASTCALL zend_hash_packed_to_hash(HashTable *ht)
317328
void *new_data, *old_data = HT_GET_DATA_ADDR(ht);
318329
Bucket *old_buckets = ht->arData;
319330
uint32_t nSize = ht->nTableSize;
331+
uint32_t nNewSize = nSize >= HT_MIN_SIZE_UNPACKED ? nSize : HT_MIN_SIZE_UNPACKED;
320332

321333
HT_ASSERT_RC1(ht);
322334
HT_FLAGS(ht) &= ~HASH_FLAG_PACKED;
323-
new_data = pemalloc(HT_SIZE_EX(nSize, HT_SIZE_TO_MASK(nSize)), GC_FLAGS(ht) & IS_ARRAY_PERSISTENT);
324-
ht->nTableMask = HT_SIZE_TO_MASK(ht->nTableSize);
335+
new_data = pemalloc(HT_SIZE_EX(nNewSize, HT_SIZE_TO_MASK(nNewSize)), GC_FLAGS(ht) & IS_ARRAY_PERSISTENT);
336+
ht->nTableMask = HT_SIZE_TO_MASK(nNewSize);
325337
HT_SET_DATA_ADDR(ht, new_data);
326338
memcpy(ht->arData, old_buckets, sizeof(Bucket) * ht->nNumUsed);
327339
pefree(old_data, GC_FLAGS(ht) & IS_ARRAY_PERSISTENT);
@@ -1158,6 +1170,9 @@ static void ZEND_FASTCALL zend_hash_do_resize(HashTable *ht)
11581170
} else if (ht->nTableSize < HT_MAX_SIZE) { /* Let's double the table size */
11591171
void *new_data, *old_data = HT_GET_DATA_ADDR(ht);
11601172
uint32_t nSize = ht->nTableSize + ht->nTableSize;
1173+
if (nSize < HT_MIN_SIZE_UNPACKED) {
1174+
nSize = HT_MIN_SIZE_UNPACKED;
1175+
}
11611176
Bucket *old_buckets = ht->arData;
11621177

11631178
ht->nTableSize = nSize;

Zend/zend_types.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -302,7 +302,8 @@ struct _zend_array {
302302
#define HT_INVALID_IDX ((uint32_t) -1)
303303

304304
#define HT_MIN_MASK ((uint32_t) -2)
305-
#define HT_MIN_SIZE 8
305+
#define HT_MIN_SIZE 2
306+
#define HT_MIN_SIZE_UNPACKED 8
306307

307308
#if SIZEOF_SIZE_T == 4
308309
# define HT_MAX_SIZE 0x04000000 /* small enough to avoid overflow checks */

ext/opcache/zend_persist.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,8 @@ static void zend_hash_persist(HashTable *ht)
115115
void *data = HT_GET_DATA_ADDR(ht);
116116
data = zend_shared_memdup_free(data, HT_USED_SIZE(ht));
117117
HT_SET_DATA_ADDR(ht, data);
118-
} else if (ht->nNumUsed > HT_MIN_SIZE && ht->nNumUsed < (uint32_t)(-(int32_t)ht->nTableMask) / 4) {
118+
} else if (ht->nNumUsed > HT_MIN_SIZE_UNPACKED && ht->nNumUsed < (uint32_t)(-(int32_t)ht->nTableMask) / 4) {
119+
/* TODO any special considerations for this? */
119120
/* compact table */
120121
void *old_data = HT_GET_DATA_ADDR(ht);
121122
Bucket *old_buckets = ht->arData;

ext/opcache/zend_persist_calc.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ static void zend_hash_persist_calc(HashTable *ht)
6161
return;
6262
}
6363

64-
if (!(HT_FLAGS(ht) & HASH_FLAG_PACKED) && ht->nNumUsed > HT_MIN_SIZE && ht->nNumUsed < (uint32_t)(-(int32_t)ht->nTableMask) / 4) {
64+
if (!(HT_FLAGS(ht) & HASH_FLAG_PACKED) && ht->nNumUsed > HT_MIN_SIZE_UNPACKED && ht->nNumUsed < (uint32_t)(-(int32_t)ht->nTableMask) / 4) {
6565
/* compact table */
6666
uint32_t hash_size;
6767

0 commit comments

Comments
 (0)