Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
48 commits
Select commit Hold shift + click to select a range
5c6d79e
ggml: add a non-blocking query for backend events
danielhanchen Sep 5, 2026
b048783
llama: coalesce sequence state transfers, and issue them asynchronously
danielhanchen Sep 5, 2026
1d98f93
server: take the park and restore copies off the decode loop
danielhanchen Sep 5, 2026
e7e88e9
ggml: map the event query onto HIP and MUSA
danielhanchen Sep 6, 2026
d33f04f
llama: require a real event query before copying a sequence asynchron…
danielhanchen Sep 6, 2026
0160ea4
server: wait for the parks still in flight before tearing the context…
danielhanchen Sep 6, 2026
c0d9297
server: charge a resume candidate its own lookahead before admitting it
danielhanchen Sep 6, 2026
cd54f60
llama: report the host memory a transfer holds, not the kind it asked…
danielhanchen Sep 6, 2026
22c90bd
llama: check the size and the flags a sequence transfer is issued with
danielhanchen Sep 6, 2026
e8f8b2f
ggml: stop collecting an error the CUDA event query never sets
danielhanchen Sep 6, 2026
86315ea
ggml: bump the backend API version for the new device interface member
danielhanchen Sep 6, 2026
888603d
server: do not leave an issued async park holding the room the decode…
danielhanchen Sep 6, 2026
2522c59
Merge branch 'feat/server-side-preemption' into feat/server-side-pree…
danielhanchen Sep 6, 2026
71ee8d4
Merge branch 'feat/server-side-preemption' into feat/server-side-pree…
danielhanchen Sep 6, 2026
4e49a05
Merge branch 'feat/server-side-preemption' into feat/server-side-pree…
danielhanchen Sep 6, 2026
53553ff
Merge branch 'feat/server-side-preemption' into feat/server-side-pree…
danielhanchen Sep 6, 2026
01dfecc
Merge branch 'feat/server-side-preemption' into feat/server-side-pree…
danielhanchen Sep 6, 2026
47ef8de
Merge branch 'feat/server-side-preemption' into feat/server-side-pree…
danielhanchen Sep 6, 2026
cd1cd4e
server: count a slot being restored in the asynchronous lookahead margin
danielhanchen Sep 6, 2026
0303888
Merge branch 'feat/server-side-preemption' into feat/server-side-pree…
danielhanchen Sep 6, 2026
cac5ee6
Merge branch 'feat/server-side-preemption' into feat/server-side-pree…
danielhanchen Sep 6, 2026
8d74efb
Merge branch 'feat/server-side-preemption' into feat/server-side-pree…
danielhanchen Sep 6, 2026
42e5369
server: a round with a context shift waits for every park and restore…
danielhanchen Sep 6, 2026
65f6173
Merge feat/server-side-preemption: keep only the shared prefix of a r…
danielhanchen Sep 6, 2026
ad3856a
Merge feat/server-side-preemption: planner accounting for reused slot…
danielhanchen Sep 6, 2026
2ba0dcf
Merge feat/server-side-preemption: the rotation parks the resident th…
danielhanchen Sep 6, 2026
2297d5d
Merge feat/server-side-preemption: a parked slot survives an aborted …
danielhanchen Sep 6, 2026
02a3e11
server: the abort sweep also leaves a slot whose copy is in flight alone
danielhanchen Sep 6, 2026
5a13c67
server: asynchronous copies and the rotation, the budget and the cach…
danielhanchen Sep 6, 2026
e432b15
Merge feat/server-side-preemption: the leader is measured by what a r…
danielhanchen Sep 6, 2026
82f40df
preempt: post no copies for a transfer that failed part way, park syn…
danielhanchen Sep 6, 2026
2b4a691
tests: a state transfer that fails one byte short posts no copies, on…
danielhanchen Sep 6, 2026
faa3dd3
Merge feat/server-side-preemption: parent and child alone in the pool…
danielhanchen Sep 6, 2026
9e827b5
preempt: a slot whose park buffer comes back pageable parks synchrono…
danielhanchen Sep 6, 2026
a4b62f2
preempt: the graphs that follow a restore wait for its copies, on the…
danielhanchen Sep 6, 2026
294d2a9
preempt: idle parked RAM is given back when another slot needs to par…
danielhanchen Sep 7, 2026
dffa102
Merge feat/server-side-preemption: recurrent caches served without pr…
danielhanchen Sep 7, 2026
012ef75
llama: the copies wait for a fence the context records after every de…
danielhanchen Sep 7, 2026
a5749d1
Merge feat/server-side-preemption: a started slot's reservation count…
danielhanchen Sep 7, 2026
a56d49e
Merge feat/server-side-preemption: the shift and the planner inside t…
danielhanchen Sep 7, 2026
7eff726
llama: staging counts by what a buffer charges; fences installed afte…
danielhanchen Sep 7, 2026
8cb58df
Merge feat/server-side-preemption: what a started slot keeps is decid…
danielhanchen Sep 7, 2026
3475eb0
llama: the shift wait runs before the draft is asked for; the last tr…
danielhanchen Sep 7, 2026
027c86a
Merge feat/server-side-preemption: the resume order read on every load
danielhanchen Sep 7, 2026
114e230
Merge feat/server-side-preemption: the rotation-budget test keeps its…
danielhanchen Sep 7, 2026
ebfa47b
llama: a context freed with live transfers drains and disowns them; t…
danielhanchen Sep 7, 2026
b1a31b4
Merge feat/server-side-preemption: the resume order belongs to the co…
danielhanchen Sep 7, 2026
58c6e33
Merge /mnt/disks/unslothai/daniel3/workspace_12/llamacpp_fork into fe…
danielhanchen Sep 7, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions common/arg.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1717,6 +1717,16 @@ common_params_context common_params_parser_init(common_params & params, llama_ex
params.preempt_ram_mib = value;
}
).set_env("LLAMA_ARG_PREEMPT_RAM").set_examples({LLAMA_EXAMPLE_SERVER}));
add_opt(common_arg(
{"--preempt-async"},
{"--no-preempt-async"},
"copy a parked sequence out of and back into the KV cache on a stream of its own, so the "
"slots that keep running do not wait for it (default: enabled, needs a backend that can "
"copy asynchronously, otherwise the copies are synchronous as before)",
[](common_params & params, bool value) {
params.preempt_async = value;
}
).set_env("LLAMA_ARG_PREEMPT_ASYNC").set_examples({LLAMA_EXAMPLE_SERVER}));
add_opt(common_arg(
{"-kvu", "--kv-unified"},
{"-no-kvu", "--no-kv-unified"},
Expand Down
1 change: 1 addition & 0 deletions common/common.h
Original file line number Diff line number Diff line change
Expand Up @@ -615,6 +615,7 @@ struct common_params {
int32_t checkpoint_min_step = 8192; // minimum spacing between context checkpoints
int32_t cache_ram_mib = 8192; // -1 = no limit, 0 - disable, 1 = 1 MiB, etc.
int32_t preempt_ram_mib = 8192; // host RAM for parked (preempted) sequences: -1 = no limit, 0 = disable preemption
bool preempt_async = true; // park and restore on a stream of their own, off the decode loop

std::string hostname = "127.0.0.1";
std::string public_path = ""; // NOLINT
Expand Down
10 changes: 10 additions & 0 deletions ggml/include/ggml-backend.h
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,9 @@ extern "C" {
GGML_API size_t ggml_backend_buffer_get_alloc_size(ggml_backend_buffer_t buffer, const struct ggml_tensor * tensor);
GGML_API void ggml_backend_buffer_clear (ggml_backend_buffer_t buffer, uint8_t value);
GGML_API bool ggml_backend_buffer_is_host (ggml_backend_buffer_t buffer);
// whether the buffer copies a strided set of rows in one call (see ggml_backend_tensor_set_2d);
// without it the generic path issues one transfer per row
GGML_API bool ggml_backend_buffer_supports_2d (ggml_backend_buffer_t buffer);
GGML_API void ggml_backend_buffer_set_usage (ggml_backend_buffer_t buffer, enum ggml_backend_buffer_usage usage);
GGML_API enum ggml_backend_buffer_usage ggml_backend_buffer_get_usage (ggml_backend_buffer_t buffer);
GGML_API ggml_backend_buffer_type_t ggml_backend_buffer_get_type (ggml_backend_buffer_t buffer);
Expand Down Expand Up @@ -125,6 +128,10 @@ extern "C" {
GGML_API void ggml_backend_event_free(ggml_backend_event_t event);
GGML_API void ggml_backend_event_record(ggml_backend_event_t event, ggml_backend_t backend);
GGML_API void ggml_backend_event_synchronize(ggml_backend_event_t event);
// non-blocking: true once everything recorded before the event has completed.
// backends without a query implementation fall back to a blocking synchronize and return true,
// which ggml_backend_dev_supports_event_query() tells apart from a real non-blocking query.
GGML_API bool ggml_backend_event_query(ggml_backend_event_t event);
GGML_API void ggml_backend_event_wait(ggml_backend_t backend, ggml_backend_event_t event);

//
Expand Down Expand Up @@ -190,6 +197,9 @@ extern "C" {
GGML_API ggml_backend_buffer_t ggml_backend_dev_buffer_from_host_ptr(ggml_backend_dev_t device, void * ptr, size_t size, size_t max_tensor_size);

GGML_API bool ggml_backend_dev_supports_op(ggml_backend_dev_t device, const struct ggml_tensor * op);
// whether ggml_backend_event_query() on this device really is non-blocking, i.e. whether
// the device implements it rather than falling back to a blocking synchronize
GGML_API bool ggml_backend_dev_supports_event_query(ggml_backend_dev_t device);
GGML_API bool ggml_backend_dev_supports_buft(ggml_backend_dev_t device, ggml_backend_buffer_type_t buft);
GGML_API bool ggml_backend_dev_offload_op(ggml_backend_dev_t device, const struct ggml_tensor * op);

Expand Down
7 changes: 6 additions & 1 deletion ggml/src/ggml-backend-impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
extern "C" {
#endif

#define GGML_BACKEND_API_VERSION 2
#define GGML_BACKEND_API_VERSION 3

//
// Backend buffer type
Expand Down Expand Up @@ -200,6 +200,11 @@ extern "C" {
ggml_backend_event_t (*event_new) (ggml_backend_dev_t dev);
void (*event_free) (ggml_backend_dev_t dev, ggml_backend_event_t event);
void (*event_synchronize) (ggml_backend_dev_t dev, ggml_backend_event_t event);

// (optional) non-blocking completion test for an event.
// kept last so that backends that do not implement it need no change: a missing entry
// is NULL, and ggml_backend_event_query() then falls back to a blocking synchronize.
bool (*event_query) (ggml_backend_dev_t dev, ggml_backend_event_t event);
};

struct ggml_backend_device {
Expand Down
1 change: 1 addition & 0 deletions ggml/src/ggml-backend-meta.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,7 @@ static const ggml_backend_device_i ggml_backend_meta_device_iface = {
/* .event_new = */ nullptr,
/* .event_free = */ nullptr,
/* .event_synchronize = */ nullptr,
/* .event_query = */ NULL,
};

static bool ggml_backend_dev_is_meta(ggml_backend_dev_t dev) {
Expand Down
21 changes: 21 additions & 0 deletions ggml/src/ggml-backend.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,10 @@ bool ggml_backend_buffer_is_host(ggml_backend_buffer_t buffer) {
return ggml_backend_buft_is_host(ggml_backend_buffer_get_type(buffer));
}

bool ggml_backend_buffer_supports_2d(ggml_backend_buffer_t buffer) {
return buffer->iface.set_tensor_2d != NULL && buffer->iface.get_tensor_2d != NULL;
}

void ggml_backend_buffer_set_usage(ggml_backend_buffer_t buffer, enum ggml_backend_buffer_usage usage) {
GGML_ASSERT(buffer);
buffer->usage = usage;
Expand Down Expand Up @@ -551,6 +555,18 @@ void ggml_backend_event_synchronize(ggml_backend_event_t event) {
event->device->iface.event_synchronize(event->device, event);
}

bool ggml_backend_event_query(ggml_backend_event_t event) {
GGML_ASSERT(event);

if (event->device->iface.event_query == NULL) {
// no way to ask: the honest answer is to wait for it and then say yes
ggml_backend_event_synchronize(event);
return true;
}

return event->device->iface.event_query(event->device, event);
}

void ggml_backend_event_wait(ggml_backend_t backend, ggml_backend_event_t event) {
GGML_ASSERT(backend);
GGML_ASSERT(backend->iface.event_wait != NULL);
Expand Down Expand Up @@ -627,6 +643,11 @@ bool ggml_backend_dev_supports_op(ggml_backend_dev_t device, const struct ggml_t
return device->iface.supports_op(device, op);
}

bool ggml_backend_dev_supports_event_query(ggml_backend_dev_t device) {
GGML_ASSERT(device);
return device->iface.event_query != NULL;
}

bool ggml_backend_dev_supports_buft(ggml_backend_dev_t device, ggml_backend_buffer_type_t buft) {
GGML_ASSERT(device);
return device->iface.supports_buft(device, buft);
Expand Down
1 change: 1 addition & 0 deletions ggml/src/ggml-blas/ggml-blas.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -469,6 +469,7 @@ static const struct ggml_backend_device_i ggml_backend_blas_device_i = {
/* .event_new = */ NULL,
/* .event_free = */ NULL,
/* .event_synchronize = */ NULL,
/* .event_query = */ NULL,
};

// backend reg interface
Expand Down
1 change: 1 addition & 0 deletions ggml/src/ggml-cann/ggml-cann.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2948,6 +2948,7 @@ static const ggml_backend_device_i ggml_backend_cann_device_interface = {
/* .event_new = */ ggml_backend_cann_device_event_new,
/* .event_free = */ ggml_backend_cann_device_event_free,
/* .event_synchronize = */ ggml_backend_cann_device_event_synchronize,
/* .event_query = */ NULL,
};

// backend reg
Expand Down
1 change: 1 addition & 0 deletions ggml/src/ggml-cpu/ggml-cpu.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -500,6 +500,7 @@ static const struct ggml_backend_device_i ggml_backend_cpu_device_i = {
/* .event_new = */ NULL,
/* .event_free = */ NULL,
/* .event_synchronize = */ NULL,
/* .event_query = */ NULL,
};

// CPU backend - backend (reg)
Expand Down
18 changes: 18 additions & 0 deletions ggml/src/ggml-cuda/ggml-cuda.cu
Original file line number Diff line number Diff line change
Expand Up @@ -5375,6 +5375,23 @@ static void ggml_backend_cuda_device_event_synchronize(ggml_backend_dev_t dev, g
CUDA_CHECK(cudaEventSynchronize((cudaEvent_t)event->context));
}

static bool ggml_backend_cuda_device_event_query(ggml_backend_dev_t dev, ggml_backend_event_t event) {
GGML_UNUSED(dev);

const cudaError_t err = cudaEventQuery((cudaEvent_t)event->context);

// not an error, and nothing to clear: cudaEventQuery() returns cudaErrorNotReady
// without recording it as the thread's last error, so collecting one here would only
// consume somebody else's, and a real launch failure would be swallowed
if (err == cudaErrorNotReady) {
return false;
}

CUDA_CHECK(err);

return true;
}

static const ggml_backend_device_i ggml_backend_cuda_device_interface = {
/* .get_name = */ ggml_backend_cuda_device_get_name,
/* .get_description = */ ggml_backend_cuda_device_get_description,
Expand All @@ -5391,6 +5408,7 @@ static const ggml_backend_device_i ggml_backend_cuda_device_interface = {
/* .event_new = */ ggml_backend_cuda_device_event_new,
/* .event_free = */ ggml_backend_cuda_device_event_free,
/* .event_synchronize = */ ggml_backend_cuda_device_event_synchronize,
/* .event_query = */ ggml_backend_cuda_device_event_query,
};

// backend reg
Expand Down
2 changes: 2 additions & 0 deletions ggml/src/ggml-cuda/vendors/hip.h
Original file line number Diff line number Diff line change
Expand Up @@ -58,10 +58,12 @@
#define cudaDeviceSynchronize hipDeviceSynchronize
#define cudaError_t hipError_t
#define cudaErrorMemoryAllocation hipErrorOutOfMemory
#define cudaErrorNotReady hipErrorNotReady
#define cudaErrorPeerAccessAlreadyEnabled hipErrorPeerAccessAlreadyEnabled
#define cudaErrorPeerAccessNotEnabled hipErrorPeerAccessNotEnabled
#define cudaEventCreateWithFlags hipEventCreateWithFlags
#define cudaEventDisableTiming hipEventDisableTiming
#define cudaEventQuery hipEventQuery
#define cudaEventRecord hipEventRecord
#define cudaEventSynchronize hipEventSynchronize
#define cudaEvent_t hipEvent_t
Expand Down
2 changes: 2 additions & 0 deletions ggml/src/ggml-cuda/vendors/musa.h
Original file line number Diff line number Diff line change
Expand Up @@ -46,10 +46,12 @@
#define cudaDeviceSynchronize musaDeviceSynchronize
#define cudaError_t musaError_t
#define cudaErrorMemoryAllocation musaErrorMemoryAllocation
#define cudaErrorNotReady musaErrorNotReady
#define cudaErrorPeerAccessAlreadyEnabled musaErrorPeerAccessAlreadyEnabled
#define cudaErrorPeerAccessNotEnabled musaErrorPeerAccessNotEnabled
#define cudaEventCreateWithFlags musaEventCreateWithFlags
#define cudaEventDisableTiming musaEventDisableTiming
#define cudaEventQuery musaEventQuery
#define cudaEventRecord musaEventRecord
#define cudaEventSynchronize musaEventSynchronize
#define cudaEvent_t musaEvent_t
Expand Down
1 change: 1 addition & 0 deletions ggml/src/ggml-et/ggml-et.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1684,6 +1684,7 @@ static const struct ggml_backend_device_i ggml_backend_et_device_i = {
/* .event_new = */ NULL,
/* .event_free = */ NULL,
/* .event_synchronize = */ NULL,
/* .event_query = */ NULL,
};

/*
Expand Down
1 change: 1 addition & 0 deletions ggml/src/ggml-hexagon/ggml-hexagon.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4274,6 +4274,7 @@ static const struct ggml_backend_device_i ggml_backend_hexagon_device_i = {
/* .event_new = */ NULL,
/* .event_free = */ NULL,
/* .event_synchronize = */ NULL,
/* .event_query = */ NULL,
};

//** backend registry
Expand Down
1 change: 1 addition & 0 deletions ggml/src/ggml-metal/ggml-metal.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -818,6 +818,7 @@ static ggml_backend_device_i ggml_backend_metal_device_i = {
/* .event_new = */ ggml_backend_metal_device_event_new,
/* .event_free = */ ggml_backend_metal_device_event_free,
/* .event_synchronize = */ ggml_backend_metal_device_event_synchronize,
/* .event_query = */ NULL,
};

// backend registry
Expand Down
1 change: 1 addition & 0 deletions ggml/src/ggml-opencl/ggml-opencl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11332,6 +11332,7 @@ struct ggml_backend_device_i ggml_backend_opencl_device_i = {
/* .event_new = */ NULL,
/* .event_free = */ NULL,
/* .event_synchronize = */ NULL,
/* .event_query = */ NULL,
};
}

Expand Down
1 change: 1 addition & 0 deletions ggml/src/ggml-openvino/ggml-openvino.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1454,6 +1454,7 @@ static const struct ggml_backend_device_i ggml_backend_openvino_device_interface
/* .event_new = */ NULL,
/* .event_free = */ NULL,
/* .event_synchronize = */ NULL,
/* .event_query = */ NULL,
};

struct ggml_backend_openvino_reg_context {
Expand Down
1 change: 1 addition & 0 deletions ggml/src/ggml-rpc/ggml-rpc.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1945,6 +1945,7 @@ static const struct ggml_backend_device_i ggml_backend_rpc_device_i = {
/* .event_new = */ NULL,
/* .event_free = */ NULL,
/* .event_synchronize = */ NULL,
/* .event_query = */ NULL,
};

// backend reg interface
Expand Down
1 change: 1 addition & 0 deletions ggml/src/ggml-sycl/ggml-sycl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6448,6 +6448,7 @@ static const ggml_backend_device_i ggml_backend_sycl_device_interface = {
/* .event_new = */ ggml_backend_sycl_device_event_new,
/* .event_free = */ ggml_backend_sycl_device_event_free,
/* .event_synchronize = */ ggml_backend_sycl_device_event_synchronize,
/* .event_query = */ NULL,
};

// backend reg
Expand Down
1 change: 1 addition & 0 deletions ggml/src/ggml-virtgpu/ggml-backend-device.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -157,4 +157,5 @@ const ggml_backend_device_i ggml_backend_remoting_device_interface = {
/* .event_new = */ NULL,
/* .event_free = */ NULL,
/* .event_synchronize = */ NULL,
/* .event_query = */ NULL,
};
1 change: 1 addition & 0 deletions ggml/src/ggml-vulkan/ggml-vulkan.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18798,6 +18798,7 @@ static const struct ggml_backend_device_i ggml_backend_vk_device_i = {
/* .event_new = */ ggml_backend_vk_device_event_new,
/* .event_free = */ ggml_backend_vk_device_event_free,
/* .event_synchronize = */ ggml_backend_vk_device_event_synchronize,
/* .event_query = */ NULL,
};

static const char * ggml_backend_vk_reg_get_name(ggml_backend_reg_t reg) {
Expand Down
1 change: 1 addition & 0 deletions ggml/src/ggml-webgpu/ggml-webgpu.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4661,6 +4661,7 @@ static struct ggml_backend_device_i ggml_backend_webgpu_device_i = {
/* .event_new = */ ggml_backend_webgpu_device_event_new,
/* .event_free = */ ggml_backend_webgpu_device_event_free,
/* .event_synchronize = */ ggml_backend_webgpu_device_event_synchronize,
/* .event_query = */ NULL,
};

/* End GGML Backend Device Interface */
Expand Down
1 change: 1 addition & 0 deletions ggml/src/ggml-zdnn/ggml-zdnn.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -547,6 +547,7 @@ static ggml_backend_device_i ggml_backend_zdnn_device_i = {
/* .event_new = */ NULL,
/* .event_free = */ NULL,
/* .event_synchronize = */ NULL,
/* .event_query = */ NULL,
};

//
Expand Down
1 change: 1 addition & 0 deletions ggml/src/ggml-zendnn/ggml-zendnn.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -781,6 +781,7 @@ static const struct ggml_backend_device_i ggml_backend_zendnn_device_i = {
/* .event_new = */ NULL,
/* .event_free = */ NULL,
/* .event_synchronize = */ NULL,
/* .event_query = */ NULL,
};

// backend reg interface
Expand Down
69 changes: 69 additions & 0 deletions include/llama.h
Original file line number Diff line number Diff line change
Expand Up @@ -927,6 +927,75 @@ extern "C" {
llama_seq_id dest_seq_id,
llama_state_seq_flags flags);

// [TAG_STATE_ASYNC] asynchronous per-sequence state transfer
//
// llama_state_seq_get_data_ext / set_data_ext do not return until every byte has moved,
// so a caller that copies a sequence out of the cache to make room stops doing anything
// else for as long as the copy takes. A transfer object issues the same copies on a
// stream of its own and hands back control immediately; the caller polls
// llama_state_seq_copy_done() and gets on with its other work in between.
//
// The transfer owns the host buffer it reads from or writes into. That buffer is pinned
// when the backend offers pinned memory, which is what makes the copy fast, and it
// cannot be freed while a copy is still using it.
//
// Between issuing and completion the caller must not touch the buffer, must not free or
// reuse the cells of a sequence being read, and must not decode a sequence being
// written. llama_state_seq_copy_free() waits for an outstanding copy first.
struct llama_state_seq_copy;

// NULL if the context's backends cannot copy asynchronously, or cannot say whether a
// copy has finished without waiting for it, which would put the stall straight back; the
// caller then uses the synchronous llama_state_seq_*_data_ext calls
LLAMA_API struct llama_state_seq_copy * llama_state_seq_copy_init(struct llama_context * ctx);
LLAMA_API void llama_state_seq_copy_free(struct llama_state_seq_copy * cpy);

// Size the transfer's host buffer, keeping no contents; NULL on failure. Grow-only:
// page-locking host memory is far too slow to do once per transfer, so the memory is
// kept between them and only given back by llama_state_seq_copy_buf_free().
LLAMA_API uint8_t * llama_state_seq_copy_buf_resize (struct llama_state_seq_copy * cpy, size_t size);
LLAMA_API uint8_t * llama_state_seq_copy_buf (struct llama_state_seq_copy * cpy);
LLAMA_API size_t llama_state_seq_copy_buf_size (struct llama_state_seq_copy * cpy);
// host memory actually held, which is what a caller budgeting host RAM has to count
LLAMA_API size_t llama_state_seq_copy_buf_capacity(struct llama_state_seq_copy * cpy);
LLAMA_API void llama_state_seq_copy_buf_free (struct llama_state_seq_copy * cpy);

// true when the buffer that is held right now is page-locked, i.e. when the copies can
// really overlap. False while no buffer is held, since none is page-locked then: a
// caller asking before the first resize wants llama_state_seq_copy_buf_can_pin().
LLAMA_API bool llama_state_seq_copy_buf_is_pinned(struct llama_state_seq_copy * cpy);

// true when the backend offers pinned host memory at all. It is what the next resize
// will ask for, not what any buffer is: an allocation can still come back pageable.
LLAMA_API bool llama_state_seq_copy_buf_can_pin(struct llama_state_seq_copy * cpy);

// Issue the copies; return the number of bytes covered, 0 on failure. size must be
// between 1 and llama_state_seq_copy_buf_size(): the buffer belongs to the transfer, and
// a size beyond it is refused rather than believed. LLAMA_STATE_SEQ_FLAGS_ON_DEVICE is
// refused too, since these copies serialise through host memory; use
// llama_state_seq_get_data_ext / set_data_ext for that flag.
LLAMA_API size_t llama_state_seq_copy_get(
struct llama_state_seq_copy * cpy,
size_t size,
llama_seq_id seq_id,
llama_state_seq_flags flags);

LLAMA_API size_t llama_state_seq_copy_set(
struct llama_state_seq_copy * cpy,
size_t size,
llama_seq_id dest_seq_id,
llama_state_seq_flags flags);

// transfers the last issue posted: one per run of adjacent cells, per tensor
LLAMA_API size_t llama_state_seq_copy_n_copies(struct llama_state_seq_copy * cpy);

// microseconds the last issue spent waiting for the compute streams before it could start
LLAMA_API int64_t llama_state_seq_copy_sync_us(struct llama_state_seq_copy * cpy);

// non-blocking completion test, and the blocking wait behind it
LLAMA_API bool llama_state_seq_copy_done(struct llama_state_seq_copy * cpy);
LLAMA_API void llama_state_seq_copy_wait(struct llama_state_seq_copy * cpy);

//
// Decoding
//
Expand Down
Loading