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
Boxiang Sun
cython
Commits
0f240448
Commit
0f240448
authored
Mar 20, 2015
by
Stefan Behnel
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
speed up adding/subtracting constant Python floats
parent
e8446db5
Changes
5
Hide whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
157 additions
and
24 deletions
+157
-24
CHANGES.rst
CHANGES.rst
+1
-1
Cython/Compiler/Optimize.py
Cython/Compiler/Optimize.py
+37
-19
Cython/Utility/Optimize.c
Cython/Utility/Optimize.c
+43
-4
tests/run/addop.pyx
tests/run/addop.pyx
+38
-0
tests/run/subop.pyx
tests/run/subop.pyx
+38
-0
No files found.
CHANGES.rst
View file @
0f240448
...
...
@@ -15,7 +15,7 @@ Features added
* Tracing is supported in ``nogil`` functions/sections and module init code.
* Adding/subtracting
small constant Python
integers is faster.
* Adding/subtracting
constant Python floats and small
integers is faster.
Bugs fixed
----------
...
...
Cython/Compiler/Optimize.py
View file @
0f240448
...
...
@@ -2784,54 +2784,72 @@ class OptimizeBuiltinCalls(Visitor.NodeRefCleanupMixin,
PyrexTypes
.
py_object_type
,
[
PyrexTypes
.
CFuncTypeArg
(
"op1"
,
PyrexTypes
.
py_object_type
,
None
),
PyrexTypes
.
CFuncTypeArg
(
"op2"
,
PyrexTypes
.
py_object_type
,
None
),
PyrexTypes
.
CFuncTypeArg
(
"int_op"
,
PyrexTypes
.
c_long_type
,
None
),
PyrexTypes
.
CFuncTypeArg
(
"inplace"
,
PyrexTypes
.
c_int_type
,
None
),
PyrexTypes
.
CFuncTypeArg
(
"intval"
,
PyrexTypes
.
c_long_type
,
None
),
PyrexTypes
.
CFuncTypeArg
(
"inplace"
,
PyrexTypes
.
c_bint_type
,
None
),
])
Pyx_PyFloat_BinopInt_func_type
=
PyrexTypes
.
CFuncType
(
PyrexTypes
.
py_object_type
,
[
PyrexTypes
.
CFuncTypeArg
(
"op1"
,
PyrexTypes
.
py_object_type
,
None
),
PyrexTypes
.
CFuncTypeArg
(
"op2"
,
PyrexTypes
.
py_object_type
,
None
),
PyrexTypes
.
CFuncTypeArg
(
"fval"
,
PyrexTypes
.
c_double_type
,
None
),
PyrexTypes
.
CFuncTypeArg
(
"inplace"
,
PyrexTypes
.
c_bint_type
,
None
),
])
def
_handle_simple_method_object___add__
(
self
,
node
,
function
,
args
,
is_unbound_method
):
return
self
.
_optimise_
int
_binop
(
'Add'
,
node
,
function
,
args
,
is_unbound_method
)
return
self
.
_optimise_
num
_binop
(
'Add'
,
node
,
function
,
args
,
is_unbound_method
)
def
_handle_simple_method_object___sub__
(
self
,
node
,
function
,
args
,
is_unbound_method
):
return
self
.
_optimise_int_binop
(
'Subtract'
,
node
,
function
,
args
,
is_unbound_method
)
return
self
.
_optimise_num_binop
(
'Subtract'
,
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
)
def
_handle_simple_method_float___sub__
(
self
,
node
,
function
,
args
,
is_unbound_method
):
return
self
.
_optimise_num_binop
(
'Subtract'
,
node
,
function
,
args
,
is_unbound_method
)
def
_optimise_
int
_binop
(
self
,
operator
,
node
,
function
,
args
,
is_unbound_method
):
def
_optimise_
num
_binop
(
self
,
operator
,
node
,
function
,
args
,
is_unbound_method
):
"""
Optimise '+' / '-' operator for (likely) small integer operations.
Optimise '+' / '-' operator for (likely)
float or
small integer operations.
"""
if
len
(
args
)
!=
2
:
return
node
if
not
node
.
type
.
is_pyobject
:
return
node
# when adding IntNode to something else, assume other operand is also numeric
if
isinstance
(
args
[
0
],
ExprNodes
.
IntNode
):
# when adding IntNode/FloatNode to something else, assume other operand is also numeric
num_nodes
=
(
ExprNodes
.
IntNode
,
ExprNodes
.
FloatNode
)
if
isinstance
(
args
[
0
],
num_nodes
):
if
args
[
1
].
type
is
not
PyrexTypes
.
py_object_type
:
return
node
int
val
=
args
[
0
]
arg_order
=
'
Int
Obj'
elif
isinstance
(
args
[
1
],
ExprNodes
.
IntNode
):
num
val
=
args
[
0
]
arg_order
=
'
C
Obj'
elif
isinstance
(
args
[
1
],
num_nodes
):
if
args
[
0
].
type
is
not
PyrexTypes
.
py_object_type
:
return
node
int
val
=
args
[
1
]
arg_order
=
'Obj
Int
'
num
val
=
args
[
1
]
arg_order
=
'Obj
C
'
else
:
return
node
if
not
intval
.
has_constant_result
()
or
abs
(
intval
.
constant_result
)
>
2
**
30
:
is_float
=
isinstance
(
numval
,
ExprNodes
.
FloatNode
)
if
not
numval
.
has_constant_result
()
or
(
not
is_float
and
abs
(
numval
.
constant_result
)
>
2
**
30
):
return
node
args
=
list
(
args
)
self
.
_inject_int_default_argument
(
intval
,
args
,
len
(
args
),
PyrexTypes
.
c_long_type
,
intval
.
constant_result
)
args
.
append
((
ExprNodes
.
FloatNode
if
is_float
else
ExprNodes
.
IntNode
)(
numval
.
pos
,
value
=
numval
.
value
,
constant_result
=
numval
.
constant_result
,
type
=
PyrexTypes
.
c_double_type
if
is_float
else
PyrexTypes
.
c_long_type
))
inplace
=
node
.
inplace
if
isinstance
(
node
,
ExprNodes
.
NumBinopNode
)
else
False
self
.
_inject_int_default_argument
(
node
,
args
,
len
(
args
),
PyrexTypes
.
c_long_type
,
int
(
inplace
))
args
.
append
(
ExprNodes
.
BoolNode
(
node
.
pos
,
value
=
inplace
,
constant_result
=
inplace
))
utility_code
=
TempitaUtilityCode
.
load_cached
(
"Py
IntBinopWithInt
"
,
"Optimize.c"
,
"Py
FloatBinop"
if
is_float
else
"PyIntBinop
"
,
"Optimize.c"
,
context
=
dict
(
op
=
operator
,
order
=
arg_order
))
return
self
.
_substitute_method_call
(
node
,
function
,
"__Pyx_Py
Int_%s%s"
%
(
operator
,
arg_order
),
self
.
Pyx_PyInt_BinopInt_func_type
,
node
,
function
,
"__Pyx_Py
%s_%s%s"
%
(
'Float'
if
is_float
else
'Int'
,
operator
,
arg_order
),
self
.
Pyx_Py
Float_BinopInt_func_type
if
is_float
else
self
.
Pyx_Py
Int_BinopInt_func_type
,
'__%s__'
%
operator
[:
3
].
lower
(),
is_unbound_method
,
args
,
may_return_none
=
True
,
with_none_check
=
False
,
...
...
Cython/Utility/Optimize.c
View file @
0f240448
...
...
@@ -479,18 +479,18 @@ fallback:
}
/////////////// PyIntBinop
WithInt
.proto ///////////////
/////////////// PyIntBinop.proto ///////////////
static
PyObject
*
__Pyx_PyInt_
{{
op
}}{{
order
}}(
PyObject
*
op1
,
PyObject
*
op2
,
long
intval
,
int
inplace
);
/*proto*/
/////////////// PyIntBinop
WithInt
///////////////
/////////////// PyIntBinop ///////////////
//@requires: TypeConversion.c::PyLongInternals
{{
py
:
pyval
,
ival
=
(
'
op2
'
,
'b'
)
if
order
==
'
Int
Obj
'
else
(
'
op1
'
,
'a'
)
}}
{{
py
:
pyval
,
ival
=
(
'
op2
'
,
'b'
)
if
order
==
'
C
Obj
'
else
(
'
op1
'
,
'a'
)
}}
static
PyObject
*
__Pyx_PyInt_
{{
op
}}{{
order
}}(
PyObject
*
op1
,
PyObject
*
op2
,
CYTHON_UNUSED
long
intval
,
int
inplace
)
{
#if CYTHON_COMPILING_IN_CPYTHON
const
long
{{
'a'
if
order
==
'
Int
Obj
'
else
'b'
}}
=
intval
;
const
long
{{
'a'
if
order
==
'
C
Obj
'
else
'b'
}}
=
intval
;
#if PY_MAJOR_VERSION < 3
if
(
likely
(
PyInt_CheckExact
({{
pyval
}})))
{
...
...
@@ -525,3 +525,42 @@ static PyObject* __Pyx_PyInt_{{op}}{{order}}(PyObject *op1, PyObject *op2, CYTHO
#endif
return
(
inplace
?
PyNumber_InPlace
{{
op
}}
:
PyNumber_
{{
op
}})(
op1
,
op2
);
}
/////////////// PyFloatBinop.proto ///////////////
static
PyObject
*
__Pyx_PyFloat_
{{
op
}}{{
order
}}(
PyObject
*
op1
,
PyObject
*
op2
,
double
floatval
,
int
inplace
);
/*proto*/
/////////////// PyFloatBinop ///////////////
//@requires: TypeConversion.c::PyLongInternals
{{
py
:
pyval
,
fval
=
(
'
op2
'
,
'b'
)
if
order
==
'
CObj
'
else
(
'
op1
'
,
'a'
)
}}
static
PyObject
*
__Pyx_PyFloat_
{{
op
}}{{
order
}}(
PyObject
*
op1
,
PyObject
*
op2
,
CYTHON_UNUSED
double
floatval
,
int
inplace
)
{
#if CYTHON_COMPILING_IN_CPYTHON
const
double
{{
'a'
if
order
==
'
CObj
'
else
'b'
}}
=
floatval
;
double
{{
fval
}};
if
(
likely
(
PyFloat_CheckExact
({{
pyval
}})))
{
{{
fval
}}
=
PyFloat_AS_DOUBLE
({{
pyval
}});
}
else
#if PY_MAJOR_VERSION < 3
if
(
likely
(
PyInt_CheckExact
({{
pyval
}})))
{
{{
fval
}}
=
(
double
)
PyInt_AS_LONG
({{
pyval
}});
}
else
#endif
#if PY_MAJOR_VERSION >= 3 && CYTHON_USE_PYLONG_INTERNALS
if
(
likely
(
PyLong_CheckExact
({{
pyval
}})))
{
switch
(
Py_SIZE
({{
pyval
}}))
{
case
-
1
:
{{
fval
}}
=
-
(
double
)((
PyLongObject
*
){{
pyval
}})
->
ob_digit
[
0
];
break
;
case
0
:
{{
fval
}}
=
0
.
0
;
break
;
case
1
:
{{
fval
}}
=
(
double
)((
PyLongObject
*
){{
pyval
}})
->
ob_digit
[
0
];
break
;
default:
{{
fval
}}
=
PyLong_AsDouble
({{
pyval
}});
break
;
}
}
else
#endif
#endif
return
(
inplace
?
PyNumber_InPlace
{{
op
}}
:
PyNumber_
{{
op
}})(
op1
,
op2
);
#if CYTHON_COMPILING_IN_CPYTHON
return
PyFloat_FromDouble
(
a
{{
'+'
if
op
==
'
Add
'
else
'-'
}}
b
);
#endif
}
tests/run/addop.pyx
View file @
0f240448
...
...
@@ -39,6 +39,25 @@ def add_x_1(x):
return
x
+
1
@
cython
.
test_fail_if_path_exists
(
'//AddNode'
)
def
add_x_1f
(
x
):
"""
>>> add_x_1f(0)
1.0
>>> add_x_1f(1)
2.0
>>> add_x_1f(-1)
0.0
>>> add_x_1f(1.5)
2.5
>>> add_x_1f(-1.5)
-0.5
>>> try: add_x_1f("abc")
... except TypeError: pass
"""
return
x
+
1.0
@
cython
.
test_fail_if_path_exists
(
'//AddNode'
)
def
add_x_large
(
x
):
"""
...
...
@@ -87,6 +106,25 @@ def add_1_x(x):
return
1
+
x
@
cython
.
test_fail_if_path_exists
(
'//AddNode'
)
def
add_1f_x
(
x
):
"""
>>> add_1f_x(0)
1.0
>>> add_1f_x(1)
2.0
>>> add_1f_x(-1)
0.0
>>> add_1f_x(1.5)
2.5
>>> add_1f_x(-1.5)
-0.5
>>> try: add_1f_x("abc")
... except TypeError: pass
"""
return
1.0
+
x
@
cython
.
test_fail_if_path_exists
(
'//AddNode'
)
def
add_large_x
(
x
):
"""
...
...
tests/run/subop.pyx
View file @
0f240448
...
...
@@ -54,6 +54,25 @@ def sub_x_1(x):
return
x
-
1
@
cython
.
test_fail_if_path_exists
(
'//SubNode'
)
def
sub_x_1f
(
x
):
"""
>>> sub_x_1f(0)
-1.0
>>> sub_x_1f(1)
0.0
>>> sub_x_1f(-1)
-2.0
>>> sub_x_1f(1.5)
0.5
>>> sub_x_1f(-1.5)
-2.5
>>> try: sub_x_1f("abc")
... except TypeError: pass
"""
return
x
-
1.0
@
cython
.
test_fail_if_path_exists
(
'//SubNode'
)
def
sub_x_large
(
x
):
"""
...
...
@@ -98,6 +117,25 @@ def sub_1_x(x):
return
1
-
x
@
cython
.
test_fail_if_path_exists
(
'//SubNode'
)
def
sub_1f_x
(
x
):
"""
>>> sub_1f_x(0)
1.0
>>> sub_1f_x(-1)
2.0
>>> sub_1f_x(1)
0.0
>>> sub_1f_x(1.5)
-0.5
>>> sub_1f_x(-1.5)
2.5
>>> try: sub_1f_x("abc")
... except TypeError: pass
"""
return
1.0
-
x
@
cython
.
test_fail_if_path_exists
(
'//SubNode'
)
def
sub_large_x
(
x
):
"""
...
...
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