Skip to content

Commit 5c38e8e

Browse files
committed
optimization to write_array_to_file to reuse PyMemoryView
1 parent 73e1540 commit 5c38e8e

1 file changed

Lines changed: 22 additions & 5 deletions

File tree

src/methods.c

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1244,6 +1244,10 @@ write_array_to_file(PyObject *Py_UNUSED(m), PyObject *args, PyObject *kwargs)
12441244
char *pack_buffer = NULL;
12451245
Py_ssize_t pack_buffer_size = 0;
12461246

1247+
/* For the write() path, reuse a single memoryview across iterations,
1248+
* rebinding it to each chunk, instead of allocating a new object each time. */
1249+
PyObject *buffer = NULL;
1250+
12471251
do {
12481252
npy_intp inner_size = *innersizeptr;
12491253
if (inner_size == 0) {
@@ -1283,20 +1287,31 @@ write_array_to_file(PyObject *Py_UNUSED(m), PyObject *args, PyObject *kwargs)
12831287
}
12841288
}
12851289
else {
1286-
/* Note: PyMemoryView_FromMemory requires non-const char*, but we pass
1290+
/* Reuse a single memoryview, rebinding it to the current chunk. This
1291+
* is safe because write() consumes the bytes synchronously and does
1292+
* not retain the view (exports return to 0 before write() returns).
1293+
* Note: PyMemoryView_FromMemory requires non-const char*, but we pass
12871294
* PyBUF_READ flag which makes the view read-only, so the cast is safe. */
1288-
PyObject *buffer = PyMemoryView_FromMemory((char *)data_to_write, chunk_size, PyBUF_READ);
12891295
if (buffer == NULL) {
1290-
goto fail;
1296+
buffer = PyMemoryView_FromMemory((char *)data_to_write, chunk_size, PyBUF_READ);
1297+
if (buffer == NULL) {
1298+
goto fail;
1299+
}
1300+
}
1301+
else {
1302+
// Rebind buf/len (and shape[0], since ndim == 1 and itemsize == 1) to this chunk
1303+
Py_buffer *view = PyMemoryView_GET_BUFFER(buffer);
1304+
view->buf = (void *)data_to_write;
1305+
view->len = chunk_size;
1306+
view->shape[0] = chunk_size;
12911307
}
12921308

12931309
PyObject *write_result = PyObject_CallMethodObjArgs(file, write_name, buffer, NULL);
1294-
Py_DECREF(buffer);
12951310
if (write_result == NULL) {
12961311
goto fail;
12971312
}
12981313

1299-
/* Check for partial writes */
1314+
// Check for partial writes
13001315
if (PyLong_Check(write_result)) {
13011316
Py_ssize_t bytes_written = PyLong_AsSsize_t(write_result);
13021317
if (bytes_written < 0 && PyErr_Occurred()) {
@@ -1322,12 +1337,14 @@ write_array_to_file(PyObject *Py_UNUSED(m), PyObject *args, PyObject *kwargs)
13221337
}
13231338

13241339
PyMem_Free(pack_buffer);
1340+
Py_XDECREF(buffer);
13251341
NpyIter_Deallocate(iter);
13261342
Py_XDECREF(write_name);
13271343
Py_RETURN_NONE;
13281344

13291345
fail:
13301346
PyMem_Free(pack_buffer);
1347+
Py_XDECREF(buffer);
13311348
NpyIter_Deallocate(iter);
13321349
Py_XDECREF(write_name);
13331350
return NULL;

0 commit comments

Comments
 (0)