JuliaMath / Interpolations.jl

Fast, continuous interpolation of discrete datasets in Julia
http://juliamath.github.io/Interpolations.jl/
Other
523 stars 111 forks source link

greetings from a C++ b-spline library #435

Closed kfjahnke closed 3 years ago

kfjahnke commented 3 years ago

Dear collegues!

I'm the author of an extensive C++ library for cardinal b-splines and everything that's needed to create and evaluate them efficiently, using SIMD and multithreading on arbitrarily strided nD arrays of multichannel data (raster processing only, it's entirely grid-based). The code is header-only C++11 template metacode with few dependencies (needs vigra, Vc optional), everything is liberally licensed. Maybe you'd like to take a look - be it for inspiration, as a cross-reference, or to suck it in:

https://bitbucket.org/kfj/vspline

With regards, Kay F. Jahnke

mkitti commented 3 years ago

This is useful thanks. Do you have binary distribution of this code? We could package it via https://binarybuilder.org/ which would make it easier to use from Julia packages.

kfjahnke commented 3 years ago

Am 30.06.21 um 22:23 schrieb Mark Kittisopikul:

This is useful thanks. Do you have binary distribution of this code? We could package it via https://binarybuilder.org/ https://binarybuilder.org/ which would make it easier to use from Julia packages.

There is no binary as such of this library. It's header-only template metacode. Having binary would require specialization, e.g. to a given set of channels in the *xel data, a fixed number of dimensions for the arrays, etc - which would limit generality, and vspline is 'generic' code. I have 'narrowed it' to a 'reasonable' scope for the python interface, maybe the code there could be used to interface well with julia (sorry, I won't be any help on the julia end, I'm just staring to look at it). The python interface uses precompiled code for 'common' use cases and run-time-instantiates the templates for constellations which aren't covered, thus combining the 'best of both worlds': the 'common' stuff loads near-instantaneously, and the less common needs a while to compile and JIT, using cppyy.

For the python interface, instantiation of the 'common' templates is done with this header

https://bitbucket.org/kfj/python-vspline/src/master/bspline/vspline_binary.h

for the declarations and a c++ file to instantiate:

https://bitbucket.org/kfj/python-vspline/src/master/bspline/vspline_binary.cc

This should be code you can wrap. The code is to make a shared object which provides the objects and functions declared in the header; there's a makefile to produce the .so (linux only for now) - I think julia can suck in shared libraries, so you wouldn't need an external program. Going down this route would limit the scope to the set of features encompassed by the instantiation code, but that's quite a mouthful already and can be extended easily. Only the run-time instantiation with arbitrary template arguments would be missing. The advantage of using the shared library would be that all dependencies would disappear - the .so can stand by itself. I've made a ZIP file of the four 'common' versions of the .so (single/multithreaded, with and without Vc):

https://bitbucket.org/kfj/python-vspline/downloads/vspline_binary.zip

And an .so to pull in what's additionally needed for use with Vc:

https://bitbucket.org/kfj/python-vspline/downloads/get_vc.so

All are for amd64 architecture.

I'd be very interested to see (speed) comparisons of my code with your native julia code, where applicable, and if you have any questions, please ask. What I've seen so far of julia is tasty, and I'm tempted to go deeper. I like the native support of nD arrays and slices, and it looks like one might be able to code b-splines with it with the same scope as my c++ library, but without the statically-typed language bother.

mkitti commented 3 years ago

Julia can use ccall to access a C API directly. C++ is a bit more complicated since the names need to be (de)mangled. https://docs.julialang.org/en/v1/manual/calling-c-and-fortran-code/#C https://github.com/JuliaInterop/CxxWrap.jl

Do you provide C API or just C++?

kfjahnke commented 3 years ago

Sorry, it's only C++ and python for now, and the shared libraries if you can interface them directly.

johnnychen94 commented 3 years ago

It should be sufficient to have BinaryBuilder build the shared library, but for C++ binding we need CxxWrap (which works very similar to pybind), it's a little bit more work to do. Or we can wait for Cxx.jl to revive.

kfjahnke commented 3 years ago

It would suit julia to have a tool like cppyy, which reduces time spent on interface writing significantly. cppyy uses an LLVM-based backend, so it might even be portable to julia - just a wild guess. My python interface to vspline is written that way, and compared to other ways I've used to wrap external code for python (swig, cffi) it was quite painless. The cppyy way is to leave the C++ code more or less untouched (it reads C++ header files) and to do the pythonization in python. cppyy supports the complete C++ language, not just a small subset, and you don't need any annotations in the C++ code. This is more than what CxxWrap or Cxx.jl seem to offer.

This may sound like a strange idea, but why don't you interface to the python module? Even if it's only as an intermediate step to see how vspline gels with julia. The python interface has richer metadata and it's easier to 'talk to'. Browsing a bit, I found PyCall.jl, which seems to be able to handle the complexity of the code. My python interface is already set up to use NumPy ndarrays, and most of vspline's high-level operations are transform/apply-type functions processing such arrays anyway, and these operations are where vspline's speed counts.

Another point to keep in mind is hardware. It's quite possible to run the python interface without any precompiled code, leaving it to cppyy to JIT the C++ header code as needed, but this takes some warm-up time during the first use of a specific instantiation. This method can produce machine instructions native to the processor which the code runs on, and with vspline's heavy use of SIMD, this is a good idea. Having stuff precompiled is a time-saver if the target platform is known, but distributing binary is somewhat contrary to the spirit of vspline.

I established that the python route is viable. With vspline's python module installed (it's simply called 'bspline'), I could do this:

julia> using PyCall; bspline = pyimport("bspline")
PyObject <module 'bspline' from '/home/kfj/.local/lib/python3.8/site-packages/bspline/__init__.py'>

julia> bf3 = bspline.basis_functor()
PyObject <bspline.basis_functor object at 0x7fb223ab5730>

julia> bf3(1.0)
0.16666666666666666

julia> bf7_1 = bspline.basis_functor(7,1)
PyObject <bspline.basis_functor object at 0x7fb2238aa580>

julia> bf7_1(1.0)
-0.3402777777777778

This is just a taster: set up two basis functors, one for the cubic b-spline's basis function, one for a heptic b-spline's first derivative. Both are evaluated at 1.0. It's meant as a proof of viability. What do you think?

mkitti commented 3 years ago

We could interface to the python module. Cxx should do what cppyy does, actually perhaps even lower overhead since it does not use an interpreted approach via Cling. The problem the author of Cxx encountered was that Julia stopped shipping the entire LLVM stack along with Julia after 1.3.x.

The tracking issue for restoring Cxx for current Julia versions is here: https://github.com/JuliaInterop/Cxx.jl/issues/486#issuecomment-754411813

I actually think Cxx would be the best match for your code since this would allow for dynamic compilation of C++ code from Julia.

mkitti commented 3 years ago

Also, I think the scope of this package is a bit different than yours. This package is focused on interpolation only.

For more general work with b-splines, there are other packages such as

kfjahnke commented 3 years ago

We could interface to the python module.

Try it out, I think that's a viable option, as demonstrated in my post. If you need help getting the python module running, please ask. It's not (yet) available from PyPI.

I actually think Cxx would be the best match for your code since this would allow for dynamic compilation of C++ code from Julia.

I don't think Cxx has sufficient scope. In the docu it says

The main interface provided by Cxx.jl is the @cxx macro. It supports two main usages:

    Static function call @cxx mynamespace::func(args...)
    Membercall (where m is a CppPtr, CppRef or CppValue) @cxx m->foo(args...)

It looks to me as if cppyy does much more, it can handle the entire C++ language and do stuff like instantiation of templates at run-time.

Also, I think the scope of this package is a bit different than yours. This package is focused on interpolation only.

For more general work with b-splines, there are other packages such as

* https://github.com/sostock/BSplines.jl

* https://github.com/jipolanco/BSplineKit.jl

My library is for interpolation and the focus is on bulk raster data processing - images, volumes, sound files etc. The basics, like access to the basis functions or the spline-specific IIR and FIR filters, might be useful for 'more general' b-spline code. But my code only handles cardinal splines. It is 'backend stuff' processing raster data very efficiently with multithreaded SIMD code. You can see it in action if you like, I use it in my image and panorama viewer for the rendering code.

kfjahnke commented 3 years ago

This exchange has inspired me. It gave me the following idea: since julia can wrap python seamlessly, a julia wrap of cppyy provides instant access to a mighty agent "understanding" all of C++ which julia can simply delegate to. The resulting state in cppyy is immediately available to julia to execute the code. To illustrate what I mean, consider this snippet:

julia> using PyCall; cppyy = pyimport("cppyy")
PyObject <module 'cppyy' from '/home/kfj/.local/lib/python3.8/site-packages/cppyy/__init__.py'>

julia> cppyy.cppdef(
              """#include <iostream>
                 template<typename T>
                 void blah ( const T& arg )
                   { std::cout << "hello " << arg << std::endl ; }""")
true

julia> cppyy.gbl.blah(1.234)
hello 1.234

This may look trivial, but what's happening here is run-time template instantiation, using automatic dispatch via the argument type, which is really quite a tricky issue for a wrapper generator. I always felt cppyy was a bit like magic, and it seems like the magic can simply be 'forwarded' to julia. Using this method might be a near-optimal way to interface between C++ and julia, with no coding effort for the wrap in itself, using a well-established external module, and no need to reinvent the wheel.

mkitti commented 3 years ago

I think that interface looks great. My understanding is that Cxx provided a cxx"" string macro in addition to @cxx that could compile arbitrary C++ code. The difference in approach is the Cxx wraps Clang directly whereas cppyy wraps Cling. This is somewhat moot because at the moment Cxx is not working for the latest Julia. I think it might be best to discuss some of these details with Cxx's author, @Gnimuc .

What you have shown would be a great Julia package and add to the existing interop opportunities.

To reach a wider Julia audience I encourage you to post this content to https://discourse.julialang.org/ . I believe they will be very interested in your ideas and your demonstration.

wlav commented 3 years ago

We could interface to the python module. Cxx should do what cppyy does, actually perhaps even lower overhead since it does not use an interpreted approach via Cling.

Cling JITs, so that's not where the overhead is. :) In fact, the bulk of the overhead of calls in the case of cppyy are in the Python interpreter itself, that's why it achieves native C++ speeds when used with PyPy, where the Python overhead can be elided.

The problem the author of Cxx encountered was that Julia stopped shipping the entire LLVM stack along with Julia after 1.3.x.

This is why in the case of cppyy, Cling, Clang, and LLVM are linked in w/o exposing their internals. Instead, it has a thin bootstrap API (in C for PyPy, in C++ for CPython) and then uses the JIT. There's some effort by the Cling folks for making that approach more friendly to any language and getting it as much as possible accepted into Clang/LLVM directly (see: https://compiler-research.org/); the motivation for upstream being lldb. Right now, we have people for Python (me), for D, and for LISP. It'd be awesome if anyone from the Julia community would be interested to see whether the same would serve their purposes and if not, help shape the work to make it useful for Julia as well.

Gnimuc commented 3 years ago

I think it might be best to discuss some of these details with Cxx's author, @Gnimuc .

I'm not the author, keno is. I just happened to be a maintainer of that package, and I don't know every detail about how it works.

I don't think Cxx has sufficient scope. In the docu it says It looks to me as if cppyy does much more, it can handle the entire C++ language and do stuff like instantiation of templates at run-time.

@kfjahnke The main interface is cxx/icxx string macros with which you can even interpolate a Julia expression that calls a C++ expression to a C++ expression. I don't think Python can do this because there is no Expr datatype in Python.

As Julia is a language that cares about type, we do const StdVector{T} = cxxt"std::vector<$T>" instead of instantiating templates at run-time.

I don't know why you prefer cppyy(Cxx.jl) over pybind11(CxxWrap.jl). If I have a C++ library and hope it could be accessed in other languages, I'd like to avoid shipping it with a Clang runtime that is only used in a wrapper, but that's only my 50 cent. I completely agree that cppyy(Cxx.jl) is way easier to work with in the development phase.

This is somewhat moot because at the moment Cxx is not working for the latest Julia.

The real reason is that no one would like to invest time in this package, which is probably because we don't have enough Julians who have a good understanding of both LLVM/Clang and Julia internal.

mkitti commented 3 years ago

Thanks for chiming in @Gnimuc . I too am the maintainer and the not the primary author of this package.

It would be great to have Cxx.jl working again against the latest Julia. Perhaps we just need some C++ as a motivation to encourage us to put additional resources into it.

Gnimuc commented 3 years ago

I'm now exploring to reimplement some of the Cxx.jl's features on LLVM.jl+Clang without hacks. Making little progress:

Screen Shot 2021-06-30 at 19 50 42

