request for a @cython.nochecks(True) compilation option - tuple access etc has a None check I can't get rid of!

801 views
Skip to first unread message

Alex Meakins

unread,
Jun 21, 2014, 11:45:29 AM6/21/14
to cython...@googlegroups.com
Hello, I'm having a small problem with the code generated when I'm unpacking a tuple in a reasonably time critical bit of code. I've optimised the unpacking as far as I believe I can, but I can't get rid of a None check that is added when the tuple is accessed. In my code I can absolutely guarantee the tuple access is valid so there is no need for the check. This is the code in question - note I have @cython.boundscheck(False) set for the enclosing function:

intersection = self.world_box.full_intersection(ray)
hit = intersection[0]
min_range = intersection[1]
max_range = intersection[2]

the full_intersection method is as follows:

cpdef tuple full_intersection(self, Ray ray):

    cdef double front_intersection, back_intersection
    cdef bint hit
   
    :
    :

    return (hit, front_intersection, back_intersection)

Cython 0.20 generates the following code:

intersection = self.world_box.full_intersection(ray)

  __pyx_t_1 = ((struct __pyx_vtabstruct_7raysect_4core_12acceleration_11boundingbox_BoundingBox *)__pyx_v_self->world_box->__pyx_vtab)->full_intersection(__pyx_v_self->world_box, __pyx_v_ray, 0); if (unlikely(!__pyx_t_1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 90; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_t_1);
  __pyx_v_intersection = ((PyObject*)__pyx_t_1);
  __pyx_t_1 = 0;

 hit = intersection[0]

  if (unlikely(__pyx_v_intersection == Py_None)) {
    PyErr_SetString(PyExc_TypeError, "'NoneType' object is not subscriptable");
    {__pyx_filename = __pyx_f[0]; __pyx_lineno = 91; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  }
  __pyx_t_4 = __Pyx_PyObject_IsTrue(PyTuple_GET_ITEM(__pyx_v_intersection, 0)); if (unlikely(__pyx_t_4 < 0)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 91; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __pyx_v_hit = __pyx_t_4;

min_range = intersection[1]

  if (unlikely(__pyx_v_intersection == Py_None)) {
    PyErr_SetString(PyExc_TypeError, "'NoneType' object is not subscriptable");
    {__pyx_filename = __pyx_f[0]; __pyx_lineno = 92; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  }
  __pyx_t_5 = __pyx_PyFloat_AsDouble(PyTuple_GET_ITEM(__pyx_v_intersection, 1)); if (unlikely((__pyx_t_5 == (double)-1) && PyErr_Occurred())) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 92; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __pyx_v_min_range = __pyx_t_5;

 max_range = intersection[2]

  if (unlikely(__pyx_v_intersection == Py_None)) {
    PyErr_SetString(PyExc_TypeError, "'NoneType' object is not subscriptable");
    {__pyx_filename = __pyx_f[0]; __pyx_lineno = 93; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  }
  __pyx_t_5 = __pyx_PyFloat_AsDouble(PyTuple_GET_ITEM(__pyx_v_intersection, 2)); if (unlikely((__pyx_t_5 == (double)-1) && PyErr_Occurred())) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 93; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __pyx_v_max_range = __pyx_t_5;


The if .... == PyNone calls are entirely unnecessary, however I can't find a way to remove them.

Would it be possible for you to provide a general cython.nochecks(True) compilation options that entirely removes all checks (bounds, wraparound, type)? This would be a great help to me as I can keep the code neat and fast. Such an option would also be very useful for getting rid of the numerous boundscheck and wraparound disabling options I have endlessly scattered in my core code.

I'll probably make this particular part of the code faster by replacing the tuple, however this is a general problem I'm having with accessing python types in known safe code.

Sturla Molden

unread,
Jun 21, 2014, 12:09:17 PM6/21/14
to cython...@googlegroups.com
On 21/06/14 17:45, Alex Meakins wrote:

Cython allows you to pass None instead of any Python type, similarly to
using a NULL pointer in C. You can turn this off on a per variable basis
by declaring the variable "not None".


> cpdef tuple full_intersection(self, Ray ray)

cpdef tuple full_intersection(TypeOfSelf self not None, Ray ray not None)



Sturla

Alex Meakins

unread,
Jun 21, 2014, 12:33:57 PM6/21/14
to cython...@googlegroups.com

Thanks for the reply, however it does not answer my question/request.

My problem is that an unnecessary None check is being added to the generated C code when unpacking a returned tuple in known safe code and I have no way of telling the compiler not to add the check.

Alex Meakins

unread,
Jun 21, 2014, 12:42:17 PM6/21/14
to cython...@googlegroups.com
T be more specific, I would like to be able to ask the compiler to produce:

intersection = self.world_box.full_intersection(ray)

  __pyx_t_1 = ((struct __pyx_vtabstruct_7raysect_4core_12acceleration_11boundingbox_BoundingBox *)__pyx_v_self->world_box->__pyx_vtab)->full_intersection(__pyx_v_self->world_box, __pyx_v_ray, 0); if (unlikely(!__pyx_t_1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 90; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_t_1);
  __pyx_v_intersection = ((PyObject*)__pyx_t_1);
  __pyx_t_1 = 0;

 hit = intersection[0]

  __pyx_v_hit = __Pyx_PyObject_IsTrue(PyTuple_GET_ITEM(__pyx_v_intersection, 0));

min_range = intersection[1]

  __pyx_v_min_range = __pyx_PyFloat_AsDouble(PyTuple_GET_ITEM(__pyx_v_intersection, 1));

 max_range = intersection[2]

  __pyx_v_max_range = __pyx_PyFloat_AsDouble(PyTuple_GET_ITEM(__pyx_v_intersection, 2));

rather than:

Sturla Molden

unread,
Jun 21, 2014, 1:45:55 PM6/21/14
to cython...@googlegroups.com
Alex Meakins <alex.m...@gmail.com> wrote:

So you have a CPU without branch prediction or a C compiler that cannot
emit code to use the "unlikely" macro?

Why should Cython optimize for 1970s' compilers and processors?


Sturla
> 1)); if (unlikely((__pyx_t_5 == (double)-1) &amp;&amp; PyErr_Occurred()))
> {__pyx_filename = __pyx_f[0]; __pyx_lineno = 92; __pyx_clineno = __LINE__;
> goto __pyx_L1_error;}
> __pyx_v_min_range = __pyx_t_5;
>
> max_range = intersection[2]
>
> if (unlikely(__pyx_v_intersection == Py_None)) {
> PyErr_SetString(PyExc_TypeError, "'NoneType' object is not
> subscriptable");
> {__pyx_filename = __pyx_f[0]; __pyx_lineno = 93; __pyx_clineno =
> __LINE__; goto __pyx_L1_error;}
> }
> __pyx_t_5 = __pyx_PyFloat_AsDouble(PyTuple_GET_ITEM(__pyx_v_intersection,
> 2)); if (unlikely((__pyx_t_5 == (double)-1) &amp;&amp; PyErr_Occurred()))
> {__pyx_filename = __pyx_f[0]; __pyx_lineno = 93; __pyx_clineno = __LINE__;
> goto __pyx_L1_error;}
> __pyx_v_max_range = __pyx_t_5;
>
> --
>
> ---
> You received this message because you are subscribed to the Google Groups
> "cython-users" group.
> To unsubscribe from this group and stop receiving emails from it, send an
> email to cython-users...@googlegroups.com.
> For more options, visit <a
> href="https://groups.google.com/d/optout.">https://groups.google.com/d/optout.</a>
>
> ------=_Part_925_18764450.1403368937296
> Content-Type: text/html; charset=UTF-8
> Content-Transfer-Encoding: quoted-printable
>
> <div dir=3D"ltr">T be more specific, I would like to be able to ask the com=
> piler to produce:<br><br><div style=3D"margin-left:40px">intersection =3D s=
> elf.world_box.full_intersection(ray)<br><br><div style=3D"margin-left:40px"=
>> &amp;nbsp; __pyx_t_1 =3D ((struct __pyx_vtabstruct_7raysect_4core_12accelerati=
> on_11boundingbox_BoundingBox *)__pyx_v_self-&amp;gt;world_box-&amp;gt;__pyx_vtab)-&amp;=
> gt;full_intersection(__pyx_v_self-&amp;gt;world_box, __pyx_v_ray, 0); if (unlik=
> ely(!__pyx_t_1)) {__pyx_filename =3D __pyx_f[0]; __pyx_lineno =3D 90; __pyx=
> _clineno =3D __LINE__; goto __pyx_L1_error;}<br>&amp;nbsp; __Pyx_GOTREF(__pyx_t=
> _1);<br>&amp;nbsp; __pyx_v_intersection =3D ((PyObject*)__pyx_t_1);<br>&amp;nbsp; _=
> _pyx_t_1 =3D 0;<br></div><br>&amp;nbsp;hit =3D intersection[0]<br><br><div styl=
> e=3D"margin-left:40px">&amp;nbsp; __pyx_v_hit =3D __Pyx_PyObject_IsTrue(PyTuple=
> _GET_ITEM(__pyx_v_intersection, 0));<br></div><br>min_range =3D intersectio=
> n[1]<br><br><div style=3D"margin-left:40px">&amp;nbsp; __pyx_v_min_range =3D __=
> pyx_PyFloat_AsDouble(PyTuple_GET_ITEM(__pyx_v_intersection, 1));<br></div><=
>> &amp;nbsp;max_range =3D intersection[2]<br><br><div style=3D"margin-left:40p=
> x">&amp;nbsp; __pyx_v_max_range =3D __pyx_PyFloat_AsDouble(PyTuple_GET_ITEM(__p=
> yx_v_intersection, 2));<br></div></div><br><p>rather than:</p><p><br></p><d=
> iv style=3D"margin-left:40px">intersection =3D self.world_box.full_intersec=
> tion(ray)<br><br><div style=3D"margin-left:40px">&amp;nbsp; __pyx_t_1 =3D ((str=
> uct __pyx_vtabstruct_7raysect_4core_12acceleration_11boundingbox_BoundingBo=
> x *)__pyx_v_self-&amp;gt;world_box-&amp;gt;__pyx_vtab)-&amp;gt;full_intersection(__pyx_=
> v_self-&amp;gt;world_box, __pyx_v_ray, 0); if (unlikely(!__pyx_t_1)) {__pyx_fil=
> ename =3D __pyx_f[0]; __pyx_lineno =3D 90; __pyx_clineno =3D __LINE__; goto=
> __pyx_L1_error;}<br>&amp;nbsp; __Pyx_GOTREF(__pyx_t_1);<br>&amp;nbsp; __pyx_v_inte=
> rsection =3D ((PyObject*)__pyx_t_1);<br>&amp;nbsp; __pyx_t_1 =3D 0;<br></div><b=
>> &amp;nbsp;hit =3D intersection[0]<br><br><div style=3D"margin-left:40px">&amp;nbs=
> p; if (unlikely(__pyx_v_intersection =3D=3D Py_None)) {<br>&amp;nbsp;&amp;nbsp;&amp;nbs=
> p; PyErr_SetString(PyExc_TypeError, "'NoneType' object is not subscriptable=
> ");<br>&amp;nbsp;&amp;nbsp;&amp;nbsp; {__pyx_filename =3D __pyx_f[0]; __pyx_lineno =3D =
> 91; __pyx_clineno =3D __LINE__; goto __pyx_L1_error;}<br>&amp;nbsp; }<br>&amp;nbsp;=
> __pyx_t_4 =3D __Pyx_PyObject_IsTrue(PyTuple_GET_ITEM(__pyx_v_intersection,=
> 0)); if (unlikely(__pyx_t_4 &amp;lt; 0)) {__pyx_filename =3D __pyx_f[0]; __pyx=
> _lineno =3D 91; __pyx_clineno =3D __LINE__; goto __pyx_L1_error;}<br>&amp;nbsp;=
> __pyx_v_hit =3D __pyx_t_4;<br></div><br>min_range =3D intersection[1]<br><=
>> <div style=3D"margin-left:40px">&amp;nbsp; if (unlikely(__pyx_v_intersection=
> =3D=3D Py_None)) {<br>&amp;nbsp;&amp;nbsp;&amp;nbsp; PyErr_SetString(PyExc_TypeError, =
> "'NoneType' object is not subscriptable");<br>&amp;nbsp;&amp;nbsp;&amp;nbsp; {__pyx_fil=
> ename =3D __pyx_f[0]; __pyx_lineno =3D 92; __pyx_clineno =3D __LINE__; goto=
> __pyx_L1_error;}<br>&amp;nbsp; }<br>&amp;nbsp; __pyx_t_5 =3D __pyx_PyFloat_AsDoubl=
> e(PyTuple_GET_ITEM(__pyx_v_intersection, 1)); if (unlikely((__pyx_t_5 =3D=
> =3D (double)-1) &amp;amp;&amp;amp; PyErr_Occurred())) {__pyx_filename =3D __pyx_f[0=
> ]; __pyx_lineno =3D 92; __pyx_clineno =3D __LINE__; goto __pyx_L1_error;}<b=
>> &amp;nbsp; __pyx_v_min_range =3D __pyx_t_5;<br></div><br>&amp;nbsp;max_range =3D =
> intersection[2]<br><br><div style=3D"margin-left:40px">&amp;nbsp; if (unlikely(=
> __pyx_v_intersection =3D=3D Py_None)) {<br>&amp;nbsp;&amp;nbsp;&amp;nbsp; PyErr_SetStri=
> ng(PyExc_TypeError, "'NoneType' object is not subscriptable");<br>&amp;nbsp;&amp;nb=
> sp;&amp;nbsp; {__pyx_filename =3D __pyx_f[0]; __pyx_lineno =3D 93; __pyx_clinen=
> o =3D __LINE__; goto __pyx_L1_error;}<br>&amp;nbsp; }<br>&amp;nbsp; __pyx_t_5 =3D _=
> _pyx_PyFloat_AsDouble(PyTuple_GET_ITEM(__pyx_v_intersection, 2)); if (unlik=
> ely((__pyx_t_5 =3D=3D (double)-1) &amp;amp;&amp;amp; PyErr_Occurred())) {__pyx_file=
> name =3D __pyx_f[0]; __pyx_lineno =3D 93; __pyx_clineno =3D __LINE__; goto =
> __pyx_L1_error;}<br>&amp;nbsp; __pyx_v_max_range =3D __pyx_t_5;<br></div></div>=
> <p></p></div>
>
> <p></p>
>
> -- <br />
> <br />
> --- <br />
> You received this message because you are subscribed to the Google Groups &amp;=
> quot;cython-users&amp;quot; group.<br />
> To unsubscribe from this group and stop receiving emails from it, send an e=
> mail to <a href=3D"mailto:cython-users...@googlegroups.com">cython=
> -users+un...@googlegroups.com</a>.<br />
> For more options, visit <a href=3D"https://groups.google.com/d/optout">http=
> s://groups.google.com/d/optout</a>.<br />
>
> ------=_Part_925_18764450.1403368937296--

Message has been deleted

Alex Meakins

unread,
Jun 21, 2014, 2:44:59 PM6/21/14
to cython...@googlegroups.com
Please re-read my first post, I asked for a nochecks option that turns off all checks, bounds checking etc... with a single statement. My code is currently full of bounds check disable and wrap around disable commands when a single nochecks command would be clearer. The removal of the remaining, entirely unnecessary if statements would also greatly improve the readability around these sections of code and also make it cleaner when debugging. So there is a clear use case for such an option.

I don't appreciate the unnecessarily hostile nature of your response to what is a reasonable request. If you are not a cython developer please stop replying.

Sturla Molden

unread,
Jun 21, 2014, 3:17:45 PM6/21/14
to cython...@googlegroups.com
Sturla Molden <sturla...@gmail.com> wrote:
> Alex Meakins <alex.m...@gmail.com> wrote:
>
> So you have a CPU without branch prediction or a C compiler that cannot
> emit code to use the "unlikely" macro?

By the way, if you are on Windows, ypu should really get MinGW or Clang
instead of MSVC.

On Linux and Mac, __builtin_expect is supported by GCC, Clang and Intel
icc.

AFAIK, Microsoft thinks profile guided optimizations are preferable to a
simple static hint like __builtin_expect. It is not, because they are
rarely used, and sometimes a coder is smart enough to know that a branch
will rarely be executed. And by the way, on a deep pipeline like Pentium 4
and later, this matters a lot.

So to answer Alex' request: The "unlikely" macro will make sure that those
intructions are virtually never fetched and executed by the processor. I.e.
the branch will be disfavored by the branch prediction. The conditional
with usually incure zero overhead. Only MSVC is stupid enough to emit code
that favors an "if (unlikely(x))" branch on the CPU. With other compilers,
the performance will be as if the conditional were not there. Unless you
have a stupid compiler (that is, MSVC), turning those None checks off will
have zero effect on the performance.

If you use MSVC, use profile guided optimizations to get the compiler to do
the right thing.

Sturla

Sturla Molden

unread,
Jun 21, 2014, 3:28:08 PM6/21/14
to cython...@googlegroups.com
Alex Meakins <alex.m...@gmail.com> wrote:
> Please re-read my first post I asked for a nochecks option that turns off
> *all* checks, bounds checking etc... with a single statement. My code is
> currently full of bounds check and wrap around checks disable commands when
> a single nochecks command would be clearer.

Cython has compiler pragmas that effect the whole file. Just put them as a
comments
on top of the pyx file or pass them as -X arguments to the compiler. You
don't have to
manually type them in as @cython.whatever decorators for each function.

http://docs.cython.org/src/reference/compilation.html#compiler-directives

Sturla

Stefan Behnel

unread,
Jun 22, 2014, 5:32:35 AM6/22/14
to cython...@googlegroups.com
Sturla Molden, 21.06.2014 21:17:
> Unless you
> have a stupid compiler (that is, MSVC), turning those None checks off will
> have zero effect on the performance.

Not to mention that the C compiler will see that a local variable is not
None on the first check, and then just know that the same check is always
false further down in the code. So even if Cython isn't always smart enough
to drop redundant None checks (it often is, but not always), it doesn't
mean they really end up producing machine code at the end, and in the cases
where they do, it still doesn't mean they take up actual time during execution.

Stefan

Stefan Behnel

unread,
Jun 22, 2014, 5:40:38 AM6/22/14
to cython...@googlegroups.com
Alex Meakins, 21.06.2014 20:44:
> Please re-read my first post, I asked for a nochecks option that turns off
> *all* checks, bounds checking etc... with a single statement. My code is
> currently full of bounds check disable and wrap around disable commands
> when a single nochecks command would be clearer. The removal of the
> remaining, entirely unnecessary if statements would also greatly improve
> the readability around these sections of code and also make it cleaner when
> debugging. So there is a clear use case for such an option.

The drawback of such an option is that it might disable more checks in a
future version of Cython, which your code may not expect. And you may not
even notice it, unless you are sufficiently lucky to have tests broad
enough to cover these checks as well.

Basically, disabling safety checks is an "I know what I'm doing" feature,
and you can't know what you're doing regarding future extensions of the
"disable all checks" option. But if we choose not to extend the option in
the future for backwards compatibility reasons, would you then come and ask
for a single "disable all checks, plus new checks X and Y" option?

I find it better to be explicit about what checks you want to disable and
where. If you really want to disable them globally for your file (and I
consider that a dangerous thing to do from a maintenance POV), then do as
Sturla told you and put the directives at the top of your file.

http://docs.cython.org/src/reference/compilation.html#how-to-set-directives


> I don't appreciate the unnecessarily hostile nature of your response to
> what is a reasonable request. If you are not a cython developer please stop
> replying.

Regardless of what anyone might think about the way Sturla replied, I don't
consider this response a good argument in the discussion either.

Stefan

Alex Meakins

unread,
Jun 22, 2014, 7:30:37 AM6/22/14
to cython...@googlegroups.com
Sturla, Stefen thank you for your replies.

I had not fully appreciated the scalability issue of nochecks(True) regarding future possible checks, it is a strong argument against the idea.

My knowledge of modern compilers and CPU instruction execution is incomplete, thank you for the reassurance that the None checks will not cause any significant overhead. Though they are a pain when reading the code! :)

Regarding my reply to Sturla: I understand that it can sometimes get irritating dealing with people who don't have as much knowledge as you. However in a user group where there will generally be a mix of people with a wide range of levels of knowledge, it doesn't do the user group any favours if inexperienced users are effectively being mocked for lacking knowledge, it puts people off contributing. For my part I apologise to Sturla and thank him for the information he subsequently posted.

Sturla Molden

unread,
Jun 22, 2014, 12:27:11 PM6/22/14
to cython...@googlegroups.com
Alex Meakins <alex.m...@gmail.com> wrote:

> My knowledge of modern compilers and CPU instruction execution is
> incomplete, thank you for the reassurance that the None checks will not
> cause any significant overhead. Though they are a pain when reading the
> code! :)

You shouldn't read the C code... :-)

Cython puts in a lot of crap that is either optimized or refactored away by
the C compiler (e.g. in memoryview indexing) or never executed on the CPU
due to intelligent branch prediction (e.g. conditional branches marked
"unlikeliy"). It is often beyond human comprehension to find out what
really matters for run-time performance. But usually performance doesn't
matter either, that's why we can use Python...

The tricky part is identifying what actually matters for YOU. Typically it
will be a tiny portion of your code. Don't be surprised if it accounts for
less than a percent of the lines of code. When you have identified that
part, you can try to optimize it beyond belief. If you think Cython is not
up to the job, you can even write that part in plain C, e.g. using Python C
API.

Turning off all security checks everywhere and optimizing everything is
never a good strategy. Premature optimization is the root of all evil in
computer programming (according to Donald Knuth and C. A. R. Hoare), so
don't do it. You will find that 99 % of your optimizations goes into
optimizing code that accounts for 1 % of your run-time. All you really have
achieved is making your code less robust. If you have bottlenecks, deal
with them on a per-bottleneck basis. A profiler will help you find them,
intuition is often of lesser value, and Cython can be told to insert hooks
for the Python profiler.

But I can assure you that a None check which is marked "unlikely" will be
very painless (perhaps except with MSVC).

Sturla

Robert Bradshaw

unread,
Jun 23, 2014, 1:35:27 PM6/23/14
to cython...@googlegroups.com
On Sun, Jun 22, 2014 at 9:26 AM, Sturla Molden <sturla...@gmail.com> wrote:
> Alex Meakins <alex.m...@gmail.com> wrote:
>
>> My knowledge of modern compilers and CPU instruction execution is
>> incomplete, thank you for the reassurance that the None checks will not
>> cause any significant overhead. Though they are a pain when reading the
>> code! :)
>
> You shouldn't read the C code... :-)

Or at least you shouldn't need to.

> Cython puts in a lot of crap that is either optimized or refactored away by
> the C compiler (e.g. in memoryview indexing) or never executed on the CPU
> due to intelligent branch prediction (e.g. conditional branches marked
> "unlikeliy"). It is often beyond human comprehension to find out what
> really matters for run-time performance. But usually performance doesn't
> matter either, that's why we can use Python...
>
> The tricky part is identifying what actually matters for YOU. Typically it
> will be a tiny portion of your code. Don't be surprised if it accounts for
> less than a percent of the lines of code. When you have identified that
> part, you can try to optimize it beyond belief. If you think Cython is not
> up to the job, you can even write that part in plain C, e.g. using Python C
> API.

Or improve Cython :). But, as mentioned, just looking at the generated
code isn't always the best indicator of performance. If you really
want to, edit the C code itself and give reproducible benchmarks. If
there is measurable performance gain to be had, we'll likely be
interested to patch in a fix, but if it comes out in the wash then
there's no need to complicate the (generating) code.

> Turning off all security checks everywhere and optimizing everything is
> never a good strategy. Premature optimization is the root of all evil in
> computer programming (according to Donald Knuth and C. A. R. Hoare), so
> don't do it. You will find that 99 % of your optimizations goes into
> optimizing code that accounts for 1 % of your run-time. All you really have
> achieved is making your code less robust. If you have bottlenecks, deal
> with them on a per-bottleneck basis. A profiler will help you find them,
> intuition is often of lesser value, and Cython can be told to insert hooks
> for the Python profiler.
>
> But I can assure you that a None check which is marked "unlikely" will be
> very painless (perhaps except with MSVC).

Sound advice.

- Robert
Reply all
Reply to author
Forward
0 new messages