-
-
Notifications
You must be signed in to change notification settings - Fork 12.6k
BUG: define "uint-alignment", fixes complex64 alignment #6377
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from 1 commit
Commits
Show all changes
8 commits
Select commit
Hold shift + click to select a range
351e4b6
BUG: _strided_masked_wrapper_transfer_function goes out of bounds
ahaldane b76c0df
BUG: raw_array_is_aligned ignores NPY_RELAXED_STRIDES_CHECKING
ahaldane 27d4ce9
ENH: Implement methods for uint-alignment
ahaldane 1252b80
ENH: Make copy-code-paths check for uint-alignment
ahaldane 8097aa3
ENH: Fix complex64 alignment
ahaldane 33e1a7d
TST: Test complex64 alignment
ahaldane 38af6dd
DOC: Document how memory alignment works as of 1.14
ahaldane 12bd7c3
MAINT: remove unneeded test in npy_is_aligned
ahaldane File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
DOC: Document how memory alignment works as of 1.14
- Loading branch information
commit 38af6ddb5896525b9553edd6c8c9817c7c3b1d21
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,96 @@ | ||
| .. _alignment: | ||
|
|
||
|
|
||
| Numpy Alignment Goals | ||
| ===================== | ||
|
|
||
| There are three use-cases related to memory alignment in numpy (as of 1.14): | ||
|
|
||
| 1. Creating structured datatypes with fields aligned like in a C-struct. | ||
| 2. Speeding up copy operations by using uint assignment in instead of memcpy | ||
| 3. Guaranteeing safe aligned access for ufuncs/setitem/casting code | ||
|
|
||
| Numpy uses two different forms of alignment to achieve these goals: | ||
| "True alignment" and "Uint alignment". | ||
|
|
||
| "True" alignment refers to the architecture-dependent alignment of an | ||
| equivalent C-type in C. For example, in x64 systems ``numpy.float64`` is | ||
| equivalent to ``double`` in C. On most systems this has either an alignment of | ||
| 4 or 8 bytes (and this can be controlled in gcc by the option | ||
| ``malign-double``). A variable is aligned in memory if its memory offset is a | ||
| multiple of its alignment. On some systems (eg sparc) memory alignment is | ||
| required, on others it gives a speedup. | ||
|
|
||
| "Uint" alignment depends on the size of a datatype. It is defined to be the | ||
| "True alignment" of the uint used by numpy's copy-code to copy the datatype, or | ||
| undefined/unaligned if there is no equivalent uint. Currently numpy uses uint8, | ||
| uint16, uint32, uint64 and uint64 to copy data of size 1,2,4,8,16 bytes | ||
| respectively, and all other sized datatypes cannot be uint-aligned. | ||
|
|
||
| For example, on a (typical linux x64 gcc) system, the numpy ``complex64`` | ||
| datatype is implemented as ``struct { float real, imag; }``. This has "true" | ||
| alignment of 4 and "uint" alignment of 8 (equal to the true alignment of | ||
| ``uint64``). | ||
|
|
||
| Variables in Numpy which control and describe alignment | ||
| ======================================================= | ||
|
|
||
| There are 4 relevant uses of the word ``align`` used in numpy: | ||
|
|
||
| * The ``dtype.alignment`` attribute (``descr->alignment`` in C). This is meant | ||
| to reflect the "true alignment" of the type. It has arch-dependent default | ||
| values for all datatypes, with the exception of structured types created | ||
| with ``align=True`` as described below. | ||
| * The ``ALIGNED`` flag of an ndarray, computed in ``IsAligned`` and checked | ||
| by ``PyArray_ISALIGNED``. This is computed from ``dtype.alignment``. | ||
| It is set to ``True`` if every item in the array is at a memory location | ||
| consistent with ``dtype.alignment``, which is the case if the data ptr and | ||
| all strides of the array are multiples of that alignment. | ||
| * The ``align`` keyword of the dtype constructor, which only affects structured | ||
| arrays. If the structure's field offsets are not manually provided numpy | ||
| determines offsets automatically. In that case, ``align=True`` pads the | ||
| structure so that each field is "true" aligned in memory and sets | ||
| ``dtype.alignment`` to be the largest of the field "true" alignments. This | ||
| is like what C-structs usually do. Otherwise if offsets or itemsize were | ||
| manually provided ``align=True`` simply checks that all the fields are | ||
| "true" aligned and that the total itemsize is a multiple of the largest | ||
| field alignment. In either case ``dtype.isalignedstruct`` is also set to | ||
| True. | ||
| * ``IsUintAligned`` is used to determine if an ndarray is "uint aligned" in | ||
| an analagous way to how ``IsAligned`` checks for true-alignment. | ||
|
|
||
| Consequences of alignment | ||
| ========================= | ||
|
|
||
| Here is how the variables above are used: | ||
|
|
||
| 1. Creating aligned structs: In order to know how to offset a field when | ||
| ``align=True``, numpy looks up ``field.dtype.alignment``. This includes | ||
| fields which are nested structured arrays. | ||
| 2. Ufuncs: If the ``ALIGNED`` flag of an array is False, ufuncs will | ||
| buffer/cast the array before evaluation. This is needed since ufunc inner | ||
| loops access raw elements directly, which might fail on some archs if the | ||
| elements are not true-aligned. | ||
| 3. Getitem/setitem/copyswap function: Similar to ufuncs, these functions | ||
| generally have two code paths. If ``ALIGNED`` is False they will | ||
| use a code path that buffers the arguments so they are true-aligned. | ||
| 4. Strided copy code: Here, "uint alignment" is used instead. If the itemsize | ||
| of an array is equal to 1, 2, 4, 8 or 16 bytes and the array is uint | ||
| aligned then instead numpy will do ``*(uintN*)dst) = *(uintN*)src)`` for | ||
| appropriate N. Otherwise numpy copies by doing ``memcpy(dst, src, N)``. | ||
| 5. Nditer code: Since this often calls the strided copy code, it must | ||
| check for "uint alignment". | ||
| 6. Cast code: if the array is "uint aligned" this will essentially do | ||
| ``*dst = CASTFUNC(*src)``. If not, it does | ||
| ``memmove(srcval, src); dstval = CASTFUNC(srcval); memmove(dst, dstval)`` | ||
| where dstval/srcval are aligned. | ||
|
|
||
| Note that in principle, only "true alignment" is required for casting code. | ||
| However, because the casting code and copy code are deeply intertwined they | ||
| both use "uint" alignment. This should be safe assuming uint alignment is | ||
| always larger than true alignment, though it can cause unnecessary buffering if | ||
| an array is "true aligned" but not "uint aligned". If there is ever a big | ||
| rewrite of this code it would be good to allow them to use different | ||
| alignments. | ||
|
|
||
|
|
||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So there are some structures that have no
uintalignment?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, that's one way to see it. Or, in pratice, those structs get assigned a
uintalignment of 0.The "uint" alignments are for use in the strided copy code, which only special-cases the 1,2,4,8, and 16 byte sizes. All other cases return a "uint" alignment of 0, which triggers
memmoveto be used instead.I'll correct/update the docs to better describe what happens for non-power-of-two sized types.