-
Notifications
You must be signed in to change notification settings - Fork 10
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
Modernize the package #11
Conversation
@maleadt Very nice! |
Replacing the Before:
After:
|
https://gist.github.com/fjolnir/2211379/d77f93efd80690125b933667f79600e67da66c1e is also interesting, where there's caching of class and instance methods, and automatic retain/release based on the function name. |
Selector lookups are usually done at load time. For instance this is what the metalcpp wrapper is doing:
Something similar could be done in an However, being able to do the selector lookup on the fly with relatively low overhead is quite useful -- for situations where one wants to quickly wrap a portion of a framework by hand and also quick experimentation -- so I find this PR really great.
Note: the process of invoking a method in Objective-C is:
So caching selector lookups and checking a hashtable in Julia at runtime is not likely to provide any improvement to just calling What the clang compiler also does is static analysis to make sure that objective-c method invocations will not generate runtime errors and that is what a Julia wrapper could do as well (i.e. not improve performance, but help ensure correct use). Also, a good place to read about the low level details is: https://developer.apple.com/documentation/objectivec/objective-c_runtime?language=objc |
I wouldn't remove that, but require type annotations so that we can statically generate code instead of having to introspect into the ObjC runtime in order to build a This is how the package currently works: julia> str = @objc [NSString string]
__NSCFConstantString Object
julia> @objc [str length]
0x0000000000000000
julia> f() = @objc [str length]
f (generic function with 1 method) Current master branch:
This PR (removal of
Proposed static approach: julia> str = @objc [NSString string]::id
Ptr{ObjectiveC.OpaqueObject} @0x00000001eb6b4d28
julia> @objc [str::id length]::NSUInteger
0x0000000000000000
julia> f() = @objc [str::Id length]::NSUInteger
f (generic function with 1 method)
julia> @benchmark f()
BenchmarkTools.Trial: 10000 samples with 946 evaluations.
Range (min … max): 98.661 ns … 137.024 ns ┊ GC (min … max): 0.00% … 0.00%
Time (median): 99.014 ns ┊ GC (median): 0.00%
Time (mean ± σ): 99.406 ns ± 2.098 ns ┊ GC (mean ± σ): 0.00% ± 0.00%
▃█▇▆▄▂ ▁▁▁▁ ▂
██████▇▆▅▄▃▅▅▅▅▅▄▅▆█████▅▅▅▅▅▄▄▃▃▅▅▅▄▄▄▄▅▄▄▅▅▆▆▃▅▄▅▄▁▁▃▁▃▄▄▄ █
98.7 ns Histogram: log(frequency) by time 109 ns <
Memory estimate: 0 bytes, allocs estimate: 0. i.e. keeping most of the syntax, only adding some type assertions so that we have the information we need. Note the use of |
…at-objc wrappers.
I faced this challenge in Gtk.jl and ended up doing something similar to this, with
Then at runtime, it would be sufficient to do the reflection calls needed to decide if a particular interface was applicable, and perhaps also to provide a custom dispatch table hook for places where it is required. This would have let it provide all of the features (multiple inheritance, concrete inheritance, interfaces with inheritance) that are not viable normally. Secondly, with the current approach, I think I should have at least implemented many convert-trait methods, and coded them up to be visible to reflection:
then when I auto-generated interface calls such as
|
Thanks for the write-up, I should think about if an how to incorporate that here. Regarding blocks, I pushed an initial version that simply looks like: function hello(x)
println("Hello, world $(x)!")
return Cint(42)
end
block = @objcblock(hello, Cint, (Cint,)) This works when attaching the block to a class, now I'll see if it works for Metal. I'm not sure I'm correctly managing memory (i.e., I'm not doing much). As our block doesn't contain any GC-managed data (only a Julia function, and IIRC functions aren't garbage collected), I think I can get away by just copying it to the heap and doing nothing (well, not doing any refcounting). I'm not sure who's responsible for releasing the block object itself though. |
No on AArch64 sadly |
This might be useful for reference. |
I have an alternative, storing the |
|
|
All of |
OK, I got the basic functionality to support Metal.jl working, so I think I'll cut a 0.1 release so that I can more easily test this package in a realistic setting. FWIW, I removed the broken bits that I didn't (yet) need, including support for defining classes, so I'll create an issue to keep track of bringing those bits back. But since ObjectiveC.jl hadn't even been tagged, and was completely broken at this point, I don't feel bad removing it for now. |
This PR updates and significantly reworks the ObjectiveC.jl package. Basic updates include adding a Project.toml, fixing deprecations, adding some tests, etc. However, I also decided to rework the core calling mechanism to not rely on introspection and dynamic code generation anymore, instead opting for a more static
@ccall
-esque approach that allows for far better performance. It should also combine nicely with e.g. a Clang.jl-based code generator, but that's future work.Reworking the core mechanism probably results in breakage of other functionality in this package, but it's hard to avoid that since there were no tests. In addition, because there hasn't even been a release, and because the package was broken on any recent Julia version anyway, it is probably fine to do so and gradually improve/unbreak functionality along the way.
cc @habemus-papadum