Skip to content

Small int cache like CPython #1502

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 2 commits into from
Oct 10, 2019
Merged
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
14 changes: 7 additions & 7 deletions tests/snippets/bools.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,12 @@

assert not None

assert bool() == False
assert bool(1) == True
assert bool({}) == False
assert bool() is False
assert bool(1) is True
assert bool({}) is False

assert bool(NotImplemented) == True
assert bool(...) == True
assert bool(NotImplemented) is True
assert bool(...) is True

if not 1:
raise BaseException
Expand Down Expand Up @@ -105,8 +105,8 @@ def __len__(self):
return 1


assert bool(TestMagicMethodLenZero()) == False
assert bool(TestMagicMethodLenOne()) == True
assert bool(TestMagicMethodLenZero()) is False
assert bool(TestMagicMethodLenOne()) is True


# check __len__ and __bool__
Expand Down
17 changes: 11 additions & 6 deletions tests/snippets/ints.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,12 @@

# magic methods should only be implemented for other ints

assert (1).__eq__(1) == True
assert (1).__ne__(1) == False
assert (1).__gt__(1) == False
assert (1).__ge__(1) == True
assert (1).__lt__(1) == False
assert (1).__le__(1) == True
assert (1).__eq__(1) is True
assert (1).__ne__(1) is False
assert (1).__gt__(1) is False
assert (1).__ge__(1) is True
assert (1).__lt__(1) is False
assert (1).__le__(1) is True
assert (1).__add__(1) == 2
assert (1).__radd__(1) == 2
assert (2).__sub__(1) == 1
Expand Down Expand Up @@ -306,3 +306,8 @@ def __int__(self):

with assert_raises(SyntaxError):
exec(src)

# Small int cache in [-5..256]
assert 1 is 1 # noqa
x = 6
assert 5 is (x-1) # noqa
22 changes: 19 additions & 3 deletions vm/src/pyobject.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use std::rc::Rc;

use num_bigint::BigInt;
use num_complex::Complex64;
use num_traits::{One, Zero};
use num_traits::{One, ToPrimitive, Zero};

use crate::bytecode;
use crate::dictdatatype::DictKey;
Expand Down Expand Up @@ -92,6 +92,9 @@ impl fmt::Display for PyObject<dyn PyObjectPayload> {
}
}

const INT_CACHE_POOL_MIN: i32 = -5;
const INT_CACHE_POOL_MAX: i32 = 256;

#[derive(Debug)]
pub struct PyContext {
pub true_value: PyIntRef,
Expand All @@ -104,6 +107,7 @@ pub struct PyContext {

pub types: TypeZoo,
pub exceptions: exceptions::ExceptionZoo,
pub int_cache_pool: Vec<PyObjectRef>,
}

pub type PyNotImplementedRef = PyRef<PyNotImplemented>;
Expand Down Expand Up @@ -149,6 +153,10 @@ impl PyContext {
create_type("NotImplementedType", &types.type_type, &types.object_type);
let not_implemented = create_object(PyNotImplemented, &not_implemented_type);

let int_cache_pool = (INT_CACHE_POOL_MIN..=INT_CACHE_POOL_MAX)
.map(|v| create_object(PyInt::new(BigInt::from(v)), &types.int_type).into_object())
.collect();

let true_value = create_object(PyInt::new(BigInt::one()), &types.bool_type);
let false_value = create_object(PyInt::new(BigInt::zero()), &types.bool_type);

Expand All @@ -159,12 +167,13 @@ impl PyContext {
false_value,
not_implemented,
none,
empty_tuple,
ellipsis,
ellipsis_type,

types,
exceptions,
empty_tuple,
int_cache_pool,
};
initialize_types(&context);

Expand Down Expand Up @@ -364,7 +373,14 @@ impl PyContext {
self.types.object_type.clone()
}

pub fn new_int<T: Into<BigInt>>(&self, i: T) -> PyObjectRef {
#[inline]
pub fn new_int<T: Into<BigInt> + ToPrimitive>(&self, i: T) -> PyObjectRef {
if let Some(i) = i.to_i32() {
if i >= INT_CACHE_POOL_MIN && i <= INT_CACHE_POOL_MAX {
let inner_idx = (i - INT_CACHE_POOL_MIN) as usize;
return self.int_cache_pool[inner_idx].clone();
}
}
PyObject::new(PyInt::new(i), self.int_type(), None)
}

Expand Down
5 changes: 4 additions & 1 deletion vm/src/vm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ use crate::stdlib;
use crate::sysmodule;
use arr_macro::arr;
use num_bigint::BigInt;
use num_traits::ToPrimitive;
#[cfg(feature = "rustpython-compiler")]
use rustpython_compiler::{compile, error::CompileError};

Expand Down Expand Up @@ -293,11 +294,13 @@ impl VirtualMachine {
}

/// Create a new python int object.
pub fn new_int<T: Into<BigInt>>(&self, i: T) -> PyObjectRef {
#[inline]
pub fn new_int<T: Into<BigInt> + ToPrimitive>(&self, i: T) -> PyObjectRef {
self.ctx.new_int(i)
}

/// Create a new python bool object.
#[inline]
pub fn new_bool(&self, b: bool) -> PyObjectRef {
self.ctx.new_bool(b)
}
Expand Down