GraphBLAS / python-suitesparse-graphblas

Python CFFI Binding around SuiteSparse:GraphBLAS
Apache License 2.0
19 stars 6 forks source link

GraphBLAS 9 JIT #129

Open alugowski opened 5 months ago

alugowski commented 5 months ago

Let's use this to track discussion and implementation of the GraphBLAS 9 JIT.

My opinion:

The JIT needs to be configured with a compiler, which is done at GraphBLAS build time by GraphBLAS_JIT_configure.cmake. Of course that's a non-starter for Python because the user's machine is different than the build machine, but AFAIK it's possible to set a new config at runtime.

One option is to pip install cmake and run that configure script at import time (or an explicit enable_jit()), then set that config. As long as there's safe a fallback, explicit documentation, and a guided experience through possible failures then this can work. Shoaib Kamil's SEJITS did this a decade ago and we used it to compile user-defined semirings.

Another is to tie in to the Python compiler packages like numba (ships LLVM and tackles the versioning and compatibility nighmare), or pythran or even Cython. This would be a nicer user experience, but I haven't dug too much into how much of GraphBLAS the JIT compiles. This may work if it's a user callback or even a kernel, but likely not if it's a large chunk.

Another is to only support the JIT on conda. I believe they have some sort of compiler guarantees in their environments, so maybe then you can assume the build-time compiler is available at runtime. May require some magic to handle different absolute paths to conda environments, but I'm sure they have some best practices for this.

DrTimothyAldenDavis commented 5 months ago

Yes, there is a way to set the compiler used in the JIT at run time, with GrB_set. I have many options for controlling the JIT with GrB_set / GrB_get:

    GxB_JIT_C_COMPILER_NAME = 7024,  // CPU JIT C compiler name
    GxB_JIT_C_COMPILER_FLAGS = 7025, // CPU JIT C compiler flags
    GxB_JIT_C_LINKER_FLAGS = 7026,   // CPU JIT C linker flags
    GxB_JIT_C_LIBRARIES = 7027,      // CPU JIT C libraries
    GxB_JIT_C_PREFACE = 7028,        // CPU JIT C preface
    GxB_JIT_C_CONTROL = 7029,        // CPU JIT C control
    GxB_JIT_CACHE_PATH = 7030,       // CPU/CUDA JIT path for compiled kernels
    GxB_JIT_C_CMAKE_LIBS = 7031,     // CPU JIT C libraries when using cmake
    GxB_JIT_USE_CMAKE = 7032,        // CPU JIT: use cmake or direct compile
    GxB_JIT_ERROR_LOG = 7033,        // CPU JIT: error log file

The defaults for all of these are found by cmake when GraphBLAS itself is compiled and so by default I use the compiler, flags, and so on, that were used to build GraphBLAS (seems like a reasonable default, but of course it is different for the python-graphblas wrapper). I configure the GraphBLAS/Config/GB_config.h.in file to create GraphBLAS/Config/GB_config.h. When GrB_init starts, it sets all those strings to the defaults in GB_config.h, and if desired they can be changed later at any time, in the end user application. I only use the definitions in GB_config.h at GrB_init time. So none of those strings in GB_config.h are baked in stone; all of them can be changed later at run time.

So that means at runtime, the python wrapper can make any decisions it likes. It can try to set up its own defaults when python starts. It could also accept some option from the python user, like gb.compiler('/usr/bin/mynewcompiler') or something, as you like.

DrTimothyAldenDavis commented 5 months ago

Regarding the Mac segfault when the JIT is enabled:

It's not a Python error. It's a SIGABORT on a call into GraphBLAS:

python-suitesparse-graphblas/suitesparse_graphblas/tests/test_io.py

Line 104 in 045d489

lib.GrB_Matrix_eWiseAdd_BinaryOp(C[0], NULL, NULL, _eq_ops[T], A[0], B[0], NULL), Somebody will have to turn on logging, or replicate it locally.

The burble can help. I could also try to replicate it on my system, if I had some help with getting things installed and compiled (I rarely use python so I'd need some help).