当前位置: 首页 > news >正文

webassembly003 ggml GGML Tensor Library part-2 官方使用说明

  • https://github.com/ggerganov/whisper.cpp/tree/1.0.3

GGML Tensor Library

  • 官方有一个函数使用说明,但是从初始版本就没修改过 : https://github1s.com/ggerganov/ggml/blob/master/include/ggml/ggml.h#L3-L173

  • This documentation is still a work in progress. If you wish some specific topics to be covered, feel free to drop a comment:
    https://github.com/ggerganov/whisper.cpp/issues/40

Overview

此库实现:

  • 张量运算
  • 自动微分
  • 基本优化算法

这个库的目的是为各种机器学习任务提供一种最小化方法(a minimalistic approach for various machine learning tasks)。包括但不限于以下内容:

  • 线性回归
  • 支持向量机
  • 神经网络

该库允许用户使用可用的张量运算来定义某个函数。该函数定义通过计算图在内部表示。函数定义中的每个张量运算都对应于图中的一个节点。定义了计算图后,用户可以选择计算函数的值和/或其相对于输入变量的梯度。更近一步,可以使用可用的优化算法之一来优化函数。

例如,在这里我们定义函数: f(x) = a*x^2 + b

   {struct ggml_init_params params = {.mem_size   = 16*1024*1024,.mem_buffer = NULL,};// memory allocation happens herestruct ggml_context * ctx = ggml_init(params);struct ggml_tensor * x = ggml_new_tensor_1d(ctx, GGML_TYPE_F32, 1);ggml_set_param(ctx, x); // x is an input variablestruct ggml_tensor * a  = ggml_new_tensor_1d(ctx, GGML_TYPE_F32, 1);struct ggml_tensor * b  = ggml_new_tensor_1d(ctx, GGML_TYPE_F32, 1);struct ggml_tensor * x2 = ggml_mul(ctx, x, x);struct ggml_tensor * f  = ggml_add(ctx, ggml_mul(ctx, a, x2), b);...}

请注意,上面的函数定义不涉及任何实际计算。只有当用户明确请求时才执行计算。例如,要计算x=2.0时的函数值:

   {...struct ggml_cgraph gf = ggml_build_forward(f);// set the input variable and parameter valuesggml_set_f32(x, 2.0f);ggml_set_f32(a, 3.0f);ggml_set_f32(b, 4.0f);ggml_graph_compute_with_ctx(ctx, &gf, n_threads);printf("f = %f\n", ggml_get_f32_1d(f, 0));...}

实际计算是在ggml_graph_compute()函数中执行的。

  • The ggml_new_tensor_…() functions create new tensors. They are allocated in the memory buffer provided to the ggml_init() function. You have to be careful not to exceed the memory buffer size. Therefore, you have to know in advance how much memory you need for your computation. Alternatively, you can allocate a large enough memory and after defining the computation graph, call the ggml_used_mem() function to find out how much memory was actually needed.

  • The ggml_set_param() function marks a tensor as an input variable. This is used by the automatic differentiation and optimization algorithms.

  • 所描述的方法允许定义函数图一次,然后多次计算其正向或反向图。所有计算都将使用在ggml_init()函数中分配的相同内存缓冲区。通过这种方式,用户可以避免运行时的内存分配开销。

  • 该库支持多维张量-最多4个维度。FP16和FP32数据类型是一类公民(The FP16 and FP32 data types are first class citizens),但理论上,该库可以扩展为支持FP8和整数数据类型。

  • 每个张量运算(tensor operation)产生一个新的张量。最初,该库被设想为只支持使用一元和二元运算。大多数可用的操作属于这两类中的一类。随着时间的推移,很明显,库需要支持更复杂的操作。支持这些行动的方法尚不清楚,但以下行动中展示了几个例子:

    • ggml_permute()
    • ggml_conv_1d_1s()
    • ggml_conv_1d_2s()
  • For each tensor operator, the library implements a forward and backward computation function. The forward function computes the output tensor value given the input tensor values. The backward function computes the adjoint of the input tensors given the adjoint of the output tensor. For a detailed explanation of what this means, take a calculus class, or watch the following video:

    What is Automatic Differentiation?
    https://www.youtube.com/watch?v=wG_nF1awSSY

Tensor data (struct ggml_tensor)

张量通过ggml_tensor结构存储在内存中。该结构提供关于张量的大小、数据类型以及存储张量数据的存储器缓冲区的信息。此外,它还包含指向“源”张量的指针,即用于计算当前张量的张量。例如:

   {struct ggml_tensor * c = ggml_add(ctx, a, b);assert(c->src[0] == a);assert(c->src[1] == b);}

多维张量按行主顺序存储。ggml_tensor结构包含每个维度中元素数(“ne”)和字节数(“nb”,又称步幅)的字段。这允许在存储器中存储不连续的张量,这对于诸如换位和置换之类的操作是有用的。所有张量运算都必须考虑步长,而不是假设张量在内存中是连续的。
在这里插入图片描述

  • nb[i] 表示在第i纬度移动的步幅

The data of the tensor is accessed via the “data” pointer. For example:

   {const int nx = 2;const int ny = 3;struct ggml_tensor * a = ggml_new_tensor_2d(ctx, GGML_TYPE_F32, nx, ny);for (int y = 0; y < ny; y++) {for (int x = 0; x < nx; x++) {*(float *) ((char *) a->data + y*a->nb[1] + x*a->nb[0]) = x + y;}}...}

或者,也可以使用一些辅助函数,例如ggml_get_f32_1d() , ggml_set_f32_1d() .

TODO

The matrix multiplication operator (ggml_mul_mat)

TODO

Multi-threading

TODO

Overview of ggml.c

TODO

SIMD optimizations

TODO

Debugging ggml

TODO

example 最小执行demo

76
├── CMakeLists.txt  https://github1s.com/ggerganov/whisper.cpp/blob/1.0.3/CMakeLists.txt
├── ggml.c   https://github1s.com/ggerganov/whisper.cpp/blob/1.0.3/ggml.c
├── ggml.h   https://github1s.com/ggerganov/whisper.cpp/blob/1.0.3/ggml.h
└── main.cpp  

CMakeLists.txt

cmake_minimum_required (VERSION 3.0)
project(76 VERSION 1.0.3)set(CMAKE_EXPORT_COMPILE_COMMANDS "on")
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
set(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/lib")if(CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR)set(WHISPER_STANDALONE ON)
#    include(cmake/GitVars.cmake)
#    include(cmake/BuildTypes.cmake)#    # configure project version
#    if (EXISTS "${CMAKE_SOURCE_DIR}/bindings/ios/Makefile-tmpl")
#        configure_file(${CMAKE_SOURCE_DIR}/bindings/ios/Makefile-tmpl ${CMAKE_SOURCE_DIR}/bindings/ios/Makefile @ONLY)
#    endif()
#    configure_file(${CMAKE_SOURCE_DIR}/bindings/javascript/package-tmpl.json ${CMAKE_SOURCE_DIR}/bindings/javascript/package.json @ONLY)
else()set(WHISPER_STANDALONE OFF)
endif()if (EMSCRIPTEN)set(BUILD_SHARED_LIBS_DEFAULT OFF)option(WHISPER_WASM_SINGLE_FILE "whisper: embed WASM inside the generated whisper.js" ON)
else()if (MINGW)set(BUILD_SHARED_LIBS_DEFAULT OFF)else()set(BUILD_SHARED_LIBS_DEFAULT ON)endif()
endif()# optionsoption(BUILD_SHARED_LIBS               "whisper: build shared libs" ${BUILD_SHARED_LIBS_DEFAULT})option(WHISPER_ALL_WARNINGS            "whisper: enable all compiler warnings"                   ON)
option(WHISPER_ALL_WARNINGS_3RD_PARTY  "whisper: enable all compiler warnings in 3rd party libs" OFF)option(WHISPER_SANITIZE_THREAD         "whisper: enable thread sanitizer"    OFF)
option(WHISPER_SANITIZE_ADDRESS        "whisper: enable address sanitizer"   OFF)
option(WHISPER_SANITIZE_UNDEFINED      "whisper: enable undefined sanitizer" OFF)option(WHISPER_BUILD_TESTS             "whisper: build tests"    ${WHISPER_STANDALONE})
option(WHISPER_BUILD_EXAMPLES          "whisper: build examples" ${WHISPER_STANDALONE})option(WHISPER_SUPPORT_SDL2            "whisper: support for libSDL2" OFF)if (APPLE)option(WHISPER_NO_ACCELERATE       "whisper: disable Accelerate framework" OFF)option(WHISPER_NO_AVX              "whisper: disable AVX" OFF)option(WHISPER_NO_AVX2             "whisper: disable AVX2" OFF)
else()option(WHISPER_SUPPORT_OPENBLAS    "whisper: support for OpenBLAS" OFF)
endif()option(WHISPER_PERF                    "whisper: enable perf timings" OFF)# sanitizersif (NOT MSVC)if (WHISPER_SANITIZE_THREAD)set(CMAKE_C_FLAGS   "${CMAKE_C_FLAGS}   -fsanitize=thread")set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=thread")endif()if (WHISPER_SANITIZE_ADDRESS)set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS}     -fsanitize=address -fno-omit-frame-pointer")set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address -fno-omit-frame-pointer")endif()if (WHISPER_SANITIZE_UNDEFINED)set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS}     -fsanitize=undefined")set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=undefined")endif()
endif()#set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -ffast-math")
#set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -march=native")# dependenciesset(CMAKE_C_STANDARD   11)
set(CMAKE_CXX_STANDARD 11)find_package(Threads REQUIRED)# on APPLE - include Accelerate framework
if (APPLE AND NOT WHISPER_NO_ACCELERATE)find_library(ACCELERATE_FRAMEWORK Accelerate)if (ACCELERATE_FRAMEWORK)message(STATUS "Accelerate framework found")set(WHISPER_EXTRA_LIBS  ${WHISPER_EXTRA_LIBS}  ${ACCELERATE_FRAMEWORK})set(WHISPER_EXTRA_FLAGS ${WHISPER_EXTRA_FLAGS} -DGGML_USE_ACCELERATE)else()message(WARNING "Accelerate framework not found")endif()
endif()if (WHISPER_SUPPORT_OPENBLAS)find_library(OPENBLAS_LIBNAMES openblas libopenblas)if (OPENBLAS_LIB)message(STATUS "OpenBLAS found")set(WHISPER_EXTRA_LIBS  ${WHISPER_EXTRA_LIBS}  ${OPENBLAS_LIB})set(WHISPER_EXTRA_FLAGS ${WHISPER_EXTRA_FLAGS} -DGGML_USE_OPENBLAS)else()message(WARNING "OpenBLAS not found")endif()
endif()# compiler flagsif (NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)set(CMAKE_BUILD_TYPE Release CACHE STRING "Build type" FORCE)set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "Debug" "Release" "RelWithDebInfo")
endif ()if (WHISPER_ALL_WARNINGS)if (NOT MSVC)set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} \-Wall                           \-Wextra                         \-Wpedantic                      \-Wshadow                        \-Wcast-qual                     \-Wstrict-prototypes             \-Wpointer-arith                 \")else()# todo : msvcendif()
endif()if (NOT MSVC)set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Werror=vla")#set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fno-math-errno -ffinite-math-only -funsafe-math-optimizations")
endif()message(STATUS "CMAKE_SYSTEM_PROCESSOR: ${CMAKE_SYSTEM_PROCESSOR}")if (${CMAKE_SYSTEM_PROCESSOR} MATCHES "arm" OR ${CMAKE_SYSTEM_PROCESSOR} MATCHES "aarch64")message(STATUS "ARM detected")
else()message(STATUS "x86 detected")if (MSVC)set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /arch:AVX2")set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /arch:AVX2")else()if (EMSCRIPTEN)set(CMAKE_C_FLAGS   "${CMAKE_C_FLAGS}   -pthread")set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pthread")else()if(NOT WHISPER_NO_AVX)set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -mavx")endif()if(NOT WHISPER_NO_AVX2)set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -mavx2")endif()set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -mfma -mf16c")endif()endif()
endif()if (WHISPER_PERF)set(WHISPER_EXTRA_FLAGS ${WHISPER_EXTRA_FLAGS} -DGGML_PERF)
endif()#
# whisper - this is the main library of the project
#set(TARGET 76)add_library(GGML  ggml.c )
add_executable(76 main.cpp)
target_link_libraries(${TARGET} GGML)

