2024-10-03 01:49:47 +02:00
// Note: porting this file to C++ is a work in progress
2024-10-03 17:39:18 +02:00
#ifdef _WIN32
#define WIN32_LEAN_AND_MEAN
#ifndef NOMINMAX
# define NOMINMAX
#endif
#include <windows.h>
#endif
2024-11-03 19:34:08 +01:00
#include "ggml-backend.h"
2023-11-13 14:16:23 +02:00
#include "ggml-backend-impl.h"
2023-10-08 20:19:14 +03:00
#include "ggml-alloc.h"
2023-11-13 14:16:23 +02:00
#include "ggml-impl.h"
2023-10-08 20:19:14 +03:00
#include <assert.h>
2023-11-13 14:16:23 +02:00
#include <limits.h>
2023-10-08 20:19:14 +03:00
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
2025-03-04 17:53:26 +01:00
#include <algorithm>
2025-08-20 16:35:28 -07:00
#include <vector>
2023-10-08 20:19:14 +03:00
2024-10-03 17:39:18 +02:00
#ifdef __APPLE__
#include <sys/types.h>
#include <sys/sysctl.h>
#endif
2023-12-07 22:26:54 +02:00
// backend buffer type
2024-01-12 20:07:38 +01:00
const char * ggml_backend_buft_name ( ggml_backend_buffer_type_t buft ) {
2025-08-30 16:32:10 +02:00
GGML_ASSERT ( buft );
2024-01-12 20:07:38 +01:00
return buft -> iface . get_name ( buft );
}
2024-10-03 01:49:47 +02:00
ggml_backend_buffer_t ggml_backend_buft_alloc_buffer ( ggml_backend_buffer_type_t buft , size_t size ) {
2025-12-15 09:24:59 +01:00
GGML_ASSERT ( buft );
2024-10-30 02:01:23 +01:00
if ( size == 0 ) {
// return a dummy buffer for zero-sized allocations
return ggml_backend_buffer_init ( buft , {}, NULL , 0 );
}
2023-12-07 22:26:54 +02:00
return buft -> iface . alloc_buffer ( buft , size );
}
size_t ggml_backend_buft_get_alignment ( ggml_backend_buffer_type_t buft ) {
2025-08-30 16:32:10 +02:00
GGML_ASSERT ( buft );
2023-12-07 22:26:54 +02:00
return buft -> iface . get_alignment ( buft );
}
2024-01-28 18:03:59 +01:00
size_t ggml_backend_buft_get_max_size ( ggml_backend_buffer_type_t buft ) {
2025-08-30 16:32:10 +02:00
GGML_ASSERT ( buft );
2024-01-28 18:03:59 +01:00
// get_max_size is optional, defaults to SIZE_MAX
if ( buft -> iface . get_max_size ) {
return buft -> iface . get_max_size ( buft );
}
return SIZE_MAX ;
}
2025-05-05 22:32:13 +02:00
size_t ggml_backend_buft_get_alloc_size ( ggml_backend_buffer_type_t buft , const struct ggml_tensor * tensor ) {
2025-08-30 16:32:10 +02:00
GGML_ASSERT ( buft );
2023-12-07 22:26:54 +02:00
// get_alloc_size is optional, defaults to ggml_nbytes
if ( buft -> iface . get_alloc_size ) {
2024-01-26 18:59:43 +01:00
size_t size = buft -> iface . get_alloc_size ( buft , tensor );
assert ( size >= ggml_nbytes ( tensor ));
return size ;
2023-12-07 22:26:54 +02:00
}
return ggml_nbytes ( tensor );
}
2023-12-21 21:07:46 +01:00
bool ggml_backend_buft_is_host ( ggml_backend_buffer_type_t buft ) {
2025-08-30 16:32:10 +02:00
GGML_ASSERT ( buft );
2023-12-21 21:07:46 +01:00
if ( buft -> iface . is_host ) {
return buft -> iface . is_host ( buft );
}
return false ;
}
2024-10-03 01:49:47 +02:00
ggml_backend_dev_t ggml_backend_buft_get_device ( ggml_backend_buffer_type_t buft ) {
2025-08-30 16:32:10 +02:00
GGML_ASSERT ( buft );
2024-10-03 01:49:47 +02:00
return buft -> device ;
}
2023-10-08 20:19:14 +03:00
// backend buffer
2024-10-03 01:49:47 +02:00
ggml_backend_buffer_t ggml_backend_buffer_init (
ggml_backend_buffer_type_t buft ,
struct ggml_backend_buffer_i iface ,
void * context ,
size_t size ) {
ggml_backend_buffer_t buffer = new ggml_backend_buffer {
2023-10-08 20:19:14 +03:00
/* .interface = */ iface ,
2023-12-07 22:26:54 +02:00
/* .buft = */ buft ,
2023-10-08 20:19:14 +03:00
/* .context = */ context ,
/* .size = */ size ,
2024-01-12 20:07:38 +01:00
/* .usage = */ GGML_BACKEND_BUFFER_USAGE_ANY
2023-10-08 20:19:14 +03:00
};
return buffer ;
}
2024-01-12 20:07:38 +01:00
const char * ggml_backend_buffer_name ( ggml_backend_buffer_t buffer ) {
2024-10-30 02:01:23 +01:00
return ggml_backend_buft_name ( ggml_backend_buffer_get_type ( buffer ));
2024-01-12 20:07:38 +01:00
}
2023-10-08 20:19:14 +03:00
void ggml_backend_buffer_free ( ggml_backend_buffer_t buffer ) {
2023-11-13 14:16:23 +02:00
if ( buffer == NULL ) {
return ;
}
2023-10-08 20:19:14 +03:00
if ( buffer -> iface . free_buffer != NULL ) {
buffer -> iface . free_buffer ( buffer );
}
2024-10-03 01:49:47 +02:00
delete buffer ;
2023-10-08 20:19:14 +03:00
}
size_t ggml_backend_buffer_get_size ( ggml_backend_buffer_t buffer ) {
2025-08-30 16:32:10 +02:00
GGML_ASSERT ( buffer );
2023-10-08 20:19:14 +03:00
return buffer -> size ;
}
2023-11-13 14:16:23 +02:00
void * ggml_backend_buffer_get_base ( ggml_backend_buffer_t buffer ) {
2025-08-30 16:32:10 +02:00
GGML_ASSERT ( buffer );
2024-10-30 02:01:23 +01:00
// get_base is optional if the buffer is zero-sized
if ( buffer -> size == 0 ) {
return NULL ;
}
2025-12-15 09:24:59 +01:00
// FIXME JG: a multi_buffer has a non-zero size, according to the above comment get_base is not optional,
// I don't know whether the above comment is correct
if ( ! buffer -> iface . get_base ) {
return NULL ;
}
2023-11-13 14:16:23 +02:00
void * base = buffer -> iface . get_base ( buffer );
GGML_ASSERT ( base != NULL && "backend buffer base cannot be NULL" );
return base ;
}
2025-02-28 05:41:47 -08:00
enum ggml_status ggml_backend_buffer_init_tensor ( ggml_backend_buffer_t buffer , struct ggml_tensor * tensor ) {
2025-08-30 16:32:10 +02:00
GGML_ASSERT ( buffer );
2023-11-13 14:16:23 +02:00
// init_tensor is optional
2023-10-08 20:19:14 +03:00
if ( buffer -> iface . init_tensor ) {
2025-02-28 05:41:47 -08:00
return buffer -> iface . init_tensor ( buffer , tensor );
2023-10-08 20:19:14 +03:00
}
2025-02-28 05:41:47 -08:00
return GGML_STATUS_SUCCESS ;
2023-10-08 20:19:14 +03:00
}
2024-10-30 02:01:23 +01:00
void ggml_backend_buffer_clear ( ggml_backend_buffer_t buffer , uint8_t value ) {
2025-08-30 16:32:10 +02:00
GGML_ASSERT ( buffer );
2024-10-30 02:01:23 +01:00
// clear is optional if the buffer is zero-sized
if ( buffer -> size == 0 ) {
return ;
}
buffer -> iface . clear ( buffer , value );
}
2024-10-03 01:49:47 +02:00
size_t ggml_backend_buffer_get_alignment ( ggml_backend_buffer_t buffer ) {
2024-01-12 20:07:38 +01:00
return ggml_backend_buft_get_alignment ( ggml_backend_buffer_get_type ( buffer ));
2023-12-07 22:26:54 +02:00
}
2024-01-28 18:03:59 +01:00
size_t ggml_backend_buffer_get_max_size ( ggml_backend_buffer_t buffer ) {
return ggml_backend_buft_get_max_size ( ggml_backend_buffer_get_type ( buffer ));
}
2025-05-05 22:32:13 +02:00
size_t ggml_backend_buffer_get_alloc_size ( ggml_backend_buffer_t buffer , const struct ggml_tensor * tensor ) {
2024-01-12 20:07:38 +01:00
return ggml_backend_buft_get_alloc_size ( ggml_backend_buffer_get_type ( buffer ), tensor );
2023-12-07 22:26:54 +02:00
}
2023-12-21 21:07:46 +01:00
bool ggml_backend_buffer_is_host ( ggml_backend_buffer_t buffer ) {
2024-01-12 20:07:38 +01:00
return ggml_backend_buft_is_host ( ggml_backend_buffer_get_type ( buffer ));
2023-12-21 21:07:46 +01:00
}
2024-01-12 20:07:38 +01:00
void ggml_backend_buffer_set_usage ( ggml_backend_buffer_t buffer , enum ggml_backend_buffer_usage usage ) {
2025-08-30 16:32:10 +02:00
GGML_ASSERT ( buffer );
2024-01-12 20:07:38 +01:00
buffer -> usage = usage ;
2024-01-28 18:03:59 +01:00
// FIXME: add a generic callback to the buffer interface
if ( ggml_backend_buffer_is_multi_buffer ( buffer )) {
ggml_backend_multi_buffer_set_usage ( buffer , usage );
}
2024-01-12 20:07:38 +01:00
}
2024-07-18 23:48:47 +02:00
enum ggml_backend_buffer_usage ggml_backend_buffer_get_usage ( ggml_backend_buffer_t buffer ) {
2025-08-30 16:32:10 +02:00
GGML_ASSERT ( buffer );
2024-07-18 23:48:47 +02:00
return buffer -> usage ;
}
2024-01-12 20:07:38 +01:00
ggml_backend_buffer_type_t ggml_backend_buffer_get_type ( ggml_backend_buffer_t buffer ) {
2025-08-30 16:32:10 +02:00
GGML_ASSERT ( buffer );
2023-12-07 22:26:54 +02:00
return buffer -> buft ;
2023-10-08 20:19:14 +03:00
}
2024-01-12 20:07:38 +01:00
void ggml_backend_buffer_reset ( ggml_backend_buffer_t buffer ) {
2025-08-30 16:32:10 +02:00
GGML_ASSERT ( buffer );
2024-01-12 20:07:38 +01:00
if ( buffer -> iface . reset ) {
buffer -> iface . reset ( buffer );
}
}
bool ggml_backend_buffer_copy_tensor ( const struct ggml_tensor * src , struct ggml_tensor * dst ) {
ggml_backend_buffer_t dst_buf = dst -> view_src ? dst -> view_src -> buffer : dst -> buffer ;
if ( dst_buf -> iface . cpy_tensor ) {
2024-06-03 20:03:26 +03:00
return dst_buf -> iface . cpy_tensor ( dst_buf , src , dst );
2024-01-12 20:07:38 +01:00
}
return false ;
}
2023-10-08 20:19:14 +03:00
// backend
2024-02-24 11:27:36 -05:00
ggml_guid_t ggml_backend_guid ( ggml_backend_t backend ) {
if ( backend == NULL ) {
return NULL ;
}
return backend -> guid ;
}
2023-10-08 20:19:14 +03:00
const char * ggml_backend_name ( ggml_backend_t backend ) {
2023-11-13 14:16:23 +02:00
if ( backend == NULL ) {
return "NULL" ;
}
2023-10-08 20:19:14 +03:00
return backend -> iface . get_name ( backend );
}
void ggml_backend_free ( ggml_backend_t backend ) {
2023-11-13 14:16:23 +02:00
if ( backend == NULL ) {
return ;
}
2023-10-08 20:19:14 +03:00
backend -> iface . free ( backend );
}
2023-12-07 22:26:54 +02:00
ggml_backend_buffer_type_t ggml_backend_get_default_buffer_type ( ggml_backend_t backend ) {
2025-08-30 16:32:10 +02:00
GGML_ASSERT ( backend );
2024-10-30 02:01:23 +01:00
return ggml_backend_dev_buffer_type ( backend -> device );
2023-12-07 22:26:54 +02:00
}
2023-10-08 20:19:14 +03:00
ggml_backend_buffer_t ggml_backend_alloc_buffer ( ggml_backend_t backend , size_t size ) {
2023-12-07 22:26:54 +02:00
return ggml_backend_buft_alloc_buffer ( ggml_backend_get_default_buffer_type ( backend ), size );
2023-10-08 20:19:14 +03:00
}
size_t ggml_backend_get_alignment ( ggml_backend_t backend ) {
2023-12-07 22:26:54 +02:00
return ggml_backend_buft_get_alignment ( ggml_backend_get_default_buffer_type ( backend ));
2023-10-08 20:19:14 +03:00
}
2024-01-28 18:03:59 +01:00
size_t ggml_backend_get_max_size ( ggml_backend_t backend ) {
return ggml_backend_buft_get_max_size ( ggml_backend_get_default_buffer_type ( backend ));
}
2023-12-07 22:26:54 +02:00
void ggml_backend_tensor_set_async ( ggml_backend_t backend , struct ggml_tensor * tensor , const void * data , size_t offset , size_t size ) {
2025-08-30 16:32:10 +02:00
GGML_ASSERT ( backend );
GGML_ASSERT ( tensor );
2023-12-07 22:26:54 +02:00
GGML_ASSERT ( tensor -> data != NULL && "tensor not allocated" );
GGML_ASSERT ( offset + size <= ggml_nbytes ( tensor ) && "tensor write out of bounds" );
2024-01-12 20:07:38 +01:00
if ( backend -> iface . set_tensor_async == NULL ) {
ggml_backend_tensor_set ( tensor , data , offset , size );
} else {
backend -> iface . set_tensor_async ( backend , tensor , data , offset , size );
}
2023-10-08 20:19:14 +03:00
}
2023-12-07 22:26:54 +02:00
void ggml_backend_tensor_get_async ( ggml_backend_t backend , const struct ggml_tensor * tensor , void * data , size_t offset , size_t size ) {
2025-08-30 16:32:10 +02:00
GGML_ASSERT ( backend );
GGML_ASSERT ( tensor );
2023-12-07 22:26:54 +02:00
GGML_ASSERT ( tensor -> data != NULL && "tensor not allocated" );
GGML_ASSERT ( offset + size <= ggml_nbytes ( tensor ) && "tensor read out of bounds" );
2024-01-12 20:07:38 +01:00
if ( backend -> iface . get_tensor_async == NULL ) {
ggml_backend_tensor_get ( tensor , data , offset , size );
} else {
backend -> iface . get_tensor_async ( backend , tensor , data , offset , size );
}
2023-10-08 20:19:14 +03:00
}
2024-10-03 01:49:47 +02:00
void ggml_backend_tensor_set ( struct ggml_tensor * tensor , const void * data , size_t offset , size_t size ) {
2024-11-20 14:56:04 +01:00
GGML_ASSERT ( tensor );
2024-01-12 20:07:38 +01:00
ggml_backend_buffer_t buf = tensor -> view_src ? tensor -> view_src -> buffer : tensor -> buffer ;
2024-10-30 02:01:23 +01:00
if ( size == 0 ) {
return ;
}
2024-01-12 20:07:38 +01:00
GGML_ASSERT ( buf != NULL && "tensor buffer not set" );
2024-03-13 18:54:21 +01:00
GGML_ASSERT ( tensor -> data != NULL && "tensor not allocated" );
2023-12-07 22:26:54 +02:00
GGML_ASSERT ( offset + size <= ggml_nbytes ( tensor ) && "tensor write out of bounds" );
2023-11-13 14:16:23 +02:00
2024-03-13 18:54:21 +01:00
buf -> iface . set_tensor ( buf , tensor , data , offset , size );
2023-10-08 20:19:14 +03:00
}
2024-10-03 01:49:47 +02:00
void ggml_backend_tensor_get ( const struct ggml_tensor * tensor , void * data , size_t offset , size_t size ) {
2024-11-20 14:56:04 +01:00
GGML_ASSERT ( tensor );
2024-01-12 20:07:38 +01:00
ggml_backend_buffer_t buf = tensor -> view_src ? tensor -> view_src -> buffer : tensor -> buffer ;
2024-10-30 02:01:23 +01:00
if ( size == 0 ) {
return ;
}
2024-03-13 18:54:21 +01:00
GGML_ASSERT ( buf != NULL && "tensor buffer not set" );
2023-11-13 14:16:23 +02:00
GGML_ASSERT ( tensor -> data != NULL && "tensor not allocated" );
2023-12-07 22:26:54 +02:00
GGML_ASSERT ( offset + size <= ggml_nbytes ( tensor ) && "tensor read out of bounds" );
2023-11-13 14:16:23 +02:00
2024-03-13 18:54:21 +01:00
buf -> iface . get_tensor ( buf , tensor , data , offset , size );
2023-10-08 20:19:14 +03:00
}
2024-11-16 22:17:59 +02:00
void ggml_backend_tensor_memset ( struct ggml_tensor * tensor , uint8_t value , size_t offset , size_t size ) {
2025-08-30 16:32:10 +02:00
GGML_ASSERT ( tensor );
2024-09-20 19:04:44 +03:00
ggml_backend_buffer_t buf = tensor -> view_src ? tensor -> view_src -> buffer : tensor -> buffer ;
2024-10-30 02:01:23 +01:00
if ( size == 0 ) {
2024-09-20 19:04:44 +03:00
return ;
}
2024-09-20 19:13:02 +03:00
2024-10-30 02:01:23 +01:00
GGML_ASSERT ( buf != NULL && "tensor buffer not set" );
GGML_ASSERT ( tensor -> data != NULL && "tensor not allocated" );
GGML_ASSERT ( offset + size <= ggml_nbytes ( tensor ) && "tensor write out of bounds" );
GGML_ASSERT ( buf -> iface . memset_tensor != NULL && "memset not implemented by backend buffer" );
2024-09-20 19:04:44 +03:00
buf -> iface . memset_tensor ( buf , tensor , value , offset , size );
}
2023-10-08 20:19:14 +03:00
void ggml_backend_synchronize ( ggml_backend_t backend ) {
2025-08-30 16:32:10 +02:00
GGML_ASSERT ( backend );
2023-12-07 22:26:54 +02:00
if ( backend -> iface . synchronize == NULL ) {
return ;
}
2023-10-08 20:19:14 +03:00
backend -> iface . synchronize ( backend );
}
ggml_backend_graph_plan_t ggml_backend_graph_plan_create ( ggml_backend_t backend , struct ggml_cgraph * cgraph ) {
2025-08-30 16:32:10 +02:00
GGML_ASSERT ( backend );
2024-03-13 18:54:21 +01:00
GGML_ASSERT ( backend -> iface . graph_plan_create != NULL );
2023-10-08 20:19:14 +03:00
return backend -> iface . graph_plan_create ( backend , cgraph );
}
void ggml_backend_graph_plan_free ( ggml_backend_t backend , ggml_backend_graph_plan_t plan ) {
2025-08-30 16:32:10 +02:00
GGML_ASSERT ( backend );
2024-03-13 18:54:21 +01:00
GGML_ASSERT ( backend -> iface . graph_plan_free != NULL );
2023-10-08 20:19:14 +03:00
backend -> iface . graph_plan_free ( backend , plan );
}
2024-03-04 10:05:42 +01:00
enum ggml_status ggml_backend_graph_plan_compute ( ggml_backend_t backend , ggml_backend_graph_plan_t plan ) {
2025-08-30 16:32:10 +02:00
GGML_ASSERT ( backend );
2024-03-13 18:54:21 +01:00
GGML_ASSERT ( backend -> iface . graph_plan_compute != NULL );
2024-03-04 10:05:42 +01:00
return backend -> iface . graph_plan_compute ( backend , plan );
2023-10-08 20:19:14 +03:00
}
2024-03-04 10:05:42 +01:00
enum ggml_status ggml_backend_graph_compute ( ggml_backend_t backend , struct ggml_cgraph * cgraph ) {
2024-03-13 18:54:21 +01:00
enum ggml_status err = ggml_backend_graph_compute_async ( backend , cgraph );
ggml_backend_synchronize ( backend );
return err ;
}
2024-03-18 11:03:04 +01:00
enum ggml_status ggml_backend_graph_compute_async ( ggml_backend_t backend , struct ggml_cgraph * cgraph ) {
2025-08-30 16:32:10 +02:00
GGML_ASSERT ( backend );
2024-01-12 20:07:38 +01:00
return backend -> iface . graph_compute ( backend , cgraph );
2023-10-08 20:19:14 +03:00
}
bool ggml_backend_supports_op ( ggml_backend_t backend , const struct ggml_tensor * op ) {
2025-08-30 16:32:10 +02:00
GGML_ASSERT ( backend );
2024-10-30 02:01:23 +01:00
return ggml_backend_dev_supports_op ( backend -> device , op );
2023-10-08 20:19:14 +03:00
}
2024-06-13 03:11:35 +02:00
bool ggml_backend_supports_buft ( ggml_backend_t backend , ggml_backend_buffer_type_t buft ) {
2025-08-30 16:32:10 +02:00
GGML_ASSERT ( backend );
2024-10-30 02:01:23 +01:00
return ggml_backend_dev_supports_buft ( backend -> device , buft );
2024-06-13 03:11:35 +02:00
}
2024-03-18 11:03:04 +01:00
bool ggml_backend_offload_op ( ggml_backend_t backend , const struct ggml_tensor * op ) {
2025-08-30 16:32:10 +02:00
GGML_ASSERT ( backend );
2024-10-30 02:01:23 +01:00
return ggml_backend_dev_offload_op ( backend -> device , op );
2024-03-18 11:03:04 +01:00
}
2024-10-03 01:49:47 +02:00
ggml_backend_dev_t ggml_backend_get_device ( ggml_backend_t backend ) {
2025-08-30 16:32:10 +02:00
GGML_ASSERT ( backend );
2024-10-03 01:49:47 +02:00
return backend -> device ;
}
2023-10-08 20:19:14 +03:00
// backend copy
void ggml_backend_tensor_copy ( struct ggml_tensor * src , struct ggml_tensor * dst ) {
GGML_ASSERT ( ggml_are_same_layout ( src , dst ) && "cannot copy tensors with different layouts" );
if ( src == dst ) {
return ;
}
2024-01-12 20:07:38 +01:00
if ( ggml_backend_buffer_is_host ( src -> buffer )) {
ggml_backend_tensor_set ( dst , src -> data , 0 , ggml_nbytes ( src ));
} else if ( ggml_backend_buffer_is_host ( dst -> buffer )) {
ggml_backend_tensor_get ( src , dst -> data , 0 , ggml_nbytes ( src ));
} else if ( ! ggml_backend_buffer_copy_tensor ( src , dst )) {
#ifndef NDEBUG
2024-10-11 15:34:45 +02:00
GGML_LOG_DEBUG ( "%s: warning: slow copy from %s to %s \n " , __func__ , ggml_backend_buffer_name ( src -> buffer ), ggml_backend_buffer_name ( dst -> buffer ));
2024-01-12 20:07:38 +01:00
#endif
2023-10-08 20:19:14 +03:00
size_t nbytes = ggml_nbytes ( src );
void * data = malloc ( nbytes );
ggml_backend_tensor_get ( src , data , 0 , nbytes );
ggml_backend_tensor_set ( dst , data , 0 , nbytes );
free ( data );
}
}
2024-03-13 18:54:21 +01:00
void ggml_backend_tensor_copy_async ( ggml_backend_t backend_src , ggml_backend_t backend_dst , struct ggml_tensor * src , struct ggml_tensor * dst ) {
2024-01-12 20:07:38 +01:00
GGML_ASSERT ( ggml_are_same_layout ( src , dst ) && "cannot copy tensors with different layouts" );
if ( src == dst ) {
return ;
}
2025-08-30 16:32:10 +02:00
GGML_ASSERT ( backend_dst );
2024-03-13 18:54:21 +01:00
if ( backend_dst -> iface . cpy_tensor_async != NULL ) {
if ( backend_dst -> iface . cpy_tensor_async ( backend_src , backend_dst , src , dst )) {
return ;
2024-01-12 20:07:38 +01:00
}
}
2024-03-13 18:54:21 +01:00
// an async copy would normally happen after all the queued operations on both backends are completed
2024-08-07 13:29:02 +02:00
// to simulate the same behavior, we need to synchronize both backends first, and do a blocking copy
ggml_backend_synchronize ( backend_src );
ggml_backend_synchronize ( backend_dst );
ggml_backend_tensor_copy ( src , dst );
2024-01-12 20:07:38 +01:00
}
2024-03-13 18:54:21 +01:00
// events
2024-10-03 01:49:47 +02:00
ggml_backend_event_t ggml_backend_event_new ( ggml_backend_dev_t device ) {
// null device is allowed for the transition period to the device interface
if ( device == NULL || device -> iface . event_new == NULL ) {
2024-03-13 18:54:21 +01:00
return NULL ;
}
2024-10-03 01:49:47 +02:00
return device -> iface . event_new ( device );
2024-03-13 18:54:21 +01:00
}
void ggml_backend_event_free ( ggml_backend_event_t event ) {
if ( event == NULL ) {
return ;
}
2024-10-03 01:49:47 +02:00
event -> device -> iface . event_free ( event -> device , event );
2024-03-13 18:54:21 +01:00
}
2024-10-03 01:49:47 +02:00
void ggml_backend_event_record ( ggml_backend_event_t event , ggml_backend_t backend ) {
2025-08-30 16:32:10 +02:00
GGML_ASSERT ( backend );
2024-10-03 01:49:47 +02:00
GGML_ASSERT ( backend -> iface . event_record != NULL );
2024-03-13 18:54:21 +01:00
2024-10-03 01:49:47 +02:00
backend -> iface . event_record ( backend , event );
2024-03-13 18:54:21 +01:00
}
void ggml_backend_event_synchronize ( ggml_backend_event_t event ) {
2025-08-30 16:32:10 +02:00
GGML_ASSERT ( event );
2024-10-03 01:49:47 +02:00
GGML_ASSERT ( event -> device -> iface . event_synchronize );
2024-03-13 18:54:21 +01:00
2024-10-03 01:49:47 +02:00
event -> device -> iface . event_synchronize ( event -> device , event );
2024-03-13 18:54:21 +01:00
}
void ggml_backend_event_wait ( ggml_backend_t backend , ggml_backend_event_t event ) {
2025-08-30 16:32:10 +02:00
GGML_ASSERT ( backend );
2024-03-13 18:54:21 +01:00
GGML_ASSERT ( backend -> iface . event_wait != NULL );
backend -> iface . event_wait ( backend , event );
}
2024-01-12 20:07:38 +01:00
2025-09-18 13:46:17 -05:00
static void ggml_backend_graph_optimize ( ggml_backend_t backend , struct ggml_cgraph * cgraph ) {
2025-09-08 13:10:07 -05:00
GGML_ASSERT ( backend );
2025-09-18 13:46:17 -05:00
if ( backend -> iface . graph_optimize != NULL ) {
backend -> iface . graph_optimize ( backend , cgraph );
2025-09-08 13:10:07 -05:00
}
}
2024-10-03 01:49:47 +02:00
// Backend device
2023-12-07 22:26:54 +02:00
2024-10-03 01:49:47 +02:00
const char * ggml_backend_dev_name ( ggml_backend_dev_t device ) {
2025-08-30 16:32:10 +02:00
GGML_ASSERT ( device );
2024-10-03 01:49:47 +02:00
return device -> iface . get_name ( device );
}
2023-12-07 22:26:54 +02:00
2024-10-03 01:49:47 +02:00
const char * ggml_backend_dev_description ( ggml_backend_dev_t device ) {
2025-08-30 16:32:10 +02:00
GGML_ASSERT ( device );
2024-10-03 01:49:47 +02:00
return device -> iface . get_description ( device );
}
2023-12-07 22:26:54 +02:00
2024-10-03 01:49:47 +02:00
void ggml_backend_dev_memory ( ggml_backend_dev_t device , size_t * free , size_t * total ) {
2025-08-30 16:32:10 +02:00
GGML_ASSERT ( device );
2024-10-03 01:49:47 +02:00
device -> iface . get_memory ( device , free , total );
}
2023-12-07 22:26:54 +02:00
2024-10-03 01:49:47 +02:00
enum ggml_backend_dev_type ggml_backend_dev_type ( ggml_backend_dev_t device ) {
2025-08-30 16:32:10 +02:00
GGML_ASSERT ( device );
2024-10-03 01:49:47 +02:00
return device -> iface . get_type ( device );
}
2023-12-07 22:26:54 +02:00
2024-10-03 01:49:47 +02:00
void ggml_backend_dev_get_props ( ggml_backend_dev_t device , struct ggml_backend_dev_props * props ) {
2024-10-07 18:27:51 +03:00
memset ( props , 0 , sizeof ( * props ));
2024-10-03 01:49:47 +02:00
device -> iface . get_props ( device , props );
}
2023-12-07 22:26:54 +02:00
2024-10-03 01:49:47 +02:00
ggml_backend_reg_t ggml_backend_dev_backend_reg ( ggml_backend_dev_t device ) {
2025-08-30 16:32:10 +02:00
GGML_ASSERT ( device );
2024-10-03 01:49:47 +02:00
return device -> reg ;
}
ggml_backend_t ggml_backend_dev_init ( ggml_backend_dev_t device , const char * params ) {
2025-08-30 16:32:10 +02:00
GGML_ASSERT ( device );
2024-10-03 01:49:47 +02:00
return device -> iface . init_backend ( device , params );
}
ggml_backend_buffer_type_t ggml_backend_dev_buffer_type ( ggml_backend_dev_t device ) {
2025-08-30 16:32:10 +02:00
GGML_ASSERT ( device );
2024-10-03 01:49:47 +02:00
return device -> iface . get_buffer_type ( device );
}
ggml_backend_buffer_type_t ggml_backend_dev_host_buffer_type ( ggml_backend_dev_t device ) {
2025-08-30 16:32:10 +02:00
GGML_ASSERT ( device );
2024-10-07 18:27:51 +03:00
if ( device -> iface . get_host_buffer_type == NULL ) {
return NULL ;
}
2024-10-03 01:49:47 +02:00
return device -> iface . get_host_buffer_type ( device );
}
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 ) {
2025-08-30 16:32:10 +02:00
GGML_ASSERT ( device );
2024-10-03 01:49:47 +02:00
return device -> iface . buffer_from_host_ptr ( device , ptr , size , max_tensor_size );
}
bool ggml_backend_dev_supports_op ( ggml_backend_dev_t device , const struct ggml_tensor * op ) {
2025-08-30 16:32:10 +02:00
GGML_ASSERT ( device );
2024-10-03 01:49:47 +02:00
return device -> iface . supports_op ( device , op );
}
bool ggml_backend_dev_supports_buft ( ggml_backend_dev_t device , ggml_backend_buffer_type_t buft ) {
2025-08-30 16:32:10 +02:00
GGML_ASSERT ( device );
2024-10-03 01:49:47 +02:00
return device -> iface . supports_buft ( device , buft );
}
bool ggml_backend_dev_offload_op ( ggml_backend_dev_t device , const struct ggml_tensor * op ) {
2025-08-30 16:32:10 +02:00
GGML_ASSERT ( device );
2024-10-07 21:55:08 +02:00
if ( device -> iface . offload_op != NULL ) {
return device -> iface . offload_op ( device , op );
}
return false ;
2024-10-03 01:49:47 +02:00
}
// Backend (reg)
const char * ggml_backend_reg_name ( ggml_backend_reg_t reg ) {
2025-08-30 16:32:10 +02:00
GGML_ASSERT ( reg );
2024-10-03 01:49:47 +02:00
return reg -> iface . get_name ( reg );
}
size_t ggml_backend_reg_dev_count ( ggml_backend_reg_t reg ) {
2025-08-30 16:32:10 +02:00
GGML_ASSERT ( reg );
2024-10-03 01:49:47 +02:00
return reg -> iface . get_device_count ( reg );
}
ggml_backend_dev_t ggml_backend_reg_dev_get ( ggml_backend_reg_t reg , size_t index ) {
2025-08-30 16:32:10 +02:00
GGML_ASSERT ( reg );
2024-10-03 01:49:47 +02:00
return reg -> iface . get_device ( reg , index );
}
void * ggml_backend_reg_get_proc_address ( ggml_backend_reg_t reg , const char * name ) {
2025-08-30 16:32:10 +02:00
GGML_ASSERT ( reg );
2024-10-03 01:49:47 +02:00
if ( ! reg -> iface . get_proc_address ) {
return NULL ;
}
return reg -> iface . get_proc_address ( reg , name );
}
2024-01-28 18:03:59 +01:00
// multi-buffer buffer
struct ggml_backend_multi_buffer_context {
ggml_backend_buffer_t * buffers ;
size_t n_buffers ;
};
2024-10-03 01:49:47 +02:00
static void ggml_backend_multi_buffer_free_buffer ( ggml_backend_buffer_t buffer ) {
2025-08-30 16:32:10 +02:00
GGML_ASSERT ( buffer );
2024-10-03 01:49:47 +02:00
ggml_backend_multi_buffer_context * ctx = ( ggml_backend_multi_buffer_context * ) buffer -> context ;
2024-01-28 18:03:59 +01:00
for ( size_t i = 0 ; i < ctx -> n_buffers ; i ++ ) {
ggml_backend_buffer_free ( ctx -> buffers [ i ]);
}
free ( ctx -> buffers );
free ( ctx );
}
2024-10-03 01:49:47 +02:00
static void ggml_backend_multi_buffer_clear ( ggml_backend_buffer_t buffer , uint8_t value ) {
2025-08-30 16:32:10 +02:00
GGML_ASSERT ( buffer );
2024-10-03 01:49:47 +02:00
ggml_backend_multi_buffer_context * ctx = ( ggml_backend_multi_buffer_context * ) buffer -> context ;
2024-01-28 18:03:59 +01:00
for ( size_t i = 0 ; i < ctx -> n_buffers ; i ++ ) {
ggml_backend_buffer_clear ( ctx -> buffers [ i ], value );
}
}
2024-10-03 01:49:47 +02:00
static const struct ggml_backend_buffer_i ggml_backend_multi_buffer_i = {
/* .free_buffer = */ ggml_backend_multi_buffer_free_buffer ,
/* .get_base = */ NULL ,
/* .init_tensor = */ NULL ,
/* .memset_tensor = */ NULL ,
/* .set_tensor = */ NULL ,
/* .get_tensor = */ NULL ,
/* .cpy_tensor = */ NULL ,
/* .clear = */ ggml_backend_multi_buffer_clear ,
/* .reset = */ NULL ,
};
2024-01-28 18:03:59 +01:00
2024-10-03 01:49:47 +02:00
ggml_backend_buffer_t ggml_backend_multi_buffer_alloc_buffer ( ggml_backend_buffer_t * buffers , size_t n_buffers ) {
ggml_backend_multi_buffer_context * ctx = ( ggml_backend_multi_buffer_context * ) malloc ( sizeof ( struct ggml_backend_multi_buffer_context ));
2024-01-28 18:03:59 +01:00
ctx -> n_buffers = n_buffers ;
ctx -> buffers = ( ggml_backend_buffer_t * ) malloc ( n_buffers * sizeof ( ggml_backend_buffer_t ));
2024-02-12 09:16:06 +02:00
GGML_ASSERT ( ctx -> buffers != NULL );
2024-01-28 18:03:59 +01:00
size_t total_size = 0 ;
for ( size_t i = 0 ; i < n_buffers ; i ++ ) {
ctx -> buffers [ i ] = buffers [ i ];
total_size += ggml_backend_buffer_get_size ( buffers [ i ]);
}
2024-10-03 01:49:47 +02:00
return ggml_backend_buffer_init ( buffers [ 0 ] -> buft , ggml_backend_multi_buffer_i , ctx , total_size );
2024-01-28 18:03:59 +01:00
}
2024-10-03 01:49:47 +02:00
bool ggml_backend_buffer_is_multi_buffer ( ggml_backend_buffer_t buffer ) {
2025-08-30 16:32:10 +02:00
GGML_ASSERT ( buffer );
2024-10-30 02:01:23 +01:00
return buffer -> iface . free_buffer == ggml_backend_multi_buffer_free_buffer ;
2024-01-28 18:03:59 +01:00
}
2024-10-03 01:49:47 +02:00
void ggml_backend_multi_buffer_set_usage ( ggml_backend_buffer_t buffer , enum ggml_backend_buffer_usage usage ) {
2025-08-30 16:32:10 +02:00
GGML_ASSERT ( buffer );
2024-01-28 18:03:59 +01:00
GGML_ASSERT ( ggml_backend_buffer_is_multi_buffer ( buffer ));
2024-10-03 01:49:47 +02:00
ggml_backend_multi_buffer_context * ctx = ( ggml_backend_multi_buffer_context * ) buffer -> context ;
2024-01-28 18:03:59 +01:00
for ( size_t i = 0 ; i < ctx -> n_buffers ; i ++ ) {
ggml_backend_buffer_set_usage ( ctx -> buffers [ i ], usage );
}
}
2024-02-12 09:16:06 +02:00
// creates a copy of the tensor with the same memory layout
static struct ggml_tensor * ggml_dup_tensor_layout ( struct ggml_context * ctx , const struct ggml_tensor * tensor ) {
struct ggml_tensor * dup = ggml_dup_tensor ( ctx , tensor );
for ( int i = 0 ; i < GGML_MAX_DIMS ; i ++ ) {
dup -> nb [ i ] = tensor -> nb [ i ];
}
return dup ;
}
static bool ggml_is_view_op ( enum ggml_op op ) {
return op == GGML_OP_VIEW || op == GGML_OP_RESHAPE || op == GGML_OP_PERMUTE || op == GGML_OP_TRANSPOSE ;
}
2023-12-07 22:26:54 +02:00
2023-11-13 14:16:23 +02:00
// scheduler
2024-03-13 18:54:21 +01:00
#ifndef GGML_SCHED_MAX_BACKENDS
#define GGML_SCHED_MAX_BACKENDS 16
#endif
#ifndef GGML_SCHED_MAX_SPLIT_INPUTS
2025-09-02 01:14:55 +02:00
#define GGML_SCHED_MAX_SPLIT_INPUTS 30
2024-03-13 18:54:21 +01:00
#endif
#ifndef GGML_SCHED_MAX_COPIES
#define GGML_SCHED_MAX_COPIES 4
#endif
2023-11-13 14:16:23 +02:00
struct ggml_backend_sched_split {
2024-02-12 09:16:06 +02:00
int backend_id ;
2023-11-13 14:16:23 +02:00
int i_start ;
int i_end ;
2024-03-13 18:54:21 +01:00
struct ggml_tensor * inputs [ GGML_SCHED_MAX_SPLIT_INPUTS ];
2023-11-13 14:16:23 +02:00
int n_inputs ;
2024-01-12 20:07:38 +01:00
// graph view of this split
2023-12-07 22:26:54 +02:00
struct ggml_cgraph graph ;
2023-11-13 14:16:23 +02:00
};
struct ggml_backend_sched {
2024-01-12 20:07:38 +01:00
bool is_reset ; // true if the scheduler has been reset since the last graph split
2024-03-13 18:54:21 +01:00
bool is_alloc ;
2024-01-12 20:07:38 +01:00
2023-11-13 14:16:23 +02:00
int n_backends ;
2024-03-13 18:54:21 +01:00
ggml_backend_t backends [ GGML_SCHED_MAX_BACKENDS ];
ggml_backend_buffer_type_t bufts [ GGML_SCHED_MAX_BACKENDS ];
2023-11-13 14:16:23 +02:00
ggml_gallocr_t galloc ;
2024-07-27 04:41:55 +02:00
// hash map of the nodes in the graph
struct ggml_hash_set hash_set ;
int * hv_tensor_backend_ids ; // [hash_set.size]
struct ggml_tensor ** hv_tensor_copies ; // [hash_set.size][n_backends][n_copies]
2024-02-12 09:16:06 +02:00
2024-03-13 18:54:21 +01:00
int * node_backend_ids ; // [graph_size]
int * leaf_backend_ids ; // [graph_size]
2023-11-13 14:16:23 +02:00
2024-06-13 03:11:35 +02:00
int * prev_node_backend_ids ; // [graph_size]
int * prev_leaf_backend_ids ; // [graph_size]
2024-01-12 20:07:38 +01:00
// copy of the graph with modified inputs
2024-07-27 04:41:55 +02:00
struct ggml_cgraph graph ;
2024-01-12 20:07:38 +01:00
2024-03-13 18:54:21 +01:00
// graph splits
2024-03-18 11:03:04 +01:00
struct ggml_backend_sched_split * splits ;
2023-11-13 14:16:23 +02:00
int n_splits ;
2024-03-18 11:03:04 +01:00
int splits_capacity ;
2023-11-13 14:16:23 +02:00
2024-03-13 18:54:21 +01:00
// pipeline parallelism support
int n_copies ;
int cur_copy ;
2025-07-25 01:07:26 -07:00
int next_copy ;
2024-03-13 18:54:21 +01:00
ggml_backend_event_t events [ GGML_SCHED_MAX_BACKENDS ][ GGML_SCHED_MAX_COPIES ];
struct ggml_tensor * graph_inputs [ GGML_SCHED_MAX_SPLIT_INPUTS ];
int n_graph_inputs ;
2023-11-13 14:16:23 +02:00
struct ggml_context * ctx ;
2024-02-12 09:16:06 +02:00
ggml_backend_sched_eval_callback callback_eval ;
void * callback_eval_user_data ;
2024-07-27 04:41:55 +02:00
char * context_buffer ;
size_t context_buffer_size ;
2024-06-13 03:11:35 +02:00
2025-05-11 20:18:39 +08:00
bool op_offload ;
2024-10-30 02:01:23 +01:00
int debug ;
2025-12-01 12:49:33 +02:00
// used for debugging graph reallocations [GGML_SCHED_DEBUG_REALLOC]
// ref: https://github.com/ggml-org/llama.cpp/pull/17617
int debug_realloc ;
int debug_graph_size ;
int debug_prev_graph_size ;
2023-11-13 14:16:23 +02:00
};
2024-07-27 04:41:55 +02:00
#define hash_id(tensor) ggml_hash_find_or_insert(&sched->hash_set, tensor)
#define tensor_backend_id(tensor) sched->hv_tensor_backend_ids[hash_id(tensor)]
#define tensor_id_copy(id, backend_id, copy_id) sched->hv_tensor_copies[(id) * sched->n_backends * sched->n_copies + (backend_id) * sched->n_copies + (copy_id)]
#define tensor_copy(tensor, backend_id, copy_id) tensor_id_copy(hash_id(tensor), backend_id, copy_id)
2023-11-13 14:16:23 +02:00
2024-02-12 09:16:06 +02:00
// returns the priority of the backend, lower id is higher priority
static int ggml_backend_sched_backend_id ( ggml_backend_sched_t sched , ggml_backend_t backend ) {
2023-11-13 14:16:23 +02:00
for ( int i = 0 ; i < sched -> n_backends ; i ++ ) {
if ( sched -> backends [ i ] == backend ) {
return i ;
}
}
2024-02-12 09:16:06 +02:00
return - 1 ;
2023-11-13 14:16:23 +02:00
}
2024-06-13 03:11:35 +02:00
static int ggml_backend_sched_backend_from_buffer ( ggml_backend_sched_t sched , const struct ggml_tensor * tensor , const struct ggml_tensor * op ) {
2024-11-17 12:25:45 +01:00
ggml_backend_buffer_t buffer = tensor -> view_src ? tensor -> view_src -> buffer : tensor -> buffer ;
2023-12-07 22:26:54 +02:00
if ( buffer == NULL ) {
2024-02-12 09:16:06 +02:00
return - 1 ;
2024-01-12 20:07:38 +01:00
}
2024-06-13 03:11:35 +02:00
// find highest prio backend that supports the buffer type and the op
2023-12-07 22:26:54 +02:00
for ( int i = 0 ; i < sched -> n_backends ; i ++ ) {
2024-06-13 03:11:35 +02:00
if ( ggml_backend_supports_buft ( sched -> backends [ i ], buffer -> buft ) &&
ggml_backend_supports_op ( sched -> backends [ i ], op )) {
2024-02-12 09:16:06 +02:00
return i ;
2023-12-07 22:26:54 +02:00
}
}
2024-03-13 18:54:21 +01:00
2024-06-13 03:11:35 +02:00
#ifndef NDEBUG
2024-10-11 15:34:45 +02:00
GGML_LOG_DEBUG ( "%s: warning: no backend supports op %s with a weight with buffer type %s used in tensor %s, the weight will need to be copied \n " ,
2024-06-13 03:11:35 +02:00
__func__ , ggml_op_desc ( tensor ), ggml_backend_buffer_name ( buffer ), tensor -> name );
#endif
2024-03-13 18:54:21 +01:00
return - 1 ;
2023-12-07 22:26:54 +02:00
}
2024-10-31 22:54:23 +01:00
#if 0
2024-08-16 04:22:55 +02:00
#define GGML_SCHED_MAX_SPLITS_DEBUG 4096
static char causes[GGML_DEFAULT_GRAPH_SIZE*16 + GGML_SCHED_MAX_SPLITS_DEBUG*GGML_SCHED_MAX_SPLIT_INPUTS][128]; // debug only
2023-12-07 22:26:54 +02:00
#define SET_CAUSE(node, ...) sprintf(causes[hash_id(node)], __VA_ARGS__)
#define GET_CAUSE(node) causes[hash_id(node)]
#else
#define SET_CAUSE(node, ...)
#define GET_CAUSE(node) ""
#endif
2023-11-13 14:16:23 +02:00
// returns the backend that should be used for the node based on the current locations
2024-02-12 09:16:06 +02:00
static int ggml_backend_sched_backend_id_from_cur ( ggml_backend_sched_t sched , struct ggml_tensor * tensor ) {
2024-01-12 20:07:38 +01:00
// assign pre-allocated nodes to their backend
2024-06-13 03:11:35 +02:00
int cur_backend_id = ggml_backend_sched_backend_from_buffer ( sched , tensor , tensor );
2024-03-18 11:03:04 +01:00
if ( cur_backend_id != - 1 ) {
2024-03-13 18:54:21 +01:00
SET_CAUSE ( tensor , "1.dst" );
2024-03-18 11:03:04 +01:00
return cur_backend_id ;
2023-11-13 14:16:23 +02:00
}
2024-03-13 18:54:21 +01:00
2023-11-13 14:16:23 +02:00
// view_src
2024-02-12 09:16:06 +02:00
if ( tensor -> view_src != NULL ) {
2024-06-13 03:11:35 +02:00
cur_backend_id = ggml_backend_sched_backend_from_buffer ( sched , tensor -> view_src , tensor );
2024-03-18 11:03:04 +01:00
if ( cur_backend_id != - 1 ) {
2024-03-13 18:54:21 +01:00
SET_CAUSE ( tensor , "1.vsrc" );
2024-03-18 11:03:04 +01:00
return cur_backend_id ;
2024-01-12 20:07:38 +01:00
}
2023-11-13 14:16:23 +02:00
}
2024-03-13 18:54:21 +01:00
2024-09-05 11:13:11 +02:00
if ( tensor -> buffer || ( tensor -> view_src && tensor -> view_src -> buffer )) {
// since the tensor is pre-allocated, it cannot be moved to another backend
2024-11-29 21:54:58 +01:00
ggml_backend_buffer_t buffer = tensor -> view_src ? tensor -> view_src -> buffer : tensor -> buffer ;
GGML_ABORT ( "pre-allocated tensor (%s) in a buffer (%s) that cannot run the operation (%s)" , tensor -> name , ggml_backend_buffer_name ( buffer ), ggml_op_name ( tensor -> op ));
2024-09-05 11:13:11 +02:00
}
2024-03-18 11:03:04 +01:00
// graph input
2024-03-13 18:54:21 +01:00
if ( tensor -> flags & GGML_TENSOR_FLAG_INPUT ) {
2024-03-18 11:03:04 +01:00
cur_backend_id = sched -> n_backends - 1 ; // last backend (assumed CPU)
2024-03-13 18:54:21 +01:00
SET_CAUSE ( tensor , "1.inp" );
2024-03-18 11:03:04 +01:00
return cur_backend_id ;
2024-03-13 18:54:21 +01:00
}
2024-03-18 11:03:04 +01:00
// operations with weights are preferably run on the same backend as the weights
2023-11-13 14:16:23 +02:00
for ( int i = 0 ; i < GGML_MAX_SRC ; i ++ ) {
2024-02-12 09:16:06 +02:00
const struct ggml_tensor * src = tensor -> src [ i ];
2023-11-13 14:16:23 +02:00
if ( src == NULL ) {
2024-02-17 23:04:16 +02:00
continue ;
2023-11-13 14:16:23 +02:00
}
2024-10-30 02:01:23 +01:00
// skip ROPE since the rope freqs tensor is too small to choose a backend based on it
// not an ideal solution
2025-01-07 16:11:57 +01:00
if ( tensor -> op != GGML_OP_ROPE && src -> buffer != NULL && src -> buffer -> usage == GGML_BACKEND_BUFFER_USAGE_WEIGHTS ) {
2024-06-13 03:11:35 +02:00
int src_backend_id = ggml_backend_sched_backend_from_buffer ( sched , src , tensor );
2024-03-18 11:03:04 +01:00
// check if a backend with higher prio wants to offload the op
2025-05-11 20:18:39 +08:00
if ( sched -> op_offload && src_backend_id == sched -> n_backends - 1 && ggml_backend_buffer_is_host ( src -> buffer )) {
2024-03-18 11:03:04 +01:00
for ( int b = 0 ; b < src_backend_id ; b ++ ) {
2024-06-17 16:51:42 +02:00
if ( ggml_backend_supports_op ( sched -> backends [ b ], tensor ) && ggml_backend_offload_op ( sched -> backends [ b ], tensor )) {
2024-03-18 11:03:04 +01:00
SET_CAUSE ( tensor , "1.off" );
return b ;
}
}
}
2024-03-13 18:54:21 +01:00
SET_CAUSE ( tensor , "1.wgt%d" , i );
2024-03-18 11:03:04 +01:00
return src_backend_id ;
2023-11-13 14:16:23 +02:00
}
}
2024-01-12 20:07:38 +01:00
2024-02-12 09:16:06 +02:00
return - 1 ;
2023-11-13 14:16:23 +02:00
}
static char * fmt_size ( size_t size ) {
static char buffer [ 128 ];
if ( size >= 1024 * 1024 ) {
2024-04-25 17:24:07 +03:00
snprintf ( buffer , sizeof ( buffer ), "%zuM" , size / 1024 / 1024 );
2023-11-13 14:16:23 +02:00
} else {
2024-04-25 17:24:07 +03:00
snprintf ( buffer , sizeof ( buffer ), "%zuK" , size / 1024 );
2023-11-13 14:16:23 +02:00
}
return buffer ;
}
2024-02-12 09:16:06 +02:00
static void ggml_backend_sched_print_assignments ( ggml_backend_sched_t sched , struct ggml_cgraph * graph ) {
2023-11-13 14:16:23 +02:00
int cur_split = 0 ;
for ( int i = 0 ; i < graph -> n_nodes ; i ++ ) {
if ( cur_split < sched -> n_splits && i == sched -> splits [ cur_split ]. i_start ) {
2024-02-12 09:16:06 +02:00
ggml_backend_t split_backend = sched -> backends [ sched -> splits [ cur_split ]. backend_id ];
2024-12-19 03:50:12 +01:00
GGML_LOG_DEBUG ( " \n ## SPLIT #%d: %s # %d inputs" , cur_split , ggml_backend_name ( split_backend ),
2023-12-07 22:26:54 +02:00
sched -> splits [ cur_split ]. n_inputs );
2023-11-13 14:16:23 +02:00
for ( int j = 0 ; j < sched -> splits [ cur_split ]. n_inputs ; j ++ ) {
2024-12-19 03:50:12 +01:00
if ( j == 0 ) {
GGML_LOG_DEBUG ( ": " );
}
2024-10-11 15:34:45 +02:00
GGML_LOG_DEBUG ( "[%s (%5.5s)] " , sched -> splits [ cur_split ]. inputs [ j ] -> name ,
2023-12-07 22:26:54 +02:00
fmt_size ( ggml_nbytes ( sched -> splits [ cur_split ]. inputs [ j ])));
2023-11-13 14:16:23 +02:00
}
2024-10-11 15:34:45 +02:00
GGML_LOG_DEBUG ( " \n " );
2023-11-13 14:16:23 +02:00
cur_split ++ ;
}
struct ggml_tensor * node = graph -> nodes [ i ];
if ( ggml_is_view_op ( node -> op )) {
continue ;
}
2024-10-30 02:01:23 +01:00
if ( sched -> debug > 1 ) {
ggml_backend_t tensor_backend = ggml_backend_sched_get_tensor_backend ( sched , node );
2026-01-19 20:03:19 +02:00
GGML_LOG_DEBUG ( "node #%3d (%10.10s): %20.20s (%5.5s) [%5.5s %8.8s] use=%d,c=%d:" , i , ggml_op_name ( node -> op ), node -> name ,
2025-06-29 02:43:36 -05:00
fmt_size ( ggml_nbytes ( node )), tensor_backend ? ggml_backend_name ( tensor_backend ) : "NULL" , GET_CAUSE ( node ),
2026-01-19 20:03:19 +02:00
graph -> use_counts [ ggml_hash_find ( & graph -> visited_hash_set , node )], node -> flags & GGML_TENSOR_FLAG_COMPUTE ? 1 : 0 );
2024-10-30 02:01:23 +01:00
for ( int j = 0 ; j < GGML_MAX_SRC ; j ++ ) {
struct ggml_tensor * src = node -> src [ j ];
if ( src == NULL ) {
continue ;
}
ggml_backend_t src_backend = ggml_backend_sched_get_tensor_backend ( sched , src );
GGML_LOG_DEBUG ( " %20.20s (%5.5s) [%5.5s %8.8s]" , src -> name ,
fmt_size ( ggml_nbytes ( src )), src_backend ? ggml_backend_name ( src_backend ) : "NULL" , GET_CAUSE ( src ));
2023-11-13 14:16:23 +02:00
}
2024-10-30 02:01:23 +01:00
GGML_LOG_DEBUG ( " \n " );
2023-11-13 14:16:23 +02:00
}
}
}
2024-06-13 03:11:35 +02:00
static bool ggml_backend_sched_buffer_supported ( ggml_backend_sched_t sched , struct ggml_tensor * t , int backend_id ) {
ggml_backend_buffer_t buf = t -> view_src ? t -> view_src -> buffer : t -> buffer ;
ggml_backend_buffer_type_t buft = NULL ;
if ( buf ) {
// the tensor is already allocated
buft = buf -> buft ;
} else {
// see if the tensor already has a backend assigned, and use the buffer type of that backend
int tensor_backend_id = tensor_backend_id ( t );
if ( tensor_backend_id == - 1 && t -> view_src ) {
tensor_backend_id = tensor_backend_id ( t -> view_src );
}
if ( tensor_backend_id != - 1 ) {
buft = sched -> bufts [ tensor_backend_id ];
}
}
return buft != NULL && ggml_backend_supports_buft ( sched -> backends [ backend_id ], buft );
}
static void ggml_backend_sched_set_if_supported ( ggml_backend_sched_t sched , struct ggml_tensor * node , int cur_backend_id , int * node_backend_id ) {
if ( ggml_backend_supports_op ( sched -> backends [ cur_backend_id ], node )) {
* node_backend_id = cur_backend_id ;
SET_CAUSE ( node , "2.sup" );
}
}
2024-01-12 20:07:38 +01:00
2023-11-13 14:16:23 +02:00
// assigns backends to ops and splits the graph into subgraphs that can be computed on the same backend
2025-08-31 06:49:03 -07:00
void ggml_backend_sched_split_graph ( ggml_backend_sched_t sched , struct ggml_cgraph * graph ) {
2024-01-12 20:07:38 +01:00
// reset splits
2023-11-13 14:16:23 +02:00
sched -> n_splits = 0 ;
2024-03-13 18:54:21 +01:00
sched -> n_graph_inputs = 0 ;
2024-01-12 20:07:38 +01:00
sched -> is_reset = false ;
2023-11-13 14:16:23 +02:00
struct ggml_init_params params = {
2024-07-27 04:41:55 +02:00
/* .mem_size = */ sched -> context_buffer_size ,
2023-12-07 22:26:54 +02:00
/* .mem_buffer = */ sched -> context_buffer ,
/* .no_alloc = */ true
2023-11-13 14:16:23 +02:00
};
2024-01-12 20:07:38 +01:00
ggml_free ( sched -> ctx );
2023-11-13 14:16:23 +02:00
sched -> ctx = ggml_init ( params );
2024-01-12 20:07:38 +01:00
if ( sched -> ctx == NULL ) {
2024-07-27 04:41:55 +02:00
GGML_ABORT ( "%s: failed to initialize context \n " , __func__ );
2024-01-12 20:07:38 +01:00
}
2023-11-13 14:16:23 +02:00
2024-01-12 20:07:38 +01:00
// pass 1: assign backends to ops with pre-allocated inputs
2023-11-13 14:16:23 +02:00
for ( int i = 0 ; i < graph -> n_leafs ; i ++ ) {
struct ggml_tensor * leaf = graph -> leafs [ i ];
2024-03-18 11:03:04 +01:00
int * leaf_backend_id = & tensor_backend_id ( leaf );
2024-07-27 04:41:55 +02:00
// do not overwrite user assignments
if ( * leaf_backend_id == - 1 ) {
* leaf_backend_id = ggml_backend_sched_backend_id_from_cur ( sched , leaf );
2023-11-13 14:16:23 +02:00
}
}
for ( int i = 0 ; i < graph -> n_nodes ; i ++ ) {
struct ggml_tensor * node = graph -> nodes [ i ];
2024-03-18 11:03:04 +01:00
int * node_backend_id = & tensor_backend_id ( node );
2024-07-27 04:41:55 +02:00
// do not overwrite user assignments
if ( * node_backend_id == - 1 ) {
* node_backend_id = ggml_backend_sched_backend_id_from_cur ( sched , node );
#if 0
// src
if (node->op == GGML_OP_NONE) {
2024-02-17 23:04:16 +02:00
continue;
2024-01-12 20:07:38 +01:00
}
2024-07-27 04:41:55 +02:00
for (int j = 0; j < GGML_MAX_SRC; j++) {
struct ggml_tensor * src = node->src[j];
if (src == NULL) {
continue;
}
int * src_backend_id = &tensor_backend_id(src);
if (*src_backend_id == -1) {
*src_backend_id = ggml_backend_sched_backend_id_from_cur(sched, src);
}
2024-01-12 20:07:38 +01:00
}
2024-07-27 04:41:55 +02:00
#endif
2023-11-13 14:16:23 +02:00
}
}
2024-01-12 20:07:38 +01:00
// pass 2: expand current backend assignments
// assign the same backend to adjacent nodes
// expand gpu backends (i.e. non last prio) up and down, ignoring cpu (the lowest priority backend)
// thus, cpu will never be used unless weights are on cpu, or there are no gpu ops between cpu ops
2024-06-13 03:11:35 +02:00
// ops unsupported by the backend being expanded will be left unassigned so that they can be assigned later when the locations of its inputs are known
// expand gpu down
2024-01-12 20:07:38 +01:00
{
2024-02-12 09:16:06 +02:00
int cur_backend_id = - 1 ;
2024-01-12 20:07:38 +01:00
for ( int i = 0 ; i < graph -> n_nodes ; i ++ ) {
struct ggml_tensor * node = graph -> nodes [ i ];
if ( ggml_is_view_op ( node -> op )) {
continue ;
}
2024-03-18 11:03:04 +01:00
int * node_backend_id = & tensor_backend_id ( node );
if ( * node_backend_id != - 1 ) {
if ( * node_backend_id == sched -> n_backends - 1 ) {
2024-01-12 20:07:38 +01:00
// skip cpu (lowest prio backend)
2024-02-12 09:16:06 +02:00
cur_backend_id = - 1 ;
2024-01-12 20:07:38 +01:00
} else {
2024-03-18 11:03:04 +01:00
cur_backend_id = * node_backend_id ;
2024-01-12 20:07:38 +01:00
}
2024-06-13 03:11:35 +02:00
} else if ( cur_backend_id != - 1 ) {
ggml_backend_sched_set_if_supported ( sched , node , cur_backend_id , node_backend_id );
2024-01-12 20:07:38 +01:00
}
}
}
2024-06-13 03:11:35 +02:00
// expand gpu up
2024-01-12 20:07:38 +01:00
{
2024-02-12 09:16:06 +02:00
int cur_backend_id = - 1 ;
2024-01-12 20:07:38 +01:00
for ( int i = graph -> n_nodes - 1 ; i >= 0 ; i -- ) {
struct ggml_tensor * node = graph -> nodes [ i ];
if ( ggml_is_view_op ( node -> op )) {
continue ;
}
2024-03-18 11:03:04 +01:00
int * node_backend_id = & tensor_backend_id ( node );
if ( * node_backend_id != - 1 ) {
if ( * node_backend_id == sched -> n_backends - 1 ) {
2024-03-13 18:54:21 +01:00
// skip cpu (lowest prio backend)
cur_backend_id = - 1 ;
} else {
2024-03-18 11:03:04 +01:00
cur_backend_id = * node_backend_id ;
2024-03-13 18:54:21 +01:00
}
2024-06-13 03:11:35 +02:00
} else if ( cur_backend_id != - 1 ) {
ggml_backend_sched_set_if_supported ( sched , node , cur_backend_id , node_backend_id );
2024-01-12 20:07:38 +01:00
}
}
}
2024-06-13 03:11:35 +02:00
// expand rest down
2024-01-12 20:38:34 +01:00
{
2024-02-12 09:16:06 +02:00
int cur_backend_id = - 1 ;
2024-01-12 20:38:34 +01:00
for ( int i = 0 ; i < graph -> n_nodes ; i ++ ) {
struct ggml_tensor * node = graph -> nodes [ i ];
if ( ggml_is_view_op ( node -> op )) {
continue ;
}
2024-03-18 11:03:04 +01:00
int * node_backend_id = & tensor_backend_id ( node );
if ( * node_backend_id != - 1 ) {
cur_backend_id = * node_backend_id ;
2024-06-13 03:11:35 +02:00
} else if ( cur_backend_id != - 1 ) {
ggml_backend_sched_set_if_supported ( sched , node , cur_backend_id , node_backend_id );
2024-01-12 20:38:34 +01:00
}
}
}
2024-06-13 03:11:35 +02:00
// expand rest up
2024-03-13 18:54:21 +01:00
{
int cur_backend_id = - 1 ;
for ( int i = graph -> n_nodes - 1 ; i >= 0 ; i -- ) {
struct ggml_tensor * node = graph -> nodes [ i ];
if ( ggml_is_view_op ( node -> op )) {
continue ;
}
2024-03-18 11:03:04 +01:00
int * node_backend_id = & tensor_backend_id ( node );
if ( * node_backend_id != - 1 ) {
cur_backend_id = * node_backend_id ;
2024-06-13 03:11:35 +02:00
} else if ( cur_backend_id != - 1 ) {
ggml_backend_sched_set_if_supported ( sched , node , cur_backend_id , node_backend_id );
2024-03-13 18:54:21 +01:00
}
}
}
2024-06-13 03:11:35 +02:00
// pass 3: upgrade nodes to higher prio backends with compatible buffer types
// if the tensor is already in the same buffer type (*) as another higher priority backend, we should move it there
// however, we also need to verify that the sources are in compatible buffer types
// (*) the actual requirement is more relaxed, the buffer type of the backend should be supported by all the users of this tensor further down the graph
// however, this is slow to verify, so we have a more strict requirement that the buffer type is the same
// this is not uncommon since multiple backends can use host memory, with the same buffer type (eg. BLAS and CPU)
// additionally, set remaining unassigned nodes to the backend with the most supported inputs
// only nodes that could not be assigned during expansion due to the backend not supporting the op should be unassigned at this point
for ( int i = 0 ; i < graph -> n_nodes ; i ++ ) {
struct ggml_tensor * node = graph -> nodes [ i ];
if ( ggml_is_view_op ( node -> op )) {
continue ;
}
int * node_backend_id = & tensor_backend_id ( node );
if ( * node_backend_id == - 1 ) {
// unassigned node: find the backend with the most supported inputs
int n_supported_best = - 1 ;
for ( int b = 0 ; b < sched -> n_backends ; b ++ ) {
if ( ggml_backend_supports_op ( sched -> backends [ b ], node )) {
int n_supported = 0 ;
for ( int j = 0 ; j < GGML_MAX_SRC ; j ++ ) {
struct ggml_tensor * src = node -> src [ j ];
if ( src == NULL ) {
continue ;
}
if (( tensor_backend_id ( src ) != - 1 || tensor_backend_id ( src -> view_src ) != - 1 ) && ggml_backend_sched_buffer_supported ( sched , src , b )) {
n_supported ++ ;
}
}
if ( n_supported > n_supported_best ) {
n_supported_best = n_supported ;
* node_backend_id = b ;
SET_CAUSE ( node , "3.best" );
}
}
}
} else {
// assigned node: upgrade to higher prio backend if possible
for ( int b = 0 ; b < * node_backend_id ; b ++ ) {
if ( sched -> bufts [ b ] == sched -> bufts [ * node_backend_id ] && ggml_backend_supports_op ( sched -> backends [ b ], node )) {
bool supported = true ;
for ( int j = 0 ; j < GGML_MAX_SRC ; j ++ ) {
struct ggml_tensor * src = node -> src [ j ];
if ( src == NULL ) {
continue ;
}
if ( ! ggml_backend_sched_buffer_supported ( sched , src , b )) {
supported = false ;
break ;
}
}
if ( supported ) {
* node_backend_id = b ;
SET_CAUSE ( node , "3.upg" );
break ;
}
}
}
}
}
2024-01-12 20:07:38 +01:00
2024-06-13 03:11:35 +02:00
// pass 4: assign backends to remaining src from dst and view_src
2023-11-13 14:16:23 +02:00
for ( int i = 0 ; i < graph -> n_nodes ; i ++ ) {
struct ggml_tensor * node = graph -> nodes [ i ];
2024-03-18 11:03:04 +01:00
int * cur_backend_id = & tensor_backend_id ( node );
if ( node -> view_src != NULL && * cur_backend_id == - 1 ) {
* cur_backend_id = tensor_backend_id ( node -> view_src );
2024-06-13 03:11:35 +02:00
SET_CAUSE ( node , "4.vsrc" );
2024-01-12 20:07:38 +01:00
}
2023-11-13 14:16:23 +02:00
for ( int j = 0 ; j < GGML_MAX_SRC ; j ++ ) {
struct ggml_tensor * src = node -> src [ j ];
if ( src == NULL ) {
2024-02-17 23:04:16 +02:00
continue ;
2023-11-13 14:16:23 +02:00
}
2024-03-18 11:03:04 +01:00
int * src_backend_id = & tensor_backend_id ( src );
if ( * src_backend_id == - 1 ) {
2024-01-12 20:07:38 +01:00
if ( src -> view_src != NULL ) {
// views are always on the same backend as the source
2024-03-18 11:03:04 +01:00
* src_backend_id = tensor_backend_id ( src -> view_src );
2024-06-13 03:11:35 +02:00
SET_CAUSE ( src , "4.vsrc" );
2024-01-12 20:07:38 +01:00
} else {
2024-03-18 11:03:04 +01:00
* src_backend_id = * cur_backend_id ;
2024-06-13 03:11:35 +02:00
SET_CAUSE ( src , "4.cur" );
2024-01-12 20:07:38 +01:00
}
2023-11-13 14:16:23 +02:00
}
}
2025-08-06 05:37:35 -07:00
// if the node is still unassigned, assign it to the first backend that supports it
for ( int b = 0 ; b < sched -> n_backends && * cur_backend_id == - 1 ; b ++ ) {
ggml_backend_sched_set_if_supported ( sched , node , b , cur_backend_id );
}
GGML_ASSERT ( * cur_backend_id != - 1 );
2023-11-13 14:16:23 +02:00
}
2024-07-27 04:41:55 +02:00
// pass 5: split graph, find tensors that need to be copied
2024-01-12 20:07:38 +01:00
{
2024-03-18 11:03:04 +01:00
int i_split = 0 ;
struct ggml_backend_sched_split * split = & sched -> splits [ 0 ];
2024-01-12 20:07:38 +01:00
// find the backend of the first split, skipping view ops
2024-07-27 04:41:55 +02:00
int i = 0 ;
for (; i < graph -> n_nodes ; i ++ ) {
2024-01-12 20:07:38 +01:00
struct ggml_tensor * node = graph -> nodes [ i ];
if ( ! ggml_is_view_op ( node -> op )) {
2024-03-18 11:03:04 +01:00
split -> backend_id = tensor_backend_id ( node );
2023-11-13 14:16:23 +02:00
break ;
}
2024-01-12 20:07:38 +01:00
}
2024-03-18 11:03:04 +01:00
split -> i_start = 0 ;
split -> n_inputs = 0 ;
int cur_backend_id = split -> backend_id ;
2024-07-27 04:41:55 +02:00
for (; i < graph -> n_nodes ; i ++ ) {
2024-01-12 20:07:38 +01:00
struct ggml_tensor * node = graph -> nodes [ i ];
2023-11-13 14:16:23 +02:00
2024-01-12 20:07:38 +01:00
if ( ggml_is_view_op ( node -> op )) {
continue ;
}
2024-03-18 11:03:04 +01:00
const int node_backend_id = tensor_backend_id ( node );
2024-01-12 20:07:38 +01:00
2025-08-06 05:37:35 -07:00
GGML_ASSERT ( node_backend_id != - 1 ); // all nodes should be assigned by now, this can happen if there is no CPU fallback
2024-01-12 20:38:34 +01:00
2024-03-18 11:03:04 +01:00
// check if we should start a new split based on the sources of the current node
bool need_new_split = false ;
if ( node_backend_id == cur_backend_id && split -> n_inputs > 0 ) {
for ( int j = 0 ; j < GGML_MAX_SRC ; j ++ ) {
struct ggml_tensor * src = node -> src [ j ];
if ( src == NULL ) {
continue ;
}
2024-10-30 02:01:23 +01:00
// check if a weight is on a different and incompatible backend
2024-03-18 11:03:04 +01:00
// by starting a new split, the memory of the previously offloaded weights can be reused
if ( src -> buffer != NULL && src -> buffer -> usage == GGML_BACKEND_BUFFER_USAGE_WEIGHTS ) {
int src_backend_id = tensor_backend_id ( src );
2024-10-30 02:01:23 +01:00
if ( src_backend_id != cur_backend_id && ! ggml_backend_sched_buffer_supported ( sched , src , cur_backend_id )) {
2024-03-18 11:03:04 +01:00
need_new_split = true ;
break ;
}
}
// check if the split has too many inputs
2024-06-13 03:11:35 +02:00
// FIXME: count the number of inputs instead of only checking when full
2024-03-18 11:03:04 +01:00
if ( split -> n_inputs == GGML_SCHED_MAX_SPLIT_INPUTS ) {
const size_t id = hash_id ( src );
2024-07-27 04:41:55 +02:00
int src_backend_id = sched -> hv_tensor_backend_ids [ id ];
2024-06-13 03:11:35 +02:00
bool supported = ggml_backend_sched_buffer_supported ( sched , src , cur_backend_id );
2024-07-27 04:41:55 +02:00
if ( src_backend_id != cur_backend_id && tensor_id_copy ( id , cur_backend_id , 0 ) == NULL && ! supported ) {
2024-03-18 11:03:04 +01:00
need_new_split = true ;
break ;
}
}
}
}
if ( node_backend_id != cur_backend_id || need_new_split ) {
split -> i_end = i ;
i_split ++ ;
if ( i_split >= sched -> splits_capacity ) {
sched -> splits_capacity *= 2 ;
2024-10-03 01:49:47 +02:00
sched -> splits = ( ggml_backend_sched_split * )
realloc ( sched -> splits , sched -> splits_capacity * sizeof ( struct ggml_backend_sched_split ));
2024-03-18 11:03:04 +01:00
GGML_ASSERT ( sched -> splits != NULL );
}
split = & sched -> splits [ i_split ];
split -> backend_id = node_backend_id ;
split -> i_start = i ;
split -> n_inputs = 0 ;
cur_backend_id = node_backend_id ;
2024-01-12 20:07:38 +01:00
}
// find inputs that are not on the same backend
for ( int j = 0 ; j < GGML_MAX_SRC ; j ++ ) {
struct ggml_tensor * src = node -> src [ j ];
if ( src == NULL ) {
2024-02-17 23:04:16 +02:00
continue ;
2024-01-12 20:07:38 +01:00
}
2024-03-13 18:54:21 +01:00
2024-07-27 04:41:55 +02:00
size_t src_id = hash_id ( src );
const int src_backend_id = sched -> hv_tensor_backend_ids [ src_id ];
2025-08-06 05:37:35 -07:00
GGML_ASSERT ( src_backend_id != - 1 ); // all inputs should be assigned by now
2024-03-13 18:54:21 +01:00
2024-06-13 03:11:35 +02:00
if ( src -> flags & GGML_TENSOR_FLAG_INPUT && sched -> n_copies > 1 ) {
2024-07-27 04:41:55 +02:00
if ( tensor_id_copy ( src_id , src_backend_id , 0 ) == NULL ) {
2024-03-13 18:54:21 +01:00
ggml_backend_t backend = sched -> backends [ src_backend_id ];
for ( int c = 0 ; c < sched -> n_copies ; c ++ ) {
struct ggml_tensor * tensor_copy ;
if ( c == sched -> cur_copy ) {
tensor_copy = src ; // use the original tensor as the current copy
} else {
tensor_copy = ggml_dup_tensor_layout ( sched -> ctx , src );
ggml_format_name ( tensor_copy , "%s#%s#%d" , ggml_backend_name ( backend ), src -> name , c );
}
2025-12-02 12:52:45 +01:00
ggml_set_input ( tensor_copy );
ggml_set_output ( tensor_copy ); // prevent ggml-alloc from overwriting the tensor
2024-07-27 04:41:55 +02:00
tensor_id_copy ( src_id , src_backend_id , c ) = tensor_copy ;
2024-03-13 18:54:21 +01:00
SET_CAUSE ( tensor_copy , "4.cpy" );
}
int n_graph_inputs = sched -> n_graph_inputs ++ ;
GGML_ASSERT ( n_graph_inputs < GGML_SCHED_MAX_SPLIT_INPUTS );
sched -> graph_inputs [ n_graph_inputs ] = src ;
}
}
2024-07-27 04:41:55 +02:00
if ( src_backend_id != cur_backend_id && ! ggml_backend_sched_buffer_supported ( sched , src , cur_backend_id )) {
2024-01-20 16:05:49 +01:00
// create a copy of the input in the split's backend
2024-07-27 04:41:55 +02:00
if ( tensor_id_copy ( src_id , cur_backend_id , 0 ) == NULL ) {
2024-02-12 09:16:06 +02:00
ggml_backend_t backend = sched -> backends [ cur_backend_id ];
2024-03-13 18:54:21 +01:00
for ( int c = 0 ; c < sched -> n_copies ; c ++ ) {
struct ggml_tensor * tensor_copy = ggml_dup_tensor_layout ( sched -> ctx , src );
ggml_format_name ( tensor_copy , "%s#%s#%d" , ggml_backend_name ( backend ), src -> name , c );
if ( sched -> n_copies > 1 ) {
ggml_set_input ( tensor_copy );
ggml_set_output ( tensor_copy ); // prevent ggml-alloc from overwriting the tensor
}
2024-07-27 04:41:55 +02:00
tensor_id_copy ( src_id , cur_backend_id , c ) = tensor_copy ;
2024-03-13 18:54:21 +01:00
SET_CAUSE ( tensor_copy , "4.cpy" );
}
2024-03-18 11:03:04 +01:00
int n_inputs = split -> n_inputs ++ ;
2024-03-13 18:54:21 +01:00
GGML_ASSERT ( n_inputs < GGML_SCHED_MAX_SPLIT_INPUTS );
2024-03-18 11:03:04 +01:00
split -> inputs [ n_inputs ] = src ;
2024-01-20 16:05:49 +01:00
}
2024-07-27 04:41:55 +02:00
node -> src [ j ] = tensor_id_copy ( src_id , cur_backend_id , sched -> cur_copy );
2023-11-13 14:16:23 +02:00
}
}
}
2024-03-18 11:03:04 +01:00
split -> i_end = graph -> n_nodes ;
sched -> n_splits = i_split + 1 ;
2023-11-13 14:16:23 +02:00
}
2024-06-13 03:11:35 +02:00
if ( sched -> debug ) {
ggml_backend_sched_print_assignments ( sched , graph );
}
2024-07-27 04:41:55 +02:00
// swap node_backend_ids and leaf _backend_ids with prevs
2024-06-13 03:11:35 +02:00
{
int * tmp = sched -> node_backend_ids ;
sched -> node_backend_ids = sched -> prev_node_backend_ids ;
sched -> prev_node_backend_ids = tmp ;
tmp = sched -> leaf_backend_ids ;
sched -> leaf_backend_ids = sched -> prev_leaf_backend_ids ;
sched -> prev_leaf_backend_ids = tmp ;
}
2023-11-13 14:16:23 +02:00
2024-10-03 01:49:47 +02:00
int graph_size = std :: max ( graph -> n_nodes , graph -> n_leafs ) + sched -> n_splits * GGML_SCHED_MAX_SPLIT_INPUTS * 2 * sched -> n_copies ;
2025-12-01 12:49:33 +02:00
// remember the actual graph_size for performing reallocation checks later [GGML_SCHED_DEBUG_REALLOC]
sched -> debug_prev_graph_size = sched -> debug_graph_size ;
sched -> debug_graph_size = graph_size ;
2024-07-27 04:41:55 +02:00
if ( sched -> graph . size < graph_size ) {
sched -> graph . size = graph_size ;
2024-10-03 01:49:47 +02:00
sched -> graph . nodes = ( ggml_tensor ** ) realloc ( sched -> graph . nodes , graph_size * sizeof ( struct ggml_tensor * ));
sched -> graph . leafs = ( ggml_tensor ** ) realloc ( sched -> graph . leafs , graph_size * sizeof ( struct ggml_tensor * ));
2024-07-27 04:41:55 +02:00
GGML_ASSERT ( sched -> graph . nodes != NULL );
GGML_ASSERT ( sched -> graph . leafs != NULL );
}
sched -> graph . n_nodes = 0 ;
sched -> graph . n_leafs = 0 ;
struct ggml_cgraph * graph_copy = & sched -> graph ;
2023-11-13 14:16:23 +02:00
for ( int i = 0 ; i < sched -> n_splits ; i ++ ) {
struct ggml_backend_sched_split * split = & sched -> splits [ i ];
2023-12-07 22:26:54 +02:00
split -> graph = ggml_graph_view ( graph , split -> i_start , split -> i_end );
2023-11-13 14:16:23 +02:00
2025-09-08 13:10:07 -05:00
// Optimize this split of the graph. This needs to happen before we make graph_copy,
// so they are in sync.
2025-09-18 13:46:17 -05:00
ggml_backend_graph_optimize ( sched -> backends [ split -> backend_id ], & split -> graph );
2025-09-08 13:10:07 -05:00
2024-03-13 18:54:21 +01:00
// add inputs to the graph copy so that they are allocated by ggml-alloc at the start of the split
2023-11-13 14:16:23 +02:00
for ( int j = 0 ; j < split -> n_inputs ; j ++ ) {
2024-03-18 11:03:04 +01:00
assert ( graph_copy -> size > ( graph_copy -> n_nodes + 1 ));
2023-11-13 14:16:23 +02:00
struct ggml_tensor * input = split -> inputs [ j ];
2024-03-18 11:03:04 +01:00
const size_t input_id = hash_id ( input );
2024-07-27 04:41:55 +02:00
struct ggml_tensor * input_cpy = tensor_id_copy ( input_id , split -> backend_id , sched -> cur_copy );
2024-02-12 09:16:06 +02:00
2024-01-12 20:07:38 +01:00
// add a dependency to the input source so that it is not freed before the copy is done
2024-02-12 09:16:06 +02:00
struct ggml_tensor * input_dep = ggml_view_tensor ( sched -> ctx , input );
2024-03-13 18:54:21 +01:00
input_dep -> src [ 0 ] = input ;
2024-07-27 04:41:55 +02:00
sched -> node_backend_ids [ graph_copy -> n_nodes ] = sched -> hv_tensor_backend_ids [ input_id ];
2024-02-12 09:16:06 +02:00
graph_copy -> nodes [ graph_copy -> n_nodes ++ ] = input_dep ;
// add a dependency to the input copy so that it is allocated at the start of the split
sched -> node_backend_ids [ graph_copy -> n_nodes ] = split -> backend_id ;
2023-11-13 14:16:23 +02:00
graph_copy -> nodes [ graph_copy -> n_nodes ++ ] = input_cpy ;
}
for ( int j = split -> i_start ; j < split -> i_end ; j ++ ) {
2024-03-18 11:03:04 +01:00
assert ( graph_copy -> size > graph_copy -> n_nodes );
2024-02-12 09:16:06 +02:00
sched -> node_backend_ids [ graph_copy -> n_nodes ] = tensor_backend_id ( graph -> nodes [ j ]);
2023-11-13 14:16:23 +02:00
graph_copy -> nodes [ graph_copy -> n_nodes ++ ] = graph -> nodes [ j ];
}
}
2024-03-13 18:54:21 +01:00
if ( sched -> n_copies > 1 ) {
// add input copies as leafs so that they are allocated first
for ( int i = 0 ; i < sched -> n_graph_inputs ; i ++ ) {
struct ggml_tensor * input = sched -> graph_inputs [ i ];
size_t id = hash_id ( input );
int backend_id = tensor_backend_id ( input );
for ( int c = 0 ; c < sched -> n_copies ; c ++ ) {
2024-07-27 04:41:55 +02:00
struct ggml_tensor * input_cpy = tensor_id_copy ( id , backend_id , c );
2024-03-13 18:54:21 +01:00
sched -> leaf_backend_ids [ graph_copy -> n_leafs ] = backend_id ;
2024-09-05 11:13:11 +02:00
assert ( graph_copy -> size > graph_copy -> n_leafs );
2024-03-13 18:54:21 +01:00
graph_copy -> leafs [ graph_copy -> n_leafs ++ ] = input_cpy ;
}
}
for ( int i = 0 ; i < sched -> n_splits ; i ++ ) {
struct ggml_backend_sched_split * split = & sched -> splits [ i ];
int backend_id = split -> backend_id ;
for ( int j = 0 ; j < split -> n_inputs ; j ++ ) {
struct ggml_tensor * input = split -> inputs [ j ];
size_t id = hash_id ( input );
for ( int c = 0 ; c < sched -> n_copies ; c ++ ) {
2024-07-27 04:41:55 +02:00
struct ggml_tensor * input_cpy = tensor_id_copy ( id , backend_id , c );
2024-03-13 18:54:21 +01:00
sched -> leaf_backend_ids [ graph_copy -> n_leafs ] = backend_id ;
2024-09-05 11:13:11 +02:00
assert ( graph_copy -> size > graph_copy -> n_leafs );
2024-03-13 18:54:21 +01:00
graph_copy -> leafs [ graph_copy -> n_leafs ++ ] = input_cpy ;
}
}
}
}
// add leafs from the original graph
for ( int i = 0 ; i < graph -> n_leafs ; i ++ ) {
struct ggml_tensor * leaf = graph -> leafs [ i ];
sched -> leaf_backend_ids [ graph_copy -> n_leafs ] = tensor_backend_id ( leaf );
2024-09-05 11:13:11 +02:00
assert ( graph_copy -> size > graph_copy -> n_leafs );
2024-03-13 18:54:21 +01:00
graph_copy -> leafs [ graph_copy -> n_leafs ++ ] = leaf ;
}
2023-11-13 14:16:23 +02:00
}
2024-02-12 09:16:06 +02:00
static bool ggml_backend_sched_alloc_splits ( ggml_backend_sched_t sched ) {
2024-06-13 03:11:35 +02:00
bool backend_ids_changed = false ;
2024-07-27 04:41:55 +02:00
for ( int i = 0 ; i < sched -> graph . n_nodes ; i ++ ) {
2024-06-18 09:37:20 +03:00
if ( sched -> node_backend_ids [ i ] != sched -> prev_node_backend_ids [ i ] &&
sched -> bufts [ sched -> node_backend_ids [ i ]] != sched -> bufts [ sched -> prev_node_backend_ids [ i ]]) {
2024-06-13 03:11:35 +02:00
backend_ids_changed = true ;
break ;
}
}
if ( ! backend_ids_changed ) {
2024-07-27 04:41:55 +02:00
for ( int i = 0 ; i < sched -> graph . n_leafs ; i ++ ) {
2024-06-18 09:37:20 +03:00
if ( sched -> leaf_backend_ids [ i ] != sched -> prev_leaf_backend_ids [ i ] &&
sched -> bufts [ sched -> leaf_backend_ids [ i ]] != sched -> bufts [ sched -> prev_leaf_backend_ids [ i ]]) {
2024-06-13 03:11:35 +02:00
backend_ids_changed = true ;
break ;
}
}
}
2024-03-13 18:54:21 +01:00
// allocate graph
2024-07-27 04:41:55 +02:00
if ( backend_ids_changed || ! ggml_gallocr_alloc_graph ( sched -> galloc , & sched -> graph )) {
2025-11-28 07:33:23 -08:00
#ifndef NDEBUG
GGML_LOG_DEBUG ( "%s: failed to allocate graph, reserving (backend_ids_changed = %d) \n " , __func__ , backend_ids_changed );
#endif
2025-12-01 12:49:33 +02:00
if ( sched -> debug_realloc > 0 ) {
// we are interested only in situations where the graph was reallocated even though its size remained the same [GGML_SCHED_DEBUG_REALLOC]
// example: https://github.com/ggml-org/llama.cpp/pull/17143
const bool unexpected = ! backend_ids_changed && sched -> debug_prev_graph_size == sched -> debug_graph_size ;
if ( unexpected || sched -> debug_realloc > 1 ) {
GGML_ABORT ( "%s: unexpected graph reallocation (graph size = %d, nodes = %d, leafs = %d), debug_realloc = %d \n " , __func__ ,
sched -> debug_graph_size , sched -> graph . n_nodes , sched -> graph . n_leafs , sched -> debug_realloc );
}
}
2024-03-13 18:54:21 +01:00
// the re-allocation may cause the split inputs to be moved to a different address
2025-05-30 09:56:19 -07:00
// synchronize without ggml_backend_sched_synchronize to avoid changing cur_copy
for ( int i = 0 ; i < sched -> n_backends ; i ++ ) {
ggml_backend_synchronize ( sched -> backends [ i ]);
}
2025-11-28 07:33:23 -08:00
2024-07-27 04:41:55 +02:00
ggml_gallocr_reserve_n ( sched -> galloc , & sched -> graph , sched -> node_backend_ids , sched -> leaf_backend_ids );
if ( ! ggml_gallocr_alloc_graph ( sched -> galloc , & sched -> graph )) {
2024-10-11 15:34:45 +02:00
GGML_LOG_ERROR ( "%s: failed to allocate graph \n " , __func__ );
2024-02-12 09:16:06 +02:00
return false ;
}
}
return true ;
2023-11-13 14:16:23 +02:00
}
2024-03-04 10:05:42 +01:00
static enum ggml_status ggml_backend_sched_compute_splits ( ggml_backend_sched_t sched ) {
2025-08-30 16:32:10 +02:00
GGML_ASSERT ( sched );
2023-11-13 14:16:23 +02:00
struct ggml_backend_sched_split * splits = sched -> splits ;
2025-08-20 16:35:28 -07:00
ggml_tensor * prev_ids_tensor = nullptr ;
std :: vector < int32_t > ids ;
std :: vector < ggml_bitset_t > used_ids ;
2025-08-21 14:09:32 -07:00
for ( int split_id = 0 ; split_id < sched -> n_splits ; split_id ++ ) {
struct ggml_backend_sched_split * split = & splits [ split_id ];
2024-02-12 09:16:06 +02:00
int split_backend_id = split -> backend_id ;
ggml_backend_t split_backend = sched -> backends [ split_backend_id ];
2023-11-13 14:16:23 +02:00
// copy the input tensors to the split backend
2025-08-21 14:09:32 -07:00
for ( int input_id = 0 ; input_id < split -> n_inputs ; input_id ++ ) {
ggml_backend_t input_backend = ggml_backend_sched_get_tensor_backend ( sched , split -> inputs [ input_id ]);
struct ggml_tensor * input = split -> inputs [ input_id ];
2024-07-27 04:41:55 +02:00
struct ggml_tensor * input_cpy = tensor_copy ( input , split_backend_id , sched -> cur_copy );
2024-01-12 20:07:38 +01:00
2024-03-13 18:54:21 +01:00
if ( input -> flags & GGML_TENSOR_FLAG_INPUT ) {
// inputs from the user must be copied immediately to prevent the user overwriting the data before the copy is done
if ( sched -> events [ split_backend_id ][ sched -> cur_copy ] != NULL ) {
ggml_backend_event_synchronize ( sched -> events [ split_backend_id ][ sched -> cur_copy ]);
} else {
ggml_backend_synchronize ( split_backend );
}
ggml_backend_tensor_copy ( input , input_cpy );
} else {
2024-03-18 11:03:04 +01:00
// wait for the split backend to finish using the input before overwriting it
2024-03-13 18:54:21 +01:00
if ( sched -> events [ split_backend_id ][ sched -> cur_copy ] != NULL ) {
ggml_backend_event_wait ( split_backend , sched -> events [ split_backend_id ][ sched -> cur_copy ]);
} else {
ggml_backend_synchronize ( split_backend );
}
2025-08-20 16:35:28 -07:00
// when offloading MoE weights, we can reduce the amount of data copied by copying only the experts that are used
ggml_tensor * node = split -> graph . nodes [ 0 ];
if ( split -> graph . n_nodes > 0 &&
ggml_backend_buffer_get_usage ( input -> buffer ) == GGML_BACKEND_BUFFER_USAGE_WEIGHTS &&
ggml_backend_buffer_is_host ( input -> buffer ) && (
( node -> src [ 0 ] == input_cpy && node -> op == GGML_OP_MUL_MAT_ID )
//|| (node->src[1] == input_cpy && node->op == GGML_OP_ADD_ID) /* GGML_OP_ADD_ID weights are small and not worth splitting */
)) {
const int64_t n_expert = node -> op == GGML_OP_MUL_MAT_ID ? input -> ne [ 2 ] : input -> ne [ 1 ];
const size_t expert_size = node -> op == GGML_OP_MUL_MAT_ID ? input -> nb [ 2 ] : input -> nb [ 1 ];
2024-08-07 13:29:02 +02:00
ggml_backend_synchronize ( input_backend );
2025-08-20 16:35:28 -07:00
// get the ids
ggml_tensor * ids_tensor = node -> src [ 2 ];
2025-08-21 14:09:32 -07:00
ggml_backend_t ids_backend = split_backend ;
// if the ids tensor is also an input of the split, it may not have been copied yet to the split backend
// in that case, we use the original ids tensor
for ( int i = input_id + 1 ; i < split -> n_inputs ; i ++ ) {
if ( ids_tensor == tensor_copy ( split -> inputs [ i ], split_backend_id , sched -> cur_copy )) {
ids_tensor = split -> inputs [ i ];
ids_backend = ggml_backend_sched_get_tensor_backend ( sched , split -> inputs [ i ]);
break ;
}
}
2025-08-20 16:35:28 -07:00
if ( ids_tensor != prev_ids_tensor ) {
ids . resize ( ggml_nbytes ( ids_tensor ) / sizeof ( int32_t ));
2025-08-21 14:09:32 -07:00
ggml_backend_tensor_get_async ( ids_backend , ids_tensor , ids . data (), 0 , ggml_nbytes ( ids_tensor ));
ggml_backend_synchronize ( ids_backend );
2025-08-20 16:35:28 -07:00
// find the used experts
used_ids . clear ();
used_ids . resize ( ggml_bitset_size ( n_expert ));
for ( int64_t i1 = 0 ; i1 < ids_tensor -> ne [ 1 ]; i1 ++ ) {
for ( int64_t i0 = 0 ; i0 < ids_tensor -> ne [ 0 ]; i0 ++ ) {
int32_t id = ids [ i1 * ids_tensor -> nb [ 1 ] / sizeof ( int32_t ) + i0 * ids_tensor -> nb [ 0 ] / sizeof ( int32_t )];
2025-08-21 14:09:32 -07:00
GGML_ASSERT ( id >= 0 && id < n_expert );
2025-08-20 16:35:28 -07:00
ggml_bitset_set ( used_ids . data (), id );
}
}
prev_ids_tensor = ids_tensor ;
}
// group consecutive experts and copy them together
auto copy_experts = [ & ]( int32_t first_id , int32_t last_id ) {
const size_t expert_offset = first_id * expert_size ;
const size_t expert_size_copy = ( last_id - first_id + 1 ) * expert_size ;
const size_t padding = std :: min < size_t > ( expert_size , 512 );
const size_t padding_end = last_id < n_expert - 1 ? padding : 0 ;
ggml_backend_tensor_set_async ( split_backend ,
input_cpy ,
( const uint8_t * ) input -> data + expert_offset , expert_offset ,
// copy a bit extra at the to ensure there are no NaNs in the padding of the last expert
// this is necessary for MMQ in the CUDA backend
expert_size_copy + padding_end );
};
int id = 0 ;
while ( ! ggml_bitset_get ( used_ids . data (), id )) {
id ++ ;
}
int32_t first_id = id ;
int32_t last_id = first_id ;
for ( ++ id ; id < n_expert ; ++ id ) {
if ( ! ggml_bitset_get ( used_ids . data (), id )) {
continue ;
}
if ( id == last_id + 1 ) {
last_id = id ;
continue ;
}
copy_experts ( first_id , last_id );
first_id = id ;
last_id = id ;
}
copy_experts ( first_id , last_id );
} else {
// try async copy, but if not possible, we can still use a sync copy without synchronizing the dst backend, since we handle the synchronization here with multiple copies and events
// TODO: add public function to facilitate this, since applications do not have direct access to the backend interface
if ( ! split_backend -> iface . cpy_tensor_async || ! split_backend -> iface . cpy_tensor_async ( input_backend , split_backend , input , input_cpy )) {
ggml_backend_synchronize ( input_backend );
if ( sched -> events [ split_backend_id ][ sched -> cur_copy ] != NULL ) {
ggml_backend_event_synchronize ( sched -> events [ split_backend_id ][ sched -> cur_copy ]);
} else {
ggml_backend_synchronize ( split_backend );
}
ggml_backend_tensor_copy ( input , input_cpy );
2024-08-07 13:29:02 +02:00
}
}
2024-03-13 18:54:21 +01:00
}
2023-11-13 14:16:23 +02:00
}
2024-01-17 18:39:41 +02:00
if ( ! sched -> callback_eval ) {
2024-03-13 18:54:21 +01:00
enum ggml_status ec = ggml_backend_graph_compute_async ( split_backend , & split -> graph );
2024-03-04 10:05:42 +01:00
if ( ec != GGML_STATUS_SUCCESS ) {
return ec ;
2024-02-12 09:16:06 +02:00
}
2024-01-17 18:39:41 +02:00
} else {
// similar to ggml_backend_compare_graph_backend
for ( int j0 = 0 ; j0 < split -> graph . n_nodes ; j0 ++ ) {
struct ggml_tensor * t = split -> graph . nodes [ j0 ];
// check if the user needs data from this node
bool need = sched -> callback_eval ( t , true , sched -> callback_eval_user_data );
int j1 = j0 ;
// determine the range [j0, j1] of nodes that can be computed together
while ( ! need && j1 < split -> graph . n_nodes - 1 ) {
t = split -> graph . nodes [ ++ j1 ];
need = sched -> callback_eval ( t , true , sched -> callback_eval_user_data );
}
struct ggml_cgraph gv = ggml_graph_view ( & split -> graph , j0 , j1 + 1 );
2024-03-13 18:54:21 +01:00
enum ggml_status ec = ggml_backend_graph_compute_async ( split_backend , & gv );
2024-03-04 10:05:42 +01:00
if ( ec != GGML_STATUS_SUCCESS ) {
return ec ;
2024-02-12 09:16:06 +02:00
}
2024-01-17 18:39:41 +02:00
2024-03-13 18:54:21 +01:00
// TODO: pass backend to the callback, then the user can decide if they want to synchronize
ggml_backend_synchronize ( split_backend );
2024-01-17 18:39:41 +02:00
if ( need && ! sched -> callback_eval ( t , false , sched -> callback_eval_user_data )) {
break ;
}
j0 = j1 ;
}
}
2023-11-13 14:16:23 +02:00
2024-03-13 18:54:21 +01:00
// record the event of this copy
if ( split -> n_inputs > 0 ) {
if ( sched -> events [ split_backend_id ][ sched -> cur_copy ] != NULL ) {
2024-10-03 01:49:47 +02:00
ggml_backend_event_record ( sched -> events [ split_backend_id ][ sched -> cur_copy ], split_backend );
2024-03-13 18:54:21 +01:00
}
2023-11-13 14:16:23 +02:00
}
}
2024-03-13 18:54:21 +01:00
2024-03-04 10:05:42 +01:00
return GGML_STATUS_SUCCESS ;
2023-11-13 14:16:23 +02:00
}
2024-03-13 18:54:21 +01:00
ggml_backend_sched_t ggml_backend_sched_new (
ggml_backend_t * backends ,
ggml_backend_buffer_type_t * bufts ,
int n_backends ,
size_t graph_size ,
2025-05-11 20:18:39 +08:00
bool parallel ,
bool op_offload ) {
2024-01-12 20:07:38 +01:00
GGML_ASSERT ( n_backends > 0 );
2024-03-13 18:54:21 +01:00
GGML_ASSERT ( n_backends <= GGML_SCHED_MAX_BACKENDS );
2024-11-14 18:04:35 +01:00
GGML_ASSERT ( ggml_backend_dev_type ( ggml_backend_get_device ( backends [ n_backends - 1 ])) == GGML_BACKEND_DEVICE_TYPE_CPU );
2023-11-13 14:16:23 +02:00
2024-10-03 01:49:47 +02:00
struct ggml_backend_sched * sched = ( ggml_backend_sched * ) calloc ( 1 , sizeof ( struct ggml_backend_sched ));
2024-01-12 20:07:38 +01:00
2024-10-30 02:01:23 +01:00
const char * GGML_SCHED_DEBUG = getenv ( "GGML_SCHED_DEBUG" );
sched -> debug = GGML_SCHED_DEBUG ? atoi ( GGML_SCHED_DEBUG ) : 0 ;
2025-12-01 12:49:33 +02:00
sched -> debug_realloc = 0 ;
#ifdef GGML_SCHED_NO_REALLOC
sched -> debug_realloc = 1 ;
#endif
const char * GGML_SCHED_DEBUG_REALLOC = getenv ( "GGML_SCHED_DEBUG_REALLOC" );
sched -> debug_realloc = GGML_SCHED_DEBUG_REALLOC ? atoi ( GGML_SCHED_DEBUG_REALLOC ) : sched -> debug_realloc ;
2024-07-27 04:41:55 +02:00
sched -> n_backends = n_backends ;
sched -> n_copies = parallel ? GGML_SCHED_MAX_COPIES : 1 ;
2024-06-13 03:11:35 +02:00
2024-01-12 20:07:38 +01:00
// initialize hash table
2024-07-27 04:41:55 +02:00
// FIXME: needs to be size*2 to account for leafs (do it in graph_split instead)
sched -> hash_set = ggml_hash_set_new ( graph_size );
2024-10-03 01:49:47 +02:00
sched -> hv_tensor_backend_ids = ( int * ) malloc ( sched -> hash_set . size * sizeof ( sched -> hv_tensor_backend_ids [ 0 ]));
sched -> hv_tensor_copies = ( ggml_tensor ** ) malloc ( sched -> hash_set . size * sched -> n_backends * sched -> n_copies * sizeof ( struct ggml_tensor * ));
2024-03-18 11:03:04 +01:00
2024-08-16 04:22:55 +02:00
const size_t ggml_sched_max_splits = graph_size ; // at most there is one split for each node in the graph
const size_t nodes_size = graph_size + ggml_sched_max_splits * GGML_SCHED_MAX_SPLIT_INPUTS * 2 ;
2024-10-03 01:49:47 +02:00
sched -> node_backend_ids = ( int * ) calloc ( nodes_size , sizeof ( sched -> node_backend_ids [ 0 ]));
sched -> leaf_backend_ids = ( int * ) calloc ( nodes_size , sizeof ( sched -> leaf_backend_ids [ 0 ]));
sched -> prev_node_backend_ids = ( int * ) calloc ( nodes_size , sizeof ( sched -> prev_node_backend_ids [ 0 ]));
sched -> prev_leaf_backend_ids = ( int * ) calloc ( nodes_size , sizeof ( sched -> prev_leaf_backend_ids [ 0 ]));
2023-11-13 14:16:23 +02:00
2025-12-01 12:49:33 +02:00
sched -> debug_graph_size = 0 ;
sched -> debug_prev_graph_size = 0 ;
2024-08-16 04:22:55 +02:00
sched -> context_buffer_size = ggml_sched_max_splits * GGML_SCHED_MAX_SPLIT_INPUTS * 2 * sizeof ( struct ggml_tensor ) + ggml_graph_overhead_custom ( graph_size , false );
2024-10-03 01:49:47 +02:00
sched -> context_buffer = ( char * ) malloc ( sched -> context_buffer_size );
2024-03-13 18:54:21 +01:00
2024-03-18 11:03:04 +01:00
const int initial_splits_capacity = 16 ;
2024-10-03 01:49:47 +02:00
sched -> splits = ( ggml_backend_sched_split * ) calloc ( initial_splits_capacity , sizeof ( sched -> splits [ 0 ]));
2024-03-18 11:03:04 +01:00
sched -> splits_capacity = initial_splits_capacity ;
2024-03-13 18:54:21 +01:00
for ( int b = 0 ; b < n_backends ; b ++ ) {
sched -> backends [ b ] = backends [ b ];
sched -> bufts [ b ] = bufts ? bufts [ b ] : ggml_backend_get_default_buffer_type ( backends [ b ]);
2024-06-13 03:11:35 +02:00
GGML_ASSERT ( ggml_backend_supports_buft ( backends [ b ], sched -> bufts [ b ]));
2024-10-18 06:46:16 +01:00
2024-03-13 18:54:21 +01:00
if ( sched -> n_copies > 1 ) {
for ( int c = 0 ; c < sched -> n_copies ; c ++ ) {
2024-10-03 01:49:47 +02:00
sched -> events [ b ][ c ] = ggml_backend_event_new ( backends [ b ] -> device );
2024-03-13 18:54:21 +01:00
}
}
2023-11-13 14:16:23 +02:00
}
2024-02-12 09:16:06 +02:00
sched -> galloc = ggml_gallocr_new_n ( sched -> bufts , n_backends );
2025-05-11 20:18:39 +08:00
sched -> op_offload = op_offload ;
2023-11-13 14:16:23 +02:00
2024-02-12 09:16:06 +02:00
ggml_backend_sched_reset ( sched );
2024-01-12 20:07:38 +01:00
2023-11-13 14:16:23 +02:00
return sched ;
}
void ggml_backend_sched_free ( ggml_backend_sched_t sched ) {
if ( sched == NULL ) {
return ;
}
2024-03-13 18:54:21 +01:00
for ( int b = 0 ; b < sched -> n_backends ; b ++ ) {
for ( int c = 0 ; c < sched -> n_copies ; c ++ ) {
ggml_backend_event_free ( sched -> events [ b ][ c ]);
}
}
2023-11-13 14:16:23 +02:00
ggml_gallocr_free ( sched -> galloc );
2024-01-12 20:07:38 +01:00
ggml_free ( sched -> ctx );
2024-07-27 04:41:55 +02:00
ggml_hash_set_free ( & sched -> hash_set );
2024-03-18 11:03:04 +01:00
free ( sched -> splits );
2024-07-27 04:41:55 +02:00
free ( sched -> hv_tensor_backend_ids );
free ( sched -> hv_tensor_copies );
2024-02-12 09:16:06 +02:00
free ( sched -> node_backend_ids );
2024-03-13 18:54:21 +01:00
free ( sched -> leaf_backend_ids );
2024-06-13 03:11:35 +02:00
free ( sched -> prev_node_backend_ids );
free ( sched -> prev_leaf_backend_ids );
2024-07-27 04:41:55 +02:00
free ( sched -> context_buffer );
free ( sched -> graph . nodes );
free ( sched -> graph . leafs );
2023-11-13 14:16:23 +02:00
free ( sched );
}
2024-02-12 09:16:06 +02:00
void ggml_backend_sched_reset ( ggml_backend_sched_t sched ) {
2025-08-30 16:32:10 +02:00
GGML_ASSERT ( sched );
2024-02-12 09:16:06 +02:00
// reset state for the next run
2024-04-26 19:08:30 +01:00
if ( ! sched -> is_reset ) {
2024-07-27 04:41:55 +02:00
ggml_hash_set_reset ( & sched -> hash_set );
memset ( sched -> hv_tensor_backend_ids , - 1 , sched -> hash_set . size * sizeof ( sched -> hv_tensor_backend_ids [ 0 ]));
memset ( sched -> hv_tensor_copies , 0 , sched -> hash_set . size * sched -> n_backends * sched -> n_copies * sizeof ( struct ggml_tensor * ));
2024-04-26 19:08:30 +01:00
sched -> is_reset = true ;
}
2024-03-13 18:54:21 +01:00
sched -> is_alloc = false ;
2023-11-13 14:16:23 +02:00
}
2025-12-15 09:24:59 +01:00
void ggml_backend_sched_reserve_size ( ggml_backend_sched_t sched , struct ggml_cgraph * measure_graph , size_t * sizes ) {
GGML_ASSERT ( sched );
GGML_ASSERT (( int ) sched -> hash_set . size >= measure_graph -> n_nodes + measure_graph -> n_leafs );
GGML_ASSERT ( sizes );
ggml_backend_sched_reset ( sched );
ggml_backend_sched_synchronize ( sched );
ggml_backend_sched_split_graph ( sched , measure_graph );
ggml_gallocr_reserve_n_size ( sched -> galloc , & sched -> graph , sched -> node_backend_ids , sched -> leaf_backend_ids , sizes );
}
2024-02-12 09:16:06 +02:00
bool ggml_backend_sched_reserve ( ggml_backend_sched_t sched , struct ggml_cgraph * measure_graph ) {
2025-08-30 16:32:10 +02:00
GGML_ASSERT ( sched );
2024-07-27 04:41:55 +02:00
GGML_ASSERT (( int ) sched -> hash_set . size >= measure_graph -> n_nodes + measure_graph -> n_leafs );
2024-03-18 11:03:04 +01:00
2024-11-17 07:31:17 +01:00
ggml_backend_sched_synchronize ( sched );
2025-07-25 01:07:26 -07:00
ggml_backend_sched_split_graph ( sched , measure_graph );
2024-07-27 04:41:55 +02:00
if ( ! ggml_gallocr_reserve_n ( sched -> galloc , & sched -> graph , sched -> node_backend_ids , sched -> leaf_backend_ids )) {
2024-02-12 09:16:06 +02:00
return false ;
}
ggml_backend_sched_reset ( sched );
2024-03-13 18:54:21 +01:00
return true ;
}
bool ggml_backend_sched_alloc_graph ( ggml_backend_sched_t sched , struct ggml_cgraph * graph ) {
2025-08-30 16:32:10 +02:00
GGML_ASSERT ( sched );
2024-07-27 04:41:55 +02:00
GGML_ASSERT (( int ) sched -> hash_set . size >= graph -> n_nodes + graph -> n_leafs );
2025-07-25 01:07:26 -07:00
GGML_ASSERT ( ! sched -> is_alloc );
sched -> cur_copy = sched -> next_copy ;
sched -> next_copy = ( sched -> next_copy + 1 ) % sched -> n_copies ;
2024-03-13 18:54:21 +01:00
ggml_backend_sched_split_graph ( sched , graph );
if ( ! ggml_backend_sched_alloc_splits ( sched )) {
return false ;
}
sched -> is_alloc = true ;
2024-02-12 09:16:06 +02:00
return true ;
}
2024-03-04 10:05:42 +01:00
enum ggml_status ggml_backend_sched_graph_compute ( ggml_backend_sched_t sched , struct ggml_cgraph * graph ) {
2024-03-13 18:54:21 +01:00
enum ggml_status err = ggml_backend_sched_graph_compute_async ( sched , graph );
ggml_backend_sched_synchronize ( sched );
return err ;
}
2024-01-12 20:07:38 +01:00
2024-03-13 18:54:21 +01:00
enum ggml_status ggml_backend_sched_graph_compute_async ( ggml_backend_sched_t sched , struct ggml_cgraph * graph ) {
2025-08-30 16:32:10 +02:00
GGML_ASSERT ( sched );
2024-03-13 18:54:21 +01:00
if ( ! sched -> is_reset && ! sched -> is_alloc ) {
2024-02-12 09:16:06 +02:00
ggml_backend_sched_reset ( sched );
2024-01-12 20:07:38 +01:00
}
2023-11-13 14:16:23 +02:00
2024-03-13 18:54:21 +01:00
if ( ! sched -> is_alloc ) {
if ( ! ggml_backend_sched_alloc_graph ( sched , graph )) {
return GGML_STATUS_ALLOC_FAILED ;
}
2024-02-12 09:16:06 +02:00
}
2024-01-12 20:07:38 +01:00
2024-03-04 10:05:42 +01:00
return ggml_backend_sched_compute_splits ( sched );
2024-02-12 09:16:06 +02:00
}
2024-01-17 18:39:41 +02:00
2024-03-13 18:54:21 +01:00
void ggml_backend_sched_synchronize ( ggml_backend_sched_t sched ) {
2025-08-30 16:32:10 +02:00
GGML_ASSERT ( sched );
2024-03-13 18:54:21 +01:00
for ( int i = 0 ; i < sched -> n_backends ; i ++ ) {
ggml_backend_synchronize ( sched -> backends [ i ]);
}
2025-05-30 09:56:19 -07:00
if ( ! sched -> is_alloc ) {
// if the graph is not already allocated, always use copy 0 after a synchronization
// this ensures that during generation the same copy is used every time,
// which avoids changes in the graph that could cause CUDA or other graphs to be disabled
2025-07-25 01:07:26 -07:00
sched -> next_copy = 0 ;
2025-05-30 09:56:19 -07:00
}
2024-03-13 18:54:21 +01:00
}
2024-01-17 18:39:41 +02:00
void ggml_backend_sched_set_eval_callback ( ggml_backend_sched_t sched , ggml_backend_sched_eval_callback callback , void * user_data ) {
2025-08-30 16:32:10 +02:00
GGML_ASSERT ( sched );
2024-01-17 18:39:41 +02:00
sched -> callback_eval = callback ;
sched -> callback_eval_user_data = user_data ;
}
2024-01-12 20:07:38 +01:00
int ggml_backend_sched_get_n_splits ( ggml_backend_sched_t sched ) {
2025-08-30 16:32:10 +02:00
GGML_ASSERT ( sched );
2024-01-12 20:07:38 +01:00
return sched -> n_splits ;
}
2024-03-13 18:54:21 +01:00
int ggml_backend_sched_get_n_copies ( ggml_backend_sched_t sched ) {
2025-08-30 16:32:10 +02:00
GGML_ASSERT ( sched );
2024-03-13 18:54:21 +01:00
return sched -> n_copies ;
}
2024-06-18 09:37:20 +03:00
int ggml_backend_sched_get_n_backends ( ggml_backend_sched_t sched ) {
2025-08-30 16:32:10 +02:00
GGML_ASSERT ( sched );
2024-06-18 09:37:20 +03:00
return sched -> n_backends ;
}
ggml_backend_t ggml_backend_sched_get_backend ( ggml_backend_sched_t sched , int i ) {
2025-08-30 16:32:10 +02:00
GGML_ASSERT ( sched );
2024-06-18 09:37:20 +03:00
GGML_ASSERT ( i >= 0 && i < sched -> n_backends );
return sched -> backends [ i ];
}
2025-09-24 16:53:48 +02:00
ggml_backend_buffer_type_t ggml_backend_sched_get_buffer_type ( ggml_backend_sched_t sched , ggml_backend_t backend ) {
GGML_ASSERT ( sched );
int backend_index = ggml_backend_sched_backend_id ( sched , backend );
GGML_ASSERT ( backend_index >= 0 && backend_index < sched -> n_backends );
return sched -> bufts [ backend_index ];
}
2024-02-12 09:16:06 +02:00
size_t ggml_backend_sched_get_buffer_size ( ggml_backend_sched_t sched , ggml_backend_t backend ) {
2025-08-30 16:32:10 +02:00
GGML_ASSERT ( sched );
2024-02-12 09:16:06 +02:00
int backend_index = ggml_backend_sched_backend_id ( sched , backend );
2024-01-12 20:07:38 +01:00
GGML_ASSERT ( backend_index >= 0 && backend_index < sched -> n_backends );
2024-03-13 18:54:21 +01:00
2024-02-12 09:16:06 +02:00
return ggml_gallocr_get_buffer_size ( sched -> galloc , backend_index );
2023-11-13 14:16:23 +02:00
}
2024-03-13 18:54:21 +01:00
void ggml_backend_sched_set_tensor_backend ( ggml_backend_sched_t sched , struct ggml_tensor * node , ggml_backend_t backend ) {
2025-08-30 16:32:10 +02:00
GGML_ASSERT ( sched );
2024-02-12 09:16:06 +02:00
int backend_index = ggml_backend_sched_backend_id ( sched , backend );
2023-11-13 14:16:23 +02:00
GGML_ASSERT ( backend_index >= 0 && backend_index < sched -> n_backends );
2024-02-12 09:16:06 +02:00
tensor_backend_id ( node ) = backend_index ;
2024-06-13 03:11:35 +02:00
SET_CAUSE ( node , "usr" );
2024-07-27 04:41:55 +02:00
sched -> is_reset = false ;
2023-11-13 14:16:23 +02:00
}
2023-12-07 22:26:54 +02:00
2024-03-13 18:54:21 +01:00
ggml_backend_t ggml_backend_sched_get_tensor_backend ( ggml_backend_sched_t sched , struct ggml_tensor * node ) {
2025-08-30 16:32:10 +02:00
GGML_ASSERT ( sched );
2024-02-12 09:16:06 +02:00
int backend_index = tensor_backend_id ( node );
if ( backend_index == - 1 ) {
2024-01-12 20:07:38 +01:00
return NULL ;
}
2024-02-12 09:16:06 +02:00
return sched -> backends [ backend_index ];
2024-01-12 20:07:38 +01:00
}
2023-12-07 22:26:54 +02:00
// utils
2024-01-12 20:07:38 +01:00
2025-02-28 05:41:47 -08:00
enum ggml_status ggml_backend_view_init ( struct ggml_tensor * tensor ) {
2025-08-30 16:32:10 +02:00
GGML_ASSERT ( tensor );
2023-12-07 22:26:54 +02:00
GGML_ASSERT ( tensor -> buffer == NULL );
GGML_ASSERT ( tensor -> view_src != NULL );
GGML_ASSERT ( tensor -> view_src -> buffer != NULL );
GGML_ASSERT ( tensor -> view_src -> data != NULL );
2024-06-03 20:03:26 +03:00
tensor -> buffer = tensor -> view_src -> buffer ;
2023-12-07 22:26:54 +02:00
tensor -> data = ( char * ) tensor -> view_src -> data + tensor -> view_offs ;
2025-02-28 05:41:47 -08:00
return ggml_backend_buffer_init_tensor ( tensor -> buffer , tensor );
2023-12-07 22:26:54 +02:00
}
2025-02-28 05:41:47 -08:00
enum ggml_status ggml_backend_tensor_alloc ( ggml_backend_buffer_t buffer , struct ggml_tensor * tensor , void * addr ) {
2025-08-30 16:32:10 +02:00
GGML_ASSERT ( tensor );
2023-12-07 22:26:54 +02:00
GGML_ASSERT ( tensor -> buffer == NULL );
GGML_ASSERT ( tensor -> data == NULL );
GGML_ASSERT ( tensor -> view_src == NULL );
GGML_ASSERT ( addr >= ggml_backend_buffer_get_base ( buffer ));
GGML_ASSERT (( char * ) addr + ggml_backend_buffer_get_alloc_size ( buffer , tensor ) <=
( char * ) ggml_backend_buffer_get_base ( buffer ) + ggml_backend_buffer_get_size ( buffer ));
tensor -> buffer = buffer ;
tensor -> data = addr ;
2025-02-28 05:41:47 -08:00
return ggml_backend_buffer_init_tensor ( buffer , tensor );
2023-12-07 22:26:54 +02:00
}
2024-02-12 09:16:06 +02:00
static struct ggml_tensor * graph_copy_dup_tensor ( struct ggml_hash_set hash_set , struct ggml_tensor ** node_copies ,
2023-12-07 22:26:54 +02:00
struct ggml_context * ctx_allocated , struct ggml_context * ctx_unallocated , struct ggml_tensor * src ) {
GGML_ASSERT ( src != NULL );
GGML_ASSERT ( src -> data && "graph must be allocated" );
2024-07-27 04:41:55 +02:00
size_t id = ggml_hash_insert ( & hash_set , src );
if ( id == GGML_HASHSET_ALREADY_EXISTS ) {
return node_copies [ ggml_hash_find ( & hash_set , src )];
2023-12-07 22:26:54 +02:00
}
struct ggml_tensor * dst = ggml_dup_tensor_layout ( src -> data && ! src -> view_src ? ctx_allocated : ctx_unallocated , src );
if ( src -> view_src != NULL ) {
2024-02-12 09:16:06 +02:00
dst -> view_src = graph_copy_dup_tensor ( hash_set , node_copies , ctx_allocated , ctx_unallocated , src -> view_src );
2023-12-07 22:26:54 +02:00
dst -> view_offs = src -> view_offs ;
}
dst -> op = src -> op ;
2026-01-19 20:03:19 +02:00
dst -> flags = src -> flags ;
2023-12-07 22:26:54 +02:00
memcpy ( dst -> op_params , src -> op_params , sizeof ( dst -> op_params ));
ggml_set_name ( dst , src -> name );
// copy src
for ( int i = 0 ; i < GGML_MAX_SRC ; i ++ ) {
struct ggml_tensor * s = src -> src [ i ];
if ( s == NULL ) {
2024-02-17 23:04:16 +02:00
continue ;
2023-12-07 22:26:54 +02:00
}
2024-02-12 09:16:06 +02:00
dst -> src [ i ] = graph_copy_dup_tensor ( hash_set , node_copies , ctx_allocated , ctx_unallocated , s );
2023-12-07 22:26:54 +02:00
}
node_copies [ id ] = dst ;
return dst ;
}
2024-07-27 04:41:55 +02:00
static void graph_copy_init_tensor ( struct ggml_hash_set * hash_set , struct ggml_tensor ** node_copies , bool * node_init , struct ggml_tensor * src ) {
2023-12-07 22:26:54 +02:00
size_t id = ggml_hash_find ( hash_set , src );
if ( node_init [ id ]) {
return ;
}
node_init [ id ] = true ;
struct ggml_tensor * dst = node_copies [ id ];
if ( dst -> view_src != NULL ) {
2024-02-12 09:16:06 +02:00
graph_copy_init_tensor ( hash_set , node_copies , node_init , src -> view_src );
2025-02-28 05:41:47 -08:00
enum ggml_status status = ggml_backend_view_init ( dst );
GGML_ASSERT ( status == GGML_STATUS_SUCCESS );
2023-12-07 22:26:54 +02:00
}
else {
ggml_backend_tensor_copy ( src , dst );
}
// init src
for ( int i = 0 ; i < GGML_MAX_SRC ; i ++ ) {
struct ggml_tensor * s = src -> src [ i ];
if ( s == NULL ) {
2024-02-17 23:04:16 +02:00
continue ;
2023-12-07 22:26:54 +02:00
}
2024-02-12 09:16:06 +02:00
graph_copy_init_tensor ( hash_set , node_copies , node_init , s );
2023-12-07 22:26:54 +02:00
}
}
struct ggml_backend_graph_copy ggml_backend_graph_copy ( ggml_backend_t backend , struct ggml_cgraph * graph ) {
2025-08-30 16:32:10 +02:00
GGML_ASSERT ( graph );
2024-07-27 04:41:55 +02:00
struct ggml_hash_set hash_set = ggml_hash_set_new ( graph -> visited_hash_set . size );
2024-10-03 01:49:47 +02:00
struct ggml_tensor ** node_copies = ( ggml_tensor ** ) calloc ( hash_set . size , sizeof ( node_copies [ 0 ])); // NOLINT
bool * node_init = ( bool * ) calloc ( hash_set . size , sizeof ( node_init [ 0 ]));
2023-12-07 22:26:54 +02:00
struct ggml_init_params params = {
/* .mem_size = */ ggml_tensor_overhead () * hash_set . size + ggml_graph_overhead_custom ( graph -> size , false ),
/* .mem_buffer = */ NULL ,
/* .no_alloc = */ true
};
struct ggml_context * ctx_allocated = ggml_init ( params );
struct ggml_context * ctx_unallocated = ggml_init ( params );
2024-01-12 20:07:38 +01:00
if ( ctx_allocated == NULL || ctx_unallocated == NULL ) {
2024-10-11 15:34:45 +02:00
GGML_LOG_ERROR ( "%s: failed to allocate context for graph copy \n " , __func__ );
2024-07-27 04:41:55 +02:00
ggml_hash_set_free ( & hash_set );
2024-01-12 20:07:38 +01:00
free ( node_copies );
free ( node_init );
ggml_free ( ctx_allocated );
ggml_free ( ctx_unallocated );
2024-10-03 01:49:47 +02:00
return {
2024-01-12 20:07:38 +01:00
/* .buffer = */ NULL ,
/* .ctx_allocated = */ NULL ,
/* .ctx_unallocated = */ NULL ,
/* .graph = */ NULL ,
};
}
2023-12-07 22:26:54 +02:00
// dup nodes
for ( int i = 0 ; i < graph -> n_nodes ; i ++ ) {
struct ggml_tensor * node = graph -> nodes [ i ];
2024-02-12 09:16:06 +02:00
graph_copy_dup_tensor ( hash_set , node_copies , ctx_allocated , ctx_unallocated , node );
2023-12-07 22:26:54 +02:00
}
// allocate nodes
ggml_backend_buffer_t buffer = ggml_backend_alloc_ctx_tensors ( ctx_allocated , backend );
2024-01-12 20:07:38 +01:00
if ( buffer == NULL ) {
2024-10-11 15:34:45 +02:00
GGML_LOG_ERROR ( "%s: failed to allocate buffer for graph copy \n " , __func__ );
2024-07-27 04:41:55 +02:00
ggml_hash_set_free ( & hash_set );
2024-01-12 20:07:38 +01:00
free ( node_copies );
free ( node_init );
ggml_free ( ctx_allocated );
ggml_free ( ctx_unallocated );
2024-10-03 01:49:47 +02:00
return {
2024-01-12 20:07:38 +01:00
/* .buffer = */ NULL ,
/* .ctx_allocated = */ NULL ,
/* .ctx_unallocated = */ NULL ,
/* .graph = */ NULL ,
};
}
2023-12-07 22:26:54 +02:00
//printf("copy buffer size: %zu MB\n", ggml_backend_buffer_get_size(buffer) / 1024 / 1024);
// copy data and init views
for ( int i = 0 ; i < graph -> n_nodes ; i ++ ) {
struct ggml_tensor * node = graph -> nodes [ i ];
2024-07-27 04:41:55 +02:00
graph_copy_init_tensor ( & hash_set , node_copies , node_init , node );
2023-12-07 22:26:54 +02:00
}
// build graph copy
struct ggml_cgraph * graph_copy = ggml_new_graph_custom ( ctx_allocated , graph -> size , false );
for ( int i = 0 ; i < graph -> n_nodes ; i ++ ) {
struct ggml_tensor * node = graph -> nodes [ i ];
2024-07-27 04:41:55 +02:00
struct ggml_tensor * node_copy = node_copies [ ggml_hash_find ( & hash_set , node )];
2023-12-07 22:26:54 +02:00
graph_copy -> nodes [ i ] = node_copy ;
}
graph_copy -> n_nodes = graph -> n_nodes ;
2024-07-27 04:41:55 +02:00
ggml_hash_set_free ( & hash_set );
2023-12-07 22:26:54 +02:00
free ( node_copies );
free ( node_init );
2024-10-03 01:49:47 +02:00
return {
2023-12-07 22:26:54 +02:00
/* .buffer = */ buffer ,
/* .ctx_allocated = */ ctx_allocated ,
/* .ctx_unallocated = */ ctx_unallocated ,
/* .graph = */ graph_copy ,
};
}
void ggml_backend_graph_copy_free ( struct ggml_backend_graph_copy copy ) {
ggml_backend_buffer_free ( copy . buffer );
ggml_free ( copy . ctx_allocated );
ggml_free ( copy . ctx_unallocated );
}
2026-01-01 01:58:27 -06:00
bool ggml_backend_compare_graph_backend ( ggml_backend_t backend1 , ggml_backend_t backend2 , struct ggml_cgraph * graph , ggml_backend_eval_callback callback , void * user_data , struct ggml_tensor const * const * test_nodes , size_t num_test_nodes ) {
2023-12-07 22:26:54 +02:00
struct ggml_backend_graph_copy copy = ggml_backend_graph_copy ( backend2 , graph );
2024-01-12 20:07:38 +01:00
if ( copy . buffer == NULL ) {
return false ;
}
2023-12-07 22:26:54 +02:00
struct ggml_cgraph * g1 = graph ;
struct ggml_cgraph * g2 = copy . graph ;
assert ( g1 -> n_nodes == g2 -> n_nodes );
2026-01-01 01:58:27 -06:00
if ( num_test_nodes != 0 ) {
GGML_ASSERT ( test_nodes );
// Compute the whole graph and only test the output for specific tensors
2025-06-29 02:43:36 -05:00
ggml_backend_graph_compute ( backend1 , g1 );
ggml_backend_graph_compute ( backend2 , g2 );
2023-12-07 22:26:54 +02:00
2026-01-01 01:58:27 -06:00
bool verified = false ;
2025-06-29 02:43:36 -05:00
for ( int i = 0 ; i < g1 -> n_nodes ; i ++ ) {
2026-01-01 01:58:27 -06:00
for ( size_t j = 0 ; j < num_test_nodes ; ++ j ) {
if ( g1 -> nodes [ i ] == test_nodes [ j ]) {
callback ( i , g1 -> nodes [ i ], g2 -> nodes [ i ], user_data );
verified = true ;
}
2025-06-29 02:43:36 -05:00
}
2023-12-07 22:26:54 +02:00
}
2026-01-01 01:58:27 -06:00
GGML_ASSERT ( verified );
2025-06-29 02:43:36 -05:00
} else {
for ( int i = 0 ; i < g1 -> n_nodes ; i ++ ) {
struct ggml_tensor * t1 = g1 -> nodes [ i ];
struct ggml_tensor * t2 = g2 -> nodes [ i ];
assert ( t1 -> op == t2 -> op && ggml_are_same_layout ( t1 , t2 ));
struct ggml_cgraph g1v = ggml_graph_view ( g1 , i , i + 1 );
struct ggml_cgraph g2v = ggml_graph_view ( g2 , i , i + 1 );
ggml_backend_graph_compute ( backend1 , & g1v );
ggml_backend_graph_compute ( backend2 , & g2v );
if ( ggml_is_view_op ( t1 -> op )) {
continue ;
}
// compare results, calculate rms etc
if ( ! callback ( i , t1 , t2 , user_data )) {
break ;
}
2023-12-07 22:26:54 +02:00
}
}
ggml_backend_graph_copy_free ( copy );
2024-01-12 20:07:38 +01:00
return true ;
2023-12-07 22:26:54 +02:00
}
2024-11-03 19:34:08 +01:00
// CPU backend - buffer
static void * ggml_backend_cpu_buffer_get_base ( ggml_backend_buffer_t buffer ) {
2025-08-30 16:32:10 +02:00
GGML_ASSERT ( buffer );
2024-11-03 19:34:08 +01:00
uintptr_t data = ( uintptr_t ) buffer -> context ;
// align the buffer
if ( data % TENSOR_ALIGNMENT != 0 ) {
data = GGML_PAD ( data , TENSOR_ALIGNMENT );
}
return ( void * ) data ;
}
static void ggml_backend_cpu_buffer_free_buffer ( ggml_backend_buffer_t buffer ) {
2025-08-30 16:32:10 +02:00
GGML_ASSERT ( buffer );
2024-11-03 19:34:08 +01:00
ggml_aligned_free ( buffer -> context , buffer -> size );
}
static void ggml_backend_cpu_buffer_memset_tensor ( ggml_backend_buffer_t buffer , struct ggml_tensor * tensor , uint8_t value , size_t offset , size_t size ) {
2025-08-30 16:32:10 +02:00
GGML_ASSERT ( tensor );
2024-11-03 19:34:08 +01:00
memset (( char * ) tensor -> data + offset , value , size );
GGML_UNUSED ( buffer );
}
static void ggml_backend_cpu_buffer_set_tensor ( ggml_backend_buffer_t buffer , struct ggml_tensor * tensor , const void * data , size_t offset , size_t size ) {
2025-08-30 16:32:10 +02:00
GGML_ASSERT ( tensor );
2024-11-03 19:34:08 +01:00
memcpy (( char * ) tensor -> data + offset , data , size );
GGML_UNUSED ( buffer );
}
static void ggml_backend_cpu_buffer_get_tensor ( ggml_backend_buffer_t buffer , const struct ggml_tensor * tensor , void * data , size_t offset , size_t size ) {
2025-08-30 16:32:10 +02:00
GGML_ASSERT ( tensor );
2024-11-03 19:34:08 +01:00
memcpy ( data , ( const char * ) tensor -> data + offset , size );
GGML_UNUSED ( buffer );
}
static bool ggml_backend_cpu_buffer_cpy_tensor ( ggml_backend_buffer_t buffer , const struct ggml_tensor * src , struct ggml_tensor * dst ) {
2025-08-30 16:32:10 +02:00
GGML_ASSERT ( src );
2024-11-03 19:34:08 +01:00
if ( ggml_backend_buffer_is_host ( src -> buffer )) {
memcpy ( dst -> data , src -> data , ggml_nbytes ( src ));
return true ;
}
return false ;
GGML_UNUSED ( buffer );
}
static void ggml_backend_cpu_buffer_clear ( ggml_backend_buffer_t buffer , uint8_t value ) {
2025-08-30 16:32:10 +02:00
GGML_ASSERT ( buffer );
2024-11-03 19:34:08 +01:00
memset ( buffer -> context , value , buffer -> size );
}
static const struct ggml_backend_buffer_i ggml_backend_cpu_buffer_i = {
/* .free_buffer = */ ggml_backend_cpu_buffer_free_buffer ,
/* .get_base = */ ggml_backend_cpu_buffer_get_base ,
/* .init_tensor = */ NULL , // no initialization required
/* .memset_tensor = */ ggml_backend_cpu_buffer_memset_tensor ,
/* .set_tensor = */ ggml_backend_cpu_buffer_set_tensor ,
/* .get_tensor = */ ggml_backend_cpu_buffer_get_tensor ,
/* .cpy_tensor = */ ggml_backend_cpu_buffer_cpy_tensor ,
/* .clear = */ ggml_backend_cpu_buffer_clear ,
/* .reset = */ NULL ,
};
static const struct ggml_backend_buffer_i ggml_backend_cpu_buffer_from_ptr_i = {
/* .free_buffer = */ NULL , // ptr is not owned by the buffer, so it does not need to be freed
/* .get_base = */ ggml_backend_cpu_buffer_get_base ,
/* .init_tensor = */ NULL , // no initialization required
/* .memset_tensor = */ ggml_backend_cpu_buffer_memset_tensor ,
/* .set_tensor = */ ggml_backend_cpu_buffer_set_tensor ,
/* .get_tensor = */ ggml_backend_cpu_buffer_get_tensor ,
/* .cpy_tensor = */ ggml_backend_cpu_buffer_cpy_tensor ,
/* .clear = */ ggml_backend_cpu_buffer_clear ,
/* .reset = */ NULL ,
};
2024-11-14 18:04:35 +01:00
// CPU backend buffer type
// this buffer type is defined here to make it available to all backends
2024-11-03 19:34:08 +01:00
static const char * ggml_backend_cpu_buffer_type_get_name ( ggml_backend_buffer_type_t buft ) {
return "CPU" ;
GGML_UNUSED ( buft );
}
static ggml_backend_buffer_t ggml_backend_cpu_buffer_type_alloc_buffer ( ggml_backend_buffer_type_t buft , size_t size ) {
void * data = ggml_aligned_malloc ( size );
if ( data == NULL ) {
GGML_LOG_ERROR ( "%s: failed to allocate buffer of size %zu \n " , __func__ , size );
return NULL ;
}
return ggml_backend_buffer_init ( buft , ggml_backend_cpu_buffer_i , data , size );
}
static size_t ggml_backend_cpu_buffer_type_get_alignment ( ggml_backend_buffer_type_t buft ) {
return TENSOR_ALIGNMENT ;
GGML_UNUSED ( buft );
}
static bool ggml_backend_cpu_buffer_type_is_host ( ggml_backend_buffer_type_t buft ) {
return true ;
GGML_UNUSED ( buft );
}
ggml_backend_buffer_type_t ggml_backend_cpu_buffer_type ( void ) {
static struct ggml_backend_buffer_type ggml_backend_cpu_buffer_type = {
/* .iface = */ {
/* .get_name = */ ggml_backend_cpu_buffer_type_get_name ,
/* .alloc_buffer = */ ggml_backend_cpu_buffer_type_alloc_buffer ,
/* .get_alignment = */ ggml_backend_cpu_buffer_type_get_alignment ,
/* .get_max_size = */ NULL , // defaults to SIZE_MAX
/* .get_alloc_size = */ NULL , // defaults to ggml_nbytes
/* .is_host = */ ggml_backend_cpu_buffer_type_is_host ,
},
2024-11-14 18:04:35 +01:00
/* .device = */ NULL , // FIXME ggml_backend_reg_dev_get(ggml_backend_cpu_reg(), 0),
2024-11-03 19:34:08 +01:00
/* .context = */ NULL ,
};
return & ggml_backend_cpu_buffer_type ;
}
static const char * ggml_backend_cpu_buffer_from_ptr_type_get_name ( ggml_backend_buffer_type_t buft ) {
return "CPU_Mapped" ;
GGML_UNUSED ( buft );
}
static ggml_backend_buffer_type_t ggml_backend_cpu_buffer_from_ptr_type ( void ) {
static struct ggml_backend_buffer_type ggml_backend_cpu_buffer_type = {
/* .iface = */ {
/* .get_name = */ ggml_backend_cpu_buffer_from_ptr_type_get_name ,
/* .alloc_buffer = */ ggml_backend_cpu_buffer_type_alloc_buffer ,
/* .get_alignment = */ ggml_backend_cpu_buffer_type_get_alignment ,
/* .get_max_size = */ NULL , // defaults to SIZE_MAX
/* .get_alloc_size = */ NULL , // defaults to ggml_nbytes
/* .is_host = */ ggml_backend_cpu_buffer_type_is_host ,
},
2024-11-14 18:04:35 +01:00
/* .device = */ NULL , // FIXME ggml_backend_reg_dev_get(ggml_backend_cpu_reg(), 0),
2024-11-03 19:34:08 +01:00
/* .context = */ NULL ,
};
return & ggml_backend_cpu_buffer_type ;
}
ggml_backend_buffer_t ggml_backend_cpu_buffer_from_ptr ( void * ptr , size_t size ) {
GGML_ASSERT (( uintptr_t ) ptr % TENSOR_ALIGNMENT == 0 && "buffer pointer must be aligned" );
return ggml_backend_buffer_init ( ggml_backend_cpu_buffer_from_ptr_type (), ggml_backend_cpu_buffer_from_ptr_i , ptr , size );
}