[C++][XLA Binding] executable serialization + allocator/compiled stats - #3277
Conversation
|
I found one problem in the IFRT bindings with my local build, will push a fix shortly. I'm currently testing these changes inside ReactantServer with a huge model repository. |
2447a46 to
06c35e0
Compare
|
I have a ReactantServer feature branch which uses the the API changes here successfully, Server startup time is down from nearly 30 minutes to just under 3: EnzymeAD/ReactantServer.jl@main...feature/reactant-executable-cache Not sure about CI / whether I can get the tests to pass before the new Reactant jll is built. I have my own internal Bazel build pulling the patched Reactant jll version here: https://github.com/csvance/Reactant.jl/releases/tag/reactantextra-local-06c35e0d |
| compile_options_proto_size)); | ||
| } | ||
|
|
||
| #pragma region PjRtLoadedExecutable serialization and memory stats |
There was a problem hiding this comment.
@wsmoses it looks like it was used many times elsewhere in API.cpp, probably because the file is so large and it serves as a way to collapse the block in an IDE? I can remove it if it would make the changes sit cleaner in the file or try and group things differently.
|
@wsmoses the tests I add pass with the local libReactant / Build Reactant_jll and I'm actively using the changes as part of a test deployment, everything is working as expected. There are some pre-existing test failures that show up in two of the local libReactant test builds, but those were there before my changes. Merging this in would open up the potential to have a better kernel cache in Reactant beyond just autotuning. It's possible that I could eventually move ReactantServer off of the internal jll PJRT API and onto the public Reactant interface. These changes would be the first step towards that. |
|
Curious if you tried with using the compiled artifact with multi-gpu ? There was some problems last time i tried in #2779. |
I will test it out shortly; should be able to rule out whether there is some sort of issue on the XLA side since that's the only thing my PR touches. |
|
@Pangoraw I tried a few different variations with both PJRT and IFRT on multiple GPUs (nVidia A6000):
I don't currently have a GPU free to test something like 0,1 -> 1,2 or 0,1 -> 2,3. The output of the compiled program was the same no matter which order it was loaded on. I only tried on a single machine; I don't have a cluster where I could try something distributed. |
|
@wsmoses should I go ahead and merge this? Unsure of the process since it involves a bump to the jll. |
to trigger a new jll build, you need to make a PR to https://github.com/EnzymeAD/ReactantBuilder targeting the relevant commit on Reactant main and bumping the Reactant_jll version. |
| bytes = len == 0 ? UInt8[] : copy(unsafe_wrap(Array, ptr, (Int(len),); own=false)) | ||
| @ccall free(ptr::Ptr{UInt8})::Cvoid |
There was a problem hiding this comment.
why not ?
| bytes = len == 0 ? UInt8[] : copy(unsafe_wrap(Array, ptr, (Int(len),); own=false)) | |
| @ccall free(ptr::Ptr{UInt8})::Cvoid | |
| return unsafe_wrap(Array, ptr, (Int(len),); own=true) |
There was a problem hiding this comment.
@Pangoraw I'll extract it from the helper in Utils.jl and just apply the unsafe_wrap(...; own=true) pattern to the two call sites. unsafe_wrap already correctly handles len == 0 here, better to just simplify.
wsmoses
left a comment
There was a problem hiding this comment.
also the api.cpp changes and the julia changes should be separate prs
0d63a6a to
3238dd5
Compare
…mpiled memory stats XLA can serialize a compiled executable and load it again later, reset a device allocator's peak counters, and report the compiler's static memory accounting for an executable, but none of that was reachable through libReactantExtra. This adds the C++ side only; the Julia bindings, API and tests follow in a separate PR once a Reactant_jll has been built from this. - PjRtLoadedExecutableSerialize / PjRtClientLoadSerializedExecutable (optional CompileOptionsProto override so a program can be placed on another device) - PjRtDeviceClearMemoryStats - PjRtLoadedExecutableGetCompiledMemoryStats with a flat JLCompiledMemoryStats struct - IFRT counterparts for the PJRT-backed IFRT client, going through IFRT's own LoadedExecutable::Serialize and Compiler::DeserializeLoadedExecutable - GetCpuPjrtApi, linking @xla//xla/pjrt/c:pjrt_c_api_cpu_internal, so the CPU plugin's PJRT C API table is reachable next to the GPU one - Darwin exported-symbol entries for the new names Built locally with deps/build_local.jl (CUDA) and exercised through the Julia bindings on CPU, on two RTX A6000s, and under the IFRT runtime. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0187bPmjLReauAiN6YP2a3cd
3238dd5 to
940d651
Compare
…ory stats (Julia) Julia side of the shims added to libReactantExtra in EnzymeAD#3277: XLA.serialize_executable, XLA.load_serialized_executable, XLA.clear_memory_stats!, XLA.compiled_memory_stats and the CompiledMemoryStats struct, with PJRT and IFRT methods and Thunk conveniences for serialize and compiled stats. The serialized bytes take ownership of the malloc'd buffer through unsafe_wrap(...; own=true) instead of being copied. test/core/executable_serialization.jl: round trip with bit-identical results, compile-options override, invalid bytes, memory stats, stats reset. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0187bPmjLReauAiN6YP2a3cd
…ory stats (Julia) Julia side of the shims added to libReactantExtra in EnzymeAD#3277: XLA.serialize_executable, XLA.load_serialized_executable, XLA.clear_memory_stats!, XLA.compiled_memory_stats and the CompiledMemoryStats struct, with PJRT and IFRT methods and Thunk conveniences for serialize and compiled stats. The serialized bytes take ownership of the malloc'd buffer through unsafe_wrap(...; own=true) instead of being copied. test/core/executable_serialization.jl: round trip with bit-identical results, compile-options override, invalid bytes, memory stats, stats reset. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0187bPmjLReauAiN6YP2a3cd
This PR exposes executable serialization for PJRT so programs can be re-used without needing any kind of compile. This matters for ReactantServer.jl because every time the server restarts it can mean compiling 100+ mlir bundles, and even with an auto-tune cache this can take 30+ minutes.
It also exposes more detailed allocation usage and allows for clearing the memory high water mark for applications which need to measure exact memory water marks such as ReactantServer.
Without these provided by Reactant, I have to maintain my own downstream PJRT C bindings or patched Reactant jll which obviously isn't ideal in the long run. I also included IFRT bindings for the same functionality rather than just raising some sort of not implemented exception.
Working on building and testing this on my end. I confirmed that the general approach I took here works inside of ReactantServer via my vendored PJRT interface but it's one thing to just do all of the PJRT calls on my end and actually get this successfully upstreamed. Currently building Reactant jll locally so I can do a full end-to-end test of the changes.