Forward && Backward

#include "ggml.h"
#include <stdio.h>
#include <stdlib.h>int main(int argc, const char ** argv) {const int n_threads = 2;struct ggml_init_params params = {.mem_size   = 128*1024*1024,.mem_buffer = NULL,
//            .no_alloc   = false,};struct ggml_context * ctx0 = ggml_init(params);{struct ggml_tensor * x = ggml_new_tensor_1d(ctx0, GGML_TYPE_F32, 1);ggml_set_param(ctx0, x);struct ggml_tensor * a = ggml_new_tensor_1d(ctx0, GGML_TYPE_F32, 1);struct ggml_tensor * b = ggml_mul(ctx0, x, x);struct ggml_tensor * f = ggml_mul(ctx0, b, a);// a*x^2// 2*a*xggml_print_objects(ctx0);struct ggml_cgraph gf = ggml_build_forward(f);struct ggml_cgraph gb = ggml_build_backward(ctx0, &gf, false);ggml_set_f32(x, 2.0f);ggml_set_f32(a, 3.0f);// FORWARDggml_graph_compute(ctx0, &gf);printf("f = %f\n", ggml_get_f32_1d(f, 0));// FORWARD + BACKWARDggml_graph_reset(&gf);ggml_set_f32(f->grad, 1.0f);ggml_graph_compute(ctx0, &gb);printf("f     = %f\n", ggml_get_f32_1d(f, 0));printf("df/dx = %f\n", ggml_get_f32_1d(x->grad, 0));// SAVE GRAPHggml_graph_dump_dot(&gf, NULL, "test1-1-forward.dot");ggml_graph_dump_dot(&gb, &gf,  "test1-1-backward.dot");}return 0;
}

OUTPUT

ggml_print_objects: objects in context 0x560856e9d2a8:- ggml_object: offset = 32, size = 176, next = 0x7f888aa7f0e0- ggml_object: offset = 240, size = 176, next = 0x7f888aa7f1b0- ggml_object: offset = 448, size = 176, next = 0x7f888aa7f280- ggml_object: offset = 656, size = 176, next = 0x7f888aa7f350- ggml_object: offset = 864, size = 176, next = 0x7f888aa7f420- ggml_object: offset = 1072, size = 176, next = 0x7f888aa7f4f0- ggml_object: offset = 1280, size = 176, next = (nil)
ggml_print_objects: --- end ---
f = 12.000000
f     = 12.000000
df/dx = 12.000000
f     = 27.000000
df/dx = 18.000000
ggml_graph_dump_dot: dot -Tpng test1-1-forward.dot -o test1-1-forward.dot.png && open test1-1-forward.dot.png
ggml_graph_dump_dot: dot -Tpng test1-1-backward.dot -o test1-1-backward.dot.png && open test1-1-backward.dot.pngProcess finished with exit code 0

Vector Example

#include "ggml.h"
#include <stdio.h>
#include <cstring>int main(int argc, const char ** argv) {const int n_threads = 2;struct ggml_init_params params = {.mem_size   = 128*1024*1024,.mem_buffer = NULL,};struct ggml_context * ctx0 = ggml_init(params);{struct ggml_tensor * x = ggml_new_tensor_1d(ctx0, GGML_TYPE_F32, 5);ggml_set_param(ctx0, x);struct ggml_tensor * a = ggml_new_tensor_1d(ctx0, GGML_TYPE_F32, 5);struct ggml_tensor * b = ggml_mul(ctx0, x, x);struct ggml_tensor * f = ggml_mul(ctx0, b, a);// a*x^2ggml_print_objects(ctx0);struct ggml_cgraph gf = ggml_build_forward(f);struct ggml_cgraph gb = ggml_build_backward(ctx0, &gf, false);std::vector<float> digit={1,2,3,4,5};memcpy(x->data, digit.data(), ggml_nbytes(x));
//        ggml_set_f32(x, 2.0f);ggml_set_f32(a, 3.0f);// FORWARDggml_graph_compute(ctx0, &gf);printf("f = %f\n", ggml_get_f32_1d(f, 0));printf("f = %f\n", ggml_get_f32_1d(f, 1));printf("f = %f\n", ggml_get_f32_1d(f, 2));printf("f = %f\n", ggml_get_f32_1d(f, 3));printf("f = %f\n", ggml_get_f32_1d(f, 4));}return 0;
}

在这里插入图片描述

Compute Graph

  • 只有在这个过程才执行真正的计算,之前都是在构建树,遍历树

构建计算图的语句 struct ggml_cgraph gf = ggml_build_forward(f);

计算图的定义及地址

在这里插入图片描述

构建过程 ggml_build_forward

struct ggml_cgraph ggml_build_forward(struct ggml_tensor * tensor) {struct ggml_cgraph result = {/*.n_nodes      =*/ 0,/*.n_leafs      =*/ 0,/*.n_threads    =*/ 0,/*.work_size    =*/ 0,/*.work         =*/ NULL,/*.nodes        =*/ { NULL },/*.grads        =*/ { NULL },/*.leafs        =*/ { NULL },/*.perf_runs    =*/ 0,/*.perf_cycles  =*/ 0,/*.perf_time_us =*/ 0,};ggml_build_forward_impl(&result, tensor, false);return result;
}
void ggml_build_forward_impl(struct ggml_cgraph * cgraph, struct ggml_tensor * tensor, bool expand) {if (!expand) {cgraph->n_nodes = 0;cgraph->n_leafs = 0;}const int n0 = cgraph->n_nodes;UNUSED(n0);ggml_visit_parents(cgraph, tensor);// 函数会递归地遍历节点的src0、src1和opt数组中的节点,并将它们添加到计算图中。这样可以确保所有的父节点都会被添加到计算图中。最后,函数会根据节点的op和grad属性来判断节点的类型。如果op为GGML_OP_NONE且grad为NULL,则说明该节点是一个叶子节点,不是梯度图的一部分(例如常量节点)。此时,函数会将该节点添加到计算图的leafs数组中。否则,函数会将该节点添加到计算图的nodes数组中,并将其grad属性添加到计算图的grads数组中。const int n_new = cgraph->n_nodes - n0;GGML_PRINT_DEBUG("%s: visited %d new nodes\n", __func__, n_new);if (n_new > 0) {// the last added node should always be starting pointassert(cgraph->nodes[cgraph->n_nodes - 1] == tensor);}
}

在这里插入图片描述

计算过程 ggml_graph_compute

        

void ggml_graph_compute(struct ggml_context * ctx, struct ggml_cgraph * cgraph) {if (cgraph->n_threads <= 0) {cgraph->n_threads = 8;}const int n_threads = cgraph->n_threads;struct ggml_compute_state_shared state_shared = {/*.spin      =*/ GGML_LOCK_INITIALIZER,/*.n_threads =*/ n_threads,/*.n_ready   =*/ 0,/*.has_work  =*/ false,/*.stop      =*/ false,};struct ggml_compute_state * workers = n_threads > 1 ? alloca(sizeof(struct ggml_compute_state)*(n_threads - 1)) : NULL;

create thread pool

        在程序启动时创建一定数量的线程,然后将待处理的任务分配给这些线程进行执行。线程池能够在任务到达时立即执行,节省了线程创建和销毁的开销。

    // create thread poolif (n_threads > 1) {ggml_lock_init(&state_shared.spin);atomic_store(&state_shared.has_work, true); // 将true存储到state_shared.has_work位置,并确保该操作是原子的//创建了n_threads-1个线程for (int j = 0; j < n_threads - 1; j++) {workers[j] = (struct ggml_compute_state) {.thrd   = 0,.params = {.type  = GGML_TASK_COMPUTE,.ith   = j + 1,.nth   = n_threads,.wsize = cgraph->work ? ggml_nbytes(cgraph->work) : 0,.wdata = cgraph->work ? cgraph->work->data : NULL,},.node   = NULL,.shared = &state_shared,};int rc = pthread_create(&workers[j].thrd, NULL, ggml_graph_compute_thread, &workers[j]);// 创建一个线程。// ggml_graph_compute_thread是一个函数指针,指向要在新创建的线程中执行的函数。assert(rc == 0);UNUSED(rc);// 使用`UNUSED(rc)`来消除编译器的未使用变量警告。}}

initialize tasks + work buffer

        这段代码的作用是初始化任务和工作缓冲区。它首先遍历计算图中的每个节点,并根据节点的操作类型分配任务数量。然后根据节点类型确定工作缓冲区的大小,并分配相应大小的内存空间。

        具体地,对于每个节点,根据其操作类型,分配相应数量的任务。例如,对于GGML_OP_NONE,GGML_OP_MUL操作,分配1个任务;对于GGML_OP_ADD操作,分配n_threads个任务(与线程数相同)。
在这里插入图片描述
在这里插入图片描述

        在分配任务数量时,还会计算工作缓冲区的大小。例如,对于GGML_OP_MUL_MAT操作,根据矩阵的大小和是否转置等因素确定任务数量,并更新工作缓冲区的大小。最后,根据工作缓冲区的大小,如果需要分配内存空间并且当前未分配,则分配相应大小的内存空间。
        但是本例的node->n_tasks > 1都为false,直接运行ggml_compute_forward即可。

    // initialize tasks + work buffer{size_t work_size = 0;// thread scheduling for the different operationsfor (int i = 0; i < cgraph->n_nodes; i++) {struct ggml_tensor * node = cgraph->nodes[i];switch (node->op) {case GGML_OP_DUP:{node->n_tasks = 1;} break;case GGML_OP_ADD:{node->n_tasks = n_threads;} break;case GGML_OP_SUB:case GGML_OP_MUL:case GGML_OP_DIV:case GGML_OP_SQR:case GGML_OP_SQRT:case GGML_OP_SUM:case GGML_OP_MEAN:case GGML_OP_REPEAT:case GGML_OP_ABS:case GGML_OP_SGN:case GGML_OP_NEG:case GGML_OP_STEP:case GGML_OP_RELU:{node->n_tasks = 1;} break;case GGML_OP_GELU:{node->n_tasks = n_threads;} break;case GGML_OP_NORM:{node->n_tasks = n_threads;} break;case GGML_OP_MUL_MAT:{// TODO: use different scheduling for different matrix sizesnode->n_tasks = n_threads;size_t cur = 0;// TODO: better way to determine if the matrix is transposedif (node->src0->nb[1] < node->src0->nb[0]) {cur = ggml_nbytes(node)*node->n_tasks; // TODO: this can become (n_tasks-1)} else {if (node->src0->type == GGML_TYPE_F16 &&node->src1->type == GGML_TYPE_F32) {
#if defined(GGML_USE_ACCELERATE) || defined(GGML_USE_OPENBLAS)if (ggml_compute_forward_mul_mat_use_blas(node->src0, node->src1, node)) {cur = sizeof(float)*(node->src0->ne[0]*node->src0->ne[1]);} else {cur = sizeof(ggml_fp16_t)*ggml_nelements(node->src1);}
#elsecur = sizeof(ggml_fp16_t)*ggml_nelements(node->src1);
#endif} else if (node->src0->type == GGML_TYPE_F32 &&node->src1->type == GGML_TYPE_F32) {cur = 0;} else {GGML_ASSERT(false);}}work_size = MAX(work_size, cur);} break;case GGML_OP_SCALE:{node->n_tasks = n_threads;} break;case GGML_OP_CPY:case GGML_OP_RESHAPE:case GGML_OP_VIEW:case GGML_OP_PERMUTE:case GGML_OP_TRANSPOSE:case GGML_OP_GET_ROWS:case GGML_OP_DIAG_MASK_INF:{node->n_tasks = 1;} break;case GGML_OP_SOFT_MAX:{node->n_tasks = n_threads;} break;case GGML_OP_ROPE:{node->n_tasks = 1;} break;case GGML_OP_CONV_1D_1S:case GGML_OP_CONV_1D_2S:{node->n_tasks = n_threads;GGML_ASSERT(node->src0->ne[3] == 1);GGML_ASSERT(node->src1->ne[2] == 1);GGML_ASSERT(node->src1->ne[3] == 1);size_t cur = 0;const int nk = node->src0->ne[0];if (node->src0->type == GGML_TYPE_F16 &&node->src1->type == GGML_TYPE_F32) {cur = sizeof(ggml_fp16_t)*(nk*ggml_up32(node->src0->ne[1])*node->src0->ne[2] +( 2*(nk/2) + node->src1->ne[0])*node->src1->ne[1]);} else if (node->src0->type == GGML_TYPE_F32 &&node->src1->type == GGML_TYPE_F32) {cur = sizeof(float)*(nk*ggml_up32(node->src0->ne[1])*node->src0->ne[2] +( 2*(nk/2) + node->src1->ne[0])*node->src1->ne[1]);} else {GGML_ASSERT(false);}work_size = MAX(work_size, cur);} break;case GGML_OP_FLASH_ATTN:{node->n_tasks = n_threads;size_t cur = 0;if (node->src1->type == GGML_TYPE_F32) {cur  = sizeof(float)*node->src1->ne[1]*node->n_tasks; // TODO: this can become (n_tasks-1)cur += sizeof(float)*node->src1->ne[1]*node->n_tasks; // this is overestimated by x2}if (node->src1->type == GGML_TYPE_F16) {cur  = sizeof(float)*node->src1->ne[1]*node->n_tasks; // TODO: this can become (n_tasks-1)cur += sizeof(float)*node->src1->ne[1]*node->n_tasks; // this is overestimated by x2}work_size = MAX(work_size, cur);} break;case GGML_OP_FLASH_FF:{node->n_tasks = n_threads;size_t cur = 0;if (node->src1->type == GGML_TYPE_F32) {cur  = sizeof(float)*node->src1->ne[1]*node->n_tasks; // TODO: this can become (n_tasks-1)cur += sizeof(float)*node->src1->ne[1]*node->n_tasks; // this is overestimated by x2}if (node->src1->type == GGML_TYPE_F16) {cur  = sizeof(float)*node->src1->ne[1]*node->n_tasks; // TODO: this can become (n_tasks-1)cur += sizeof(float)*node->src1->ne[1]*node->n_tasks; // this is overestimated by x2}work_size = MAX(work_size, cur);} break;case GGML_OP_NONE:{node->n_tasks = 1;} break;case GGML_OP_COUNT:{assert(false);} break;};}if (cgraph->work != NULL && work_size > cgraph->work_size) {assert(false); // TODO: better handling}if (work_size > 0 && cgraph->work == NULL) {cgraph->work_size = work_size + CACHE_LINE_SIZE*(n_threads - 1);GGML_PRINT_DEBUG("%s: allocating work buffer for graph (%zu bytes)\n", __func__, cgraph->work_size);cgraph->work = ggml_new_tensor_1d(ctx, GGML_TYPE_I8, cgraph->work_size);}}
    const int64_t perf_start_cycles  = ggml_perf_cycles(); #计时用const int64_t perf_start_time_us = ggml_perf_time_us();#计时用for (int i = 0; i < cgraph->n_nodes; i++) {GGML_PRINT_DEBUG_5("%s: %d/%d\n", __func__, i, cgraph->n_nodes);struct ggml_tensor * node = cgraph->nodes[i];// TODO: this could be used to avoid unnecessary computations, but it needs to be improved//if (node->grad == NULL && node->perf_runs > 0) {//    continue;//}const int64_t perf_node_start_cycles  = ggml_perf_cycles();const int64_t perf_node_start_time_us = ggml_perf_time_us();

INIT

        // INITstruct ggml_compute_params params = {/*.type  =*/ GGML_TASK_INIT,/*.ith   =*/ 0,/*.nth   =*/ node->n_tasks,/*.wsize =*/ cgraph->work ? ggml_nbytes(cgraph->work) : 0,/*.wdata =*/ cgraph->work ? cgraph->work->data : NULL,};ggml_compute_forward(&params, node);

COMPUTE

        // COMPUTEif (node->n_tasks > 1) {if (atomic_fetch_add(&state_shared.n_ready, 1) == n_threads - 1) {atomic_store(&state_shared.has_work, false);}while (atomic_load(&state_shared.has_work)) {ggml_lock_lock  (&state_shared.spin);ggml_lock_unlock(&state_shared.spin);}// launch thread poolfor (int j = 0; j < n_threads - 1; j++) {workers[j].params = (struct ggml_compute_params) {.type  = GGML_TASK_COMPUTE,.ith   = j + 1,.nth   = n_threads,.wsize = cgraph->work ? ggml_nbytes(cgraph->work) : 0,.wdata = cgraph->work ? cgraph->work->data : NULL,};workers[j].node = node;}atomic_fetch_sub(&state_shared.n_ready, 1);while (atomic_load(&state_shared.n_ready) > 0) {ggml_lock_lock  (&state_shared.spin);ggml_lock_unlock(&state_shared.spin);}atomic_store(&state_shared.has_work, true);}params.type = GGML_TASK_COMPUTE;ggml_compute_forward(&params, node);// wait for thread poolif (node->n_tasks > 1) {if (atomic_fetch_add(&state_shared.n_ready, 1) == n_threads - 1) {atomic_store(&state_shared.has_work, false);}while (atomic_load(&state_shared.has_work)) {ggml_lock_lock  (&state_shared.spin);ggml_lock_unlock(&state_shared.spin);}atomic_fetch_sub(&state_shared.n_ready, 1);while (atomic_load(&state_shared.n_ready) != 0) {ggml_lock_lock  (&state_shared.spin);ggml_lock_unlock(&state_shared.spin);}}

FINALIZE

        // FINALIZEif (node->n_tasks > 1) {if (atomic_fetch_add(&state_shared.n_ready, 1) == n_threads - 1) {atomic_store(&state_shared.has_work, false);}while (atomic_load(&state_shared.has_work)) {ggml_lock_lock  (&state_shared.spin);ggml_lock_unlock(&state_shared.spin);}// launch thread poolfor (int j = 0; j < n_threads - 1; j++) {workers[j].params = (struct ggml_compute_params) {.type  = GGML_TASK_FINALIZE,.ith   = j + 1,.nth   = n_threads,.wsize = cgraph->work ? ggml_nbytes(cgraph->work) : 0,.wdata = cgraph->work ? cgraph->work->data : NULL,};workers[j].node = node;}atomic_fetch_sub(&state_shared.n_ready, 1);while (atomic_load(&state_shared.n_ready) > 0) {ggml_lock_lock  (&state_shared.spin);ggml_lock_unlock(&state_shared.spin);}atomic_store(&state_shared.has_work, true);}params.type = GGML_TASK_FINALIZE;ggml_compute_forward(&params, node);// wait for thread poolif (node->n_tasks > 1) {if (atomic_fetch_add(&state_shared.n_ready, 1) == n_threads - 1) {atomic_store(&state_shared.has_work, false);}while (atomic_load(&state_shared.has_work)) {ggml_lock_lock  (&state_shared.spin);ggml_lock_unlock(&state_shared.spin);}atomic_fetch_sub(&state_shared.n_ready, 1);while (atomic_load(&state_shared.n_ready) != 0) {ggml_lock_lock  (&state_shared.spin);ggml_lock_unlock(&state_shared.spin);}}// performance stats (node){int64_t perf_cycles_cur  = ggml_perf_cycles()  - perf_node_start_cycles;int64_t perf_time_us_cur = ggml_perf_time_us() - perf_node_start_time_us;node->perf_runs++;node->perf_cycles  += perf_cycles_cur;node->perf_time_us += perf_time_us_cur;}}

join thread pool

    // join thread poolif (n_threads > 1) {atomic_store(&state_shared.stop, true);atomic_store(&state_shared.has_work, true);for (int j = 0; j < n_threads - 1; j++) {int rc = pthread_join(workers[j].thrd, NULL);assert(rc == 0);UNUSED(rc);}ggml_lock_destroy(&state_shared.spin);}// performance stats (graph){int64_t perf_cycles_cur  = ggml_perf_cycles()  - perf_start_cycles;int64_t perf_time_us_cur = ggml_perf_time_us() - perf_start_time_us;cgraph->perf_runs++;cgraph->perf_cycles  += perf_cycles_cur;cgraph->perf_time_us += perf_time_us_cur;GGML_PRINT_DEBUG("%s: perf (%d) - cpu = %.3f / %.3f ms, wall = %.3f / %.3f ms\n",__func__, cgraph->perf_runs,(double) perf_cycles_cur      / (double) ggml_cycles_per_ms(),(double) cgraph->perf_cycles  / (double) ggml_cycles_per_ms() / (double) cgraph->perf_runs,(double) perf_time_us_cur     / 1000.0,(double) cgraph->perf_time_us / 1000.0 / cgraph->perf_runs);}
}

相关文章:

webassembly003 ggml GGML Tensor Library part-2 官方使用说明

https://github.com/ggerganov/whisper.cpp/tree/1.0.3 GGML Tensor Library 官方有一个函数使用说明&#xff0c;但是从初始版本就没修改过 : https://github1s.com/ggerganov/ggml/blob/master/include/ggml/ggml.h#L3-L173 This documentation is still a work in progres…...

ES主集群的优化参考点

因为流量比较大&#xff0c; 导致ES线程数飙高&#xff0c;cpu直往上窜&#xff0c;查询耗时增加&#xff0c;并传导给所有调用方&#xff0c;导致更大范围的延时。如何解决这个问题呢&#xff1f; ES负载不合理&#xff0c;热点问题严重。ES主集群一共有几十个节点&#xff0…...

全国范围内-二手房小区数据-2023-8月更新

收录融合去重多个平台数据&#xff1a;80万&#xff0c;仅供数字参考 数据纬度字段名注释枚举值基础信息id主键id&#xff1a;名称城市来源生成 md5值00001073838501125ec4473463ead9ccname名称瑞祥安文创园address地址(朝阳)双桥路东柳村口南口lng经度116.581903lat纬度39.89…...

第4章 循环变换

4.1 适配体系结构特征的关键技术 由于高级语言隐藏了底层硬件体系结构的大量细节&#xff0c;如果不经过优化直接将高级程序设计语言编写的程序部署在底层硬件上&#xff0c;往往无法充分利用底层硬件体系结构的处理能力。 算子融合不仅可以提…...

spring cloud使用git作为配置中心,git开启了双因子认证,如何写本地配置文件

问题 spring cloud使用git作为配置中心&#xff0c;git开启了双因子认证&#xff0c;死活认证不成功&#xff01;&#xff01;&#xff01;&#xff01;&#xff01; 报错关键字 org.eclipse.jgit.api.errors.TransportException: https://git.qualink.com/zhaoxin15/sc-confi…...

JVM内存管理、内存分区:堆、方法区、虚拟机栈、本地方法栈、程序计数器

内存管理 内存分区 线程共享 堆 存放实例&#xff0c;字符串常量&#xff08;直接引用&#xff09;&#xff0c;静态变量&#xff0c;线程分配缓冲区&#xff08;TLAB线程私有&#xff09;。垃圾收集器管理的区域 方法区 非堆&#xff0c;和堆相对的概念。存储已被虚拟机加载的…...

L1-047 装睡(Python实现) 测试点全过

题目 你永远叫不醒一个装睡的人 —— 但是通过分析一个人的呼吸频率和脉搏&#xff0c;你可以发现谁在装睡&#xff01;医生告诉我们&#xff0c;正常人睡眠时的呼吸频率是每分钟15-20次&#xff0c;脉搏是每分钟50-70次。下面给定一系列人的呼吸频率与脉搏&#xff0c;请你找…...

Mysql优化原理分析

一、存储引擎 1.1 MyISAM 一张表生成三个文件 xxx.frm&#xff1a;存储表结构xxx.MYD&#xff1a;存储表数据xxx.MYI&#xff1a;存储表索引 索引文件和数据文件是分离的&#xff08;非聚集&#xff09; select * from t where t.col1 30; 先去t.MYI文件查找30对应的索引…...

软考高级系统架构设计师系列案例考点专题一:软件架构设计

软考高级系统架构设计师系列案例考点专题一:软件架构设计 一、考点梳理及精讲1.质量属性判断与质量属性效用树2.必备概念3.架构风格对比4.MVC架构5.J2EE架构6.面向服务的架构SOA7.企业服务总线ESB一、考点梳理及精讲 系统架构设计师方面的知识在案例分析中每年必考1~2题,并且…...

css实现垂直上下布局的两种常用方法

例子&#xff1a;将两个<span>元素在<div>内垂直居中放置. 方法一&#xff1a;使用 Flexbox 来实现。 在下面的示例中&#xff0c;我将为 <div> 元素添加样式&#xff0c;使其成为一个 Flex 容器&#xff0c;并使用 Flexbox 属性将其中的两个 <span>…...

【Jetpack】Navigation 导航组件 ⑤ ( NavigationUI 类使用 )

文章目录 一、NavigationUI 类简介二、NavigationUI 类使用流程1、创建 Fragment2、创建 NavigationGraph3、Activity 导入 NavHostFragment4、创建菜单5、Activity 界面开发 NavigationUI 的主要逻辑 ( 重点 )a、添加 Fragment 布局b、处理 Navigation 导航逻辑 ( 重点 )c、启…...

基于NAudio实现简单的音乐播放器

《测试.net开源音频库NAudio》介绍了使用NAudio实现音乐播放和录音的基本用法&#xff0c;本文基于NAudio的音乐播放功能实现简单的mp3音乐播放器程序&#xff0c;主要实现以下功能&#xff1a;   1&#xff09;导入文件夹中的mp3音乐文件&#xff0c;直接导入多个mp3音乐文件…...

C++之“00000001“和“\x00\x00\x00\x01“用法区别(一百八十六)

简介&#xff1a; CSDN博客专家&#xff0c;专注Android/Linux系统&#xff0c;分享多mic语音方案、音视频、编解码等技术&#xff0c;与大家一起成长&#xff01; 优质专栏&#xff1a;Audio工程师进阶系列【原创干货持续更新中……】&#x1f680; 人生格言&#xff1a; 人生…...

Java“魂牵”京东店铺所有商品数据接口,京东店铺所有商品API接口,京东API接口申请指南

要通过京东的API获取店铺所有商品数据&#xff0c;您可以使用京东开放平台提供的接口来实现。以下是一种使用Java编程语言实现的示例&#xff0c;展示如何通过京东开放平台API获取整店商品数据&#xff1a; 首先&#xff0c;确保您已注册成为京东开放平台的开发者&#xff0c;…...

vuex详细用法

Vuex是一个专门为Vue.js应用程序开发的状态管理模式。它可以帮助我们在Vue组件之间共享和管理数据&#xff0c;以及实现更好的代码组织和调试。 在Vue.js中&#xff0c;组件之间的数据通信可以通过props和事件来实现。然而&#xff0c;随着应用程序规模的增长&#xff0c;组件…...

微前端-monorepo-无界

文章目录 前言一、微前端二 、monorepo三 、pnpm硬链接软链接&#xff08;符号链接&#xff09;幽灵依赖依赖安装耗时长monorepo项目搭建子模块复用 四、无界接入无界无界预加载无界传参 总结 前言 本文主要记录微前端框架 无界 的使用与理解以及monorepo代码管理方式。 一、微…...

阿里云矢量图标透明背景转换/展示时变为黑色解决方法

下载了一个矢量图标&#xff0c;背景是透明的 上传到minio然后在前端展示&#xff0c;发现透明&#xff08;白色&#xff09;的地方变成黑色了 处理方法&#xff1a;去除透明的底色。使用window的画图程序打开保存一遍&#xff0c;将透明色转为白色 OK...

Linux之Shell(二)

Linux之Shell 函数系统函数basenamedirname 自定义函数 正则表达式入门常规匹配常用特殊字符 文本处理工具cutawk 综合应用案例归档文件发送消息 函数 系统函数 basename 基本语法 basename [string / pathname] [suffix] 功能描述&#xff1a;basename 命令会删掉所有的前缀…...

以太网POE供电浪涌静电防护推荐TVS二极管

POE是一种传输技术&#xff0c;可在以太网电缆上传输电力和数据。1000M千兆以太网POE供电端口广泛用于安防、视频监控以及智能电网等工业系统&#xff0c;以实现系统内的数据、视频传输、流量控制、以及通过总线实现供电。由于工业以太网工作环境非常严酷苛刻&#xff0c;对于以…...

如何在 JavaScript 中查看结构体数组?

调试 JavaScript 代码的最简单方法是使用许多开发人员使用的 console.log()。有时&#xff0c;我们需要了解数组的结构和存储的值以进行调试。以下介绍如何查看结构数组。 JavaScript 的各种方法允许我们检查数组的结构。例如&#xff0c;我们可以知道数组是否包含对象、嵌套数…...

【SpringBoot学习笔记】02.静态资源与首页订制

静态资源 Spring Boot 通过 MVC 的自动配置类 WebMvcAutoConfiguration 为这些 WebJars 前端资源提供了默认映射规则&#xff0c;部分源码如下。 jar包&#xff1a; JAR 文件就是 Java Archive File&#xff0c;顾名思意&#xff0c;它的应用是与 Java 息息相关的&#xff0c;…...

kotlin 转 Java

今天突然想研究下有些kotlin文件转为Java到底长什么样&#xff0c;好方便优化kotlin代码&#xff0c;搞了半天发现一个非常简单的Android Studio或者Intellij idea官方插件Kotlin&#xff0c;Kotlin是插件的名字&#xff0c;真是醉了&#xff1b; 这里以AS为例&#xff0c;使用…...

【Harmony】在Harmony上面可以使用的Android常用的开源库

序言 Harmony开发中&#xff0c;由于不像Android开发经过这么多年的发展&#xff0c;各种类库都是比较完善的&#xff0c;这就导致在Harmony开发中很多Android类库是不能使用的&#xff0c;但是也有一些是可以使用的&#xff0c;下面是我在Harmony开发中实际开发中可以使用的部…...

数学建模:灰色关联分析

&#x1f506; 文章首发于我的个人博客&#xff1a;欢迎大佬们来逛逛 灰色关联分析法 算法流程 建立一个m行 n列的矩阵 X X X &#xff0c;其中 m 表示评价对象&#xff0c; n表示评价指标首先进行矩阵的归一化&#xff0c;得到归一化后的矩阵 d a t a data data获取参考向…...

nodepad++ 插件的安装

nodepad 插件的安装 一、插件安装二、安装插件&#xff1a;Json Viewer nodepad 有 插件管理功能&#xff0c;其中有格式化json以及可以将json作为树查看的插件&#xff1a; Json Viewer 一、插件安装 1、首先下载最新的notepad 64位【https://notepad-plus.en.softonic.com…...

学习分享:Ubuntu 下使用 Qt 打开串口报错 Permission denied

Ubuntu 下使用 Qt 打开串口报错 Permission denied 错误描述 提前声明一下&#xff0c;开发环境是 Ubuntu18.04&#xff0c;用户是非 root 用户。 因项目需求&#xff0c;需要使用到 Qt 收发串口数据&#xff0c;曾经写过一个串口调试助手的程序 通俗易懂玩QT&#xff1a;串口…...

Javaweb入门

Spring Spring发展到今天已经形成一种开发生态圈&#xff0c;Spring提供若干个子项目&#xff0c;每个项目用于完成特定的功能。 Spring Boot可以帮助我们非常快速的构建应用程序、简化开发、提高效率 SpringBootWeb入门 需求&#xff1a;使用Spring Boot开发一个web应用&a…...

后端开发基础概念

后端开发基础概念 目前处于项目上手阶段&#xff0c;在学习项目过程中&#xff0c;有一些一知半解或者不明白含义的专业名词或者缩写&#xff0c;在此汇总。里面的内容很多都是基于个人理解&#xff0c;水平有限如果有出错的地方还请各位大佬批评指正。 2023年8月31日00:34:22…...

ELK原理和介绍

为什么用到ELK&#xff1a; 一般我们需要进行日志分析场景&#xff1a;直接在日志文件中 grep、awk 就可以获得自己想要的信息。但在规模较大的场景中&#xff0c;此方法效率低下&#xff0c;面临问题包括日志量太大如何归档、文本搜索太慢怎么办、如何多维度查询。需要集中化…...

FBX SDK 开发环境配置 visual studio 2022

FBX | Adaptable File Formats for 3D Animation Software | Autodesk. 下载windows的sdk并安装. 创建一个c console 工程 设置include目录 添加预处理宏 FBX_SHARED1 添加fbx sdk lib 目录 添加依赖lib : libfbxsdk-md.lib libxml2-md.lib zlib-md.lib 配置完毕....

vue面试题_vue2和vue3的区别

1、数据绑定原理不同 vue2&#xff1a;vue2的数据绑定是利用ES5的一个API&#xff1a;Object.definePropert() 对数据进行劫持&#xff0c;结合发布订阅模式的方式来实现的。 vue3&#xff1a;vue3中使用了ES6的Proxy API对数据代理。相比vue2.x&#xff0c;使用proxy的优势如…...

Shiro整合SpringBoot,实战下的应用场景

文章目录 前言一、springBootshiro环境准备1.数据库2.ssmp环境搭建3.实体类4.三层搭建5.初始化测试数据 二、Shiro过滤器1.Shiro认证过滤器2.Shiro授权过滤器 三、springBootshiro身份认证1.创建Realm,重写认证方法doGetAuthenticationInfo2.创建shiro配置类3.Postman测试 四、…...

C语言——全局变量和局部变量重名了会怎么样

