Skip to content

Conversation

@aadya940
Copy link
Contributor

@aadya940 aadya940 commented Nov 7, 2025

Fixes #30048

This PR adopts better copying strategies than the previous element by element copying for non-contiguos paths.

@aadya940 aadya940 marked this pull request as draft November 7, 2025 02:45
@mattip
Copy link
Member

mattip commented Nov 7, 2025

Quick comment: benchmarks belong in benchmarks/benchmarks, not in tests

@aadya940 aadya940 marked this pull request as ready for review November 7, 2025 21:00
Copy link
Member

@seberg seberg left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, this looks good, but I think we can simplify it a fair bit.


/* Writable Buffer */
char* dest = PyBytes_AS_STRING(ret);
memset(dest, 0, numbytes);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You don't need this if you have the NEEDS_INIT check above.

for (int i = 0; i < PyArray_NDIM(self); i++) {
strides[i] = stride;
stride *= PyArray_DIM(self, i);
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You should't need any of this, PyArray_NewFromDescr should fill in the strides for you.

if (ret == NULL) {
Py_DECREF(it);

PyArray_CLEARFLAGS(dest_array, NPY_ARRAY_OWNDATA);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also should not be needed.

self.noncontig.tobytes()

def time_tobytes_contiguous(self):
np.ascontiguousarray(self.noncontig).tobytes()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think we should time this. You could use .T just directly in the non-contiguous version also (since the .T will be irrelevantly fast here).

pytest.raises(ValueError, a.getfield, 'uint64', 0)

@pytest.mark.parametrize("shape", [(3, 224, 224), (8, 512, 512)])
def test_tobytes_noncontiguous_not_slower_than_copy(shape):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you rename this to just _no_copy_fastpath or so and see if there are other ravel tests where this might fit well. (e.g. we often have a TestRavel class).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think TestRegression is the place where many of the ravel and tobytes methods are tested.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

test_multiarray has a few test_ravel* functions, below those would be nicest I think.

Copy link
Member

@seberg seberg left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, moving the tests would be nice, tiny typos if you iterate anyway.

return NULL;
}

/* Avoid Ravel where possible for lesser copies. */
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
/* Avoid Ravel where possible for lesser copies. */
/* Avoid Ravel where possible for fewer copies. */

if (!PyDataType_REFCHK(PyArray_DESCR(self)) &&
((PyArray_DESCR(self)->flags & NPY_NEEDS_INIT) == 0)) {

/* Allocate final Byte Object */
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
/* Allocate final Byte Object */
/* Allocate final Bytes Object */

@aadya940
Copy link
Contributor Author

Thanks for the quick review @seberg

@seberg
Copy link
Member

seberg commented Nov 12, 2025

Thanks, I think this amount of added complexity is a simple enough to warrant adding it.

@seberg seberg merged commit eba60dc into numpy:main Nov 12, 2025
77 checks passed
cakedev0 pushed a commit to cakedev0/numpy that referenced this pull request Dec 5, 2025
…30170)

* Add optimal copy path for non-contiguos arrays in ToString C Impl.

* Add tests for 	obytes new path

* Add imports

* fix comments

* fix memory issues and add benchmarks

* run ruff --fix

* simplify function

* minor fix

* minor typos and move tests
IndifferentArea pushed a commit to IndifferentArea/numpy that referenced this pull request Dec 7, 2025
…30170)

* Add optimal copy path for non-contiguos arrays in ToString C Impl.

* Add tests for 	obytes new path

* Add imports

* fix comments

* fix memory issues and add benchmarks

* run ruff --fix

* simplify function

* minor fix

* minor typos and move tests
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

ENH: tobytes operation is slow when data is not contiguous

3 participants