Description
Zero-Copy Julia to R Array Bridge via ALTREP.
Description
Provides zero-copy R views of Julia-owned arrays by implementing ALTREP (Alternative Representations) classes that return pointers directly into Julia's memory. The package integrates with 'JuliaCall' and uses C-level finalizers for safe cross-runtime garbage collection.
README.md
jlview — Zero-Copy Julia↔R Arrays via ALTREP
jlview provides zero-copy R views of Julia-owned arrays using R’s ALTREP (Alternative Representations) framework. Instead of copying data between Julia and R, jlview returns lightweight R vectors that point directly into Julia’s memory.
| Latency | R Memory | |
|---|---|---|
| jlview (zero-copy) | 38 ms | 0 MB |
| copy (collect) | 2.7 s | 9.3 GB |
| Improvement | 72× | 100% savings |
Benchmark: 50K × 25K Float64 matrix (9.3 GB)
Installation
# install.packages("remotes")
remotes::install_github("tanaylab/jlview")
Requirements
Usage
library(jlview)
JuliaCall::julia_setup()
# Create a Julia array
JuliaCall::julia_command("x = randn(10000, 1000)")
# Zero-copy view — R sees Julia's memory directly
m <- jlview(JuliaCall::julia_eval("x"))
dim(m) # [1] 10000 1000
sum(m) # works natively, no data copied
# Named arrays are supported
JuliaCall::julia_command("using NamedArrays")
JuliaCall::julia_command("named = NamedArray(randn(3), [\"a\", \"b\", \"c\"])")
v <- jlview_named_vector(JuliaCall::julia_eval("named"))
v["a"] # access by name, still zero-copy
# Sparse matrices (zero-copy values, shifted indices)
JuliaCall::julia_command("using SparseArrays")
JuliaCall::julia_command("sp = sprand(1000, 500, 0.01)")
s <- jlview_sparse(JuliaCall::julia_eval("sp"))
class(s) # "dgCMatrix"
# Explicit release to free Julia memory early
jlview_release(m)
Key Features
- Zero-copy dense arrays — Float64, Int32 map directly to R’s REALSXP, INTSXP
- Type conversion — Float32, Int64, Int16 are converted once in Julia, then zero-copy to R
- Named arrays — NamedArray row/column names attached atomically without triggering copy
- Sparse matrices —
dgCMatrixwith zero-copy values (nzval) and shifted indices - Copy-on-write — R’s standard COW semantics: reads are zero-copy, writes trigger materialization
- GC safety — Julia arrays are pinned while R holds references; three-layer defense (pinning dict, memory pressure tracking, explicit release)
- Fork safety — Safe with
parallel::mclapply()(PID-guarded finalizers) - Serialization —
saveRDS()/readRDS()work correctly (materializes on save)
Supported Types
| Julia type | R type | Method |
|---|---|---|
Array{Float64} | numeric | Direct zero-copy |
Array{Int32} | integer | Direct zero-copy |
Array{Float32} | numeric | Convert in Julia, then zero-copy |
Array{Int64} | numeric | Convert in Julia, then zero-copy |
Array{Int16} | integer | Convert in Julia, then zero-copy |
Array{UInt8} | raw | ALTREP RAWSXP zero-copy |
Array{Bool} | logical | Copy (layout incompatible) |
String[] | character | Copy (layout incompatible) |
How It Works
jlview uses R’s ALTREP framework to create R vectors backed by Julia memory:
- Pin — The Julia array is stored in a global dictionary, preventing Julia’s GC from collecting it
- Wrap — An ALTREP R vector is created whose
Dataptrreturns Julia’s raw data pointer - Use — R operations (sum, subsetting, etc.) read directly from Julia’s memory
- Release — When R garbage-collects the ALTREP object, a C finalizer calls Julia to unpin the array
The entire pin→ALTREP→finalizer path is implemented in C using Julia’s C API (jl_call1), avoiding JuliaCall overhead and ensuring safety during R’s garbage collection.