Packages
snakepit
0.11.0
0.13.0
0.12.0
0.11.1
0.11.0
0.10.1
0.10.0
0.9.1
0.9.0
0.8.9
0.8.8
0.8.7
0.8.6
0.8.5
0.8.4
0.8.3
0.8.2
0.8.1
0.8.0
0.7.7
0.7.6
0.7.5
0.7.4
0.7.3
0.7.2
0.7.1
0.7.0
0.6.11
0.6.10
0.6.9
0.6.8
0.6.7
0.6.6
0.6.5
0.6.4
0.6.3
0.6.2
0.6.1
0.6.0
0.5.1
0.5.0
0.4.3
0.4.2
0.4.1
0.4.0
0.3.3
0.3.2
0.3.1
0.3.0
0.2.1
0.2.0
0.1.2
0.1.1
0.1.0
High-performance pooler and session manager for external language integrations. Supports Python, Node.js, Ruby, and more with gRPC streaming, session management, and production-ready process cleanup.
Current section
Files
Jump to
Current section
Files
lib/snakepit/zero_copy.ex
defmodule Snakepit.ZeroCopy do
@moduledoc """
Zero-copy interop helpers for DLPack and Arrow.
Handles create/import lifecycle for zero-copy handles, with copy-based
fallbacks when unavailable.
"""
alias Snakepit.Logger, as: SLog
alias Snakepit.ZeroCopyRef
@table :snakepit_zero_copy_handles
@log_category :bridge
@default_config %{
enabled: false,
dlpack: true,
arrow: true,
allow_fallback: true,
max_bytes: 64 * 1024 * 1024,
strict: false
}
@type export_opts ::
[
device: ZeroCopyRef.device(),
dtype: atom() | String.t(),
shape: tuple() | list(),
owner: :elixir | :python
]
def to_dlpack(term, opts \\ []), do: export(:dlpack, term, opts)
def to_arrow(term, opts \\ []), do: export(:arrow, term, opts)
def from_dlpack(ref, opts \\ []), do: import(:dlpack, ref, opts)
def from_arrow(ref, opts \\ []), do: import(:arrow, ref, opts)
def close(ref) do
ref = normalize_ref(ref)
ensure_table()
:ets.delete(@table, ref.ref)
:ok
end
defp export(kind, term, opts) when kind in [:dlpack, :arrow] do
start = System.monotonic_time(:microsecond)
config = config()
meta = build_metadata(kind, term, opts)
cond do
zero_copy_enabled?(config, kind) ->
ref = store_handle(kind, term, meta, copy?: false)
emit(:export, start, ref)
{:ok, ref}
config.allow_fallback ->
SLog.warning(
@log_category,
"Zero-copy #{kind} unavailable; falling back to copy (reason: :zero_copy_unavailable)"
)
ref = store_handle(kind, copy_term(term), meta, copy?: true)
emit(:fallback, start, ref)
{:ok, ref}
config.strict ->
{:error, :zero_copy_unavailable}
true ->
{:error, :zero_copy_unavailable}
end
end
defp import(kind, ref, _opts) when kind in [:dlpack, :arrow] do
start = System.monotonic_time(:microsecond)
ref = normalize_ref(ref)
ensure_table()
case :ets.lookup(@table, ref.ref) do
[{_ref, stored_kind, value, metadata}] when stored_kind == kind ->
emit(:import, start, Map.merge(ref, metadata))
{:ok, value}
[{_ref, stored_kind, _value, _metadata}] ->
{:error, {:invalid_zero_copy_kind, stored_kind}}
_ ->
{:error, :zero_copy_handle_not_found}
end
end
defp store_handle(kind, term, metadata, opts) do
ensure_table()
ref = make_ref()
entry = {ref, kind, term, metadata}
true = :ets.insert(@table, entry)
%ZeroCopyRef{
kind: kind,
device: metadata.device,
dtype: metadata.dtype,
shape: metadata.shape,
owner: metadata.owner,
ref: ref,
copy: Keyword.get(opts, :copy?, false),
bytes: metadata.bytes,
metadata: metadata.extra
}
end
defp build_metadata(kind, term, opts) do
%{
kind: kind,
device: Keyword.get(opts, :device, :cpu),
dtype: Keyword.get(opts, :dtype),
shape: Keyword.get(opts, :shape),
owner: Keyword.get(opts, :owner, :elixir),
bytes: term_bytes(term),
extra: Keyword.get(opts, :metadata, %{})
}
end
defp emit(event, start_us, %ZeroCopyRef{} = ref) do
duration_us = System.monotonic_time(:microsecond) - start_us
measurements =
%{duration_ms: duration_us / 1000}
|> maybe_put(:bytes, ref.bytes)
metadata = %{
kind: ref.kind,
device: ref.device,
dtype: ref.dtype,
shape: ref.shape
}
:telemetry.execute([:snakepit, :zero_copy, event], measurements, metadata)
end
defp maybe_put(map, _key, nil), do: map
defp maybe_put(map, key, value), do: Map.put(map, key, value)
defp copy_term(term) do
term
|> :erlang.term_to_binary()
|> :erlang.binary_to_term()
end
defp term_bytes(term) when is_binary(term), do: byte_size(term)
defp term_bytes(term) do
if function_exported?(:erlang, :external_size, 1) do
:erlang.external_size(term)
else
nil
end
end
defp zero_copy_enabled?(config, :dlpack),
do: config.enabled && config.dlpack
defp zero_copy_enabled?(config, :arrow),
do: config.enabled && config.arrow
defp config do
config =
:snakepit
|> Application.get_env(:zero_copy, [])
|> Map.new()
Map.merge(@default_config, config)
end
defp normalize_ref(%ZeroCopyRef{} = ref), do: ref
defp normalize_ref(map) when is_map(map), do: ZeroCopyRef.from_map(map)
defp normalize_ref(_), do: %ZeroCopyRef{kind: :dlpack, ref: make_ref()}
defp ensure_table do
case :ets.whereis(@table) do
:undefined ->
:ets.new(@table, [:named_table, :set, :public, {:read_concurrency, true}])
_ ->
@table
end
end
end