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
You can install the package from CRAN:
install.packages("jlview")Or you can install the package from GitHub:
# install.packages("remotes")
remotes::install_github("tanaylab/jlview")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.