In [1]:
%load_ext cython

Regular class vs extensions class

In [12]:
%%cython
cimport cython

cdef class FUSEErrorExt(Exception):
    '''
    This exception may be raised by request handlers to indicate that
    the requested operation could not be carried out. The system call
    that resulted in the request (if any) will then fail with error
    code *errno_*.
    '''

    # If we call this variable "errno", we will get syntax errors
    # during C compilation (maybe something else declares errno as
    # a macro?)
    cdef int errno_

    property errno:
        '''Error code to return to client process'''
        def __get__(self):
            return self.errno_
        def __set__(self, val):
            self.errno_ = val

    def __init__(self, errno):
        self.errno_ = errno
warning: /home/nikratio/.cache/ipython/cython/_cython_magic_f3365cad4f189403d0322b37c637671e.pyx:8:5: freelists cannot be used on subtypes, only the base class can manage them
In [4]:
class FUSEErrorInt(Exception):
    def __init__(self, errno):
        self.errno = errno
In [5]:
def test_ext():
    a = 0
    for i in range(100):
        try:
            raise FUSEErrorExt(i)
        except FUSEErrorExt as exc:
            a += exc.errno
        except:
            print('This should not happen')
    return a

def test_int():
    a = 0
    for i in range(100):
        try:
            raise FUSEErrorInt(i)
        except FUSEErrorInt as exc:
            a += exc.errno
        except:
            print('This should not happen')
    return a
In [6]:
assert test_ext() == test_int()
%timeit test_ext()
%timeit test_int()
The slowest run took 8.54 times longer than the fastest. This could mean that an intermediate result is being cached.
10000 loops, best of 3: 36 µs per loop
10000 loops, best of 3: 57.9 µs per loop

Instantiation vs Factory Function with Cache

(Unfortunately we cannot use @cython.freelist for derived classes)

In [7]:
cache = dict()
def getError(errno):
    try:
        return cache[errno]
    except KeyError:
        cache[errno] = FUSEErrorExt(errno)
        return cache[errno]
    
def test_ext_cached():
    a = 0
    for i in range(100):
        try:
            raise getError(i)
        except FUSEErrorExt as exc:
            a += exc.errno
        except:
            print('This should not happen')
    return a
In [8]:
assert test_ext() == test_ext_cached()
%timeit test_ext()
%timeit test_ext_cached()
10000 loops, best of 3: 32.7 µs per loop
10000 loops, best of 3: 32.4 µs per loop

Catching Exception vs Ordinary Return

In [9]:
def handler(i):
    return getError(i)

def test_ext_direct():
    a = 0
    for i in range(100):
        res = handler(i)
        if isinstance(res, FUSEErrorExt):
            a += res.errno
    return a
In [10]:
assert test_ext_cached() == test_ext_direct()
%timeit test_ext_cached()
%timeit test_ext_direct()
The slowest run took 5.52 times longer than the fastest. This could mean that an intermediate result is being cached.
10000 loops, best of 3: 32.4 µs per loop
10000 loops, best of 3: 28.4 µs per loop
In [ ]: