Skip to content

Commit 0658032

Browse files
authored
Merge pull request RustPython#2221 from youknowone/tuple-opt
tuple optimization
2 parents 2a95f30 + 39856bc commit 0658032

File tree

12 files changed

+126
-106
lines changed

12 files changed

+126
-106
lines changed

derive/src/pystructseq.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ pub(crate) fn impl_pystruct_sequence(
3636
self.#field_names,
3737
vm,
3838
)),*];
39-
items.into()
39+
::rustpython_vm::obj::objtuple::PyTuple::_new(items.into_boxed_slice())
4040
}
4141
}
4242
};

vm/src/exceptions.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ impl PyBaseException {
5757
cause: PyRwLock::new(None),
5858
context: PyRwLock::new(None),
5959
suppress_context: AtomicCell::new(false),
60-
args: PyRwLock::new(PyTuple::from(args).into_ref(vm)),
60+
args: PyRwLock::new(PyTupleRef::with_elements(args, &vm.ctx)),
6161
}
6262
}
6363

@@ -68,7 +68,7 @@ impl PyBaseException {
6868

6969
#[pymethod(name = "__init__")]
7070
fn init(&self, args: PyFuncArgs, vm: &VirtualMachine) -> PyResult<()> {
71-
*self.args.write() = PyTuple::from(args.args).into_ref(vm);
71+
*self.args.write() = PyTupleRef::with_elements(args.args, &vm.ctx);
7272
Ok(())
7373
}
7474

@@ -80,7 +80,7 @@ impl PyBaseException {
8080
#[pyproperty(setter)]
8181
fn set_args(&self, args: PyIterable, vm: &VirtualMachine) -> PyResult<()> {
8282
let args = args.iter(vm)?.collect::<PyResult<Vec<_>>>()?;
83-
*self.args.write() = PyTuple::from(args).into_ref(vm);
83+
*self.args.write() = PyTupleRef::with_elements(args, &vm.ctx);
8484
Ok(())
8585
}
8686

vm/src/obj/objbytearray.rs

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ use crate::byteslike::PyBytesLike;
1717
use crate::common::cell::{PyRwLock, PyRwLockReadGuard, PyRwLockWriteGuard};
1818
use crate::function::{OptionalArg, OptionalOption};
1919
use crate::obj::objbytes::PyBytes;
20-
use crate::obj::objtuple::PyTuple;
20+
use crate::obj::objtuple::PyTupleRef;
2121
use crate::pyobject::{
2222
BorrowValue, Either, IdProtocol, IntoPyObject, PyClassImpl, PyComparisonValue, PyContext,
2323
PyIterable, PyObjectRef, PyRef, PyResult, PyValue, TryFromObject,
@@ -533,14 +533,21 @@ impl PyByteArray {
533533
}
534534

535535
#[pymethod(magic)]
536-
fn reduce_ex(zelf: PyRef<Self>, _proto: usize, vm: &VirtualMachine) -> (PyClassRef, PyTuple) {
536+
fn reduce_ex(
537+
zelf: PyRef<Self>,
538+
_proto: usize,
539+
vm: &VirtualMachine,
540+
) -> (PyClassRef, PyTupleRef) {
537541
Self::reduce(zelf, vm)
538542
}
539543

540544
#[pymethod(magic)]
541-
fn reduce(zelf: PyRef<Self>, vm: &VirtualMachine) -> (PyClassRef, PyTuple) {
545+
fn reduce(zelf: PyRef<Self>, vm: &VirtualMachine) -> (PyClassRef, PyTupleRef) {
542546
let bytes = PyBytes::from(zelf.borrow_value().elements.clone()).into_pyobject(vm);
543-
(Self::class(vm), PyTuple::from(vec![bytes]))
547+
(
548+
Self::class(vm),
549+
PyTupleRef::with_elements(vec![bytes], &vm.ctx),
550+
)
544551
}
545552
}
546553

vm/src/obj/objbytes.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ use crate::bytesinner::{
1515
};
1616
use crate::byteslike::PyBytesLike;
1717
use crate::function::{OptionalArg, OptionalOption};
18-
use crate::obj::objtuple::{PyTuple, PyTupleRef};
18+
use crate::obj::objtuple::PyTupleRef;
1919
use crate::pyobject::{
2020
BorrowValue, Either, IntoPyObject, PyClassImpl, PyComparisonValue, PyContext, PyIterable,
2121
PyObjectRef, PyRef, PyResult, PyValue, TryFromObject,
@@ -452,7 +452,7 @@ impl PyBytes {
452452
.iter()
453453
.map(|x| x.into_pyobject(vm))
454454
.collect();
455-
PyTuple::from(param).into_ref(vm)
455+
PyTupleRef::with_elements(param, &vm.ctx)
456456
}
457457
}
458458

vm/src/obj/objlist.rs

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ use crate::bytesinner;
1717
use crate::common::cell::{PyRwLock, PyRwLockReadGuard, PyRwLockWriteGuard};
1818
use crate::function::OptionalArg;
1919
use crate::pyobject::{
20-
BorrowValue, PyClassImpl, PyComparisonValue, PyContext, PyIterable, PyObjectRef, PyRef,
20+
BorrowValue, Either, PyClassImpl, PyComparisonValue, PyContext, PyIterable, PyObjectRef, PyRef,
2121
PyResult, PyValue, TryFromObject, TypeProtocol,
2222
};
2323
use crate::sequence::{self, SimpleSeq};
@@ -192,7 +192,12 @@ impl PyList {
192192

193193
#[pymethod(name = "__getitem__")]
194194
fn getitem(zelf: PyRef<Self>, needle: PyObjectRef, vm: &VirtualMachine) -> PyResult {
195-
get_item(vm, zelf.as_object(), &zelf.borrow_value(), needle)
195+
Ok(
196+
match get_item(vm, zelf.as_object(), &zelf.borrow_value(), needle)? {
197+
Either::A(obj) => obj,
198+
Either::B(vec) => vm.ctx.new_list(vec),
199+
},
200+
)
196201
}
197202

198203
#[pymethod(name = "__iter__")]

vm/src/obj/objrange.rs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ use num_traits::{One, Signed, Zero};
66
use super::objint::{PyInt, PyIntRef};
77
use super::objiter;
88
use super::objslice::{PySlice, PySliceRef};
9-
use super::objtuple::PyTuple;
109
use super::objtype::PyClassRef;
1110

1211
use crate::common::hash::PyHash;
@@ -244,12 +243,12 @@ impl PyRange {
244243
}
245244

246245
#[pymethod(name = "__reduce__")]
247-
fn reduce(&self, vm: &VirtualMachine) -> (PyClassRef, PyTuple) {
246+
fn reduce(&self, vm: &VirtualMachine) -> (PyClassRef, PyObjectRef) {
248247
let range_paramters: Vec<PyObjectRef> = vec![&self.start, &self.stop, &self.step]
249248
.iter()
250249
.map(|x| x.as_object().clone())
251250
.collect();
252-
let range_paramters_tuple = PyTuple::from(range_paramters);
251+
let range_paramters_tuple = vm.ctx.new_tuple(range_paramters);
253252
(vm.ctx.types.range_type.clone(), range_paramters_tuple)
254253
}
255254

vm/src/obj/objsequence.rs

Lines changed: 11 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,10 @@ use num_bigint::BigInt;
44
use num_traits::{One, Signed, ToPrimitive, Zero};
55

66
use super::objint::{PyInt, PyIntRef};
7-
use super::objlist::PyList;
87
use super::objsingletons::PyNone;
98
use super::objslice::{PySlice, PySliceRef};
10-
use super::objtuple::PyTuple;
119
use crate::function::OptionalArg;
12-
use crate::pyobject::{BorrowValue, PyObject, PyObjectRef, PyResult, TryFromObject, TypeProtocol};
10+
use crate::pyobject::{BorrowValue, Either, PyObjectRef, PyResult, TryFromObject, TypeProtocol};
1311
use crate::vm::VirtualMachine;
1412

1513
pub trait PySliceableSequenceMut {
@@ -465,38 +463,23 @@ pub fn get_item(
465463
sequence: &PyObjectRef,
466464
elements: &[PyObjectRef],
467465
subscript: PyObjectRef,
468-
) -> PyResult {
466+
) -> PyResult<Either<PyObjectRef, Vec<PyObjectRef>>> {
469467
if let Some(i) = subscript.payload::<PyInt>() {
470468
let value = i.borrow_value().to_isize().ok_or_else(|| {
471469
vm.new_index_error("cannot fit 'int' into an index-sized integer".to_owned())
472470
})?;
473471
let pos_index = get_pos(value, elements.len())
474472
.ok_or_else(|| vm.new_index_error("Index out of bounds!".to_owned()))?;
475-
return Ok(elements[pos_index].clone());
476-
}
477-
478-
let slice = subscript.payload::<PySlice>().ok_or_else(|| {
479-
vm.new_type_error(format!(
480-
"{} indices must be integers or slices",
481-
sequence.lease_class().name
482-
))
483-
})?;
484-
let items = if sequence.payload::<PyList>().is_some() {
485-
PyObject::new(
486-
PyList::from(elements.get_slice_items(vm, slice)?),
487-
sequence.class(),
488-
None,
489-
)
490-
} else if sequence.payload::<PyTuple>().is_some() {
491-
PyObject::new(
492-
PyTuple::from(elements.get_slice_items(vm, slice)?),
493-
sequence.class(),
494-
None,
495-
)
473+
Ok(Either::A(elements[pos_index].clone()))
496474
} else {
497-
panic!("sequence get_item called for non-sequence")
498-
};
499-
Ok(items)
475+
let slice = subscript.payload::<PySlice>().ok_or_else(|| {
476+
vm.new_type_error(format!(
477+
"{} indices must be integers or slices",
478+
sequence.lease_class().name
479+
))
480+
})?;
481+
Ok(Either::B(elements.get_slice_items(vm, slice)?))
482+
}
500483
}
501484

502485
//Check if given arg could be used with PySliceableSequence.get_slice_range()

vm/src/obj/objtuple.rs

Lines changed: 64 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,8 @@ use super::objsequence::get_item;
66
use super::objtype::PyClassRef;
77
use crate::function::OptionalArg;
88
use crate::pyobject::{
9-
self, BorrowValue, IdProtocol, IntoPyObject,
10-
PyArithmaticValue::{self, *},
11-
PyClassImpl, PyComparisonValue, PyContext, PyObjectRef, PyRef, PyResult, PyValue,
9+
self, BorrowValue, Either, IdProtocol, IntoPyObject, PyArithmaticValue, PyClassImpl,
10+
PyComparisonValue, PyContext, PyObjectRef, PyRef, PyResult, PyValue, TypeProtocol,
1211
};
1312
use crate::sequence::{self, SimpleSeq};
1413
use crate::slots::{Comparable, Hashable, PyComparisonOp};
@@ -21,7 +20,7 @@ use rustpython_common::hash::PyHash;
2120
/// If the argument is a tuple, the return value is the same object.
2221
#[pyclass(module = false, name = "tuple")]
2322
pub struct PyTuple {
24-
elements: Vec<PyObjectRef>,
23+
elements: Box<[PyObjectRef]>,
2524
}
2625

2726
impl fmt::Debug for PyTuple {
@@ -31,12 +30,6 @@ impl fmt::Debug for PyTuple {
3130
}
3231
}
3332

34-
impl From<Vec<PyObjectRef>> for PyTuple {
35-
fn from(elements: Vec<PyObjectRef>) -> Self {
36-
PyTuple { elements }
37-
}
38-
}
39-
4033
impl<'a> BorrowValue<'a> for PyTuple {
4134
type Borrowed = &'a [PyObjectRef];
4235

@@ -77,25 +70,53 @@ impl PyTuple {
7770

7871
pub type PyTupleRef = PyRef<PyTuple>;
7972

73+
impl PyTupleRef {
74+
pub(crate) fn with_elements(elements: Vec<PyObjectRef>, ctx: &PyContext) -> Self {
75+
if elements.is_empty() {
76+
ctx.empty_tuple.clone()
77+
} else {
78+
let elements = elements.into_boxed_slice();
79+
Self::new_ref(PyTuple { elements }, ctx.types.tuple_type.clone(), None)
80+
}
81+
}
82+
}
83+
8084
pub(crate) fn get_value(obj: &PyObjectRef) -> &[PyObjectRef] {
8185
obj.payload::<PyTuple>().unwrap().borrow_value()
8286
}
8387

8488
#[pyimpl(flags(BASETYPE), with(Hashable, Comparable))]
8589
impl PyTuple {
90+
/// Creating a new tuple with given boxed slice.
91+
/// NOTE: for usual case, you probably want to use PyTupleRef::with_elements.
92+
/// Calling this function implies trying micro optimization for non-zero-sized tuple.
93+
pub(crate) fn _new(elements: Box<[PyObjectRef]>) -> Self {
94+
Self { elements }
95+
}
96+
8697
#[pymethod(name = "__add__")]
87-
fn add(&self, other: PyObjectRef, vm: &VirtualMachine) -> PyArithmaticValue<PyTuple> {
88-
if let Some(other) = other.payload_if_subclass::<PyTuple>(vm) {
89-
let elements: Vec<_> = self
90-
.elements
91-
.boxed_iter()
92-
.chain(other.borrow_value().boxed_iter())
93-
.cloned()
94-
.collect();
95-
Implemented(elements.into())
96-
} else {
97-
NotImplemented
98-
}
98+
fn add(
99+
zelf: PyRef<Self>,
100+
other: PyObjectRef,
101+
vm: &VirtualMachine,
102+
) -> PyArithmaticValue<PyRef<Self>> {
103+
let added = other.downcast::<Self>().map(|other| {
104+
if other.elements.is_empty() && zelf.class().is(&vm.ctx.types.tuple_type) {
105+
zelf
106+
} else if zelf.elements.is_empty() && other.class().is(&vm.ctx.types.tuple_type) {
107+
other
108+
} else {
109+
let elements = zelf
110+
.elements
111+
.boxed_iter()
112+
.chain(other.borrow_value().boxed_iter())
113+
.cloned()
114+
.collect::<Vec<_>>()
115+
.into_boxed_slice();
116+
Self { elements }.into_ref(vm)
117+
}
118+
});
119+
PyArithmaticValue::from_option(added.ok())
99120
}
100121

101122
#[pymethod(name = "__bool__")]
@@ -149,16 +170,26 @@ impl PyTuple {
149170

150171
#[pymethod(name = "__mul__")]
151172
#[pymethod(name = "__rmul__")]
152-
fn mul(&self, counter: isize) -> PyTuple {
153-
let new_elements: Vec<_> = sequence::seq_mul(&self.elements, counter)
154-
.cloned()
155-
.collect();
156-
new_elements.into()
173+
fn mul(&self, counter: isize, vm: &VirtualMachine) -> PyRef<Self> {
174+
if self.elements.is_empty() || counter == 0 {
175+
vm.ctx.empty_tuple.clone()
176+
} else {
177+
let elements = sequence::seq_mul(&self.elements, counter)
178+
.cloned()
179+
.collect::<Vec<_>>()
180+
.into_boxed_slice();
181+
Self { elements }.into_ref(vm)
182+
}
157183
}
158184

159185
#[pymethod(name = "__getitem__")]
160186
fn getitem(zelf: PyRef<Self>, needle: PyObjectRef, vm: &VirtualMachine) -> PyResult {
161-
get_item(vm, zelf.as_object(), &zelf.elements, needle)
187+
Ok(
188+
match get_item(vm, zelf.as_object(), &zelf.elements, needle)? {
189+
Either::A(obj) => obj,
190+
Either::B(vec) => vm.ctx.new_tuple(vec),
191+
},
192+
)
162193
}
163194

164195
#[pymethod(name = "index")]
@@ -186,10 +217,10 @@ impl PyTuple {
186217
cls: PyClassRef,
187218
iterable: OptionalArg<PyObjectRef>,
188219
vm: &VirtualMachine,
189-
) -> PyResult<PyTupleRef> {
220+
) -> PyResult<PyRef<Self>> {
190221
let elements = if let OptionalArg::Present(iterable) = iterable {
191222
let iterable = if cls.is(&vm.ctx.types.tuple_type) {
192-
match iterable.downcast_exact::<PyTuple>(vm) {
223+
match iterable.downcast_exact::<Self>(vm) {
193224
Ok(tuple) => return Ok(tuple),
194225
Err(iterable) => iterable,
195226
}
@@ -199,9 +230,10 @@ impl PyTuple {
199230
vm.extract_elements(&iterable)?
200231
} else {
201232
vec![]
202-
};
233+
}
234+
.into_boxed_slice();
203235

204-
PyTuple::from(elements).into_ref_with_type(vm, cls)
236+
Self { elements }.into_ref_with_type(vm, cls)
205237
}
206238
}
207239

vm/src/obj/objtype.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -185,7 +185,7 @@ impl PyClass {
185185
#[pyproperty(name = "__mro__")]
186186
fn get_mro(zelf: PyRef<Self>) -> PyTuple {
187187
let elements: Vec<PyObjectRef> = zelf.iter_mro().map(|x| x.as_object().clone()).collect();
188-
PyTuple::from(elements)
188+
PyTuple::_new(elements.into_boxed_slice())
189189
}
190190

191191
#[pyproperty(magic)]

0 commit comments

Comments
 (0)