From 03f3789efe4da2c56d2841ed027ef6735ca2f11b Mon Sep 17 00:00:00 2001 From: John Zwinck Date: Thu, 14 Sep 2017 00:16:23 +0800 Subject: ENH: Align data in np.save() at 64 bytes (#9025) Previously, saving format version 1 would align to 16 bytes, and saving version 2 would align improperly (bug #8085). Alignment is now always at least 64 bytes in either version, which supports memory mapping of the saved files on Linux, where mmap() offset must be a multiple of the page size. Why 64 bytes? Simply because we don't know of a case where more is needed. AVX alignment is 32 bytes; AVX-512 is 64. Fixes #8085, closes #8598. --- numpy/lib/format.py | 51 +++++++++++++++++++++++++++++---------------------- 1 file changed, 29 insertions(+), 22 deletions(-) (limited to 'numpy/lib/format.py') diff --git a/numpy/lib/format.py b/numpy/lib/format.py index 14dec01d5..84af2afc8 100644 --- a/numpy/lib/format.py +++ b/numpy/lib/format.py @@ -100,9 +100,9 @@ the header data HEADER_LEN. The next HEADER_LEN bytes form the header data describing the array's format. It is an ASCII string which contains a Python literal expression of a dictionary. It is terminated by a newline (``\\n``) and padded with -spaces (``\\x20``) to make the total length of -``magic string + 4 + HEADER_LEN`` be evenly divisible by 16 for alignment -purposes. +spaces (``\\x20``) to make the total of +``len(magic string) + 2 + len(length) + HEADER_LEN`` be evenly divisible +by 64 for alignment purposes. The dictionary contains three keys: @@ -163,6 +163,7 @@ else: MAGIC_PREFIX = b'\x93NUMPY' MAGIC_LEN = len(MAGIC_PREFIX) + 2 +ARRAY_ALIGN = 64 # plausible values are powers of 2 between 16 and 4096 BUFFER_SIZE = 2**18 # size of buffer for reading npz files in bytes # difference between version 1.0 and 2.0 is a 4 byte (I) header length @@ -304,27 +305,33 @@ def _write_array_header(fp, d, version=None): header.append("'%s': %s, " % (key, repr(value))) header.append("}") header = "".join(header) - # Pad the header with spaces and a final newline such that the magic - # string, the header-length short and the header are aligned on a - # 16-byte boundary. Hopefully, some system, possibly memory-mapping, - # can take advantage of our premature optimization. - current_header_len = MAGIC_LEN + 2 + len(header) + 1 # 1 for the newline - topad = 16 - (current_header_len % 16) - header = header + ' '*topad + '\n' header = asbytes(_filter_header(header)) - hlen = len(header) - if hlen < 256*256 and version in (None, (1, 0)): + hlen = len(header) + 1 # 1 for newline + padlen_v1 = ARRAY_ALIGN - ((MAGIC_LEN + struct.calcsize('