kfjahnke commented 3 years ago

@mkitti:

I think that interface looks great. My understanding is that Cxx provided a cxx"" string macro in addition to @cxx that could compile arbitrary C++ code. The difference in approach is the Cxx wraps Clang directly whereas cppyy wraps Cling. This is somewhat moot because at the moment Cxx is not working for the latest Julia. I think it might be best to discuss some of these details with Cxx's author, @Gnimuc .

I certainly don't know enough of either julia or the two packets in question as to make any judgements, and I did not mean to. I only saw that the cxx package wasn't functional, and when I saw how smoothly the import of my python bspline package (which is based on cppyy) integrated into julia, it dawned upon me that importing cppyy itself would be a viable option to suck arbitrary C++ code into julia, never mind the python layer in between (which might be JITted away anyway). The code in my package spends nearly all of it's time on the C++ side, crunching numbers, and whether invoking this code takes a few thousand more or less cycles is irrelevant, so for me the extra layer doesn't matter. Cxx would have been the one I would have tried, because I want to keep my C++ code clean of annotations, but you don't bet on a dead horse, so unless it 'revives' I'll stick with my little language slalom ;)

What you have shown would be a great Julia package and add to the existing interop opportunities.

As you have seen from my little three-liner, there is no need for a julia package: the python package already does the trick. And since julia can simply map the wrapped code into julia's namespace, the only place where the python intercession is visible is where cppy is imported.

To reach a wider Julia audience I encourage you to post this content to https://discourse.julialang.org/ . I believe they will be very interested in your ideas and your demonstration.

Would you be so kind and post there, maybe simply a pointer to this discussion? I'm not a member, and I'll have to think about whether I want to become one.

@wlav

It'd be awesome if anyone from the Julia community would be interested to see whether the same would serve their purposes and if not, help shape the work to make it useful for Julia as well.

I agree. And I'd like to state here how much I think of cppyy. I always felt that it was silly to interface two OOP languages (Python and C++ in this case) with a C-level interface (I mean for the user - on the machine level it's fine), but for years and years that was pretty much what you had to do - plus, likely, writing annotations, i-files and what not. cppyy did away with all that. All of the sudden I could simply use extremely sophisticated state of the art C++ template metacode libraries from python, and even be more flexible than in C++, because cppyy allows me to have any template instantiated at run-time, whereas in C++ I have to make up my mind beforehand, because in C++ template instatiation is a compile-time thing. Now I could spend my time making the interface nice to use, rather than getting it to run in the first place. Not to mention other stuff like writing vectorized functional building blocks in C++ at runtime and mixing them with precompiled ones in a pixel pipeline... If the Cxx package (once it revives) can do all that for julia, I'll happily use it.

In a way, this interoperability is crucial. It's not a matter of just having a new package which can do a few cool things. If a language can effortlessly interface with C++, it gains access to a huge repository of software, and it can also delegate some of it's workload to hand-crafted C++ code for the specific purpose. And, beyond that, with LLVM in the background, the language you use to communicate the operations you want to have done might become a matter of picking the right one for the intended purpose, rather than picking the one which happens to work with a set of tools you already have.

I started using python, because I felt that a dynamic language was extremely expressive and flexible for many tasks, and on top of that, python was an ideal glue language to do digital mashup. But it turned out that a lot of my needs would be bit-twiddling, and C++ was the way to go, short of writing assembler code. I always struggled with the 'gap' in between, and cppy built a big fat bridge for me.

@Gnimuc

I don't think Cxx has sufficient scope. ...

This was my impression after skimming the docu - I'm not in a position to make an informed choice, for now I'm only browsing, trying to fathom how I might get 'into julia' with my bundle of code, so I have the backend right there before maybe starting to port stuff to native julia. I found a viable path and I was excited about it, hence my posts.

@kfjahnke The main interface is cxx/icxx string macros with which you can even interpolate a Julia expression that calls a C++ expression to a C++ expression. I don't think Python can do this because there is no Expr datatype in Python.

See there, you've lost me ;)

I don't know why you prefer cppyy(Cxx.jl) over pybind11(CxxWrap.jl). If I have a C++ library and hope it could be accessed in other languages, I'd like to avoid shipping it with a Clang runtime that is only used in a wrapper, but that's only my 50 cent. I completely agree that cppyy(Cxx.jl) is way easier to work with in the development phase.

Again, I did not make any choices here. I'm new to julia, and I'm trying to find my way. I agree that using cppyy is quite a fat load of stuff to pull in just to effortlessly get an interface to C++, and for end-user code it may be too heavy - for now. But trust in Moore's law: a few years down the line, it will look much less bulky than it appears now. And if the toolchain it relies on will become adpoted more widely (I am quite convinced of that) the extra you need to get it going will dwindle to a negligible few bits.

This is somewhat moot because at the moment Cxx is not working for the latest Julia.

The real reason is that no one would like to invest time in this package, which is probably because we don't have enough Julians who have a good understanding of both LLVM/Clang and Julia internal.

I've said above that I think good C++ interoperability is crucial, and I say it again. Any Julians out there in a position to tackle this issue? Please come forward! I wouldn't be surprised if 'bending' cppyy the julia way would turn out a very good option, and I can't see why the authors of Cxx and CxxWrap don't just get together with the people wlav proposed and come out with a joint effort.

And, finally, at @mkitti again, you want a C++ incentive, how about my b-spline library? Never mind my python module has a man-month's worth of pythonization code to make it easy to handle the static code from a dynamic language and use NumPy Arrays with C++ - maybe julia can do it even better!

Gnimuc commented 3 years ago

I don't think Cxx has sufficient scope. ...

This was my impression after skimming the docu - I'm not in a position to make an informed choice, for now I'm only browsing, trying to fathom how I might get 'into julia' with my bundle of code, so I have the backend right there before maybe starting to port stuff to native julia. I found a viable path and I was excited about it, hence my posts.

@kfjahnke The main interface is cxx/icxx string macros with which you can even interpolate a Julia expression that calls a C++ expression to a C++ expression. I don't think Python can do this because there is no Expr datatype in Python.

See there, you've lost me ;)

Oh, never mind. I didn't mean that. 🙂

At this moment, the Julia interop ecosystem is far from mature, the only stable way to wrap a C++ template library is to use CxxWrap.jl in pybind11's style. If it's not urgent, I guess it's worth waiting a couple of months to see whether Cxx.jl can be resurrected to work on the latest Julia version or not.

wlav commented 3 years ago

Again, I did not make any choices here. I'm new to julia, and I'm trying to find my way. I agree that using cppyy is quite a fat load of stuff to pull in just to effortlessly get an interface to C++, and for end-user code it may be too heavy - for now. But trust in Moore's law: a few years down the line, it will look much less bulky than it appears now.

OTOH, cppyy is only 1/4th the size of Angry Birds, which folks would install without questioning overhead. :)

And as much as this topic is about wrappers, the other run-time things it enables include such things as cross-language inheritance and callbacks (crucial for component frameworks, e.g. GUIs), basic automatic memory management, transparent smartptr handling, and in upstream's use automatic I/O (which actually plays nice with Python's pickle). There is also the CPU performance component to it, especially when instantiating templates that can provide optimizations if the exact layout is known, see e.g. Eigen (and for that matter, see Julia, no?).

But back to Julia proper: when the perspective comes from living in a Julia world with the occasional C++ code, the experience is quite different from trying to use Julia in a predominantly C++/Python world. Next week, there is a Python in High Energy Physics (HEP) workshop. On the last day, the last talk is titled "Julia, a HEP dream comes true". And as much as that talk is going to sing the praises of Julia, to convert people, the question is not just how great Julia is, but more importantly how it can live in a world where the other 99% of your collaborators have not seen the light yet and how you access 25 years of legacy software and exabytes of data in said legacy software from a Julia environment.

I appreciate that that is not your problem. :) But in the past, folks in HEP have tried to make inroads with Java, Perl, Ruby, and Go to no avail, b/c they had no practical answer to C++/Python interop. Thus, a stable, functional Cxx.jl (as I understand its use) would be incredibly helpful. So, pretty please? :)

00sapo commented 3 years ago

I don't know why you prefer cppyy(Cxx.jl) over pybind11(CxxWrap.jl).

Since the discussion looks unrelated to spline, let me explain why I have preferred to use Cxx and not CxxWrap.

  1. I needed a fast way to develop to port a package that already has python and JavaScript bindings. I have a few deadlines very near but I also wanted to move to Julia
  2. It came out that Cxx was really helpful. I have various Cpp objects that inherit from a type Algorithm. Each Algorithm has a compute function that takes some inputs and returns some output. There are something like >300 different Algorithm, each one with its specific set of input types and output types (C++ compute returns void and stores results in Algorithm fields of parametric type). Cxx allowed me to write a function in which icxx returns a different object type based on the input types. This is perfectly fine in Julia (and consequently in Cxx), but it would be rather hard to do in pure C++ (and so in CxxWrap). It seems from the web that using C++ 17, it could be done, but it would still be rather hard since I don't really know the correspondances between input types and output. In CxxWrap I would need to wrap each all the C++ classes and methods, even if most of them are there just for internal C++ working, and after that, I would get what in Cxx I did with no more than 300 lines.
kfjahnke commented 3 years ago

@mkitti

To reach a wider Julia audience I encourage you to post this content to https://discourse.julialang.org/ . I believe they will be very interested in your ideas and your demonstration.

I wrote an elaborate post to https://discourse.julialang.org/ which was rejected because 'new users may only post two links'. WTF?! So I take the liberty to post my original text here and link to it there, to avoid having my effort being thwarted by julialang.org's policy - I may be new to them but from my login via github they might have gleaned I'm not just anyone... Sorry to burden this issue tracker again, maybe the content can be moved later when I become 'established' ;)

**** Begin original post to https://discourse.julialang.org/, version with less hyperlinks pointing here is here

Browsing the julia universe, I found interpolations.jl, which offers b-spline interpolation. I am the author of a comprehensive C++ b-spline library, so I thought I'd post a greeting issue to the colleagues and point them to my work. In this issue, a discussion about interfacing C++ and julia ensued: It transpired that usage of C++ in julia currently has unresolved issues, like the unavailability of Cxx.jl for newer julia versions. This gave me the idea to wrap the experimental python module I made for my library instead. This module is wrapped using cppyy. Importing the python module with pycall worked, and shortly after this initial success it dawned upon me that importing cppyy itself into julia would be a viable option to interface arbitrary C++ code with julia.

I posted my findings and ideas to the issue mentioned above, and there was a vivid echo, including a few posts from cppyy's author, whom I'd also written to, to tell him about what I tought was an exciting discovery. I'd like to invite you over to the issue on Interpolation.jl's tracker to see how the discussion went so far - I've written this here post following the suggestion of @mkitti. This site also seems to be a more appropriate location for the discussion than Interpolation.jl's issue tracker where it happened to originate.

Just to give you a rough idea: cppyy is a well-established package based on cling, which offers a wide selection of tools for python/C++ interoperability. Other languages able to bind python code seem to have found it an option to interface with C++ (wlav mentions Perl and Go), so my idea wasn't really novel, it's just that noone seems to have thought of it on the julia side. I think it might be an interesting route to explore - even without any further integration, the power of this approach shows with a simple three-liner, which I quote here:

julia> using PyCall; cppyy = pyimport("cppyy")
PyObject <module 'cppyy' from '.../python3.8/site-packages/cppyy/__init__.py'>

julia> cppyy.cppdef(
        """#include <iostream>
            template<typename T>
            void blah ( const T& arg )
            { std::cout << "hello " << arg << std::endl ; }""")
true

julia> cppyy.gbl.blah(1.234)
hello 1.234

Note the RTTI triggered by the call's argument, which is quite a mouthful for a wrappee, and the inclusion of the entire iostream.h header, which, incidentally, makes it's entire content usable by the wrapper, even though this isn't exploited here. As wlav pointed out, cppyy offers far more than just wrapping C++, he mentions

cross-language inheritance and callbacks (crucial for component frameworks, e.g. GUIs), basic automatic memory management, transparent smartptr handling, and in upstream's use automatic I/O

and in another post he invites julians to cooperate. I hope this may trigger a mutually beneficial cooperation.

With regards

Kay F. Jahnke

wlav commented 3 years ago

Just to add for the record: the example script as above does not work for cppyy 2.0.0 (Clang9), but does for 1.9.6 (Clang5). There's an LLVM symbol being picked up from Julia (LLVM11) in the former case, which has a different implementation than expected, causing a crash. Still investigating and will fix (a similar issue was already reported earlier, where another library was loading LLVM10 into the process), but if anyone wants to try the above, install a cppyy version from the 1.9.x series rather than the latest.

00sapo commented 3 years ago

@kfjahnke cppyy has a strong limitation: it's python, and python is single thread. I think one of the main reason why people leave Python and R to embrace Julia is that Julia offers thread parallelism... Using cppyy would mean to lose the thread programming offered by Julia.

Gnimuc commented 3 years ago

Just to add for the record: the example script as above does not work for cppyy 2.0.0 (Clang9), but does for 1.9.6 (Clang5). There's an LLVM symbol being picked up from Julia (LLVM11) in the former case, which has a different implementation than expected, causing a crash.

@wlav, I think you already got the idea why it's not trivial to make Cxx.jl work on the latest Julia. :) Julia maintains its own LLVM and the language itself evolves fastly and the internal is not relatively stable. As a result, if a package is based on some internals, it will be likely broken for certain new Julia versions. This is exactly the reason why I'm currently exploring to rewrite Cxx.jl without using hacks.

@kfjahnke cppyy has a strong limitation: it's python, and python is single thread. I think one of the main reason why people leave Python and R to embrace Julia is that Julia offers thread parallelism... Using cppyy would mean to lose the thread programming offered by Julia.

No. That's not the case. Please don't make invalid statements like this, which may damage the fame of the Julia community. People from other communities may get something wrong about Julia, but let's be nice and make some explanations instead of being hostile.

I think @kfjahnke just wants to find a package in Julia that does the same job as cppyy in Python for writing a wrapper of his b-spline template C++ library. In Julia world, this package is Cxx.jl. Although Cxx.jl provided a superset of functionality 7 years ago, it doesn't work with the latest Julia version for now. So, @kfjahnke thought it would be great to bring some work from cppyy to Julia, and hence the discourse post.

I'd like to add some clarifications of what I thought here(disclaimer: the only thing I know about cppyy is that it is based on Cling, please correct me if I wrote something wrong about cppyy):

  1. cppyy is based on Cling because Python is not based on LLVM and it has to use an LLVM-based interpreter to evaluate the code.
  2. Julia is based on LLVM and is able to directly evaluate LLVM IR, so a Clang CompilerInstance is what Cxx.jl needs, using Cling is redundant.
  3. Julia is a relatively small community and we don't have enough manpower for something not urgent.

In short, Julia has its own infrastructure for such things, so directly copying the same solution from other languages is not appropriate.

Fun Fact 1: CUDA.jl is based on GPUCompiler.jl which is based on LLVM.jl for providing native CUDA kernel programming in Julia. I don't know much about Python, but there seems to be a similar functionality provided by Numba. Do you guys think it's a good idea to bring Numba to Julia world and build something onto it?

Fun Fact 2: Cxx.jl does use a few lines of code from Cling for its C++ repl support.

wlav commented 3 years ago

@wlav, I think you already got the idea why it's not trivial to make Cxx.jl work on the latest Julia. :)

Absolutely! But that's why libCling.so is not supposed to import or export any LLVM symbols, but it appears that with the recent upgrade, one escaped attention. And, to come full circle, I'm reasonably sure that the proximate cause is a local patch to Clang that I made for Kay. :) I.e. it's a cppyy problem, not a Cling problem. Nevertheless, it's a genuine bug and fixable.

(And yes, this sandboxing comes with an extra memory cost that Cxx.jl avoids.)

Julia maintains its own LLVM and the language itself evolves fastly and the internal is not relatively stable. [..] In Julia world, this package is Cxx.jl.

Just for my understanding, is the problem fast changes in Julia or fast changes in LLVM?

1. `cppyy` is based on Cling because Python is not based on LLVM and it has to use an LLVM-based interpreter to evaluate the code.

cppyy predates Cling by a decade or so. :) Before Cling, it used CINT, later combined with gccxml. Cling was written to replace CINT, hence cppyy followed.

In short, Julia has its own infrastructure for such things, so directly copying the same solution from other languages is not appropriate.

Agreed, but the work done by the https://compiler-research.org/ folks is/will be in large parts upstreamed to LLVM (b/c they want it for improving lldb), so I do expect that it will be useful for Cxx.jl too, especially if there are folk from the Julia community interested in joining the discussions.

Do you guys think it's a good idea to bring Numba to Julia world and build something onto it?

I wouldn't think so: the point of Numba is to be able to write C/C++/CUDA code in an as-close-as-possible Python-style. It then annotates the code with static types, given a start type, which allows translation and subsequent lowering. The PyPy tracing JIT works somewhat similar (but does not use LLVM). Yet, Julia does this natively, no? I.e., there's no need for an external tool?

(Numba mixes quite well with cppyy, btw.: the type to start the annotator is typically given by the input types, i.e. you can construct a faux C++ overload from them by annotating and rewrapping the function pointers provided by Numba.)

Fun Fact 2: Cxx.jl does use a few lines of code from Cling for its C++ repl support.

Cool! :)

00sapo commented 3 years ago

@kfjahnke cppyy has a strong limitation: it's python, and python is single thread. I think one of the main reason why people leave Python and R to embrace Julia is that Julia offers thread parallelism... Using cppyy would mean to lose the thread programming offered by Julia.

No. That's not the case. Please don't make invalid statements like this, which may damage the fame of the Julia community. People from other communities may get something wrong about Julia, but let's be nice and make some explanations instead of being hostile.

Ehi, I don't want to be hostile. It's just a fact. If you try to use PyCall to wrap cppyy, you lose thread programming, and that's a limitation compared to Cxx. It should be entirely rewritten in pure Julia. I'm not enough expert about LLVM and Clang, but the latter could definitely be a path if Cxx fails in supporting the newer Julia versions.

P.S. I would really like to help you with Cxx, but, as I said, I don't know where to start.

wlav commented 3 years ago

It's just a fact. If you try to use PyCall to wrap cppyy, you lose thread programming, and that's a limitation compared to Cxx.

If PyCall properly ensures the GIL when entering Python APIs (I don't know, not being familiar with its implementation, but it's likely and anyway not technically difficult), then cppyy can release it. Yes, all language crossings will effectively form a critical section, but that's only a bottleneck if little time is spent in Julia and/or C++. That's not the case here.

It should be entirely rewritten in pure Julia.

Yes, on that we all agree. :) If only for performance reasons.

P.S. I would really like to help you with Cxx, but, as I said, I don't know where to start.

Maybe there's a test suite available for it? Fixing failing tests is usually a good way of exploring and learning about the internals of a package.

mkitti commented 3 years ago

I'm closing this in favor of continuing the conversation at https://discourse.julialang.org/t/using-cppyy-for-julia-c-interoperability/63979

kfjahnke commented 3 years ago

@mkitti

Hang on!

Can't we get the item back on-topic? I'd still like to get you julians interested in my b-spline library, no matter which way - whether you want to wrap it, look at it for inspiration, or to use it as a cross reference. This item went off-topic after my 'being inspired' about using vspline via python and cppyy, and this turn of the discussion is better at the julialang.org thread, but the initial topic should remain here, shouldn't it?

Kay

mkitti commented 3 years ago

Hi Kay,

As far as I can tell the actionable items at the beginning would both start by creating a separate package or packages. A PythonVSpline.jl might be the easiest one to start with. A VSpline.jl that uses Clang.jl (or Cxx.jl) to generate compiled interpolation code would be even better. I would be happy to help with either of those projects.

Even if these packages were created, this package would likely not have a dependency on either. Interpolations.jl is not meant to be a monolithic package that incorporates all the interpolation techniques. It is simply one of many interpolation packages available to Julia, and in particular it is one written entirely in Julia. For example, another package, https://github.com/kbarbary/Dierckx.jl, is one that wraps a Fortran library. It is mentioned in the Interpolations.jl docs here: http://juliamath.github.io/Interpolations.jl/latest/ , and I would be happy to refer to one based on your vspline code.

At the moment, I do not see anything immediately actionable from the perspective of Interpolations.jl. Perhaps I have missed it. In that case, please create another issue summarizing that action in the title that will be easier to track over time.

Otherwise, I believe that Julia Discourse is the best venue to discuss, seek with help, and launch Julia packages based on your existing C++ code.

-Mark

kfjahnke commented 3 years ago

@mkitti: Thanks for your kind offer to help wrap my module. I stubbornly cling (hah) to my proposal to go via the python module, and I've tried to build up more of a case for this route in https://discourse.julialang.org/t/using-cppyy-for-julia-c-interoperability/63979/12 I tried to relay to you there via an @ reference, but julialang.org forbade me to mention more than two other users because I'm new, so I post here again to make sure you are aware of my post, which is also a follow-up to the discussion above.