Skip to content

Commit cc2456e

Browse files
authored
Merge pull request #291 from PyO3/minor-simplifications
RFC: Simplify constructors of one-dimensional arrays
2 parents e933e27 + eb8e2c0 commit cc2456e

File tree

5 files changed

+239
-82
lines changed

5 files changed

+239
-82
lines changed

benches/array.rs

Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
#![feature(test)]
2+
3+
extern crate test;
4+
use test::{black_box, Bencher};
5+
6+
use std::ops::Range;
7+
8+
use numpy::PyArray1;
9+
use pyo3::{Python, ToPyObject};
10+
11+
struct Iter(Range<usize>);
12+
13+
impl Iterator for Iter {
14+
type Item = usize;
15+
16+
fn next(&mut self) -> Option<Self::Item> {
17+
self.0.next()
18+
}
19+
}
20+
21+
fn from_iter(bencher: &mut Bencher, size: usize) {
22+
iter_with_gil(bencher, |py| {
23+
let iter = black_box(Iter(0..size));
24+
25+
PyArray1::from_iter(py, iter);
26+
});
27+
}
28+
29+
#[bench]
30+
fn from_iter_small(bencher: &mut Bencher) {
31+
from_iter(bencher, 2_usize.pow(5));
32+
}
33+
34+
#[bench]
35+
fn from_iter_medium(bencher: &mut Bencher) {
36+
from_iter(bencher, 2_usize.pow(10));
37+
}
38+
39+
#[bench]
40+
fn from_iter_large(bencher: &mut Bencher) {
41+
from_iter(bencher, 2_usize.pow(15));
42+
}
43+
44+
struct ExactIter(Range<usize>);
45+
46+
impl Iterator for ExactIter {
47+
type Item = usize;
48+
49+
fn next(&mut self) -> Option<Self::Item> {
50+
self.0.next()
51+
}
52+
53+
fn size_hint(&self) -> (usize, Option<usize>) {
54+
self.0.size_hint()
55+
}
56+
}
57+
58+
impl ExactSizeIterator for ExactIter {
59+
fn len(&self) -> usize {
60+
self.0.len()
61+
}
62+
}
63+
64+
fn from_exact_iter(bencher: &mut Bencher, size: usize) {
65+
iter_with_gil(bencher, |py| {
66+
let iter = black_box(ExactIter(0..size));
67+
68+
PyArray1::from_exact_iter(py, iter);
69+
});
70+
}
71+
72+
#[bench]
73+
fn from_exact_iter_small(bencher: &mut Bencher) {
74+
from_exact_iter(bencher, 2_usize.pow(5));
75+
}
76+
77+
#[bench]
78+
fn from_exact_iter_medium(bencher: &mut Bencher) {
79+
from_exact_iter(bencher, 2_usize.pow(10));
80+
}
81+
82+
#[bench]
83+
fn from_exact_iter_large(bencher: &mut Bencher) {
84+
from_exact_iter(bencher, 2_usize.pow(15));
85+
}
86+
87+
fn from_slice(bencher: &mut Bencher, size: usize) {
88+
let vec = (0..size).collect::<Vec<_>>();
89+
90+
iter_with_gil(bencher, |py| {
91+
let slice = black_box(&vec);
92+
93+
PyArray1::from_slice(py, slice);
94+
});
95+
}
96+
97+
#[bench]
98+
fn from_slice_small(bencher: &mut Bencher) {
99+
from_slice(bencher, 2_usize.pow(5));
100+
}
101+
102+
#[bench]
103+
fn from_slice_medium(bencher: &mut Bencher) {
104+
from_slice(bencher, 2_usize.pow(10));
105+
}
106+
107+
#[bench]
108+
fn from_slice_large(bencher: &mut Bencher) {
109+
from_slice(bencher, 2_usize.pow(15));
110+
}
111+
112+
fn from_object_slice(bencher: &mut Bencher, size: usize) {
113+
let vec = Python::with_gil(|py| (0..size).map(|val| val.to_object(py)).collect::<Vec<_>>());
114+
115+
iter_with_gil(bencher, |py| {
116+
let slice = black_box(&vec);
117+
118+
PyArray1::from_slice(py, slice);
119+
});
120+
}
121+
122+
#[bench]
123+
fn from_object_slice_small(bencher: &mut Bencher) {
124+
from_object_slice(bencher, 2_usize.pow(5));
125+
}
126+
127+
#[bench]
128+
fn from_object_slice_medium(bencher: &mut Bencher) {
129+
from_object_slice(bencher, 2_usize.pow(10));
130+
}
131+
132+
#[bench]
133+
fn from_object_slice_large(bencher: &mut Bencher) {
134+
from_object_slice(bencher, 2_usize.pow(15));
135+
}
136+
137+
fn iter_with_gil(bencher: &mut Bencher, mut f: impl FnMut(Python)) {
138+
Python::with_gil(|py| {
139+
bencher.iter(|| {
140+
let pool = unsafe { py.new_pool() };
141+
142+
f(pool.python());
143+
});
144+
});
145+
}

src/array.rs

Lines changed: 6 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -356,10 +356,6 @@ impl<T, D> PyArray<T, D> {
356356
let ptr = self.as_array_ptr();
357357
(*ptr).data as *mut _
358358
}
359-
360-
pub(crate) unsafe fn copy_ptr(&self, other: *const T, len: usize) {
361-
ptr::copy_nonoverlapping(other, self.data(), len)
362-
}
363359
}
364360

365361
struct InvertedAxes(u32);
@@ -959,7 +955,7 @@ impl<T: Element> PyArray<T, Ix1> {
959955
unsafe {
960956
let array = PyArray::new(py, [slice.len()], false);
961957
if T::IS_COPY {
962-
array.copy_ptr(slice.as_ptr(), slice.len());
958+
ptr::copy_nonoverlapping(slice.as_ptr(), array.data(), slice.len());
963959
} else {
964960
let data_ptr = array.data();
965961
for (i, item) in slice.iter().enumerate() {
@@ -983,7 +979,7 @@ impl<T: Element> PyArray<T, Ix1> {
983979
/// });
984980
/// ```
985981
pub fn from_vec<'py>(py: Python<'py>, vec: Vec<T>) -> &'py Self {
986-
IntoPyArray::into_pyarray(vec, py)
982+
vec.into_pyarray(py)
987983
}
988984

989985
/// Construct one-dimension PyArray from a type which implements
@@ -1000,20 +996,8 @@ impl<T: Element> PyArray<T, Ix1> {
1000996
/// });
1001997
/// ```
1002998
pub fn from_exact_iter(py: Python<'_>, iter: impl ExactSizeIterator<Item = T>) -> &Self {
1003-
// NumPy will always zero-initialize object pointers,
1004-
// so the array can be dropped safely if the iterator panics.
1005-
unsafe {
1006-
let len = iter.len();
1007-
let array = Self::new(py, [len], false);
1008-
let mut idx = 0;
1009-
for item in iter {
1010-
assert!(idx < len);
1011-
array.uget_raw([idx]).write(item);
1012-
idx += 1;
1013-
}
1014-
assert!(idx == len);
1015-
array
1016-
}
999+
let data = iter.collect::<Box<[_]>>();
1000+
data.into_pyarray(py)
10171001
}
10181002

10191003
/// Construct one-dimension PyArray from a type which implements
@@ -1032,29 +1016,8 @@ impl<T: Element> PyArray<T, Ix1> {
10321016
/// });
10331017
/// ```
10341018
pub fn from_iter(py: Python<'_>, iter: impl IntoIterator<Item = T>) -> &Self {
1035-
let iter = iter.into_iter();
1036-
let (min_len, max_len) = iter.size_hint();
1037-
let mut capacity = max_len.unwrap_or_else(|| min_len.max(512 / mem::size_of::<T>()));
1038-
unsafe {
1039-
// NumPy will always zero-initialize object pointers,
1040-
// so the array can be dropped safely if the iterator panics.
1041-
let array = Self::new(py, [capacity], false);
1042-
let mut length = 0;
1043-
for (i, item) in iter.enumerate() {
1044-
length += 1;
1045-
if length > capacity {
1046-
capacity *= 2;
1047-
array
1048-
.resize(capacity)
1049-
.expect("PyArray::from_iter: Failed to allocate memory");
1050-
}
1051-
array.uget_raw([i]).write(item);
1052-
}
1053-
if capacity > length {
1054-
array.resize(length).unwrap()
1055-
}
1056-
array
1057-
}
1019+
let data = iter.into_iter().collect::<Vec<_>>();
1020+
data.into_pyarray(py)
10581021
}
10591022

10601023
/// Extends or trancates the length of 1 dimension PyArray.
@@ -1339,8 +1302,6 @@ impl<T: Element + AsPrimitive<f64>> PyArray<T, Ix1> {
13391302
mod tests {
13401303
use super::*;
13411304

1342-
use std::ops::Range;
1343-
13441305
#[test]
13451306
fn test_get_unchecked() {
13461307
pyo3::Python::with_gil(|py| {
@@ -1370,36 +1331,4 @@ mod tests {
13701331
py_run!(py, arr, "assert arr.dtype.hasobject");
13711332
});
13721333
}
1373-
1374-
struct InsincereIterator(Range<usize>, usize);
1375-
1376-
impl Iterator for InsincereIterator {
1377-
type Item = usize;
1378-
1379-
fn next(&mut self) -> Option<Self::Item> {
1380-
self.0.next()
1381-
}
1382-
}
1383-
1384-
impl ExactSizeIterator for InsincereIterator {
1385-
fn len(&self) -> usize {
1386-
self.1
1387-
}
1388-
}
1389-
1390-
#[test]
1391-
#[should_panic]
1392-
fn from_exact_iter_too_short() {
1393-
Python::with_gil(|py| {
1394-
PyArray::from_exact_iter(py, InsincereIterator(0..3, 5));
1395-
});
1396-
}
1397-
1398-
#[test]
1399-
#[should_panic]
1400-
fn from_exact_iter_too_long() {
1401-
Python::with_gil(|py| {
1402-
PyArray::from_exact_iter(py, InsincereIterator(0..5, 3));
1403-
});
1404-
}
14051334
}

src/convert.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
//! Defines conversion traits between Rust types and NumPy data types.
22
#![deny(missing_docs)]
33

4-
use std::{mem, os::raw::c_int};
4+
use std::{mem, os::raw::c_int, ptr};
55

66
use ndarray::{ArrayBase, Data, Dimension, IntoDimension, Ix1, OwnedRepr};
77
use pyo3::Python;
@@ -143,12 +143,12 @@ where
143143
let len = self.len();
144144
match self.order() {
145145
Some(order) if A::IS_COPY => {
146-
// if the array is contiguous, copy it by `copy_ptr`.
146+
// if the array is contiguous, copy it by `copy_nonoverlapping`.
147147
let strides = self.npy_strides();
148148
unsafe {
149149
let array =
150150
PyArray::new_(py, self.raw_dim(), strides.as_ptr(), order.to_flag());
151-
array.copy_ptr(self.as_ptr(), len);
151+
ptr::copy_nonoverlapping(self.as_ptr(), array.data(), len);
152152
array
153153
}
154154
}

0 commit comments

Comments
 (0)