Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
C
cython
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
Gwenaël Samain
cython
Commits
160949a7
Commit
160949a7
authored
Mar 28, 2015
by
Stefan Behnel
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
optimise truediv/floordiv for C long sized Python integers and truediv for floats
parent
03f88fb4
Changes
6
Hide whitespace changes
Inline
Side-by-side
Showing
6 changed files
with
150 additions
and
14 deletions
+150
-14
CHANGES.rst
CHANGES.rst
+2
-2
Cython/Compiler/Optimize.py
Cython/Compiler/Optimize.py
+20
-1
Cython/Compiler/Visitor.py
Cython/Compiler/Visitor.py
+5
-1
Cython/Utility/Optimize.c
Cython/Utility/Optimize.c
+55
-10
tests/run/future_division.pyx
tests/run/future_division.pyx
+34
-0
tests/run/non_future_division.pyx
tests/run/non_future_division.pyx
+34
-0
No files found.
CHANGES.rst
View file @
160949a7
...
...
@@ -19,8 +19,8 @@ Features added
* Adding/subtracting constant Python floats and small integers is faster.
* Binary and/or/xor/rshift operations and
modulus with small constant Python
integers are faster.
* Binary and/or/xor/rshift operations and
division/modulus with small
constant Python
integers are faster.
Bugs fixed
----------
...
...
Cython/Compiler/Optimize.py
View file @
160949a7
...
...
@@ -2825,6 +2825,25 @@ class OptimizeBuiltinCalls(Visitor.NodeRefCleanupMixin,
return
node
return
self
.
_optimise_num_binop
(
'Remainder'
,
node
,
function
,
args
,
is_unbound_method
)
def
_handle_simple_method_object___floordiv__
(
self
,
node
,
function
,
args
,
is_unbound_method
):
return
self
.
_optimise_num_div
(
'FloorDivide'
,
node
,
function
,
args
,
is_unbound_method
)
def
_handle_simple_method_object___truediv__
(
self
,
node
,
function
,
args
,
is_unbound_method
):
return
self
.
_optimise_num_div
(
'TrueDivide'
,
node
,
function
,
args
,
is_unbound_method
)
def
_optimise_num_div
(
self
,
operator
,
node
,
function
,
args
,
is_unbound_method
):
if
len
(
args
)
!=
2
or
not
args
[
1
].
has_constant_result
()
or
args
[
1
].
constant_result
==
0
:
return
node
if
isinstance
(
args
[
1
],
ExprNodes
.
IntNode
):
if
not
(
-
2
**
30
<=
args
[
1
].
constant_result
<=
2
**
30
):
return
node
elif
isinstance
(
args
[
1
],
ExprNodes
.
FloatNode
):
if
not
(
-
2
**
53
<=
args
[
1
].
constant_result
<=
2
**
53
):
return
node
else
:
return
node
return
self
.
_optimise_num_binop
(
operator
,
node
,
function
,
args
,
is_unbound_method
)
def
_handle_simple_method_float___add__
(
self
,
node
,
function
,
args
,
is_unbound_method
):
return
self
.
_optimise_num_binop
(
'Add'
,
node
,
function
,
args
,
is_unbound_method
)
...
...
@@ -2861,7 +2880,7 @@ class OptimizeBuiltinCalls(Visitor.NodeRefCleanupMixin,
is_float
=
isinstance
(
numval
,
ExprNodes
.
FloatNode
)
if
is_float
:
if
operator
not
in
(
'Add'
,
'Subtract'
):
if
operator
not
in
(
'Add'
,
'Subtract'
,
'TrueDivide'
):
return
node
elif
abs
(
numval
.
constant_result
)
>
2
**
30
:
return
node
...
...
Cython/Compiler/Visitor.py
View file @
160949a7
...
...
@@ -14,6 +14,7 @@ from . import Nodes
from
.
import
ExprNodes
from
.
import
Errors
from
.
import
DebugFlags
from
.
import
Future
import
cython
...
...
@@ -434,7 +435,7 @@ find_special_method_for_binary_operator = {
'>'
:
'__gt__'
,
'+'
:
'__add__'
,
'&'
:
'__and__'
,
'/'
:
'__
true
div__'
,
'/'
:
'__div__'
,
'//'
:
'__floordiv__'
,
'<<'
:
'__lshift__'
,
'%'
:
'__mod__'
,
...
...
@@ -515,6 +516,9 @@ class MethodDispatcherTransform(EnvTransform):
operand1
,
operand2
=
node
.
operand1
,
node
.
operand2
if
special_method_name
==
'__contains__'
:
operand1
,
operand2
=
operand2
,
operand1
elif
special_method_name
==
'__div__'
:
if
Future
.
division
in
self
.
current_env
().
global_scope
().
context
.
future_directives
:
special_method_name
=
'__truediv__'
obj_type
=
operand1
.
type
if
obj_type
.
is_builtin_type
:
type_name
=
obj_type
.
name
...
...
Cython/Utility/Optimize.c
View file @
160949a7
...
...
@@ -495,13 +495,17 @@ static PyObject* __Pyx_PyInt_{{op}}{{order}}(PyObject *op1, PyObject *op2, long
#if CYTHON_COMPILING_IN_CPYTHON
{{
py
:
from
Cython
.
Utility
import
pylong_join
}}
{{
py
:
pyval
,
ival
=
(
'
op2
'
,
'b'
)
if
order
==
'
CObj
'
else
(
'
op1
'
,
'a'
)
}}
{{
py
:
c_op
=
{
'
Add
'
:
'+'
,
'
Subtract
'
:
'-'
,
'
Remainder
'
:
'%'
,
'
Or
'
:
'|'
,
'
Xor
'
:
'^'
,
'
And
'
:
'&'
,
'
Rshift
'
:
'
>>
'
}[
op
]
}}
{{
py
:
slot_name
=
{
'
TrueDivide
'
:
'
true_divide
'
,
'
FloorDivide
'
:
'
floor_divide
'
}.
get
(
op
,
op
.
lower
())
}}
{{
py
:
c_op
=
{
'
Add
'
:
'+'
,
'
Subtract
'
:
'-'
,
'
Remainder
'
:
'%'
,
'
TrueDivide
'
:
'/'
,
'
FloorDivide
'
:
'/'
,
'
Or
'
:
'|'
,
'
Xor
'
:
'^'
,
'
And
'
:
'&'
,
'
Rshift
'
:
'
>>
'
}[
op
]
}}
static
PyObject
*
__Pyx_PyInt_
{{
op
}}{{
order
}}(
PyObject
*
op1
,
PyObject
*
op2
,
CYTHON_UNUSED
long
intval
,
int
inplace
)
{
#if PY_MAJOR_VERSION < 3
if
(
likely
(
PyInt_CheckExact
({{
pyval
}})))
{
const
long
{{
'a'
if
order
==
'
CObj
'
else
'b'
}}
=
intval
;
{{
if
c_op
in
'
+-%
'
}}
{{
if
c_op
in
'
+-%
'
or
op
==
'
FloorDivide
'
}}
long
x
;
{{
endif
}}
long
{{
ival
}}
=
PyInt_AS_LONG
({{
pyval
}});
...
...
@@ -512,7 +516,7 @@ static PyObject* __Pyx_PyInt_{{op}}{{order}}(PyObject *op1, PyObject *op2, CYTHO
x
=
(
long
)((
unsigned
long
)
a
{{
c_op
}}
b
);
if
(
likely
((
x
^
a
)
>=
0
||
(
x
^
{{
'~'
if
op
==
'
Subtract
'
else
''
}}
b
)
>=
0
))
return
PyInt_FromLong
(
x
);
return
PyLong_Type
.
tp_as_number
->
nb_
{{
op
.
lower
()
}}(
op1
,
op2
);
return
PyLong_Type
.
tp_as_number
->
nb_
{{
slot_name
}}(
op1
,
op2
);
{{
elif
c_op
==
'%'
}}
// modulus with differing signs isn't safely portable, emulate CPython
if
(
unlikely
(
a
<
0
))
{
...
...
@@ -522,6 +526,30 @@ static PyObject* __Pyx_PyInt_{{op}}{{order}}(PyObject *op1, PyObject *op2, CYTHO
x
=
a
%
b
;
}
return
PyInt_FromLong
(
x
);
{{
elif
op
==
'
TrueDivide
'
}}
if
(
8
*
sizeof
(
long
)
<=
53
||
likely
({{
ival
}}
<=
(
1L
<<
53
)
&&
{{
ival
}}
>=
(
-
(
1L
<<
53
))))
{
return
PyFloat_FromDouble
((
double
)
a
/
(
double
)
b
);
}
// let Python do the rounding
return
PyInt_Type
.
tp_as_number
->
nb_
{{
slot_name
}}(
op1
,
op2
);
{{
elif
op
==
'
FloorDivide
'
}}
if
((
a
^
b
)
>=
0
)
{
{{
if
order
==
'
ObjC
'
}}
// INT_MIN / -1 is the only case that overflows
if
(
unlikely
(
b
==
-
1
&&
((
unsigned
long
)
a
)
==
0
-
(
unsigned
long
)
a
))
return
PyInt_Type
.
tp_as_number
->
nb_
{{
slot_name
}}(
op1
,
op2
);
{{
endif
}}
x
=
a
/
b
;
}
else
{
// use manual rounding when result is negative (signs differ)
long
la
=
labs
(
a
),
lb
=
labs
(
b
);
x
=
la
/
lb
;
if
(
x
*
lb
!=
la
)
x
=
-
x
-
1
;
else
x
=
-
x
;
}
return
PyInt_FromLong
(
x
);
{{
else
}}
// other operations are safe, no overflow
return
PyInt_FromLong
(
a
{{
c_op
}}
b
);
...
...
@@ -545,22 +573,39 @@ static PyObject* __Pyx_PyInt_{{op}}{{order}}(PyObject *op1, PyObject *op2, CYTHO
{{
for
_size
in
(
2
,
3
,
4
)}}
{{
for
_case
in
(
-
_size
,
_size
)}}
case
{{
_case
}}
:
{{
if
c_op
!=
'%'
or
_case
>
0
}}
if
(
8
*
sizeof
(
long
)
-
1
>
{{
_size
}}
*
PyLong_SHIFT
)
{
if
(
8
*
sizeof
(
long
)
-
1
>
{{
_size
}}
*
PyLong_SHIFT
{{
if
op
==
'
TrueDivide
'
}}
&&
{{
_size
-
1
}}
*
PyLong_SHIFT
<
53
{{
endif
}}
)
{
{{
ival
}}
=
{{
'-'
if
_case
<
0
else
''
}}(
long
)
{{
pylong_join
(
_size
,
'
digits
'
)}};
break
;
}
{{
endif
}}
// in negative case, fall through to positive calculation for '%'
// in negative case, fall through to positive calculation for '%'
and '//'
// if size doesn't fit into a long anymore, fall through to default
{{
endfor
}}
{{
endfor
}}
default:
return
PyLong_Type
.
tp_as_number
->
nb_
{{
op
.
lower
()
}}(
op1
,
op2
);
default:
return
PyLong_Type
.
tp_as_number
->
nb_
{{
slot_name
}}(
op1
,
op2
);
}
{{
if
c_op
==
'%'
}}
x
=
a
%
b
;
if
(
unlikely
(
size
<
0
)
&&
x
)
{
x
=
b
-
x
;
}
{{
elif
op
==
'
TrueDivide
'
}}
if
(
8
*
sizeof
(
long
)
<=
53
||
(
size
>=
-
52
/
PyLong_SHIFT
&&
size
<=
52
/
PyLong_SHIFT
)
||
likely
({{
ival
}}
<=
(
1L
<<
53
)
&&
{{
ival
}}
>=
(
-
(
1L
<<
53
))))
{
return
PyFloat_FromDouble
((
double
)
a
/
(
double
)
b
);
}
return
PyLong_Type
.
tp_as_number
->
nb_
{{
slot_name
}}(
op1
,
op2
);
{{
elif
op
==
'
FloorDivide
'
}}
if
((
a
^
b
)
>=
0
)
{
x
=
a
/
b
;
}
else
{
// use manual rounding when result is negative (signs differ)
long
{{
'
la
=
%
s
(
a
),
lb
=
%
s
(
b
)
'
%
((
''
,
'
labs
'
)
if
order
==
'
ObjC
'
else
(
'
labs
'
,
''
))}};
x
=
la
/
lb
;
if
(
x
*
lb
!=
la
)
x
=
-
x
-
1
;
else
x
=
-
x
;
}
{{
else
}}
x
=
a
{{
c_op
}}
b
;
{{
endif
}}
...
...
@@ -568,13 +613,13 @@ static PyObject* __Pyx_PyInt_{{op}}{{order}}(PyObject *op1, PyObject *op2, CYTHO
}
#endif
{{
if
c_op
in
'
+-
'
}}
{{
if
c_op
in
'
+-
'
or
op
==
'
TrueDivide
'
}}
if
(
PyFloat_CheckExact
({{
pyval
}}))
{
const
long
{{
'a'
if
order
==
'
CObj
'
else
'b'
}}
=
intval
;
double
result
;
double
{{
ival
}}
=
PyFloat_AS_DOUBLE
({{
pyval
}});
// copied from floatobject.c in Py3.5:
PyFPE_START_PROTECT
(
"{{op.lower()}}"
,
return
NULL
)
PyFPE_START_PROTECT
(
"{{op.lower()
if not op.endswith('Divide') else 'divide'
}}"
,
return
NULL
)
result
=
((
double
)
a
)
{{
c_op
}}
(
double
)
b
;
PyFPE_END_PROTECT
(
result
)
return
PyFloat_FromDouble
(
result
);
...
...
@@ -599,7 +644,7 @@ static PyObject* __Pyx_PyFloat_{{op}}{{order}}(PyObject *op1, PyObject *op2, dou
#if CYTHON_COMPILING_IN_CPYTHON
{{
py
:
from
Cython
.
Utility
import
pylong_join
}}
{{
py
:
pyval
,
fval
=
(
'
op2
'
,
'b'
)
if
order
==
'
CObj
'
else
(
'
op1
'
,
'a'
)
}}
{{
py
:
c_op
=
'+'
if
op
==
'
Add
'
else
'-'
}}
{{
py
:
c_op
=
{
'
Add
'
:
'+'
,
'
Subtract
'
:
'-'
,
'
TrueDivide
'
:
'/'
}[
op
]
}}
static
PyObject
*
__Pyx_PyFloat_
{{
op
}}{{
order
}}(
PyObject
*
op1
,
PyObject
*
op2
,
double
floatval
,
int
inplace
)
{
const
double
{{
'a'
if
order
==
'
CObj
'
else
'b'
}}
=
floatval
;
...
...
@@ -654,7 +699,7 @@ static PyObject* __Pyx_PyFloat_{{op}}{{order}}(PyObject *op1, PyObject *op2, dou
}
// copied from floatobject.c in Py3.5:
PyFPE_START_PROTECT
(
"{{op.lower()}}"
,
return
NULL
)
PyFPE_START_PROTECT
(
"{{op.lower()
if not op.endswith('Divide') else 'divide'
}}"
,
return
NULL
)
result
=
a
{{
c_op
}}
b
;
PyFPE_END_PROTECT
(
result
)
return
PyFloat_FromDouble
(
result
);
...
...
tests/run/future_division.pyx
View file @
160949a7
...
...
@@ -36,15 +36,49 @@ def constants():
"""
return
1
/
2
,
1
//
2
,
5
/
2.0
,
5
//
2.0
,
5
/
2
,
5
//
2
def
py_mix
(
a
):
"""
>>> py_mix(1)
(0.5, 0, 0.5, 0.0, 0.5, 0)
>>> py_mix(1.0)
(0.5, 0.0, 0.5, 0.0, 0.5, 0.0)
>>> 2**53 / 2.0
4503599627370496.0
>>> py_mix(2**53)
(4503599627370496.0, 4503599627370496, 4503599627370496.0, 4503599627370496.0, 4503599627370496.0, 4503599627370496)
>>> py_mix(2**53 + 1)
(4503599627370496.0, 4503599627370496, 4503599627370496.0, 4503599627370496.0, 4503599627370496.0, 4503599627370496)
>>> py_mix(2**53 + 1.0)
(4503599627370496.0, 4503599627370496.0, 4503599627370496.0, 4503599627370496.0, 4503599627370496.0, 4503599627370496.0)
"""
return
a
/
2
,
a
//
2
,
a
/
2.0
,
a
//
2.0
,
a
/
2
,
a
//
2
def
py_mix_by_neg1
(
a
):
"""
>>> py_mix_by_neg1(0)
(-0.0, 0, -0.0, -0.0, -0.0, 0)
>>> py_mix_by_neg1(-1)
(1.0, 1, 1.0, 1.0, 1.0, 1)
>>> py_mix_by_neg1(int(2**31-1))
(-2147483647.0, -2147483647, -2147483647.0, -2147483647.0, -2147483647.0, -2147483647)
>>> py_mix_by_neg1(int(-2**31-1))
(2147483649.0, 2147483649, 2147483649.0, 2147483649.0, 2147483649.0, 2147483649)
>>> results = py_mix_by_neg1(int(2**63-1))
>>> results[0] == results[2] == results[3] == results[4] == float(2**63-1) / -1.0 or results
True
>>> results[1] == results[5] == (2**63-1) // -1 or results
True
>>> results = py_mix_by_neg1(int(-2**63-1))
>>> results[0] == results[2] == results[3] == results[4] == float(-2**63-1) / -1.0 or results
True
>>> results[1] == results[5] == (-2**63-1) // -1 or results
True
"""
return
a
/-
1
,
a
//-
1
,
a
/-
1.0
,
a
//-
1.0
,
a
/-
1
,
a
//-
1
def
py_mix_rev
(
a
):
"""
>>> py_mix_rev(4)
...
...
tests/run/non_future_division.pyx
View file @
160949a7
...
...
@@ -36,15 +36,49 @@ def constants():
"""
return
1
/
2
,
1
//
2
,
5
/
2.0
,
5
//
2.0
,
5
/
2
,
5
//
2
def
py_mix
(
a
):
"""
>>> py_mix(1)
(0, 0, 0.5, 0.0, 0, 0)
>>> py_mix(1.0)
(0.5, 0.0, 0.5, 0.0, 0.5, 0.0)
>>> 2**53 / 2.0
4503599627370496.0
>>> py_mix(2**53)
(4503599627370496, 4503599627370496, 4503599627370496.0, 4503599627370496.0, 4503599627370496, 4503599627370496)
>>> py_mix(2**53 + 1)
(4503599627370496, 4503599627370496, 4503599627370496.0, 4503599627370496.0, 4503599627370496, 4503599627370496)
>>> py_mix(2**53 + 1.0)
(4503599627370496.0, 4503599627370496.0, 4503599627370496.0, 4503599627370496.0, 4503599627370496.0, 4503599627370496.0)
"""
return
a
/
2
,
a
//
2
,
a
/
2.0
,
a
//
2.0
,
a
/
2
,
a
//
2
def
py_mix_by_neg1
(
a
):
"""
>>> py_mix_by_neg1(0)
(0, 0, -0.0, -0.0, 0, 0)
>>> py_mix_by_neg1(-1)
(1, 1, 1.0, 1.0, 1, 1)
>>> py_mix_by_neg1(int(2**31-1))
(-2147483647, -2147483647, -2147483647.0, -2147483647.0, -2147483647, -2147483647)
>>> py_mix_by_neg1(int(-2**31-1))
(2147483649, 2147483649, 2147483649.0, 2147483649.0, 2147483649, 2147483649)
>>> results = py_mix_by_neg1(int(2**63-1))
>>> results[2] == results[3] == float(2**63-1) / -1.0 or results
True
>>> results[0] == results[1] == results[4] == results[5] == (2**63-1) // -1 or results
True
>>> results = py_mix_by_neg1(int(-2**63-1))
>>> results[2] == results[3] == float(-2**63-1) / -1.0 or results
True
>>> results[0] == results[1] == results[4] == results[5] == (-2**63-1) // -1 or results
True
"""
return
a
/-
1
,
a
//-
1
,
a
/-
1.0
,
a
//-
1.0
,
a
/-
1
,
a
//-
1
def
py_mix_rev
(
a
):
"""
>>> py_mix_rev(4)
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment