Skip to content

extract::<i64>() failure is extremely slow #3182

@samuelcolvin

Description

@samuelcolvin

Bug Description

extracting an int is extremely slow when it fails.

Benchmarks:

test extract_int_downcast_fail    ... bench:           1 ns/iter (+/- 0)
test extract_int_downcast_success ... bench:           7 ns/iter (+/- 0)
test extract_int_extract_fail     ... bench:         114 ns/iter (+/- 8) 😱 
test extract_int_extract_success  ... bench:           7 ns/iter (+/- 0)
test extract_str_downcast_fail    ... bench:           1 ns/iter (+/- 0)
test extract_str_downcast_success ... bench:           3 ns/iter (+/- 0)
test extract_str_extract_fail     ... bench:          23 ns/iter (+/- 0)
test extract_str_extract_success  ... bench:           4 ns/iter (+/- 0)

I'm suggesting this difference is big enough to count as a bug, but happy to be corrected.

Steps to Reproduce

Benchmark Code
#[bench]
fn extract_str_extract_success(bench: &mut Bencher) {
    Python::with_gil(|py| {
        let s = PyString::new(py, "Hello, World!") as &PyAny;

        bench.iter(|| {
            let v = black_box(s).extract::<&str>().unwrap();
            black_box(v);
        });
    });
}

#[bench]
fn extract_str_extract_fail(bench: &mut Bencher) {
    Python::with_gil(|py| {
        let d = PyDict::new(py) as &PyAny;

        bench.iter(|| {
            match black_box(d).extract::<&str>() {
                Ok(v) => panic!("should err {}", v),
                Err(e) => black_box(e),
            }
        });
    });
}

#[bench]
fn extract_str_downcast_success(bench: &mut Bencher) {
    Python::with_gil(|py| {
        let s = PyString::new(py, "Hello, World!") as &PyAny;

        bench.iter(|| {
            let py_str = black_box(s).downcast::<PyString>().unwrap();
            let v = py_str.to_str().unwrap();
            black_box(v);
        });
    });
}

#[bench]
fn extract_str_downcast_fail(bench: &mut Bencher) {
    Python::with_gil(|py| {
        let d = PyDict::new(py) as &PyAny;

        bench.iter(|| {
            match black_box(d).downcast::<PyString>() {
                Ok(v) => panic!("should err {}", v),
                Err(e) => black_box(e),
            }
        });
    });
}

#[bench]
fn extract_int_extract_success(bench: &mut Bencher) {
    Python::with_gil(|py| {
        let int_obj: PyObject = 123.into_py(py);
        let int = int_obj.as_ref(py);

        bench.iter(|| {
            let v = black_box(int).extract::<i64>().unwrap();
            black_box(v);
        });
    });
}


#[bench]
fn extract_int_extract_fail(bench: &mut Bencher) {
    Python::with_gil(|py| {
        let d = PyDict::new(py) as &PyAny;

        bench.iter(|| {
            match black_box(d).extract::<i64>() {
                Ok(v) => panic!("should err {}", v),
                Err(e) => black_box(e),
            }
        });
    });
}

#[bench]
fn extract_int_downcast_success(bench: &mut Bencher) {
    Python::with_gil(|py| {
        let int_obj: PyObject = 123.into_py(py);
        let int = int_obj.as_ref(py);

        bench.iter(|| {
            let py_int = black_box(int).downcast::<PyInt>().unwrap();
            let v = py_int.extract::<i64>().unwrap();
            black_box(v);
        });
    });
}


#[bench]
fn extract_int_downcast_fail(bench: &mut Bencher) {
    Python::with_gil(|py| {
        let d = PyDict::new(py) as &PyAny;

        bench.iter(|| {
            match black_box(d).downcast::<PyInt>() {
                Ok(v) => panic!("should err {}", v),
                Err(e) => black_box(e),
            }
        });
    });
}

Backtrace

na

Your operating system and version

MacOS

Your Python version (python --version)

Python 3.10.5

Your Rust version (rustc --version)

rustc 1.68.0-nightly (4781233a7 2023-01-16)

Your PyO3 version

0.18.3

How did you install python? Did you use a virtualenv?

pyenv

Additional Info

No response

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions