char-fork/llama.cpp

git clone https://git.t4t.associates/char-fork/llama.cpp

hipuddingCANN: fix multi-thread set_tensor race conditions (#20151)632219af7

char/aliceai-t5-moe
22.8 KiB651 linesraw
1/*
2 * Copyright (c) 2023-2026 The ggml authors
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a copy
5 * of this software and associated documentation files (the "Software"), to
6 * deal in the Software without restriction, including without limitation the
7 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
8 * sell copies of the Software, and to permit persons to whom the Software is
9 * furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice shall be included in
12 * all copies or substantial portions of the Software.
13 *
14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
19 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
20 * IN THE SOFTWARE.
21 */
22
23#ifndef CANN_COMMON_H
24#define CANN_COMMON_H
25
26#include "../ggml-impl.h"
27#include "../include/ggml-cann.h"
28#include "../include/ggml.h"
29
30#include <acl/acl.h>
31#include <unistd.h>
32
33#include <atomic>
34#include <condition_variable>
35#include <cstdio>
36#include <functional>
37#include <iostream>
38#include <list>
39#include <map>
40#include <memory>
41#include <mutex>
42#include <optional>
43#include <string>
44#include <thread>
45#include <vector>
46
47#define MATRIX_ROW_PADDING    512
48#define GGML_CANN_MAX_STREAMS 8
49
50/**
51 * @brief Handles CANN-related errors by printing an error message and
52 *        terminating the program.
53 * @param stmt The statement that caused the error.
54 * @param func The function in which the error occurred.
55 * @param file The file in which the error occurred.
56 * @param line The line number at which the error occurred.
57 * @param msg The error message.
58 */
59[[noreturn]] void ggml_cann_error(const char * stmt, const char * func, const char * file, int line, const char * msg);
60
61/**
62 * @brief Checks the result of a CANN function call and invokes the error
63 *        handler if the call fails.
64 * @param stmt The CANN function call to check.
65 * @param success The success code that indicates the call was successful.
66 * @param error_fn The function to call to retrieve the error message.
67 */
68#define ACL_CHECK_GEN(stmt, success, error_fn)                                \
69    do {                                                                      \
70        int err_code = (stmt);                                                \
71        if (err_code != (success)) {                                          \
72            ggml_cann_error(#stmt, __func__, __FILE__, __LINE__, error_fn()); \
73        }                                                                     \
74    } while (0);
75
76#define ACL_CHECK(stmt) ACL_CHECK_GEN(stmt, 0, aclGetRecentErrMsg)
77
78/**
79 * @brief Contains information about CANN devices.
80 */
81struct ggml_cann_device_info {
82    /**
83     * @brief Number of CANN devices available.
84     */
85    int32_t device_count;
86
87    /**
88     * @brief Information about a single CANN device.
89     */
90    struct cann_device_info {
91        int    cc;              /**< Compute capability.                   */
92        size_t smpb;            /**< Maximum shared memory per block.      */
93        bool   vmm;             /**< Virtual memory support.               */
94        size_t vmm_granularity; /**< Granularity of virtual memory.        */
95        size_t total_vram;      /**< Total video RAM available on the device. */
96    };
97
98    cann_device_info devices[GGML_CANN_MAX_DEVICES] = {}; /**< Array of CANN device information. */
99};
100
101const ggml_cann_device_info & ggml_cann_info();
102
103void    ggml_cann_set_device(int32_t device);
104
105std::optional<std::string> get_env_as_lowercase(const std::string & name);
106bool                       parse_bool(const std::string & value);
107int                        parse_integer(const std::string & value);
108
109/**
110 * @brief Abstract base class for memory pools used by CANN.
111 */
112struct ggml_cann_pool {
113    /**
114     * @brief Virtual destructor for the memory pool.
115     */
116    virtual ~ggml_cann_pool() = default;
117
118    /**
119     * @brief Allocates memory from the pool.
120     *
121     * @param size         The size of the memory block to allocate.
122     * @param actual_size  Pointer to a variable where the actual allocated size
123     *                     will be stored.
124     * @return             Pointer to the allocated memory block.
125     */
126    virtual void * alloc(size_t size, size_t * actual_size) = 0;
127
128    /**
129     * @brief Frees a previously allocated memory block.
130     *
131     * @param ptr   Pointer to the memory block to free.
132     * @param size  Size of the memory block to free.
133     * @note Note that all CANN opertors are running async. Make sure memory is
134     *       still avaiable before this operator finished.
135     */
136    virtual void free(void * ptr, size_t size) = 0;
137};
138
139/**
140 * @brief RAII wrapper for managing memory allocations from a CANN memory pool.
141 */
142struct ggml_cann_pool_alloc {
143    ggml_cann_pool * pool        = nullptr; /**< Pointer to the memory pool. */
144    void *           ptr         = nullptr; /**< Pointer to the allocated memory block. */
145    size_t           actual_size = 0;       /**< Actual size of the allocated memory block. */
146
147    /**
148     * @brief Default constructor.
149     */
150    ggml_cann_pool_alloc() = default;
151
152    /**
153     * @brief Constructor that initializes the memory pool.
154     * @param pool Reference to the memory pool.
155     */
156    explicit ggml_cann_pool_alloc(ggml_cann_pool & pool) : pool(&pool) {}
157
158    /**
159     * @brief Constructor that initializes the memory pool and allocates memory.
160     * @param pool Reference to the memory pool.
161     * @param size Size of the memory block to allocate.
162     */
163    ggml_cann_pool_alloc(ggml_cann_pool & pool, size_t size) : pool(&pool) { alloc(size); }
164
165    /**
166     * @brief Destructor that frees the allocated memory block.
167     */
168    ~ggml_cann_pool_alloc() {
169        if (ptr != nullptr) {
170            pool->free(ptr, actual_size);
171        }
172    }
173
174    /**
175     * @brief Allocates memory from the pool.
176     * @param size Size of the memory block to allocate.
177     * @return Pointer to the allocated memory block.
178     */
179    void * alloc(size_t size) {
180        GGML_ASSERT(pool != nullptr);
181        GGML_ASSERT(ptr == nullptr);
182        ptr = pool->alloc(size, &this->actual_size);
183        return ptr;
184    }
185
186    /**
187     * @brief Allocates memory from a specific memory pool.
188     * @param pool Reference to the memory pool.
189     * @param size Size of the memory block to allocate.
190     * @return Pointer to the allocated memory block.
191     */
192    void * alloc(ggml_cann_pool & pool, size_t size) {
193        this->pool = &pool;
194        return alloc(size);
195    }
196
197    /**
198     * @brief Gets the pointer to the allocated memory block.
199     * @return Pointer to the allocated memory block.
200     */
201    void * get() { return ptr; }
202
203    // Deleted copy constructor
204    ggml_cann_pool_alloc(const ggml_cann_pool_alloc &) = delete;
205
206    // Deleted move constructor
207    ggml_cann_pool_alloc(ggml_cann_pool_alloc &&) = delete;
208
209    // Deleted copy assignment operator
210    ggml_cann_pool_alloc & operator=(const ggml_cann_pool_alloc &) = delete;
211
212    // Deleted move assignment operator
213    ggml_cann_pool_alloc & operator=(ggml_cann_pool_alloc &&) = delete;
214};
215
216#ifdef USE_ACL_GRAPH
217struct ggml_graph_node_properties {
218    // dst tensor
219    void *    node_address;
220    ggml_type node_type;
221    int64_t   ne[GGML_MAX_DIMS];
222    size_t    nb[GGML_MAX_DIMS];
223
224    // src tensor
225    void *    src_address[GGML_MAX_SRC];
226    ggml_type src_type[GGML_MAX_SRC];
227    int64_t   src_ne[GGML_MAX_SRC][GGML_MAX_DIMS];
228    size_t    src_nb[GGML_MAX_SRC][GGML_MAX_DIMS];
229
230    // op
231    ggml_op node_op;
232    int32_t op_params[GGML_MAX_OP_PARAMS / sizeof(int32_t)];
233
234    /**
235     * @brief Check if a ggml tensor node matches this property set.
236     *
237     * This function compares all relevant fields (address, op type, shape, source inputs, op params)
238     * to determine whether the current node matches these previously recorded properties.
239     *
240     * @param node The current ggml tensor node.
241     * @return true if all fields match (excluding GGML_OP_VIEW); false otherwise.
242     */
243    bool has_matching_properties(ggml_tensor * node) {
244        if (node->data != this->node_address && node->op != GGML_OP_VIEW) {
245            return false;
246        }
247
248        if (node->op != this->node_op) {
249            return false;
250        }
251
252        if (node->type != this->node_type) {
253            return false;
254        }
255
256        for (int i = 0; i < GGML_MAX_DIMS; i++) {
257            if (node->ne[i] != this->ne[i]) {
258                return false;
259            }
260            if (node->nb[i] != this->nb[i]) {
261                return false;
262            }
263        }
264
265        for (int i = 0; i < GGML_MAX_SRC; i++) {
266            if (node->src[i]) {
267                if (node->src[i]->data != this->src_address[i] && node->op != GGML_OP_VIEW) {
268                    return false;
269                }
270
271                if (node->src[i]->type != this->src_type[i]) {
272                    return false;
273                }
274
275                for (int d = 0; d < GGML_MAX_DIMS; d++) {
276                    if (node->src[i]->ne[d] != this->src_ne[i][d]) {
277                        return false;
278                    }
279                    if (node->src[i]->nb[d] != this->src_nb[i][d]) {
280                        return false;
281                    }
282                }
283            } else {
284                if (this->src_address[i] != nullptr) {
285                    return false;
286                }
287            }
288        }
289
290        return memcmp(this->op_params, node->op_params, GGML_MAX_OP_PARAMS) == 0;
291    }
292};
293
294struct ggml_cann_graph {
295    ~ggml_cann_graph() {
296        if (graph != nullptr) {
297            ACL_CHECK(aclmdlRIDestroy(graph));
298        }
299    }
300
301    aclmdlRI graph = nullptr;
302
303    std::vector<ggml_graph_node_properties> ggml_graph_properties;
304
305    /**
306     * @brief Create a new CANN graph from a ggml computation graph.
307     *
308     * This function creates a new ggml_cann_graph object and fills its node properties
309     * (operation type, dimensions, strides, input sources, and operation parameters)
310     * based on the current ggml computation graph.
311     *
312     * Each node in the ggml graph is mapped to a property entry in the new CANN graph:
313     * - node address
314     * - operation type
315     * - shape (ne) and strides (nb)
316     * - source tensor addresses
317     * - operation parameters
318     *
319     * @param cgraph The current ggml computation graph.
320     * @return Pointer to the newly created ggml_cann_graph object.
321     */
322    static ggml_cann_graph * create_from_cgraph(ggml_cgraph * cgraph) {
323        ggml_cann_graph * new_graph = new ggml_cann_graph();
324        new_graph->ggml_graph_properties.resize(cgraph->n_nodes);
325
326        for (int node_idx = 0; node_idx < cgraph->n_nodes; ++node_idx) {
327            ggml_tensor * node = cgraph->nodes[node_idx];
328            auto &        prop = new_graph->ggml_graph_properties[node_idx];
329
330            prop.node_address = node->data;
331            prop.node_op      = node->op;
332            prop.node_type    = node->type;
333
334            std::copy_n(node->ne, GGML_MAX_DIMS, prop.ne);
335            std::copy_n(node->nb, GGML_MAX_DIMS, prop.nb);
336
337            for (int src = 0; src < GGML_MAX_SRC; ++src) {
338                if (node->src[src]) {
339                    prop.src_address[src] = node->src[src]->data;
340                    prop.src_type[src]    = node->src[src]->type;
341                    std::copy_n(node->src[src]->ne, GGML_MAX_DIMS, prop.src_ne[src]);
342                    std::copy_n(node->src[src]->nb, GGML_MAX_DIMS, prop.src_nb[src]);
343                } else {
344                    prop.src_address[src] = nullptr;
345                    prop.src_type[src]    = GGML_TYPE_COUNT;
346                    std::fill_n(prop.src_ne[src], GGML_MAX_DIMS, 0);
347                    std::fill_n(prop.src_nb[src], GGML_MAX_DIMS, 0);
348                }
349            }
350
351            memcpy(prop.op_params, node->op_params, GGML_MAX_OP_PARAMS);
352        }
353
354        return new_graph;
355    }
356
357    /**
358     * @brief Check whether this CANN graph matches the given ggml computation graph.
359     *
360     * This function compares the number of nodes and each node's properties
361     * (operation type, dimensions, strides, inputs, and operation parameters)
362     * to determine whether this CANN graph matches the given ggml graph.
363     *
364     * @param cgraph The current ggml computation graph.
365     * @return true if this CANN graph matches the ggml graph; false otherwise.
366     */
367    bool matches_cgraph(ggml_cgraph * cgraph) {
368        if (this->ggml_graph_properties.size() != static_cast<size_t>(cgraph->n_nodes)) {
369            return false;
370        }
371
372        for (int i = 0; i < cgraph->n_nodes; ++i) {
373            if (!this->ggml_graph_properties[i].has_matching_properties(cgraph->nodes[i])) {
374                return false;
375            }
376        }
377
378        return true;
379    }
380};
381
382/**
383 * @brief LRU cache for managing ggml_cann_graph objects.
384 *
385 * This class maintains a list of shared_ptr to ggml_cann_graph objects
386 * and enforces a maximum capacity. It provides methods to push new graphs,
387 * move existing graphs to the front (most recently used), and clear the cache.
388 */
389struct ggml_cann_graph_lru_cache {
390    size_t capacity;                         /**< Maximum number of graphs in the cache. */
391
392    std::list<ggml_cann_graph *> cache_list; /**< List storing cached graphs as raw pointers. */
393
394    ggml_cann_graph_lru_cache() { capacity = parse_integer(get_env_as_lowercase("GGML_CANN_GRAPH_CACHE_CAPACITY").value_or("12")); }
395
396    /**
397     * @brief Push a new graph to the front of the cache.
398     * If the cache exceeds capacity, the least recently used graph is deleted.
399     * @param new_node Pointer to the new ggml_cann_graph to cache.
400     *        Ownership is transferred to the cache (cache will delete it).
401     */
402    void push(ggml_cann_graph * new_node) {
403        if (cache_list.size() >= capacity) {
404            ggml_cann_graph * old = cache_list.back();
405            cache_list.pop_back();
406            delete old;  // free the old graph
407        }
408        cache_list.push_front(new_node);
409    }
410
411    /**
412     * @brief Clear all graphs from the cache (also frees memory).
413     */
414    void clear() {
415        for (auto ptr : cache_list) {
416            delete ptr;
417        }
418        cache_list.clear();
419    }
420
421    /**
422     * @brief Destructor that clears the cache and frees all cached graphs.
423     */
424    ~ggml_cann_graph_lru_cache() { clear(); }
425
426    /**
427     * @brief Find a cached CANN graph that matches the given ggml graph and move it to front.
428     *
429     * This function iterates through the cached CANN graphs stored in the LRU cache and
430     * compares them against the given ggml computation graph. If a matching graph is found,
431     * it is promoted to the front of the LRU cache and returned. Otherwise, the function
432     * returns nullptr.
433     *
434     * @param cgraph The current ggml computation graph.
435     * @return true if found; false otherwise.
436     */
437    bool find_and_move_to_front(ggml_cgraph * cgraph) {
438        for (auto & graph_ptr : this->cache_list) {
439            if (graph_ptr->matches_cgraph(cgraph)) {
440                cache_list.remove(graph_ptr);
441                cache_list.push_front(graph_ptr);
442                return true;
443            }
444        }
445        return false;
446    }
447};
448#endif  // USE_ACL_GRAPH
449
450struct ggml_cann_rope_cache {
451    ~ggml_cann_rope_cache() {
452        if (theta_scale_cache) {
453            ACL_CHECK(aclrtFree(theta_scale_cache));
454        }
455        if (sin_cache) {
456            ACL_CHECK(aclrtFree(sin_cache));
457        }
458        if (cos_cache) {
459            ACL_CHECK(aclrtFree(cos_cache));
460        }
461        if (position_select_index) {
462            ACL_CHECK(aclrtFree(position_select_index));
463        }
464        if (theta_scale_exp_host) {
465            free(theta_scale_exp_host);
466        }
467        if (position_select_index_host) {
468            free(position_select_index_host);
469        }
470        if (yarn_ramp_cache) {
471            ACL_CHECK(aclrtFree(yarn_ramp_cache));
472        }
473    }
474
475    bool equal(int64_t theta_scale_length,
476               int64_t position_length,
477               float   ext_factor,
478               float   theta_scale,
479               float   freq_scale,
480               float   attn_factor,
481               bool    is_neox,
482               bool    indep_sects,
483               bool    mrope_used,
484               bool    is_imrope,
485               int     sections[4]) {
486        return this->theta_scale_length == theta_scale_length && this->position_length == position_length &&
487               this->ext_factor == ext_factor && this->theta_scale == theta_scale && this->freq_scale == freq_scale &&
488               this->attn_factor == attn_factor && this->is_neox == is_neox && this->indep_sects == indep_sects &&
489               this->mrope_used == mrope_used && this->is_imrope == is_imrope && this->sections[0] == sections[0] &&
490               this->sections[1] == sections[1] && this->sections[2] == sections[2] && this->sections[3] == sections[3];
491    }
492
493    void set(int64_t theta_scale_length,
494             int64_t position_length,
495             float   ext_factor,
496             float   theta_scale,
497             float   freq_scale,
498             float   attn_factor,
499             bool    is_neox,
500             bool    indep_sects,
501             bool    mrope_used,
502             bool    is_imrope,
503             int     sections[4]) {
504        this->theta_scale_length = theta_scale_length;
505        this->position_length    = position_length;
506        this->ext_factor         = ext_factor;
507        this->theta_scale        = theta_scale;
508        this->freq_scale         = freq_scale;
509        this->attn_factor        = attn_factor;
510        this->is_neox            = is_neox;
511        this->indep_sects        = indep_sects;
512        this->mrope_used         = mrope_used;
513        this->is_imrope          = is_imrope;
514        this->sections[0]        = sections[0];
515        this->sections[1]        = sections[1];
516        this->sections[2]        = sections[2];
517        this->sections[3]        = sections[3];
518    }
519
520    // memory cache, prepare before inferencing.
521    void *  theta_scale_cache          = nullptr;
522    float * theta_scale_exp_host       = nullptr;
523    int *   position_select_index_host = nullptr;
524    void *  position_select_index      = nullptr;
525    void *  yarn_ramp_cache            = nullptr;
526    // sin/cos cache, used only to accelerate first layer on each device
527    void *  sin_cache                  = nullptr;
528    void *  cos_cache                  = nullptr;
529    // Properties to check before reusing the sincos cache
530    int64_t theta_scale_length         = 0;
531    int64_t position_length            = 0;
532    bool    cached                     = false;
533    float   ext_factor                 = 0.0f;
534    float   theta_scale                = 0.0f;
535    float   freq_scale                 = 0.0f;
536    float   attn_factor                = 0.0f;
537    bool    is_neox                    = false;
538    bool    indep_sects                = false;
539    bool    mrope_used                 = false;
540    int     sections[4]                = { 0, 0, 0, 0 };
541    bool    is_imrope                  = false;
542};
543
544struct ggml_cann_tensor_cache {
545    ~ggml_cann_tensor_cache() {
546        if (cache != nullptr) {
547            ACL_CHECK(aclrtFree(cache));
548        }
549    }
550
551    void *  cache = nullptr;
552    int64_t size  = 0;
553};
554
555/**
556 * @brief Context for managing CANN backend operations.
557 */
558struct ggml_backend_cann_context {
559    int32_t     device;               /**< Device ID. */
560    std::string name;                 /**< Name of the device. */
561    std::string description;          /**< Description of the device. */
562    aclrtEvent  copy_event = nullptr; /**< Event for managing copy operations. */
563#ifdef USE_ACL_GRAPH
564    /// Cached CANN ACL graph used for executing the current ggml computation graph.
565    ggml_cann_graph_lru_cache graph_lru_cache;
566    bool                      acl_graph_mode = true;
567#endif
568    bool                   async_mode;
569    // Rope Cache
570    ggml_cann_rope_cache   rope_cache;
571    // Constant Pool
572    ggml_cann_tensor_cache rms_norm_one_tensor_cache;
573    ggml_cann_tensor_cache rms_norm_zero_tensor_cache;
574
575    aclrtStream streams[GGML_CANN_MAX_STREAMS] = { nullptr }; /**< Array of streams for the device. */
576
577    /**
578     * @brief Constructor for initializing the context with a given device.
579     * @param device Device ID.
580     */
581    explicit ggml_backend_cann_context(int device) : device(device), name("CANN" + std::to_string(device)) {
582        ggml_cann_set_device(device);
583        description = aclrtGetSocName();
584
585#ifdef USE_ACL_GRAPH
586        acl_graph_mode = parse_bool(get_env_as_lowercase("GGML_CANN_ACL_GRAPH").value_or("on"));
587        GGML_LOG_INFO("%s: device %d execution mode is %s (%s)\n", __func__, device, acl_graph_mode ? "GRAPH" : "EAGER",
588                      acl_graph_mode ? "acl graph enabled" : "acl graph disabled");
589#endif
590    }
591
592    /**
593     * @brief Destructor for cleaning up resources.
594     */
595    ~ggml_backend_cann_context() {
596        ggml_cann_set_device(device);
597        if (copy_event != nullptr) {
598            ACL_CHECK(aclrtDestroyEvent(copy_event));
599        }
600        for (int i = 0; i < GGML_CANN_MAX_STREAMS; ++i) {
601            if (streams[i] != nullptr) {
602                ACL_CHECK(aclrtDestroyStream(streams[i]));
603            }
604        }
605    }
606
607    /**
608     * @brief Get or create a stream for a given index.
609     * @param stream Index of the stream.
610     * @return The stream corresponding to the given index.
611     */
612    aclrtStream stream(int stream) {
613        if (streams[stream] == nullptr) {
614            // If the device is not set here, destroying the stream later may cause a mismatch
615            // between the thread contexts where the stream was created and destroyed.
616            // However, I printed the device_id, thread_id, and stream, and they are all consistent.
617            ACL_CHECK(aclrtSetDevice(device));
618            ACL_CHECK(aclrtCreateStream(&streams[stream]));
619        }
620        return streams[stream];
621    }
622
623    /**
624     * @brief Get or create the default stream (index 0).
625     * @return The default stream.
626     */
627    aclrtStream stream() { return stream(0); }
628
629    // TODO: each stream should have a memory pool.
630    std::unique_ptr<ggml_cann_pool> mem_pool; /**< Memory pool for the device. */
631
632    /**
633     * @brief Create a new memory pool for a given device.
634     * @param device Device ID.
635     * @return A unique pointer to the new memory pool.
636     */
637    static std::unique_ptr<ggml_cann_pool> new_pool_for_device(int device);
638
639    /**
640     * @brief Get or create the memory pool for the context.
641     * @return Reference to the memory pool.
642     */
643    ggml_cann_pool & pool() {
644        if (mem_pool == nullptr) {
645            mem_pool = new_pool_for_device(device);
646        }
647        return *mem_pool;
648    }
649};
650
651#endif  // CANN_COMMON_H