Post by Arthur de Souza RibeiroHi Stefan, about your first comment : "And it's better to let Cython know
Post by Arthur de Souza Ribeirothat this name refers to a function." in line 69 of encoder.pyx file I
didn't understand well what does that mean, can you explain more this
comment?
Hmm, sorry, I think that was not so important. That code line is only used
to override the Python implementation with the implementation from the
external C accelerator module. To do that, it assigns either of the two
functions to a name. So, when that name is called in the code, Cython cannot
know that it actually is a function, and has to resort to Python calling,
whereas a visible c(p)def function that is defined inside of the same module
could be called faster.
I missed the fact that this name isn't really used inside of the module, so
whether Cython knows that it's a function or not isn't really all that
important.
So, I don't have to be worried about this, right?
Post by Arthur de Souza Ribeirohttps://github.com/arthursribeiro/JSON-module/commit/e2d80e0aeab6d39ff2d9b847843423ebdb9c57b7#diff-4
I saw your comment and what I understood of it is that the alias that are
being attributed to the type names make code slower, I tried to compile in
cython the same way that it was in python, but, there is something wrong
with it. It says:
Error compiling Cython file:
------------------------------------------------------------
...
def _make_iterencode(dict markers, _default, _encoder, _indent, _floatstr,
_key_separator, _item_separator, bint _sort_keys, bint _skipkeys,
bint _one_shot,
## HACK: hand-optimized bytecode; turn globals into locals
ValueError=ValueError,
dict=dict,
float=float,
^
------------------------------------------------------------
encoder.pyx:273:13: Empty declarator
I turned that way because I think the user can maybe change what types are
going to be used and cython do not allow do these things like python. (for
reserved words)
Post by Arthur de Souza RibeiroAbout the other comments, I think I solved them all, any problem with them
Post by Arthur de Souza Ribeiroor other ones, please tell me. I'll try to fix.
It looks like you fixed a good deal of them.
I actually tried to work with your code, but I'm not sure how you are
building it. Could you give me a hint on that?
I'm manually building them using setup.py files, for every module I create
one and build manually, I don't think that's the best way to do it, but, to
test things, that's the way I'm doing.
Post by Arthur de Souza RibeiroWhere did you actually take the original code from? Python 3.2? Or from
Python's hg branch?
I took the original code from Python 3.2
Post by Arthur de Souza RibeiroNote that it's not obvious from your initial commit what you actually
Post by Arthur de Souza RibeiroPost by Stefan Behnelchanged. It would have been better to import the original file first, rename
it to .pyx, and then commit your changes.
I created a directory named 'Diff files' where I put the files generated by
'diff' command that i run in my computer, if you think it still be better if
I commit and then change, there is no problem for me...
Diff only gives you the final outcome. Committing on top of the original
files has the advantage of making the incremental changes visible
separately. That makes it clearer what you tried, and a good commit comment
will then make it clear why you did it.
I think it's more important to get some performance
Post by Arthur de Souza RibeiroPost by Stefan Behnelnumbers to see how your module behaves compared to the C accelerator module
(_json.c). I think the best approach to this project would actually be to
start with profiling the Python implementation to see where performance
problems occur (or to look through _json.c to see what the CPython
developers considered performance critical), and then put the focus on
trying to speed up only those parts of the Python implementation, by adding
static types and potentially even rewriting them in a way that Cython can
optimise them better.
I've profilled the module I created and the module that is in Python 3.2,
the result is that the cython module spent about 73% less time then python's
That's a common mistake when profiling: the actual time it takes to run is
not meaningful. Depending on how far the two profiled programs differ, they
may interact with the profiler in more or less intensive ways (as is clearly
the case here), so the total time it takes for the programs to run can
differ quite heavily under profiling, even if the non-profiled programs run
at exactly the same speed.
Also, I don't think you have enabled profiling for the Cython code. You can
do that by passing the "profile=True" directive to the compiler, or by
putting it at the top of the source files. That will add module-inner
function calls to the profiling output. Note, however, that enabling
profiling will slow down the execution, so disable it when you measure
absolute run times.
http://docs.cython.org/src/tutorial/profiling_tutorial.html
As you said, I didn't enable profiling for Cython code, I did it and got a
bigger number of function calls if compared to the old ones. I added a new
test case for list object and profiled the code, as you said, They differ
exactly by the number of calls to isinstance function, the result stayed
like this:
------------------------------------ USING Profiler
------------------------------------
JSONModule nested_dict
Tue Apr 19 23:16:48 2011 Profile.prof
200003 function calls in 0.964 seconds
Random listing order was used
ncalls tottime percall cumtime percall filename:lineno(function)
1 0.000 0.000 0.964 0.964 {built-in method exec}
50000 0.114 0.000 0.804 0.000 __init__.pyx:179(dumps)
50000 0.217 0.000 0.690 0.000 encoder.pyx:193(encode)
50000 0.473 0.000 0.473 0.000 encoder.pyx:214(iterencode)
50000 0.089 0.000 0.893 0.000 {JSONModule.dumps}
1 0.071 0.071 0.964 0.964 <string>:1(<module>)
1 0.000 0.000 0.000 0.000 {method 'disable' of
'_lsprof.Profiler' objects}
json nested_dict
Tue Apr 19 23:16:49 2011 Profile.prof
300003 function calls in 1.350 seconds
Random listing order was used
ncalls tottime percall cumtime percall filename:lineno(function)
1 0.000 0.000 1.350 1.350 {built-in method exec}
50000 0.157 0.000 1.255 0.000 __init__.py:180(dumps)
50000 0.115 0.000 0.115 0.000 {method 'join' of 'str'
objects}
50000 0.558 0.000 0.558 0.000 encoder.py:193(iterencode)
50000 0.317 0.000 1.099 0.000 encoder.py:172(encode)
1 0.094 0.094 1.350 1.350 <string>:1(<module>)
100000 0.108 0.000 0.108 0.000 {built-in method isinstance}
1 0.000 0.000 0.000 0.000 {method 'disable' of
'_lsprof.Profiler' objects}
JSONModule ustring
Tue Apr 19 23:16:49 2011 Profile.prof
150003 function calls in 0.297 seconds
Random listing order was used
ncalls tottime percall cumtime percall filename:lineno(function)
1 0.000 0.000 0.297 0.297 {built-in method exec}
50000 0.099 0.000 0.160 0.000 __init__.pyx:179(dumps)
50000 0.061 0.000 0.061 0.000 encoder.pyx:193(encode)
50000 0.082 0.000 0.242 0.000 {JSONModule.dumps}
1 0.055 0.055 0.297 0.297 <string>:1(<module>)
1 0.000 0.000 0.000 0.000 {method 'disable' of
'_lsprof.Profiler' objects}
json ustring
Tue Apr 19 23:16:50 2011 Profile.prof
200003 function calls in 0.419 seconds
Random listing order was used
ncalls tottime percall cumtime percall filename:lineno(function)
1 0.000 0.000 0.419 0.419 {built-in method exec}
50000 0.118 0.000 0.346 0.000 __init__.py:180(dumps)
50000 0.052 0.000 0.052 0.000 {built-in method
encode_basestring_ascii}
50000 0.138 0.000 0.228 0.000 encoder.py:172(encode)
1 0.072 0.072 0.419 0.419 <string>:1(<module>)
50000 0.038 0.000 0.038 0.000 {built-in method isinstance}
1 0.000 0.000 0.000 0.000 {method 'disable' of
'_lsprof.Profiler' objects}
JSONModule xlist
Tue Apr 19 23:16:50 2011 Profile.prof
200003 function calls in 0.651 seconds
Random listing order was used
ncalls tottime percall cumtime percall filename:lineno(function)
1 0.000 0.000 0.651 0.651 {built-in method exec}
50000 0.108 0.000 0.500 0.000 __init__.pyx:179(dumps)
50000 0.154 0.000 0.392 0.000 encoder.pyx:193(encode)
50000 0.238 0.000 0.238 0.000 encoder.pyx:214(iterencode)
50000 0.086 0.000 0.585 0.000 {JSONModule.dumps}
1 0.065 0.065 0.651 0.651 <string>:1(<module>)
1 0.000 0.000 0.000 0.000 {method 'disable' of
'_lsprof.Profiler' objects}
json xlist
Tue Apr 19 23:16:51 2011 Profile.prof
300003 function calls in 1.029 seconds
Random listing order was used
ncalls tottime percall cumtime percall filename:lineno(function)
1 0.000 0.000 1.029 1.029 {built-in method exec}
50000 0.145 0.000 0.940 0.000 __init__.py:180(dumps)
50000 0.062 0.000 0.062 0.000 {method 'join' of 'str'
objects}
50000 0.323 0.000 0.323 0.000 encoder.py:193(iterencode)
50000 0.304 0.000 0.795 0.000 encoder.py:172(encode)
1 0.089 0.089 1.029 1.029 <string>:1(<module>)
100000 0.106 0.000 0.106 0.000 {built-in method isinstance}
1 0.000 0.000 0.000 0.000 {method 'disable' of
'_lsprof.Profiler' objects}
----------------------------------------------------------------------------------------
Post by Arthur de Souza RibeiroColours tend to pass rather badly through mailing lists. Many people
disable the HTML presentation of e-mails, and plain text does not have
colours. But it was still obvious enough what you meant.
Sorry about this.
Post by Arthur de Souza RibeiroThank you for the numbers. Could you add absolute timings using timeit? And
maybe also try with larger input data?
Using timer, I got the following output:
------------------------------------ USING Timeit
--------------------------------------
JSONModule nested_dict spent 11.39 usec/pass (cython)
JSONModule ustring spent 0.94 usec/pass (cython)
JSONModule xlist spent 5.71 usec/pass (cython)
json nested_dict spent 16.61 usec/pass
json ustring spent 1.88 usec/pass
json xlist spent 10.38 usec/pass
----------------------------------------------------------------------------------------
The testcases are the same of the profile ones.
Post by Arthur de Souza RibeiroISTM that a lot of overhead comes from calls that Cython can easily
optimise all by itself: isinstance() and (bytes|unicode).join(). That's the
kind of observation that previously let me suggest to start by benchmarking
and profiling in the first place. Cython compiled code has quite different
performance characteristics from code executing in CPython's interpreter, so
it's important to start by getting an idea of how the code behaves when
compiled, and then optimising it in the places where it still needs to run
faster.
As you said, starting profiling is a better approach, specially because
every change made reflects in time changes (using timeit of profiling).
Post by Arthur de Souza RibeiroOptimisation is an incremental process, and you will often end up reverting
changes along the way when you see that they did not improve the
performance, or maybe just made it so slightly faster that the speed
improvement is not worth the code degradation of the optimisation change in
question.
Could you try to come up with a short list of important code changes you
made that let this module run faster, backed by some timings that show the
difference with and without each change?
To let this module run faster, the bests changes were in classes definitions
(I'm going to show numbers soon) using cinit and defining the variables made
the code faster. Another changes that I made were in for loops. I created an
int variable and made to loop in range of a number instead of a 'for x in
...' statement. The other ones were about to add static types specially to
int and boolean types.
Thank you very much again.
Best Regards.
[]s
Arthur