Skip to content

HeapTypeExt and Protocol Methods Pointer #3819

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

Merged
merged 11 commits into from
Jul 24, 2022

Conversation

qingshi163
Copy link
Contributor

No description provided.

@qingshi163 qingshi163 changed the title [WIP] HeapTypeExt and Protocol Methods Pointer HeapTypeExt and Protocol Methods Pointer Jun 25, 2022
@qingshi163 qingshi163 requested a review from youknowone June 25, 2022 20:08
@qingshi163
Copy link
Contributor Author

@youknowone can you run the performance check? I think we may use week order read after replace AtomicCell to AtomicPtr

@qingshi163 qingshi163 marked this pull request as ready for review July 8, 2022 08:30
@youknowone
Copy link
Member

I am running tests. I used benchmark tests from https://github.com/python/pyperformance/tree/main/pyperformance/data-files/benchmarks
It has pyperf dependency. I manually installed it to my RUSTPYTHONPATH.

running command was:

./rustpython_main fannkuch.py -o reports/fannkuch_main.json

comparing command was:

python -m pyperf compare_to --table reports/*nbody*
$ python3 -m pyperf compare_to --table reports/*nbody*
+-----------+-------------------+----------------------+-------------------------+-------------------------+
| Benchmark | nbody_cpython3.10 | nbody_cpython3.9     | nbody_rustpython_3819   | nbody_rustpython_main   |
+===========+===================+======================+=========================+=========================+
| nbody     | 104 ms            | 101 ms: 1.03x faster | 1.78 sec: 17.16x slower | 1.80 sec: 17.36x slower |
+-----------+-------------------+----------------------+-------------------------+-------------------------+
$ python3 -m pyperf compare_to --table reports/*fannkuch*
+-----------+----------------------+----------------------+--------------------------+--------------------------+
| Benchmark | fannkuch_cpython3.10 | fannkuch_cpython3.9  | fannkuch_rustpython_3819 | fannkuch_rustpython_main |
+===========+======================+======================+==========================+==========================+
| fannkuch  | 352 ms               | 354 ms: 1.01x slower | 3.63 sec: 10.31x slower  | 3.66 sec: 10.41x slower  |
+-----------+----------------------+----------------------+--------------------------+--------------------------+
$ python3 -m pyperf compare_to --table reports/*scimark* 
+-------------------------+---------------------+-----------------------+-------------------------+-------------------------+
| Benchmark               | scimark_cpython3.10 | scimark_cpython3.9    | scimark_rustpython_3819 | scimark_rustpython_main |
+=========================+=====================+=======================+=========================+=========================+
| scimark_fft             | 291 ms              | 241 ms: 1.21x faster  | 3.82 sec: 13.11x slower | 3.87 sec: 13.28x slower |
+-------------------------+---------------------+-----------------------+-------------------------+-------------------------+
| scimark_lu              | 120 ms              | 115 ms: 1.05x faster  | 1.14 sec: 9.45x slower  | 1.14 sec: 9.49x slower  |
+-------------------------+---------------------+-----------------------+-------------------------+-------------------------+
| scimark_monte_carlo     | 75.0 ms             | 86.9 ms: 1.16x slower | 802 ms: 10.69x slower   | 799 ms: 10.65x slower   |
+-------------------------+---------------------+-----------------------+-------------------------+-------------------------+
| scimark_sor             | 143 ms              | 165 ms: 1.15x slower  | 1.69 sec: 11.83x slower | 1.71 sec: 11.92x slower |
+-------------------------+---------------------+-----------------------+-------------------------+-------------------------+
| scimark_sparse_mat_mult | 4.06 ms             | 3.47 ms: 1.17x faster | 44.0 ms: 10.84x slower  | 44.0 ms: 10.84x slower  |
+-------------------------+---------------------+-----------------------+-------------------------+-------------------------+
| Geometric mean          | (ref)               | 1.02x faster          | 11.12x slower           | 11.17x slower           |
+-------------------------+---------------------+-----------------------+-------------------------+-------------------------+

Self::number_general_op(number, other, inner_div, vm)
}),
..PyNumberMethods::NOT_IMPLEMENTED
macro_rules! atomic_func {
Copy link
Member

Choose a reason for hiding this comment

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

putting in type/slots.rs will be better to share the macro over all protocol implementations

Comment on lines -883 to -889
const AS_NUMBER: PyNumberMethods;

#[inline]
#[pyslot]
fn as_number(_zelf: &PyObject, _vm: &VirtualMachine) -> &'static PyNumberMethods {
&Self::AS_NUMBER
}
Copy link
Member

Choose a reason for hiding this comment

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

is this need to be changed?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

we need static variable, const variable is not globally static, it creates instance wherever is used.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

return static reference from a const variable gives me a warning with compiler.

}
}

static AS_NUMBER: PyNumberMethods = PyNumberMethods {
Copy link
Member

Choose a reason for hiding this comment

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

even if we make this out of trait, it still can be defined in fn as_number() method unless we need it as a pub

Comment on lines 12 to 13
};
use crate::{
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
};
use crate::{

@@ -1,189 +1,155 @@
use crossbeam_utils::atomic::AtomicCell;
use once_cell::sync::OnceCell;

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

pub number_methods: PyNumberMethods,
}

pub struct PointerSlot<T>(NonNull<T>);
Copy link
Member

Choose a reason for hiding this comment

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

What's the benefit of this type comparing to &'static?
They seem to guarantee same things.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It just hint the reference should either be static or from type, I don't know is it neccesary.

Comment on lines +115 to +225
impl<'a> From<&'a PyObject> for PyNumber<'a> {
fn from(obj: &'a PyObject) -> Self {
Copy link
Member

Choose a reason for hiding this comment

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

This change means having a PyNumber object doesn't guarantee it is an actual number object.
Do you intend to change it?
In that case, we'd better to carefully comment about it because it is counter-intutitive to rust sense.

I still think OnceCell for methods is an overkill because it can be safely managed only with properly designed types.

Copy link
Contributor Author

@qingshi163 qingshi163 Jul 11, 2022

Choose a reason for hiding this comment

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

I think PyNumber shouldn't garrentee anything as it could be int, float, subclass, complex or class with _int_, _float_ etc, so what the caller expect is depend on what method been called.

The reason I use OnceCell is because we always use the type id first to avoid method call and I think that is more common case like get int from PyInt

Copy link
Member

Choose a reason for hiding this comment

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

We will obtain an PyNumber object only when we need PyNumber operations, isn't it? The method can be obtained when we create PyNumber, not when retrieving methods from it. PyNumber itself is a Once. OnceCell in PyNumber is duplicated encoding of data.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

so maybe a function that will create PyNumber only if it is not exact type could be better?

Copy link
Member

Choose a reason for hiding this comment

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

I am sorry. I didn't understand well what you meant by "create PyNumber only if it is not exact type". could you help me to understand it?

I think we need to create a PyNumber type object only when we need to call methods() of it. In that case, we don't need OnceCell for PyNumber - because we will create a PyNumber object always with methods. If we don't need method, we can handle PyObjectRef instead of PyNumber.

Copy link
Member

Choose a reason for hiding this comment

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

because recent patches are adding number protocol implementations, quickly merging this one will be less painful in future.
I want to know how do you think about this OnceCell problem for now. You know about it the best.

Copy link
Member

Choose a reason for hiding this comment

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

@qingshi163 do you mind if I try to edit this PR to work this way? I want to listen your decision because you are the best professional for PyNumber.

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 am thinking we should remove OnceCell from PyNumber but keep From<&PyObject> trait.
Than we modify or create function under VirtualMachine(to_int, to_index, to_float, etc) for all the function that could short cut with type id and do the check there. so we will always call number methods if we created PyNumber.

We should always use functions under VirtualMachine instead PyNumber if we do not sure the type.

I'd like to keep From<&PyObject> is because PyNumber is not garuentee anything about the type. we should not check the type when creating it.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Sure you can modify the pr, I am quite busy and I want to fix the sre-engine first.

@youknowone
Copy link
Member

oh, I learned about those performance tools from @corona10, thank you!

@youknowone youknowone force-pushed the heaptypeext branch 2 times, most recently from 3884481 to e8fe8ea Compare July 23, 2022 08:19
@youknowone youknowone force-pushed the heaptypeext branch 2 times, most recently from d345c31 to 09c4fa3 Compare July 24, 2022 05:57
@youknowone
Copy link
Member

$ python3 -m pyperf compare_to --table reports/*nbody*
+-----------+-------------------+----------------------+-------------------------+-------------------------+
| Benchmark | nbody_cpython3.10 | nbody_cpython3.9     | nbody_rustpython_3819   | nbody_rustpython_main   |
+===========+===================+======================+=========================+=========================+
| nbody     | 103 ms            | 101 ms: 1.02x faster | 1.78 sec: 17.27x slower | 1.77 sec: 17.17x slower |
+-----------+-------------------+----------------------+-------------------------+-------------------------+

$ python3 -m pyperf compare_to --table reports/*fannkuch*
+-----------+----------------------+----------------------+--------------------------+--------------------------+
| Benchmark | fannkuch_cpython3.10 | fannkuch_cpython3.9  | fannkuch_rustpython_3819 | fannkuch_rustpython_main |
+===========+======================+======================+==========================+==========================+
| fannkuch  | 349 ms               | 348 ms: 1.00x faster | 3.59 sec: 10.31x slower  | 3.66 sec: 10.48x slower  |
+-----------+----------------------+----------------------+--------------------------+--------------------------+

$ python3 -m pyperf compare_to --table reports/*scimark* 
+-------------------------+---------------------+-----------------------+-------------------------+-------------------------+
| Benchmark               | scimark_cpython3.10 | scimark_cpython3.9    | scimark_rustpython_3819 | scimark_rustpython_main |
+=========================+=====================+=======================+=========================+=========================+
| scimark_fft             | 290 ms              | 239 ms: 1.21x faster  | 3.82 sec: 13.17x slower | 3.84 sec: 13.24x slower |
+-------------------------+---------------------+-----------------------+-------------------------+-------------------------+
| scimark_lu              | 120 ms              | 114 ms: 1.06x faster  | 1.13 sec: 9.41x slower  | 1.14 sec: 9.46x slower  |
+-------------------------+---------------------+-----------------------+-------------------------+-------------------------+
| scimark_monte_carlo     | 75.6 ms             | 86.1 ms: 1.14x slower | 790 ms: 10.45x slower   | 803 ms: 10.62x slower   |
+-------------------------+---------------------+-----------------------+-------------------------+-------------------------+
| scimark_sor             | 142 ms              | 164 ms: 1.16x slower  | 1.69 sec: 11.89x slower | 1.68 sec: 11.87x slower |
+-------------------------+---------------------+-----------------------+-------------------------+-------------------------+
| scimark_sparse_mat_mult | 4.04 ms             | 3.43 ms: 1.18x faster | 44.1 ms: 10.91x slower  | 44.2 ms: 10.95x slower  |
+-------------------------+---------------------+-----------------------+-------------------------+-------------------------+
| Geometric mean          | (ref)               | 1.03x faster          | 11.10x slower           | 11.16x slower           |
+-------------------------+---------------------+-----------------------+-------------------------+-------------------------+

$ python3 -m pyperf compare_to --table reports/*int*    
+-----------+-----------------+-----------------------+----------------------+----------------------+
| Benchmark | int_cpython3.10 | int_cpython3.9        | int_rustpython_3819  | int_rustpython_main  |
+===========+=================+=======================+======================+======================+
| int       | 47.8 ns         | 41.8 ns: 1.14x faster | 389 ns: 8.13x slower | 392 ns: 8.19x slower |
+-----------+-----------------+-----------------------+----------------------+----------------------+

$ cat int.py
import pyperf

def bench_int(loops):
    range_it = range(loops)
    t0 = pyperf.perf_counter()

    for _ in range_it:
        int(1.1)

    return pyperf.perf_counter() - t0

if __name__ == '__main__':
    DEFAULT_ITERATIONS=1000
    runner = pyperf.Runner()
    runner.metadata['description'] = "n-body benchmark"

    args = runner.parse_args()
    runner.bench_time_func('int', bench_int)

@youknowone
Copy link
Member

@qingshi163 could you review changes?

Copy link
Contributor Author

@qingshi163 qingshi163 left a comment

Choose a reason for hiding this comment

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

Great!

@youknowone youknowone merged commit 2e7ec1d into RustPython:main Jul 24, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants