-
-
Notifications
You must be signed in to change notification settings - Fork 5.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Better integration of "foreign types"? #36770
Comments
Since this is supposed to be a "fake" type, that just lets the GC read the layout field, its should be fine to teach precompile just to make an actual copy of the object whenever it encounters one of these DataTypes. |
@vtjnash apologies, but I don't quite follow: "make an actual copy of the object" -> what does "the object" refer to here? As far I understand things, there are no instances of the foreign type during precompilation -- indeed, I assume the foreign type does not even exist during precompilation, as Assuming I am right, it seems I need a way to tell Julia earlier about the to-be-created foreign type, by inserting something into the code which informs it that To illustrate the concrete issue: Right now, we have code like this, where import GAP
import GAP: GapObj
struct Group
x::GapObj
end
foo(G::Group)::Bool = GAP.Groups.IsAbelian(G.x)
G = Group(GAP.Globals.CyclicGroup(10))
# very silly example
function bar(G::Group)
x = true
for i = 1:10000
x = x && foo(G)
end
end Unfortunately, just to be able to set I also wonder whether if we were able to directly write |
Looks like we may have to solve this if we want #43990. By "make a copy" I assume you mean an opaque buffer? How do you figure out how big the object is? I don't see that in the arguments to |
@timholy I don't understand what "make a copy" refers to? @vtjnash brought it up, I asked for clarification, but so far got none. In general, we can of course extend the foreign type API to cover new needs by Julia; if you can tell me what is needed, I can help come up with a way to provide it. Note that the description of how things work in Oscar resp. GAP.jl is no longer fully accurate: we've worked around many of the issues we had in the past by moving the initialization of the foreign type into Actually, there is also some hackery going on: for aesthetic reasons, we modify the foreign type in GAP.jl to changes its parent module from GAP_jll to GAP; this is mostly to ensure users are less confused; we tried to do with a custom Internally, there are actually three foreign types, but only one ( I could explain more, but that's probably besides the point. I had a look at PR #43990 but could not determine from that what the exact problem is there resp. how it relates to Oscar.jl / GAP.jl / GAP_jll / foreign types. But we are certainly happy to discuss ways to move this forward |
Here's the error: julia> using Oscar
fatal: error thrown and no exception handler available.
ErrorException("Cannot serialize instances of foreign datatypes")
ijl_error at /home/tim/src/julia-master/src/rtutils.c:41
jl_serialize_value_ at /home/tim/src/julia-master/src/dump.c:978
jl_serialize_value_ at /home/tim/src/julia-master/src/dump.c:605 [inlined]
jl_serialize_value_ at /home/tim/src/julia-master/src/dump.c:778
jl_serialize_value_ at /home/tim/src/julia-master/src/dump.c:605 [inlined]
jl_serialize_value_ at /home/tim/src/julia-master/src/dump.c:829
jl_serialize_value_ at /home/tim/src/julia-master/src/dump.c:605 [inlined]
jl_serialize_value_ at /home/tim/src/julia-master/src/dump.c:660
jl_serialize_value_ at /home/tim/src/julia-master/src/dump.c:605 [inlined]
jl_serialize_value_ at /home/tim/src/julia-master/src/dump.c:850
jl_serialize_value_ at /home/tim/src/julia-master/src/dump.c:605 [inlined]
jl_serialize_value_ at /home/tim/src/julia-master/src/dump.c:660
jl_serialize_value_ at /home/tim/src/julia-master/src/dump.c:605 [inlined]
jl_serialize_value_ at /home/tim/src/julia-master/src/dump.c:850
jl_serialize_value_ at /home/tim/src/julia-master/src/dump.c:605 [inlined]
serialize_htable_keys at /home/tim/src/julia-master/src/dump.c:1033 [inlined]
ijl_save_incremental at /home/tim/src/julia-master/src/dump.c:2578
jl_write_compiler_output at /home/tim/src/julia-master/src/precompile.c:65
ijl_atexit_hook at /home/tim/src/julia-master/src/init.c:207
jl_repl_entrypoint at /home/tim/src/julia-master/src/jlapi.c:707
main at /home/tim/src/julia-master/cli/loader_exe.c:59
__libc_start_main at /lib/x86_64-linux-gnu/libc.so.6 (unknown line)
_start at /home/tim/src/julia-master/usr/bin/julia (unknown line) Debugging statements show this occurs while serializing |
It seems one could handle this by adding two new fields to |
So why is serializing function ADDR_OBJ(obj::GapObj)
mptr = Ptr{Ptr{Csize_t}}(pointer_from_objref(obj))
return unsafe_load(mptr)
end
...
function GET_FUNC_PTR(obj::GapObj, narg::Int)
#@assert TNUM_OBJ(obj) == T_FUNCTION
#@assert 0 <= narg && narg <= 7
bag_ptr = Ptr{Ptr{Nothing}}(ADDR_OBJ(obj))
unsafe_load(bag_ptr, narg + 1)
end So why does serializing this involve an instance of type It would be really good to know which objects it tries to serialize -- because only for some will serialization be possible, I am afraid... |
I'd like to debug this (in particular, it would be good to know |
Oh wait, or is just serializing the type what causes problems, as opposed to serializing instances? Sorry for the clearly naive questiojns |
Not naive questions at all. Here's what's happening: Methods contain a
So it seems calling https://github.com/oscar-system/GAP.jl/blob/68938062694bc18e01e44f6574a6fcecffd08663/src/wrappers.jl#L61is the problem: calling it forces compilation, which adds a new root to the method, and serializing the new root is what triggers the error. EDIT: the particular specialization is |
MethodInstance for GAP._call_gap_func(::GAP.GapObj, ::GAP.FFE)
julia> r = m.roots[4]
GAP: <Attribute "String">
julia> typeof(r)
GAP.GapObj of I'm no longer sure of the connection to |
Unfortunately that is a GAP function object and as such will be difficult to serialize. That said, I still think it would be good to add a (de)serialize code interface for foreign types, even if initially it only supports a few types -- at least we'd then have something to build on. Even better would be if these (de)serialize functions would be allowed to raise an exception to help debugging. In this particular code, IsString(x) = GAP.Globals.IsString(x) The Perhaps I could also do something like this:
but I'll have to benchmark how that performs (and whether it works at all 😂) |
I see. So even if we modified Another option is to add |
If we modify So it's not a solution on the short term, though laying foundations for it sooner rather than later still would be good, IMHO. As to At the end of the day, I don't think any Julia package which interfaces with external libraries via |
So I tried adding Yet this did not: foo(x::GapObj) = Globals.String(x)
precompile(foo, (GapObj,)) nor this const _StringRef = Ref{GapObj}()
function bar(x::GapObj)
if !isassigned(_StringRef)
_StringRef[] = GAP.Globals.String
end
_StringRef[](x)
end
precompile(bar, (GapObj,)) To be honest, I don't quite understand why these are fine, but if the latter really is, then that would be a good replacement for the current |
The fundamental point is that unless you can serialize all your objects, Oscar is broken with respect to precompilation. The only reason you can make it work as-is is because Julia's current precompilation is very incomplete, and you've discovered a way to design the package that happens to work with Julia's current limitations. But when we remove those limitations, your current status won't be viable anymore. If there were a prospect of serializing GAP objects in the short-term I'd be happy to work with you, but given that it sounds super-hard and won't land anytime soon, we have to begin by acknowledging the fundamental reality that you'll have to drop some of the things that have currently been skirting the limits of viability.
Because you're hardly caching any compiled code, so the only thing precompilation really does for you is save parsing and lowering steps (which are quite fast). Here's a comparison between one of "my" packages and Oscar (run on Julia 1.6): (@v1.6) pkg> activate --temp
Activating new environment at `/tmp/jl_a2ZhO4/Project.toml`
(jl_a2ZhO4) pkg> add MethodAnalysis, ImageCore, Oscar
<output suppressed>
julia> using MethodAnalysis, ImageCore, Oscar
<output suppressed>
julia> function instances_owned_by(mod::Module)
# get all child modules of `mod`
mods = Module[]
visit(mod) do item
if item isa Module && parentmodule(item) == mod
push!(mods, item)
return true
end
return false # don't recurse into Methods, MethodTables, MethodInstances, etc.
end
# get all MethodInstances owned by one of the modules in `mods`
# these are the only MethodInstances that can be precompiled in current versions of Julia
return filter(methodinstances(mod)) do mi
m = mi.def
m isa Method && return m.module ∈ mods
return m ∈ mods
end
end
instances_owned_by (generic function with 1 method)
julia> length(instances_owned_by(ImageCore))
2011
julia> length(instances_owned_by(Oscar))
15 This, despite the fact that Oscar owns 24947 separate methods (wow!) vs ImageCore's 1242. So relatively speaking you're doing about 2500x less precompilation. But, it's nevertheless true that with precompilation Oscar takes ~16s on my machine to load, and ~30s if you add Presumably the best way forward would be to split out the GAP-specific parts into a separate package that you load on top of Oscar, leave precompilation of Oscar on, and then set |
First off: thank you for taking the time to explain, this is very helpful to me. I hope you don't mind me asking more ...
Sorry for being dense, but: Why do all objects need to be serializable? I do see why e.g. objects we are "calling" or otherwise directly referencing from functions that get precompiled need to be serialized. But most GAP object types don't fall into this category, they can only be generated at runtime (note that for Julia, they all appear as Also: doesn't this also affect other packages which e.g. store pointers to external tools, like e.g. CxxWrap.jl does? AFAIK there is no way to provide a custom serializer for these types, either, or is there? I am talking about types like this: struct CxxPtr{T} <: CxxBaseRef{T}
cpp_object::Ptr{T}
CxxPtr{T}(x::Ptr) where {T} = new{T}(x)
CxxPtr{T}(x::CxxBaseRef) where {T} = new{T}(x.cpp_object)
end Mind you, I am not trying to nitpick here, I am just trying to understand what the actual requirements are / where they come from.
Well, it's not as if we deliberately tried to find a loophole or whatever. We worked with what we had, and many of these things are not documented AFAIK :-/.
If it really has to be done for "all" objects: agreed.
Of course we'd love to cache more. We've been thinking about this for quite some time. BTW, in my tests, GAP.jl is hardly the only problem point; we think CxxWrap is also a major contributor to the precompilation and startup troubles we've been facing (that's not intended as a dig on CxxWrap, mind you, kudos to Bart and others who worked on it).
Wow, thanks for these data points and that code snippets, that'll be very helpful in getting more things precompiled! Because as I said: we'd love to do more, to cut down our startup time, which is atrocious as you point out yourself below. As I said, we tried in the past, but usually quickly run into problems, and lacked the know-how to overcome them. Some of them I now understand were already then caused by GAP.jl (though there were no diagnostics then that we knew that would have told us that; I've landed a patch some time ago for Julia to at least tell us when precompilation crashes due to a "foreign type".
Aye. It actually used to be far worse a couple Julia versions before.
That's not really an option, Oscar.jl must remain at the top, though of course in principle we could do something equivalent by moving most "non-GAP" code out of Oscar.jl into a separate package (which could even live in the same repository, in a subdir) and let Oscar.jl depend on that. I'd still rather avoid this, but it is an option, agreed.
That would be super sweet indeed. BTW, thank you for working on all this, and all the tools and packages to help with debugging and improving these things, that's a great service to the community -- I wish I had more time to dig into them more and leverage them to their fullest. I and others working on Oscar will keep trying to do so, but there's only so much time in a day... sigh. Perhaps we can hire someone with expertise on this for a time, we (shameless plug: if you or anyone else here happen to know someone interested in working at a German university on this kind of stuff, we have lots of funding for Phd and Postdoc positions that could work on areas related to this.)
So right now, I am still hoping that I can get away with our "loopholes" a bit longer. I have a modified version of GAP.jl which does not trigger the precompilation crash anymore, but I have not yet tested it with your PR. And of course even if it does work, what you say above makes it sound likely that the next variant of the issue may pop up soon... I need to understand it better. And I would love to have serialization on the long run, too :-) |
Yes, and iirc precipitation zeros pointers on serialization. On the top of my head you are not allowed to cache the runtime pointers and have to reconstitute them during init and various packages do so. |
@vchuravy ah, interesting. I'd like to learn more, is this documented anywhere? I just tried searching https://docs.julialang.org/en/v1/manual/calling-c-and-fortran-code/ and https://docs.julialang.org/en/v1/base/c/ but found nothing (maybe I searched for the wrong things or the wrong pages, though). |
It is in https://docs.julialang.org/en/v1/manual/modules/#Module-initialization-and-precompilation
Using a |
Thanks for the pointer! I am working on a PR to get rid of the use of |
It seems that with GAP.jl from oscar-system/GAP.jl#780 it is possible to precompile Oscar.jl while using PR #43990. |
We just discussed the serialization of ptrs and no wonder if this is not a serious hidden footgun, a time bomb waiting to explode: code which works fine right now may sometime soon (when more optimization are added) start to crash, namely when suddenly objects containing So perhaps a serialization interface may become necessary even for regular (mutable) struct types, if they contain pointers? Or at the very least, perhaps a way to mark a struct type as "not serializable", so that attempts to serialize it lead to an error, instead of working and producing weird errors later on. Of course to be effective, ideally it then also would give the user some information about what it was trying to serialize (just like it now at least says that it died because of serializing a "foreign type" -- though even better would be if the full serialization stack could be printed, so that one can find out what triggered it; something I couldn't do in the past, but Tim thankfully helped me with here, thank you again) |
Rewrite `@wrap`, `@gapwrap` and `@gapattribute` to not use `@generated` anymore: we used that as a trick to delay loading of certain objects from GAP to runtime; but this was always brittle and will fail in the future when Julia becomes more effective and aggressive precompilation support. We replace this with a simpler approach where the GAP objects now really are only loaded at runtime, and store in global `const Ref{GapObj}` variables. This is in theory slightly less efficient (as now every function call has to check whether the `Ref` is already assigned), but this is tiny. In practice we still have a noticeable improvement over directly calling the relevant GAP functions. Indeed: Before this patch: julia> @Btime GAP.Wrappers.IsString(1); 6.125 ns (0 allocations: 0 bytes) julia> @Btime GAP.Wrappers.String(1); 154.647 ns (6 allocations: 160 bytes) After this patch: julia> @Btime GAP.Wrappers.IsString(1); 9.844 ns (0 allocations: 0 bytes) julia> @Btime GAP.Wrappers.String(1); 156.197 ns (6 allocations: 160 bytes) For reference, without the wrappers (same before and after this patch): julia> @Btime GAP.Globals.IsString(1); 35.624 ns (0 allocations: 0 bytes) julia> @Btime GAP.Globals.String(1); 182.200 ns (6 allocations: 160 bytes) This patch is motivated by JuliaLang/julia#36770 and JuliaLang/julia#43990
Rewrite `@wrap`, `@gapwrap` and `@gapattribute` to not use `@generated` anymore: we used that as a trick to delay loading of certain objects from GAP to runtime; but this was always brittle and will fail in the future when Julia becomes more effective and aggressive precompilation support. We replace this with a simpler approach where the GAP objects now really are only loaded at runtime, and store in global `const Ref{GapObj}` variables. This is in theory slightly less efficient (as now every function call has to check whether the `Ref` is already assigned), but this is tiny. In practice we still have a noticeable improvement over directly calling the relevant GAP functions. Indeed: Before this patch: julia> @Btime GAP.Wrappers.IsString(1); 6.125 ns (0 allocations: 0 bytes) julia> @Btime GAP.Wrappers.String(1); 154.647 ns (6 allocations: 160 bytes) After this patch: julia> @Btime GAP.Wrappers.IsString(1); 9.844 ns (0 allocations: 0 bytes) julia> @Btime GAP.Wrappers.String(1); 156.197 ns (6 allocations: 160 bytes) For reference, without the wrappers (same before and after this patch): julia> @Btime GAP.Globals.IsString(1); 35.624 ns (0 allocations: 0 bytes) julia> @Btime GAP.Globals.String(1); 182.200 ns (6 allocations: 160 bytes) This patch is motivated by JuliaLang/julia#36770 and JuliaLang/julia#43990
Rewrite `@wrap`, `@gapwrap` and `@gapattribute` to not use `@generated` anymore: we used that as a trick to delay loading of certain objects from GAP to runtime; but this was always brittle and will fail in the future when Julia becomes more effective and aggressive precompilation support. We replace this with a simpler approach where the GAP objects now really are only loaded at runtime, and store in global `const Ref{GapObj}` variables. This is in theory slightly less efficient (as now every function call has to check whether the `Ref` is already assigned), but this is tiny. In practice we still have a noticeable improvement over directly calling the relevant GAP functions. Indeed: Before this patch: julia> @Btime GAP.Wrappers.IsString(1); 6.125 ns (0 allocations: 0 bytes) julia> @Btime GAP.Wrappers.String(1); 154.647 ns (6 allocations: 160 bytes) After this patch: julia> @Btime GAP.Wrappers.IsString(1); 9.844 ns (0 allocations: 0 bytes) julia> @Btime GAP.Wrappers.String(1); 156.197 ns (6 allocations: 160 bytes) For reference, without the wrappers (same before and after this patch): julia> @Btime GAP.Globals.IsString(1); 35.624 ns (0 allocations: 0 bytes) julia> @Btime GAP.Globals.String(1); 182.200 ns (6 allocations: 160 bytes) This patch is motivated by JuliaLang/julia#36770 and JuliaLang/julia#43990
As you discovered later with your generated function fix, anything that makes it into some aspect of the compiled code needs to be serializable. Unless, as you say, we develop some mechanism to mark certain items as non-serializable.
Agreed 100%. Just last week I tried to warm up some code by evaluating into a temporary module (JuliaDebug/JuliaInterpreter.jl#514 (comment)) and would have gone with that had it not thrown an error. You've been required to use the lack of errors as validation of your strategy, and that's not easy (and the errors haven't covered all "dangerous" things). And wow, very impressive fix to the crash! In turn, I will try to add to #43990 more detail about the source of the error when serializing foreign types. I'm not sure how simple it will be to "unwind the stack" because currently it's not easy to even detect the error until you encounter it and with C there isn't an exception mechanism to rely on. We could leave some breadcrumbs (a stack of "what I'm serializing now") but there might be performance implications. Still, since that would only affect serialization and not deserialization, maybe it would be worth it (it's OK if compilation is a bit slow as long as usage is fast, because most people who aren't the package developer compile rarely and use frequently). |
Actually may claims may have been premature. I just retested after some sleep and some revising of my quick and dirty PR and now it does crash again :-(. I will check more carefully once I am finished teaching. Sorry :-( |
Any hints you might have on how to pinpoint the function/code causing the serializing failure from gdb/lldb would be appreciated . |
I'll push a branch that implements a way to unwind the serialization stack. I assume it's only useful if built on #43990, right? |
Yeah. Thanks! |
OK, there's a branch called julia> using Oscar
[ Info: Precompiling Oscar [f1435218-dba5-11e9-1e4d-f1a5fab5fc13]
Serialization error encountered. Here is a stack of Methods, MethodInstances, CodeInstances, and method roots (as an svec(index, root)):
Array{Any, (6,)}[
reinterpret(Type{Ptr{Nothing}}, GAP.FFE) from reinterpret(Type{T}, Any) where {T},
_JULIA_TO_GAP(GAP.FFE) from _JULIA_TO_GAP(GAP.FFE),
_call_gap_func(GAP.GapObj, GAP.FFE) from _call_gap_func(GAP.GapObj, Any),
String(GAP.FFE) from String(Any),
String(Any),
svec(3, GAP.GapObj())]
Current item is at end.
fatal: error thrown and no exception handler available.
ErrorException("Cannot serialize instances of foreign datatypes")
ijl_error at /home/tim/src/julia-master/src/rtutils.c:41
jl_serialize_value_ at /home/tim/src/julia-master/src/dump.c:995
jl_serialize_value_ at /home/tim/src/julia-master/src/dump.c:609 [inlined]
jl_serialize_value_ at /home/tim/src/julia-master/src/dump.c:785
jl_serialize_value_ at /home/tim/src/julia-master/src/dump.c:609 [inlined]
jl_serialize_value_ at /home/tim/src/julia-master/src/dump.c:841
jl_serialize_value_ at /home/tim/src/julia-master/src/dump.c:609 [inlined]
jl_serialize_value_ at /home/tim/src/julia-master/src/dump.c:664
jl_serialize_value_ at /home/tim/src/julia-master/src/dump.c:609 [inlined]
jl_serialize_value_ at /home/tim/src/julia-master/src/dump.c:864
jl_serialize_value_ at /home/tim/src/julia-master/src/dump.c:609 [inlined]
jl_serialize_value_ at /home/tim/src/julia-master/src/dump.c:664
jl_serialize_value_ at /home/tim/src/julia-master/src/dump.c:609 [inlined]
jl_serialize_value_ at /home/tim/src/julia-master/src/dump.c:864
jl_serialize_value_ at /home/tim/src/julia-master/src/dump.c:609 [inlined]
jl_serialize_value_ at /home/tim/src/julia-master/src/dump.c:664
jl_serialize_value_ at /home/tim/src/julia-master/src/dump.c:609 [inlined]
jl_serialize_value_ at /home/tim/src/julia-master/src/dump.c:864
jl_serialize_value_ at /home/tim/src/julia-master/src/dump.c:609 [inlined]
serialize_htable_keys at /home/tim/src/julia-master/src/dump.c:1049 [inlined]
ijl_save_incremental at /home/tim/src/julia-master/src/dump.c:2590
jl_write_compiler_output at /home/tim/src/julia-master/src/precompile.c:65
ijl_atexit_hook at /home/tim/src/julia-master/src/init.c:207
jl_repl_entrypoint at /home/tim/src/julia-master/src/jlapi.c:707
main at /home/tim/src/julia-master/cli/loader_exe.c:59
__libc_start_main at /lib/x86_64-linux-gnu/libc.so.6 (unknown line)
_start at /home/tim/src/julia-master/usr/bin/julia (unknown line)
ERROR: Failed to precompile Oscar [f1435218-dba5-11e9-1e4d-f1a5fab5fc13] to /home/tim/.julia/compiled/v1.8/Oscar/jl_beUYcF. The last item on the list merits some additional comment: as the header explains, that's the sign of a method root. The interpretation is that it errorred while serializing a method root, which came from a method ( You can see the roots of a method with julia> m = which(Oscar.GAP.Wrappers.String, (Any,))
String(x) in GAP.Wrappers at /home/tim/.julia/packages/GAP/wF2n6/src/wrappers.jl:61
julia> m.roots
6-element Vector{Any}:
GAP
:_call_gap_func
MethodInstance for GAP._call_gap_func(::GAP.GapObj, ::GAP.FFE)
GAP: <Attribute "String">
typeof(GAP._call_gap_func) (singleton type of function _call_gap_func, subtype of Function)
Symbol("/home/tim/.julia/packages/GAP/wF2n6/src/wrappers.jl") Note that only "new" roots are being added to this debug stack. New roots are those added after the module was defined: that root was added after the GAP module was closed, because some downstream package forced compilation of that MethodInstance. Details are probably most thoroughly described in #42016. |
Thanks @timholy I'll try. BTW Oscar is now at 0.8.0 (not that it'll make a difference) Actually, it (Oscar master with my GAP.jl PR deved) does precompile fine with that PR. But since I already said that once, and then said it again, I'll test some more. I was switching between various Julia dev versions at that time, perhaps some precompilation cache was borked somewhere? I'll try to force it to precompile and run the test suite and some more |
Rewrite `@wrap`, `@gapwrap` and `@gapattribute` to not use `@generated` anymore: we used that as a trick to delay loading of certain objects from GAP to runtime; but this was always brittle and will fail in the future when Julia becomes more effective and aggressive precompilation support. We replace this with a simpler approach where the GAP objects now really are only loaded at runtime, and store in global `const Ref{GapObj}` variables. This is in theory slightly less efficient (as now every function call has to check whether the `Ref` is already assigned), but this is tiny. In practice we still have a noticeable improvement over directly calling the relevant GAP functions. Indeed: Before this patch: julia> @Btime GAP.Wrappers.IsString(1); 6.125 ns (0 allocations: 0 bytes) julia> @Btime GAP.Wrappers.String(1); 154.647 ns (6 allocations: 160 bytes) After this patch: julia> @Btime GAP.Wrappers.IsString(1); 9.844 ns (0 allocations: 0 bytes) julia> @Btime GAP.Wrappers.String(1); 156.197 ns (6 allocations: 160 bytes) For reference, without the wrappers (same before and after this patch): julia> @Btime GAP.Globals.IsString(1); 35.624 ns (0 allocations: 0 bytes) julia> @Btime GAP.Globals.String(1); 182.200 ns (6 allocations: 160 bytes) This patch is motivated by JuliaLang/julia#36770 and JuliaLang/julia#43990
I've discussed this a bit with @timholy on Slack, and did I've now released GAP.jl 0.7.6. Once that is in the registry, @timholy could rerun his tests for PR #43990 with latest Oscar.jl & GAP.jl, and hopefully it'll pass now. |
Two years ago, our PR was accepted which among other things added
jl_new_foreign_type
to the Julia kernel. We've been relying on that work since then in GAP.jl.However, it has one annoying limitation: these foreign types are not visible to precompilation. We worked around this by adding an abstract supertype
GapObj
. Instead of the actual foreign type, we then useGapObj
throughout our Julia code. That was fine until we started to write higher level Julia types which containGapObj
members: As we discovered, by havingx::GapObj
instead ofx::ActualConcreteType
, various optimization are disabled. I am now looking into working around this by parametrizing all our structs (not sure whether that will work out). But I've also wondered if it might be possible to enhance precompilation to deal with our foreign type. Thinking extremely naively, perhaps one could add a syntax extensionforeign type MyForeignType end
or some other means (say, some special macro/function call/whatever) to inform the precompiler about the foreign type? It could then parse code referencing this, and would trust that the__init__
section of the package in which it occurs will actually provide such an external type. However, I don't even know whether that is enough information about the type for the precompiler, perhaps it needs much more; perhaps some of it could be added, perhaps others make the whole idea impossible. I clearly don't know enough about how precompilation works. Before I try to dig deeper and find out, I thought I could ask the experts whether this sounds even remotely realistic to achieve? Or perhaps I am overlooking a much simpler solution?UPDATE 17. June 2022: Just to say, things have evolved quite a bit since I opened this issue. In particular, the foreign type is now initialized in
GAP_jll
, and this seems enough to enable precompilation inGAP.jl
, which usesGAP_jll
. So all is good from that point of view. It would still be nice to be able to implement more, e.g. having some kind of interface to enable handling (de)serialization of instances of this type. I realize it'll be up to us to provide one.The text was updated successfully, but these errors were encountered: