Optimizing code with Cython

Hi,

I am optimizing my code using cython. The big advantage of cython is that, in addition to compiled c++ code, I can use multiprocessing without significant overhead. However the problem with multiprocessing is that GIL is not allowed. Then no python objects and calls are allowed when I call nogil.

My code (touch.pyx) basically is:

@cython.boundscheck(False)
cdef float getClosestCollision(object py_physics_manager, str host_name, float max_distance, object origin, object tip):
    """
    Check if objects are colliding with collision line and get the collision info of the
    closest one.
    """
    cdef:
        LPoint3 cpp_origin, cpp_tip
        BulletWorld* cpp_physics_manager
    with nogil:
        cpp_origin = <LPoint3>((<Dtool_PyInstDef*>origin)._ptr_to_object)
        cpp_tip = <LPoint3>((<Dtool_PyInstDef*>tip)._ptr_to_object)
        cpp_physics_manager = <BulletWorld*>((<Dtool_PyInstDef*>py_physics_manager)._ptr_to_object)
        collisions = cpp_physics_manager.ray_test_all(cpp_origin, cpp_tip)
        if collisions.get_num_hits() > 1:
            # Get an list of collisions ordered by the closest ones
            fractions = [hit.get_hit_fraction() for hit in collisions.get_hits() if
                         hit.get_node().get_name() != host_name]
            if len(fractions) > 2:
                return max(fractions) * max_distance
            elif len(fractions) == 1:
                return fractions[0] * max_distance
            else:
                return 0
        else:
            return 0

Now the “header file” (touch.pxd) to help the link between my code and libpanda.lib:

from libcpp.string cimport string

cdef extern from "py_panda.h":
    cdef struct Dtool_PyInstDef:
        void* _ptr_to_object

cdef extern from "lvecBase3_src.h":
    cdef cppclass LVecBase3:
        inline float get_x()
        inline float get_y()
        inline float get_z()

cdef extern from "lpoint3.h":
    cdef cppclass LPoint3:
        inline float get_x()
        inline float get_y()
        inline float get_z()

cdef extern from "lvector3.h":
    cdef cppclass LVector3:
        inline float get_x()
        inline float get_y()
        inline float get_z()

cdef extern from "nodePath.h":
    cdef cppclass NodePath:
        inline string get_name()
        inline NodePath get_parent(Thread *current_thread)
        LPoint3 get_relative_point(NodePath &other, LVecBase3 &point)

cdef extern from "bitMask.h":
    cdef cppclass BitMask32

cdef extern from "bulletAllHitsRayResult.h":
    cdef struct BulletRayHit:
        NodePath *get_node()
        LPoint3 get_hit_pos()
        LVector3 get_hit_normal()
        float get_hit_fraction()

    cdef struct BulletAllHitsRayResult:
        LPoint3 get_from_pos()
        LPoint3 get_to_pos()
        bint has_hits()
        int get_num_hits()
        BulletRayHit get_hit(int idx)

cdef extern from "bulletWorld.h":
    cdef cppclass BulletWorld:
        BulletAllHitsRayResult ray_test_all(LPoint3 &from_pos, LPoint3 &to_pos, CollideMask &mask) nogil except +

My Setup.py:

import sys
import os
import shutil
from pathlib import Path
from setuptools import setup, Extension
from Cython.Build import cythonize

include_dirs = []
library_dirs = []
if os.name == "nt":
    include_dirs = [r"C:\Program Files\Python37\include",
                    r"C:\Panda3D-1.10.6-x64\include"]
    library_dirs = [r"C:\Panda3D-1.10.6-x64\lib"]
elif os.name == "posix":
    include_dirs = ["/Developer/Panda3D/include/"]
    library_dirs = ["/Developer/Panda3D/python/libs/",
                    "/Developer/Panda3D/lib/"]
libraries = ["libp3framework",
             "libpanda",
             "libpandafx",
             "libpandaexpress",
             "libp3dtool",
             "libp3dtoolconfig",
             "libp3pystub",
             "libp3direct"]
extensions = [
    Extension("*",
              sources=["*.pyx"],
              include_dirs=include_dirs,
              library_dirs=library_dirs,
              libraries=libraries,
              language="c++"),
    Extension("*",
              sources=["senses/*.pyx"],
              include_dirs=include_dirs,
              library_dirs=library_dirs,
              libraries=libraries,
              language="c++"),
    Extension("*",
              sources=["ui/*.pyx"],
              include_dirs=include_dirs,
              library_dirs=library_dirs,
              libraries=libraries,
              language="c++")]
setup(name="Avatar Studio",
      package_dir={'avatar_studio': ''},
      ext_modules=cythonize(extensions,
                            language_level=3))

Unfortunately this is not working:

senses\touch.cpp(4445): error C2065: 'LPoint3': undeclared identifier
senses\touch.cpp(4445): error C2146: syntax error: missing ';' before identifier '__pyx_v_cpp_origin'
senses\touch.cpp(4445): error C2065: '__pyx_v_cpp_origin': undeclared identifier
senses\touch.cpp(4446): error C2065: 'LPoint3': undeclared identifier
senses\touch.cpp(4446): error C2146: syntax error: missing ';' before identifier '__pyx_v_cpp_tip'
senses\touch.cpp(4446): error C2065: '__pyx_v_cpp_tip': undeclared identifier
senses\touch.cpp(4458): error C2065: '__pyx_v_cpp_origin': undeclared identifier
senses\touch.cpp(4458): error C2065: 'LPoint3': undeclared identifier
senses\touch.cpp(4467): error C2065: '__pyx_v_cpp_tip': undeclared identifier
senses\touch.cpp(4467): error C2065: 'LPoint3': undeclared identifier
error: command 'C:\\Program Files (x86)\\Microsoft Visual Studio\\2019\\BuildTools\\VC\\Tools\\MSVC\\14.27.29110\\bin\\HostX86\\x64\\cl.exe' failed with exit status 2

LPoint3 is not found by the linker. Could some help to point where is the error. Is the header lpoint3.h correct?

The piece of the header (touch.pxd) declaring LPoint3:

cdef extern from "lpoint3.h":
    cdef cppclass LPoint3:
        inline float get_x()
        inline float get_y()
        inline float get_z()

@rdb rdb I’m not a c++ experient user but I see this in lpoint3_src.h which is included in lpoint3.h:

class EXPCL_PANDA_LINMATH FLOATNAME(LPoint3) : public FLOATNAME(LVecBase3)
{

}

Checking the FLOATNAME macro I found that it would return LPoint##f, would be this translated to LPoint3f? So should I use LPoint3f instead of LPoint3?

Thanks in advance!