Skip to content
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

Running C extensions natively #3118

Closed
eregon opened this issue Jun 21, 2023 · 6 comments
Closed

Running C extensions natively #3118

eregon opened this issue Jun 21, 2023 · 6 comments

Comments

@eregon
Copy link
Member

eregon commented Jun 21, 2023

We want to experiment with running C extensions natively instead of on Sulong.
Similar to what GraalPython did for 23.0.
We might still run libtruffleruby on Sulong, or at least initially we would because this is much easier.

  • This should improve compatibility with bigger extensions such as grpc which are proving difficult to run on Sulong due to the very large amount of C/C++/etc code they run.
  • This would eliminate warmup for native extensions.
  • It will speed up native compilation by not needing bitcode
  • It can reuse the compiler toolchain of the host system, which means we would not need the llvm toolchain anymore (saves disk space) if this mode is good enough to replace running C extensions on Sulong
  • Peak performance might be lower for some extensions which benefit from fast C->Ruby and Ruby->C calls, due to having to create handles for every Ruby->C call and going through NFI/JNI/Panama for C->Ruby calls. OTOH, the native code in the extension might run faster as Sulong e.g. reimplements vector instructions. We will need to benchmark this.
  • Some struct accesses cannot be virtualized by Sulong anymore and so these structs likely either can't be supported or we need to allocate a native struct and copy there (for read-only fields, because writing to them would have no effect). Hopefully very few extensions rely on direct struct access. The Ruby C API seems to have very few exposed structs.
@eregon
Copy link
Member Author

eregon commented Oct 6, 2023

I made a lot of progress on this this week and now all C API specs pass when running extensions natively (i.e. not on Sulong, except for libtruffleruby itself)!
WIP in #3286, it also needs changes in NFI.

@eregon
Copy link
Member Author

eregon commented Oct 6, 2023

One change is that loading extensions might fail early with undefined symbol like:

(currently)
$ jt ruby -ropenssl -e0
... openssl.so: undefined symbol: rb_during_gc) (RuntimeError)

So while with Sulong we could afford to not define functions of the C API we did not implement yet, we have to define them with native to just load the extension, even if we just rb_tr_error() or so inside. That's rather unfortunate because then an extension cannot easily know if a function is available or not using have_func anymore.

EDIT: actually we can solve this by using RTLD_LAZY instead of the default RTLD_NOW.
That seems a good solution and avoids defining a bunch of functions for Ripper and other extensions.

@eregon
Copy link
Member Author

eregon commented Nov 9, 2023

  • Some struct accesses cannot be virtualized by Sulong anymore and so these structs likely either can't be supported or we need to allocate a native struct and copy there (for read-only fields, because writing to them would have no effect). Hopefully very few extensions rely on direct struct access. The Ruby C API seems to have very few exposed structs.

The main one here is struct RBasic.
Reading the klass field can be replaced by RBASIC_CLASS (also exists on CRuby).
Accessing the flags field can be replaced by RBASIC_FLAGS/RBASIC_SET_FLAGS (do not exist on CRuby).
This can be used to define these macros in any case:

#ifndef RBASIC_FLAGS
#define RBASIC_FLAGS(obj) (RBASIC(obj)->flags)
#endif

#ifndef RBASIC_SET_FLAGS
#define RBASIC_SET_FLAGS(obj, flags_to_set) (RBASIC(obj)->flags = flags_to_set)
#endif

There are also various flags macros like RB_FL_TEST which exist on CRuby and are supported on TruffleRuby before & after. In general native extensions should not use RBasic flags so this should have little impact.

The reason accessing RBASIC(obj)->flags is no longer supported is because there is no way to implement it, because writing to the flags would just be ignored, because we store flags in a different way, e.g. the frozen flag is stored in the Shape, not in native memory.

@eregon
Copy link
Member Author

eregon commented Nov 16, 2023

Merged in a9afcef.
Still a few cleanups and optimizations to come so I'll keep this open for now.

@eregon
Copy link
Member Author

eregon commented Nov 30, 2023

After 55f4a97 the size of the truffleruby native standalone as an archive goes from 183MiB to 93MiB (on linux-amd64, similar for other platforms), so half the size, a nice saving.
And uncompressed size on disk goes from 602MiB to 299MiB.

@eregon
Copy link
Member Author

eregon commented Mar 14, 2024

This is done.
There will be more optimizations following, but the core work is done for 24.0.

@eregon eregon closed this as completed Mar 14, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
Status: Done
Development

No branches or pull requests

1 participant