Skip to content

Instantly share code, notes, and snippets.

@temporaer
Created February 8, 2013 11:28
Show Gist options
  • Save temporaer/4738296 to your computer and use it in GitHub Desktop.
Save temporaer/4738296 to your computer and use it in GitHub Desktop.
Observations on the effect of using nogil or not in Cython function declarations
#cython: boundscheck=False
#cython: cdivision=True
#cython: wraparound=False
# While learning about Cython, I found code which proudly adds
# 'nogil' to function heads wherever possible.
#
# In the generated C-Code, the effect is mainly three lines:
#
#
# __Pyx_RefNannyDeclarations
# __Pyx_RefNannySetupContext("with_gil", 0);
#
# # function body here
#
# __Pyx_RefNannyFinishContext();
#
#
# What exactly is the overhead of these lines in a single-threaded program?
# After some googling and discussion, I simply timed it with this cython
# module. It recursively calls a function /many/ times, either with, or without
# the `nogil` appendix to the function head.
#
# The result: On my machine, there is no significant difference.
#
# Number of function calls: 13492900
# with nogil:
# 9.15090990067
# without nogil:
# 9.17464494705
#
# On the other hand, if you remove the default parameters (value=10), my
# compiler (gcc 4.6.3) picks up the tail recursion in the `nogil` case -- and
# optimizes all the function calls away, resulting in millisecond-runtime.
# Without nogil, speed also improves to 1.6s! I'd attribute this to the
# statistic above being dominated by the overhead of handling default
# parameters in Cython.
#
#
# Take home messages:
# - Do not use default parameters in cdef functions
# - Use the nogil, it may help the compiler do things that it can't do
# otherwise.
#
import timeit
cdef int without_gil(int value=10) nogil:
if value == 0:
return 1
cdef int counter = 0
for i in xrange(value):
counter += without_gil(value - 1)
return counter + value
cdef int with_gil(int value=10):
if value == 0:
return 1
cdef int counter = 0
for i in xrange(value):
counter += with_gil(value - 1)
return counter + value
def run_with_gil(): with_gil(10)
def run_without_gil(): without_gil(10)
def main():
print "Number of function calls: ", with_gil(10)
print "with nogil: "
print timeit.timeit("gil_or_nogil.run_without_gil()", "import gil_or_nogil", number=100)
print "without nogil: "
print timeit.timeit("gil_or_nogil.run_with_gil()", "import gil_or_nogil", number=100)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment