Swift · Serialization

Serialization

import USearch 3.0.0-rc.1 · 4c3e5023

Checkpoints ride the SafeGraphs interchange format, and the path extension picks the layout: a .safegraphs monolith, or a .safegraphs.json manifest beside its shards.

try index.on(executor).save(path: "index.safegraphs")
let copied = try DenseIndex.loadRam("index.safegraphs", config: DenseLoadRamConfig(executor: executor))
let mapped = try DenseIndex.viewRam("index.safegraphs")

let described = try DenseIndex.metadataRead("index.safegraphs")
print(described.dimensions, described.metric, described.quantization)

loadRam and loadVram copy the checkpoint into a new mutable index; viewRam serves it in place through a memory mapping, and every mutating verb on a viewed index reports readOnly. Saving demands a mutation-free window, while concurrent readers keep searching throughout.

An in-memory checkpoint is the same bytes reached another way. saveBuffer returns them as a Monolith read in place from an Arena, since only the call knows how many there are, and loadRamBuffer and metadataReadBuffer read them back through a span.

let monolith = try index.on(executor).saveBuffer(arena: arena)
let restored = try monolith.withUnsafeBytes { bytes in
    try DenseIndex.loadRamBuffer(bytes.bytes, config: DenseLoadRamBufferConfig(executor: executor))
}

API

StructDenseMetadataThe geometry and metric a checkpoint froze - everything a reader learns without opening it, and nothing a reader must choose for itself.4
DenseIndex.swift:102 ↗
struct DenseMetadata

A checkpoint records neither the placement nor the bound on entries sharing one key: the first says where it was written rather than where a reader must keep it, and the second bounds a live handle's mutable state rather than the serialized form. Both stay the caller's to choose when loading.

Conforms to

Sendable
dimensionsThe scalars per stored vector.
DenseIndex.swift:104 ↗
var dimensions: Int
metricThe distance kind the checkpoint scores with.
DenseIndex.swift:106 ↗
var metric: DenseMetric
quantizationThe scalar type its vectors are stored and quantized into.
DenseIndex.swift:108 ↗
var quantization: Scalar
connectivityThe connections-per-node bound the graph was built with.
DenseIndex.swift:110 ↗
var connectivity: Int
StructDenseSaveConfigThe knobs of save.3
DenseIndex.swift:514 ↗
struct DenseSaveConfig
progressCalled with shards written.
DenseIndex.swift:516 ↗
var progress: Progress?
progressStepsReports the call makes at most, the last at completion; zero derives to ten.
DenseIndex.swift:518 ↗
var progressSteps: Int
init
DenseIndex.swift:520 ↗
init(progress: Progress? = nil, progressSteps: Int = 0)
StructDenseSaveBufferConfigThe knobs of saveBuffer.3
DenseIndex.swift:527 ↗
struct DenseSaveBufferConfig
progressCalled with bytes written.
DenseIndex.swift:529 ↗
var progress: Progress?
progressStepsReports the call makes at most, the last at completion; zero derives to ten.
DenseIndex.swift:531 ↗
var progressSteps: Int
init
DenseIndex.swift:533 ↗
init(progress: Progress? = nil, progressSteps: Int = 0)
StructDenseLoadRamConfigThe knobs of loadRam.7
DenseIndex.swift:548 ↗
struct DenseLoadRamConfig
executorThe executor the read fans out over, one shard per thread; nil reads on the calling thread.
DenseIndex.swift:550 ↗
var executor: Executor?
memoryDomainsThe memory domains to place the parts on, part p loading into shard p modulo their count; empty derives every node the process may allocate on.
DenseIndex.swift:553 ↗
var memoryDomains: [Int32]
cpuClassThe class to dispatch as; nil derives the CPU's own.
DenseIndex.swift:555 ↗
var cpuClass: CPUClass?
valuesPerKeyThe bound on entries sharing one key, which no checkpoint records; zero is unbounded.
DenseIndex.swift:557 ↗
var valuesPerKey: Int
progressCalled with shards placed.
DenseIndex.swift:559 ↗
var progress: Progress?
progressStepsReports the call makes at most, the last at completion; zero derives to ten.
DenseIndex.swift:561 ↗
var progressSteps: Int
init
DenseIndex.swift:563 ↗
init(executor: Executor? = nil, memoryDomains: [Int32] = [], cpuClass: CPUClass? = nil, valuesPerKey: Int = 0, progress: Progress? = nil, progressSteps: Int = 0)
StructDenseLoadVramConfigThe knobs of loadVram.7
DenseIndex.swift:581 ↗
struct DenseLoadVramConfig
executorThe executor the upload runs on, one stream per device; nil uploads from the calling thread.
DenseIndex.swift:583 ↗
var executor: Executor?
devicesThe devices to place the parts on, part p loading onto device p modulo their count; empty derives one free device.
DenseIndex.swift:586 ↗
var devices: [Int32]
gpuClassThe class to dispatch as; nil derives the devices' own.
DenseIndex.swift:588 ↗
var gpuClass: GPUClass?
valuesPerKeyThe bound on entries sharing one key, which no checkpoint records; zero is unbounded.
DenseIndex.swift:590 ↗
var valuesPerKey: Int
progressCalled with shards placed.
DenseIndex.swift:592 ↗
var progress: Progress?
progressStepsReports the call makes at most, the last at completion; zero derives to ten.
DenseIndex.swift:594 ↗
var progressSteps: Int
init
DenseIndex.swift:596 ↗
init(executor: Executor? = nil, devices: [Int32] = [], gpuClass: GPUClass? = nil, valuesPerKey: Int = 0, progress: Progress? = nil, progressSteps: Int = 0)
StructDenseLoadRamBufferConfigThe knobs of loadRamBuffer.7
DenseIndex.swift:614 ↗
struct DenseLoadRamBufferConfig
executorThe executor the copy fans out over; nil copies on the calling thread.
DenseIndex.swift:616 ↗
var executor: Executor?
memoryDomainsThe memory domains to place shards on; the monolith lands whole on the first.
DenseIndex.swift:618 ↗
var memoryDomains: [Int32]
cpuClassThe class to dispatch as; nil derives the CPU's own.
DenseIndex.swift:620 ↗
var cpuClass: CPUClass?
valuesPerKeyThe bound on entries sharing one key, which no checkpoint records; zero is unbounded.
DenseIndex.swift:622 ↗
var valuesPerKey: Int
progressCalled with bytes placed.
DenseIndex.swift:624 ↗
var progress: Progress?
progressStepsReports the call makes at most, the last at completion; zero derives to ten.
DenseIndex.swift:626 ↗
var progressSteps: Int
init
DenseIndex.swift:628 ↗
init(executor: Executor? = nil, memoryDomains: [Int32] = [], cpuClass: CPUClass? = nil, valuesPerKey: Int = 0, progress: Progress? = nil, progressSteps: Int = 0)
StructDenseLoadVramBufferConfigThe knobs of loadVramBuffer.7
DenseIndex.swift:646 ↗
struct DenseLoadVramBufferConfig
executorThe executor the upload runs on, one stream per device; nil uploads from the calling thread.
DenseIndex.swift:648 ↗
var executor: Executor?
devicesThe devices to place shards on; the monolith lands whole on the first.
DenseIndex.swift:650 ↗
var devices: [Int32]
gpuClassThe class to dispatch as; nil derives the devices' own.
DenseIndex.swift:652 ↗
var gpuClass: GPUClass?
valuesPerKeyThe bound on entries sharing one key, which no checkpoint records; zero is unbounded.
DenseIndex.swift:654 ↗
var valuesPerKey: Int
progressCalled with bytes placed.
DenseIndex.swift:656 ↗
var progress: Progress?
progressStepsReports the call makes at most, the last at completion; zero derives to ten.
DenseIndex.swift:658 ↗
var progressSteps: Int
init
DenseIndex.swift:660 ↗
init(executor: Executor? = nil, devices: [Int32] = [], gpuClass: GPUClass? = nil, valuesPerKey: Int = 0, progress: Progress? = nil, progressSteps: Int = 0)
StructDenseViewNvmeConfigThe knobs of viewNvme.4
DenseIndex.swift:678 ↗
struct DenseViewNvmeConfig
executorThe NVMe executor whose rings read the checkpoint's headers; nil reads on the calling thread.
DenseIndex.swift:680 ↗
var executor: Executor?
pathsThe devices the parts are served from - a filesystem path, a transport identifier, or a FUSE mount over object storage; empty serves each part where its manifest points.
DenseIndex.swift:683 ↗
var paths: [String]
cpuClassThe class to dispatch as; nil derives the CPU's own.
DenseIndex.swift:685 ↗
var cpuClass: CPUClass?
init
DenseIndex.swift:687 ↗
init(executor: Executor? = nil, paths: [String] = [], cpuClass: CPUClass? = nil)
StructMonolithThe monolith saveBuffer wrote into an arena, read in place.2
DenseIndex.swift:838 ↗
struct Monolith
countThe bytes in the monolith.
DenseIndex.swift:845 ↗
var count: Int { get }
withUnsafeBytesLends the serialized checkpoint to body, which must not keep the buffer past its return.
DenseIndex.swift:848 ↗
func withUnsafeBytes<Result, Failure>(
    _ body: (UnsafeRawBufferPointer) throws(Failure) -> Result
) throws(Failure) -> Result where Failure : Error