前言 &#xff08;1&#xff09;今天在交流群里面看到这样一个问题: 为什么这个程序中下面我定义的void型函数smart在全局变量前声明了&#xff0c;但是在man函数中调用了smart函数&#xff0c;m的值打印出来还是0。 #include<stdio.h>int m; void smart(void);int main(…...

linux下vi或vim操作Found a swap file by the name的原因及解决方法--九五小庞

在linux下用vi或vim打开Test.java文件时 [rootlocalhost tmp]# vi Test.java出现了如下信息&#xff1a; E325: ATTENTION Found a swap file by the name ".Test.java.swp" owned by: root dated: Wed Dec 7 13:52:56 2011 file name: /var/tmp/Test.java modif…...

通过RD Client远程连接windows电脑踩坑点

通过RD Client远程连接windows电脑操作的个人踩坑点&#xff0c;记录下来&#xff0c;防止下一次还犯。 配置&#xff1a; win10专业版腾讯云服务器Ubuntu22.04小米平板RD client 首先是安装frp 这一部分参考的是&#xff1a;通过RD Client远程连接windows电脑&#xff08;…...

学习node之——如何在项目中使用MySQL、前后端的身份认证

上一篇文章只写了一丢丢&#xff0c;这篇才是正片&#xff0c;look look look 一、使用mysql模块操作数据库 1、查询数据 这里连接数据库的用户和密码都是我们在安装mysql时配置的密码。每个人的users表格里面数据不同&#xff0c;结果也会不一样哟&#xff01; // 导入mys…...

AUTOSAR从入门到精通-【应用篇】参照AUTOSAR架构的柴油车后处理集成电控系统软件设计与研究(续)

目录 3.3底层驱动模块开发 3.3.1利用S-Function编写底层驱动模块 3.3.2编写TLC文件来控制自动代码生成过程...

Linux 内核动态打印调试(dev_info、 dev_dbg )

目录 前言 1 printk消息级别 2 调整内核printk打印级别 3 dev_xxx函数简介 4 配置内核使用动态打印 5 动态调试使用方法 6 动态打印调试的基本原理 &#x1f388;个人主页&#x1f388;&#xff1a;linux_嵌入式大师之路的博客-CSDN博客&#x1f389;&#x1f389;&…...

深入浅出AXI协议(3)——握手过程

一、前言 在之前的文章中我们快速地浏览了一下AXI4协议中的接口信号&#xff0c;对此我们建议先有一个简单的认知&#xff0c;接下来在使用到的时候我们还会对各种信号进行一个详细的讲解&#xff0c;在这篇文章中我们将讲述AXI协议的握手协议。 二、握手协议概述 在前面的文章…...

Ansible学习笔记5

copy模块&#xff1a;&#xff08;重点&#xff09; copy模块用于对文件的远程拷贝&#xff08;如把本地的文件拷贝到远程主机上。&#xff09; 在master的主机上准备一个文件&#xff0c;拷贝文件到group1的所有主机上。 这个用的频率非常高&#xff0c;非常有用的一个模块…...

LeetCode 面试题 02.06. 回文链表

文章目录 一、题目二、C# 题解 一、题目 编写一个函数&#xff0c;检查输入的链表是否是回文的。 点击此处跳转题目。 示例 1&#xff1a; 输入&#xff1a; 1->2 输出&#xff1a; false 示例 2&#xff1a; 输入&#xff1a; 1->2->2->1 输出&#xff1a; true …...

linux环境没有curl或者telnet命令解决方法与区分linux环境类型

如何区分你当前使用的 Linux 系统是 Ubuntu、CentOS 还是 Alpine&#xff0c;查看 /etc/os-release 文件 [rootlocalhost ~]# cat /etc/os-release NAME"CentOS Linux" VERSION"7 (Core)" ID"centos" ID_LIKE"rhel fedora" VERSION_I…...

golang channel

channel是不同协程之间异步通信的数据结构。 基本用法 1 构造 ch:make(chan int)//无缓冲 ch:make(chan int,10)//有缓冲2 读操作 val:<-ch <-ch val,ok:<-ch3 写 var data int ch<-data4 关闭 close(ch)5 多路复用 select{ case <-parent.Done():child.…...

高等职业学校物联网实训室建设方案

一、概述 1.1专业背景 物联网&#xff08;Internet of Things&#xff09;被称为继计算机、互联网之后世界信息产业第三次浪潮&#xff0c;它并非一个全新的技术领域&#xff0c;而是现代信息技术发展到一定阶段后出现的一种聚合性应用与技术提升&#xff0c;是随着传感网、通…...

Python基础学习第四天:Python注释

创建注释 注释以 &#xff03; 开头&#xff0c;Python 将忽略它们&#xff1a; 实例 #This is a comment print("Hello, World!")运行实例 注释可以放在一行的末尾&#xff0c;Python 将忽略该行的其余部分&#xff1a; 实例 print("Hello, World!")…...

Puppeteer中使用Stealth.min.js库

这里需要安装npm install puppeteer-extra puppeteer-extra-plugin-stealth&#xff0c;然后&#xff0c;在启动浏览器时&#xff0c;Puppeteer 会自动应用 Stealth.min.js 插件的功能。 const puppeteer require(puppeteer-extra); const StealthPlugin require(puppeteer-…...

JVM ZGC垃圾收集器

ZGC垃圾收集器 ZGC&#xff08;“Z”并非什么专业名词的缩写&#xff0c;这款收集器的名字就叫作Z Garbage Collector&#xff09;是一款在JDK 11中新加入的具有实验性质[1]的低延迟垃圾收集器&#xff0c;是由Oracle公司研发的。 ZGC收集器是一款基于Region内存布局的&#…...

事务管理-事务进阶-propagation属性

目录 事务属性-传播行为 propagation 案例 需求 步骤 具体代码 小结 事务属性-传播行为 propagation 事务传播行为&#xff1a;指的就是当一个事务方法被另一个事务方法调用时&#xff0c;这个事务方法应该如何进行事务控制。即如果事务方法A中调用了事务方法B&#xff0c…...

树多选搜索查询,搜索后选中状态仍保留

<template><div class"half-transfer"><div class"el-transfer-panel"><div><el-checkbox v-model"selectAll" change"handleSelectAll">全部</el-checkbox></div><el-input v-model&qu…...

数据结构--字典树(trie)

概念&#xff1a; Trie 是一种能够快速插入和查询字符串的多叉树结构。、 节点的编号各不相同&#xff0c;根节点编号为0&#xff0c;其他节点用来标识路径&#xff0c;还可以标记单词的插入次数&#xff0c;边表示字符。 tire 维护字符串的集合&#xff0c;支持两种操作&…...