You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
This repository was archived by the owner on Jun 27, 2025. It is now read-only.
// NOTE: this used to be a Py_ssize_t, which can be 32 bits on some machines and might easily overflow with a few very large indices. Using an explicit 64-bit int seems safer
347
349
staticnpy_int64key_count_global=0;
348
350
351
+
349
352
// Fill the int_cache up to size_needed with PyObject ints; `size` is not the key_count_global.
Copy file name to clipboardExpand all lines: doc/articles/npy-opt.txt
+32-61Lines changed: 32 additions & 61 deletions
Original file line number
Diff line number
Diff line change
@@ -1,37 +1,55 @@
1
1
2
-
These changes integrate direct support of NumPy arrays given as keys to `AutoMap`s and `FrozenAutoMap`s. Improvements are made in `AutoMap` initialization, whereby an array is converted to a list using specialized array methods (`tolist()`) when necessary. Improvements are made in `FrozenAutoMap` initialization, whereby an immutable array, when given as keys, is held as a reference without copy to a list; further, hashing and lookup make use of C types, avoiding any creation of PyObjects.
2
+
These changes integrate direct support of NumPy arrays given as keys to `AutoMap`s and `FrozenAutoMap`s, optimizing their usage.
3
3
4
-
Operation with non-array keys, hash table usage and scanning, and usage of the PyObject integer cache, remains unchanged.
4
+
Improvements are made in `AutoMap` initialization, whereby an array is converted to a list using optimal array methods; that list is then held as the keys.
5
5
6
-
On initialization (`fam_init`), a `KeysArrayType` enum value is assigned to the `keys_array_type` attribute of `FAMObject`. This is used for branching in all places where divergent behavior is required between keys stored as lists (as was done previously) or as keys stored as typed arrays.
6
+
Improvements are made in `FrozenAutoMap` initialization, whereby an immutable array (for integer, floating point, and flexible dtypes), when given as keys, is held as a reference without copy to a list. Further, hashing and lookup make use of C types, avoiding any management of PyObjects.
7
7
8
+
For array dtypes not explicitly handled, or for non-array keys, `FrozenAutoMap` operation is unchanged. In all cases, hash table layout and scanning, and management of the PyObject integer cache, are the same as before.
8
9
9
-
All `FrozenAutoMap` usage of arrays reduces memory usage: no new `PyObject`s are created.
10
+
A key change is that, on initialization (`fam_init`), a `KeysArrayType` enum value is assigned to the `keys_array_type` attribute of `FAMObject`. This is used for branching in all places where divergent behavior is required between keys stored as lists (as was done previously) or as keys stored as typed arrays.
10
11
12
+
Performance panels compare FAM(L), FAM(A), AM(A), and Dict (`FrozenAutoMap` created from a list, `FrozenAutoMap` created from an array, `AutoMap` created from an array, and a dictionary implementing an `AutoMap` mapping). Key indicators are the performance of instantiation and lookup.
11
13
14
+
The relevant comparison for StaticFrame usage is between FAM(A) and AM(A), the latter approximating what StaticFrame does presently when creating `FrozenAutoMap`s. (FAM(L) is not available to StaticFrame as AutoMaps are always created from an array, not a list of Python objects.)
12
15
13
-
General Changes
16
+
Across all supported types, FAM(A) initialization is more than twice as fast as AM(A). FAM(A) lookup performance varies greatly by type, but always out-performs AM(A), in some cases more than twice as fast as AM(A). In all tests, we see signs that out-performance grows with scale.
14
17
15
-
More usage of `PySequence_Fast_ITEMS` where possible:
16
-
In `fam_init()` for initialization from lists
17
-
In `fami` structs for keys and int_cache access during iteration.
18
+
Independent of performance time, All `FrozenAutoMap` usage of arrays reduces memory usage: no new `PyObject`s are created and the passed array simply has reference incremented.
19
+
20
+
21
+
22
+
Key Changes
18
23
19
24
Split the old `fam_new()` into `fam_new()` and `fam_init()`, implemented `__setstate__()`, `__getstate__()`:
20
25
To support pickling a FAM with a NumPy array, `__setstate__()` must re-set the `writeable` flag of an arary to False.
21
26
To integrate `__setstate__()`, the old `fam_new()` had to be divided into a `fam_new()` and a `fam_init()`.
22
27
28
+
Based on `keys_array_type`, `fam_init` calls type-specific insert routines, which use type-specific hash routines to add entries to the hash table.
29
+
30
+
On lookup, type-specific lookup routines are called based on `keys_array_type`. These routines identify PyObjects as PyArray scalars or native PyObject types, extract the appropriate C-type, compute a hash, and use type-specific lookup routines to discover the position in the keys array.
31
+
32
+
23
33
Split `copy()` into `copy()` and `copy_to_new()`.
24
-
Due to splitting `fam_new()`, copy allocation and copy setting needed to split into to methods.
34
+
Due to now having `fam_new()` and `fam_init()`, copy allocation and copy setting needed to split into two methods. Now, in `fam_init`, if a `FAMType` is identified as the keys argument, `copy_to_new()` is used to transfer values from the argument to the new instance. The `copy()` function remains, now using `fam_new()` and `copy_to_new()`.
25
35
26
-
Additions to the `fam` struct:
27
-
Added `key_buffer`: For Unicode arrays, this is a dynamically allocated buffer of size equal to one more than the array's max number of characters. This buffer is given to `PyUnicode_AsUCS4`, which will add a NULL and is why the size of the buffer is one more than max characters. This is only used for a FAM with Unicode array keys; all other usage keeps this as NULL.
36
+
Additions to the `FAMObject` struct:
37
+
`key_array_type`: A `KeysArrayType` enum specifying a list or array dtype. As a list is assigned zero (and all other array dtypes as non-zero), the value can be used to branch on non-array versus array processing.
28
38
29
-
Extended property tests for FAMs with arrays
30
-
Tests initialization of both contiguous and non-contiguous arrays
31
-
Tests all features tested for non-array-based AMs
39
+
`keys_size`: As determining size must branch on `keys_array_type`, this attribute is used to track size, avoiding having to go the underly keys container.
40
+
41
+
`key_buffer`: For Unicode arrays, this is a dynamically allocated buffer of size equal to one more than the array's max number of characters. This buffer is given to `PyUnicode_AsUCS4`, which will add a NULL and is why the size of the buffer is one more than max characters. This is only used for a FAM with Unicode array keys; all other usage keeps this as NULL.
32
42
33
43
The type of `key_count_global` is now a platform independent 64 bit signed integer. Perviously, it was a `Py_ssize_t`, which is 32 bits on 32 bit systems and could overflow in scenarios when many indicies are created.
34
44
45
+
Extended property tests for FAMs with arrays
46
+
A custom Hypothesis strategy has been implemented to deliver both contiguous and non-contiguous arrays.
47
+
48
+
New Hypothesis tests for array-initialized `FrozenAutoMap`s now cover all features previously tested by Hypothesis.
49
+
50
+
51
+
52
+
35
53
36
54
37
55
@@ -123,50 +141,3 @@ Second Approach
123
141
124
142
Given PyObjects, can convert to C-types for type-specific loookup.
125
143
126
-
127
-
Performance
128
-
129
-
NumPy unicode arrays had particularly bad performance
0 commit comments