Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

experimental: owned objects API #1308

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ hashbrown = { version = "0.9", optional = true }
assert_approx_eq = "1.1.0"
trybuild = "1.0.23"
rustversion = "1.0"
maplit = "1.0"

[features]
default = ["macros"]
Expand Down
2 changes: 2 additions & 0 deletions benches/bench_call.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ fn bench_call_0(b: &mut Bencher) {
let foo = module.getattr("foo").unwrap();

b.iter(|| {
let _pool = unsafe { py.new_pool() };
for _ in 0..1000 {
foo.call0().unwrap();
}
Expand All @@ -45,6 +46,7 @@ fn bench_call_method_0(b: &mut Bencher) {
let foo = module.getattr("Foo").unwrap().call0().unwrap();

b.iter(|| {
let _pool = unsafe { py.new_pool() };
for _ in 0..1000 {
foo.call_method0("foo").unwrap();
}
Expand Down
17 changes: 14 additions & 3 deletions benches/bench_dict.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ fn iter_dict(b: &mut Bencher) {
let dict = (0..LEN as u64).map(|i| (i, i * 2)).into_py_dict(py);
let mut sum = 0;
b.iter(|| {
let _pool = unsafe { py.new_pool() };
for (k, _v) in dict.iter() {
let i: u64 = k.extract().unwrap();
sum += i;
Expand All @@ -29,6 +30,7 @@ fn dict_get_item(b: &mut Bencher) {
let dict = (0..LEN as u64).map(|i| (i, i * 2)).into_py_dict(py);
let mut sum = 0;
b.iter(|| {
let _pool = unsafe { py.new_pool() };
for i in 0..LEN {
sum += dict.get_item(i).unwrap().extract::<usize>().unwrap();
}
Expand All @@ -41,7 +43,10 @@ fn extract_hashmap(b: &mut Bencher) {
let py = gil.python();
const LEN: usize = 100_000;
let dict = (0..LEN as u64).map(|i| (i, i * 2)).into_py_dict(py);
b.iter(|| HashMap::<u64, u64>::extract(dict));
b.iter(|| {
let _pool = unsafe { py.new_pool() };
HashMap::<u64, u64>::extract(dict)
});
}

#[bench]
Expand All @@ -50,7 +55,10 @@ fn extract_btreemap(b: &mut Bencher) {
let py = gil.python();
const LEN: usize = 100_000;
let dict = (0..LEN as u64).map(|i| (i, i * 2)).into_py_dict(py);
b.iter(|| BTreeMap::<u64, u64>::extract(dict));
b.iter(|| {
let _pool = unsafe { py.new_pool() };
BTreeMap::<u64, u64>::extract(dict)
});
}

#[bench]
Expand All @@ -60,5 +68,8 @@ fn extract_hashbrown_map(b: &mut Bencher) {
let py = gil.python();
const LEN: usize = 100_000;
let dict = (0..LEN as u64).map(|i| (i, i * 2)).into_py_dict(py);
b.iter(|| hashbrown::HashMap::<u64, u64>::extract(dict));
b.iter(|| {
let _pool = unsafe { py.new_pool() };
hashbrown::HashMap::<u64, u64>::extract(dict)
});
}
214 changes: 214 additions & 0 deletions benches/bench_experimental.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,214 @@
#![feature(test)]

extern crate test;
use pyo3::experimental::objects::{IntoPyDict, PyList, PySet, PyTuple};
use pyo3::experimental::prelude::*;
use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet};
use test::Bencher;

macro_rules! test_module {
($py:ident, $code:literal) => {
PyModule::from_code($py, indoc::indoc!($code), file!(), "test_module")
.expect("module creation failed")
};
}

#[bench]
fn iter_dict(b: &mut Bencher) {
let gil = Python::acquire_gil();
let py = gil.python();
const LEN: usize = 100_000;
let dict = (0..LEN as u64).map(|i| (i, i * 2)).into_py_dict(py);
let mut sum = 0;
b.iter(|| {
for (k, _v) in dict.iter() {
let i: u64 = k.extract().unwrap();
sum += i;
}
});
}

#[bench]
fn dict_get_item(b: &mut Bencher) {
let gil = Python::acquire_gil();
let py = gil.python();
const LEN: usize = 50_000;
let dict = (0..LEN as u64).map(|i| (i, i * 2)).into_py_dict(py);
let dict_ref = &*dict;
let mut sum = 0;
b.iter(|| {
for i in 0..LEN {
sum += dict_ref.get_item(i).unwrap().extract::<u64>().unwrap();
}
});
}

#[bench]
fn extract_hashmap(b: &mut Bencher) {
let gil = Python::acquire_gil();
let py = gil.python();
const LEN: usize = 100_000;
let dict = (0..LEN as u64).map(|i| (i, i * 2)).into_py_dict(py);
b.iter(|| HashMap::<u64, u64>::extract(&dict));
}

#[bench]
fn extract_btreemap(b: &mut Bencher) {
let gil = Python::acquire_gil();
let py = gil.python();
const LEN: usize = 100_000;
let dict = (0..LEN as u64).map(|i| (i, i * 2)).into_py_dict(py);
b.iter(|| BTreeMap::<u64, u64>::extract(&dict));
}

#[bench]
#[cfg(feature = "hashbrown")]
fn extract_hashbrown_map(b: &mut Bencher) {
let gil = Python::acquire_gil();
let py = gil.python();
const LEN: usize = 100_000;
let dict = (0..LEN as u64).map(|i| (i, i * 2)).into_py_dict(py);
b.iter(|| hashbrown::HashMap::<u64, u64>::extract(dict));
}

#[bench]
fn bench_call_0(b: &mut Bencher) {
Python::with_gil(|py| {
let module = test_module!(
py,
r#"
def foo(): pass
"#
);

let foo = module.getattr("foo").unwrap();

b.iter(|| {
for _ in 0..1000 {
foo.call0().unwrap();
}
});
})
}

#[bench]
fn bench_call_method_0(b: &mut Bencher) {
Python::with_gil(|py| {
let module = test_module!(
py,
r#"
class Foo:
def foo(self): pass
"#
);

let foo = module.getattr("Foo").unwrap().call0().unwrap();

b.iter(|| {
for _ in 0..1000 {
foo.call_method0("foo").unwrap();
}
});
})
}

#[bench]
fn iter_list(b: &mut Bencher) {
let gil = Python::acquire_gil();
let py = gil.python();
const LEN: usize = 100_000;
let list = PyList::new(py, 0..LEN);
let mut sum = 0;
b.iter(|| {
for x in list.iter() {
let i: u64 = x.extract().unwrap();
sum += i;
}
});
}

#[bench]
fn list_get_item(b: &mut Bencher) {
let gil = Python::acquire_gil();
let py = gil.python();
const LEN: usize = 50_000;
let list = PyList::new(py, 0..LEN);
let mut sum = 0;
b.iter(|| {
for i in 0..LEN {
sum += list.get_item(i as isize).extract::<usize>().unwrap();
}
});
}

#[bench]
fn iter_set(b: &mut Bencher) {
let gil = Python::acquire_gil();
let py = gil.python();
const LEN: usize = 100_000;
let set = PySet::new(py, &(0..LEN).collect::<Vec<_>>()).unwrap();
let mut sum = 0;
b.iter(|| {
for x in set.iter() {
let i: u64 = x.extract().unwrap();
sum += i;
}
});
}

#[bench]
fn extract_hashset(b: &mut Bencher) {
let gil = Python::acquire_gil();
let py = gil.python();
const LEN: usize = 100_000;
let set = PySet::new(py, &(0..LEN).collect::<Vec<_>>()).unwrap();
b.iter(|| HashSet::<u64>::extract(&set));
}

#[bench]
fn extract_btreeset(b: &mut Bencher) {
let gil = Python::acquire_gil();
let py = gil.python();
const LEN: usize = 100_000;
let set = PySet::new(py, &(0..LEN).collect::<Vec<_>>()).unwrap();
b.iter(|| BTreeSet::<u64>::extract(&set));
}

#[bench]
#[cfg(feature = "hashbrown")]
fn extract_hashbrown_set(b: &mut Bencher) {
let gil = Python::acquire_gil();
let py = gil.python();
const LEN: usize = 100_000;
let set = PySet::new(py, &(0..LEN).collect::<Vec<_>>()).unwrap();
b.iter(|| hashbrown::HashSet::<u64>::extract(set));
}

#[bench]
fn iter_tuple(b: &mut Bencher) {
let gil = Python::acquire_gil();
let py = gil.python();
const LEN: usize = 100_000;
let tuple = PyTuple::new(py, 0..LEN);
let mut sum = 0;
b.iter(|| {
for x in tuple.iter() {
let i: u64 = x.extract().unwrap();
sum += i;
}
});
}

#[bench]
fn tuple_get_item(b: &mut Bencher) {
let gil = Python::acquire_gil();
let py = gil.python();
const LEN: usize = 50_000;
let tuple = PyTuple::new(py, 0..LEN);
let mut sum = 0;
b.iter(|| {
for i in 0..LEN {
sum += tuple.get_item(i).extract::<usize>().unwrap();
}
});
}
2 changes: 2 additions & 0 deletions benches/bench_list.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ fn iter_list(b: &mut Bencher) {
let list = PyList::new(py, 0..LEN);
let mut sum = 0;
b.iter(|| {
let _pool = unsafe { py.new_pool() };
for x in list.iter() {
let i: u64 = x.extract().unwrap();
sum += i;
Expand All @@ -28,6 +29,7 @@ fn list_get_item(b: &mut Bencher) {
let list = PyList::new(py, 0..LEN);
let mut sum = 0;
b.iter(|| {
let _pool = unsafe { py.new_pool() };
for i in 0..LEN {
sum += list.get_item(i as isize).extract::<usize>().unwrap();
}
Expand Down
16 changes: 13 additions & 3 deletions benches/bench_set.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ fn iter_set(b: &mut Bencher) {
let set = PySet::new(py, &(0..LEN).collect::<Vec<_>>()).unwrap();
let mut sum = 0;
b.iter(|| {
let _pool = unsafe { py.new_pool() };
for x in set.iter() {
let i: u64 = x.extract().unwrap();
sum += i;
Expand All @@ -27,7 +28,10 @@ fn extract_hashset(b: &mut Bencher) {
let py = gil.python();
const LEN: usize = 100_000;
let set = PySet::new(py, &(0..LEN).collect::<Vec<_>>()).unwrap();
b.iter(|| HashSet::<u64>::extract(set));
b.iter(|| {
let _pool = unsafe { py.new_pool() };
HashSet::<u64>::extract(set)
});
}

#[bench]
Expand All @@ -36,7 +40,10 @@ fn extract_btreeset(b: &mut Bencher) {
let py = gil.python();
const LEN: usize = 100_000;
let set = PySet::new(py, &(0..LEN).collect::<Vec<_>>()).unwrap();
b.iter(|| BTreeSet::<u64>::extract(set));
b.iter(|| {
let _pool = unsafe { py.new_pool() };
BTreeSet::<u64>::extract(set)
});
}

#[bench]
Expand All @@ -46,5 +53,8 @@ fn extract_hashbrown_set(b: &mut Bencher) {
let py = gil.python();
const LEN: usize = 100_000;
let set = PySet::new(py, &(0..LEN).collect::<Vec<_>>()).unwrap();
b.iter(|| hashbrown::HashSet::<u64>::extract(set));
b.iter(|| {
let _pool = unsafe { py.new_pool() };
hashbrown::HashSet::<u64>::extract(set)
});
}
2 changes: 2 additions & 0 deletions benches/bench_tuple.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ fn iter_tuple(b: &mut Bencher) {
let tuple = PyTuple::new(py, 0..LEN);
let mut sum = 0;
b.iter(|| {
let _pool = unsafe { py.new_pool() };
for x in tuple.iter() {
let i: u64 = x.extract().unwrap();
sum += i;
Expand All @@ -28,6 +29,7 @@ fn tuple_get_item(b: &mut Bencher) {
let tuple = PyTuple::new(py, 0..LEN);
let mut sum = 0;
b.iter(|| {
let _pool = unsafe { py.new_pool() };
for i in 0..LEN {
sum += tuple.get_item(i).extract::<usize>().unwrap();
}
Expand Down
14 changes: 14 additions & 0 deletions guide/src/experimental_objects.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# Experimental: Objects

Practical differences:
- No longer possible to extract `HashMap<&str, &str>` (e.g.) from a PyDict - these strings cannot be guaranteed to be safe to borrow without pyo3 owned references. Instead you should use `HashMap<String, String>`.
- Iterators from PyOwned must now be prefixed with &* - e.g. `in set` -> `in &*set`
- return values `&'py PyAny` -> `PyOwned<'py, Any>`
- Distinction between _types_ `Any` and _objects_ `PyAny`.

- PyString -> PyStr
- PyLong -> PyInt

TODO:
- Might want to create a new Python type which returns the new signatures from e.g. pyo3::experimental. This might be too painful.
- Probably provide "experimental" forms of all the macros to make the migration possible.
2 changes: 1 addition & 1 deletion pyo3-derive-backend/src/pymethod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -481,7 +481,7 @@ fn impl_arg_param(

if spec.is_args(&name) {
return quote! {
let #arg_name = <#ty as pyo3::FromPyObject>::extract(_args.as_ref())
let #arg_name = <#ty as pyo3::FromPyObject>::extract(_args)
.map_err(#transform_error)?;
};
} else if spec.is_kwargs(&name) {
Expand Down
Loading