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

Opencl

**OpenCL(Open Computing Language)**是一种用于异构平台(包括CPU、GPU、FPGA、DSP等)上的并行计算框架和编程标准。它由Khronos Group制定,旨在提供一种跨平台、统一的编程接口,使开发者可以利用不同硬件设备进行高性能并行计算。


OpenCL的核心概念

  • 平台(Platform):代表不同厂商的计算平台,如NVIDIA CUDA、AMD GPU、Intel CPU等。
  • 设备(Device):平台下的具体计算硬件,比如GPU、CPU核。
  • 上下文(Context):管理设备和资源的环境。
  • 命令队列(Command Queue):提交任务(如内核执行、内存操作)到设备。
  • 内核(Kernel):在设备上运行的计算函数。
  • 程序对象(Program):包含编译后的内核代码。

OpenCL的使用流程

  1. 获取平台和设备
  2. 创建上下文和命令队列
  3. 编译内核程序(Kernel)代码
  4. 创建缓冲区(Buffer)
  5. 设置内核参数
  6. 运行(Enqueue)内核
  7. 读取结果
  8. 清理释放资源

OpenCL的代码结构通常由两部分组成:主程序代码(Host Program) 和 内核代码(Kernel Code)。它们可以用C或C++编写,取决于你的编译环境和API的使用。

  • OpenCL API本身是基于C的,所以在“主程序”中,通常用C或C++都可以调用。
  • 可以用C++,比如用STL容器、更复杂的封装,也可以用cl.hpp(C++封装版的OpenCL头文件),提供更面向对象的接口。
  • 内核代码通常用C语言风格,代码在.cl文件中。

1. 文件后缀

  • Host程序(主代码):一般用 .c(纯C)或 .cpp(C++)文件编写。例如:

    • main.cpp
    • host.c
  • 内核代码(Device端代码):用特殊的源文件,常用后缀包括:

    • .cl(主要文件扩展名)
    • 也可以用.cpp或其他扩展,不过标准和习惯是用.cl

总结:

  • .cl文件:存放OpenCL内核程序(GPU、CPU上的设备程序)
  • 主程序文件(C或C++):调用OpenCL API,负责加载、编译内核、管理数据等

2. 代码结构

例:典型的OpenCL程序结构(包括两个文件)

a. kernel.cl (内核代码)(在GPU上运行)
// kernel function 向量加法
__kernel void vector_add(__global const float* A, __global const float* B, __global float* C, int N) {int i = get_global_id(0);if (i < N) {C[i] = A[i] + B[i];}
}
b. 主程序用C++调用OpenCL API(在CPU上执行)】
  • 包含OpenCL API调用:
    • 选择平台和设备
    • 创建上下文和命令队列
    • 加载内核代码(读入kernel.cl文件内容)
    • 编译程序
    • 设置参数、分配缓冲区
    • 启动核函数
    • 读出和处理结果

        方法1:

#include <iostream>  
#include <vector>  
#include <fstream>  
#include <streambuf>  
#include <CL/cl.h>  const char* kernel_file = "kernel.cl";  int main() {  // 1. 读取内核源码文件  std::ifstream kernel_stream(kernel_file);  if (!kernel_stream.is_open()) {  std::cerr << "Failed to open kernel file." << std::endl;  return -1;  }  std::string kernel_code((std::istreambuf_iterator<char>(kernel_stream)), std::istreambuf_iterator<char>());  const char* kernel_source = kernel_code.c_str();  // 2. 获取平台  cl_platform_id platform;  clGetPlatformIDs(1, &platform, NULL);  // 3. 获取设备(GPU或CPU)  cl_device_id device;  clGetDeviceIDs(platform, CL_DEVICE_TYPE_DEFAULT, 1, &device, NULL);  // 4. 创建上下文  cl_context context = clCreateContext(NULL, 1, &device, NULL, NULL, NULL);  // 5. 创建命令队列  cl_command_queue queue = clCreateCommandQueue(context, device, 0, NULL);  // 6. 编译内核程序  const size_t source_size = kernel_code.size();  cl_program program = clCreateProgramWithSource(context, 1, &kernel_source, &source_size, NULL);  if (clBuildProgram(program, 1, &device, NULL, NULL, NULL) != CL_SUCCESS) {  // 输出编译错误信息  size_t log_size;  clGetProgramBuildInfo(program, device, CL_PROGRAM_BUILD_LOG, 0, NULL, &log_size);  std::vector<char> build_log(log_size);  clGetProgramBuildInfo(program, device, CL_PROGRAM_BUILD_LOG, log_size, build_log.data(), NULL);  std::cerr << "Error in kernel:\n" << build_log.data() << std::endl;  clReleaseProgram(program);  clReleaseContext(context);  return -1;  }  // 7. 创建内核  cl_kernel kernel = clCreateKernel(program, "vector_add", NULL);  // 8. 创建向量数据  const int N = 1024;  std::vector<float> A(N, 1.0f);  std::vector<float> B(N, 2.0f);  std::vector<float> C(N, 0);  // 9. 创建缓冲区  cl_mem bufA = clCreateBuffer(context, CL_MEM_READ_ONLY | CL_MEM_COPY_HOST_PTR, sizeof(float) * N, A.data(), NULL);  cl_mem bufB = clCreateBuffer(context, CL_MEM_READ_ONLY | CL_MEM_COPY_HOST_PTR, sizeof(float) * N, B.data(), NULL);  cl_mem bufC = clCreateBuffer(context, CL_MEM_WRITE_ONLY, sizeof(float) * N, NULL, NULL);  // 10. 设置内核参数  clSetKernelArg(kernel, 0, sizeof(cl_mem), &bufA);  clSetKernelArg(kernel, 1, sizeof(cl_mem), &bufB);  clSetKernelArg(kernel, 2, sizeof(cl_mem), &bufC);  clSetKernelArg(kernel, 3, sizeof(int), &N);  // 11. 定义全局与本地工作项数  size_t global_size = ((N + 255) / 256) * 256;  // 以256为块大小的倍数  size_t local_size = 256;  // 12. 执行内核  clEnqueueNDRangeKernel(queue, kernel, 1, NULL, &global_size, &local_size, 0, NULL, NULL);  // 13. 读取结果  clEnqueueReadBuffer(queue, bufC, CL_TRUE, 0, sizeof(float) * N, C.data(), 0, NULL, NULL);  // 14. 输出前几个结果验证  std::cout << "C[0] = " << C;// 释放资源clReleaseMemObject(bufA);clReleaseMemObject(bufB);clReleaseMemObject(bufC);clReleaseKernel(kernel);clReleaseProgram(program);clReleaseCommandQueue(queue);clReleaseContext(context);
}

方法2:

#include <CL/cl.h>  
#include <iostream>  
#include <vector>  const char* kernel_source = R"(  
__kernel void vector_add(__global const float* A, __global const float* B, __global float* C, int N) {  int i = get_global_id(0);  if (i < N) {  C[i] = A[i] + B[i];  }  
}  
)";  int main() {  // 1. 获取平台和设备  cl_platform_id platform;  cl_device_id device;  clGetPlatformIDs(1, &platform, NULL);  clGetDeviceIDs(platform, CL_DEVICE_TYPE_GPU, 1, &device, NULL);  // 2. 创建上下文和命令队列  cl_context context = clCreateContext(NULL, 1, &device, NULL, NULL, NULL);  cl_command_queue queue = clCreateCommandQueue(context, device, 0, NULL);  int N = 1024;  std::vector<float> A(N, 1.0f), B(N, 2.0f), C(N);  // 3. 创建缓冲区  cl_mem bufferA = clCreateBuffer(context, CL_MEM_READ_ONLY | CL_MEM_COPY_HOST_PTR, sizeof(float) * N, A.data(), NULL);  cl_mem bufferB = clCreateBuffer(context, CL_MEM_READ_ONLY | CL_MEM_COPY_HOST_PTR, sizeof(float) * N, B.data(), NULL);  cl_mem bufferC = clCreateBuffer(context, CL_MEM_WRITE_ONLY, sizeof(float) * N, NULL, NULL);  // 4. 编译程序  cl_program program = clCreateProgramWithSource(context, 1, &kernel_source, NULL, NULL);  clBuildProgram(program, 0, NULL, NULL, NULL, NULL);  cl_kernel kernel = clCreateKernel(program, "vector_add", NULL);  // 5. 设置内核参数  clSetKernelArg(kernel, 0, sizeof(cl_mem), &bufferA);  clSetKernelArg(kernel, 1, sizeof(cl_mem), &bufferB);  clSetKernelArg(kernel, 2, sizeof(cl_mem), &bufferC);  clSetKernelArg(kernel, 3, sizeof(int), &N);  // 6. 运行内核  size_t global_size = N;  clEnqueueNDRangeKernel(queue, kernel, 1, NULL, &global_size, NULL, 0, NULL, NULL);  // 7. 读取结果  clEnqueueReadBuffer(queue, bufferC, CL_TRUE, 0, sizeof(float) * N, C.data(), 0, NULL, NULL);  // 8. 输出和清理  std::cout << "C[0] = " << C[0] << std::endl;  clReleaseMemObject(bufferA);  clReleaseMemObject(bufferB);  clReleaseMemObject(bufferC);  clReleaseKernel(kernel);  clReleaseProgram(program);  clReleaseCommandQueue(queue);  clReleaseContext(context);  return 0;  

cmake 编译opencl代码

用CMake管理OpenCL项目,包括编译.cl内核文件和保护源代码,主要涉及以下几个方面:


1. 在CMake中如何处理.cl文件

方案一:将.cl文件作为资源文件(非源代码)打包到项目中
  • 将内核代码存放在项目目录内,比如kernels/vector_add.cl
  • 使用configure_file()file()命令,将内核文件复制到输出目录。
  • 在运行时,主程序用文件IO加载vector_add.cl的内容。

示例:CMakeLists.txt

cmake_minimum_required(VERSION 3.14)
project(OpenCLExample)# 添加可执行文件
add_executable(opencl_example main.cpp)# 包含OpenCL头文件路径(根据你的环境调整)
target_include_directories(opencl_example PRIVATE /path/to/OpenCL/headers)# 安装内核源码文件
install(FILES kernels/vector_add.cl DESTINATION ${CMAKE_BINARY_DIR}/kernels)# 在配置阶段复制内核文件到输出目录
configure_file(kernels/vector_add.cl ${CMAKE_BINARY_DIR}/kernels/vector_add.cl COPYONLY)

在代码中加载内核

// 在运行时加载内核文件内容
std::ifstream kernel_file("path/to/kernels/vector_add.cl");
std::string kernel_source((std::istreambuf_iterator<char>(kernel_file)), std::istreambuf_iterator<char>());

或使用与configure_file()相配合的路径。


2. 编译内核文件到二进制(.bin)以保护源代码

  • 可以在CMake中调用clCreateProgramWithBinary(),预编译内核成二进制(.bin文件),避免暴露源码。

步骤

  • 先用clBuildProgram()生成内核二进制(用OpenCL工具或程序)
  • .bin文件存放在项目中
  • 使用clCreateProgramWithBinary()加载预编译的二进制

示例(伪代码)

// 读取内核的二进制文件
std::ifstream bin_file("vector_add.bin", std::ios::binary);
std::vector<unsigned char> binary((std::istreambuf_iterator<char>(bin_file)), std::istreambuf_iterator<char>());
// 传入clCreateProgramWithBinary
cl_program program = clCreateProgramWithBinary(context, 1, &device, &binary.size(), (const unsigned char**)&binary[0], &binary_status, &err);

3. 如何保护代码不泄露?

  • 预编译成二进制(pocl,SPIR-V、OpenCL二进制)
    • 编译内核为二进制文件,这样源代码不会被直接暴露。
  • 代码混淆
    • 不常用,难以实现,效果有限。
  • 硬件保护方案(如GPU的安全特性)
    • 一些GPU厂商提供程序保护,但难以完全防止逆向。

4. 总结

操作说明
.cl源文件处理在CMake中复制到可访问路径,运行时加载,保护较弱
预编译成二进制文件用OpenCL工具或程序编译成.bin文件,运行时用clCreateProgramWithBinary()加载,增强保护
保护源码最好只发行二进制,或者使用硬件/软件级别的保护方案

5.示例

一、项目结构示范
OpenCLProject/
├── CMakeLists.txt
├── src/
│   └── main.cpp
└── kernels/└── vector_add.cl
二、操作流程和示例
1. 准备OpenCL内核源代码

**kernels/vector_add.cl**内容:

__kernel void vector_add(__global const float* A, __global const float* B, __global float* C, int N) {int i = get_global_id(0);if (i < N) {C[i] = A[i] + B[i];}
}
2. 预编译内核,生成二进制(二进制保护)

方法:

  • 使用官方OpenCL SDK提供的工具(如clBuildProgram后用clGetProgramInfo()CL_PROGRAM_BINARY_SIZESCL_PROGRAM_BINARIES)在程序运行时生成
  • 或者用OpenCL API在代码运行时将源编译为二进制文件(clCompileProgram)简便方案:
  • 用OpenCL程序在第一次运行时,将clBuildProgram的二进制保存到文件

示例:在main.cpp中添加代码(仅做提示,实际在项目中编写)

// 省略初始化(平台、设备、上下文)...
cl_program program = clCreateProgramWithSource(context, 1, &kernel_source, &source_size, &err);
clBuildProgram(program, 1, &device, NULL, NULL, NULL);// 获取二进制
size_t binary_size;
clGetProgramInfo(program, CL_PROGRAM_BINARY_SIZES, sizeof(size_t), &binary_size, NULL);
unsigned char* binary = new unsigned char[binary_size];
unsigned char* binaries[] = {binary};
clGetProgramInfo(program, CL_PROGRAM_BINARIES, sizeof(unsigned char*), &binaries, NULL);// 保存二进制到文件
std::ofstream bin_file("vector_add.bin", std::ios::binary);
bin_file.write((char*)binary, binary_size);
bin_file.close();delete[] binary;
clReleaseProgram(program);

此文件vector_add.bin可以在部署时分发,用于替换源代码,避免泄露。


3. 在正式运行时用二进制程序

用二进制加载(示例代码片段)

// 加载二进制文件
std::ifstream bin_file("vector_add.bin", std::ios::binary);
size_t bin_size;
bin_file.seekg(0, std::ios::end);
bin_size = bin_file.tellg();
bin_file.seekg(0, std::ios::beg);
unsigned char* binary_data = new unsigned char[bin_size];
bin_file.read((char*)binary_data, bin_size);
bin_file.close();cl_int binary_status;
cl_program bin_program = clCreateProgramWithBinary(context, 1, &device, &bin_size, (const unsigned char**)&binary_data, &binary_status, &err);
delete[] binary_data;clBuildProgram(bin_program, 0, NULL, NULL, NULL, NULL);
cl_kernel kernel_bin = clCreateKernel(bin_program, "vector_add", NULL);

完整main.cpp

支持两种方式:一次性加载内核源码执行,以及加载预编译的二进制文件(保护源码)。

#include <CL/cl.h>  
#include <iostream>  
#include <vector>  
#include <fstream>  
#include <string>  // 选择使用源码或二进制  
const bool USE_BINARY = true; // 设置为true加载二进制,为false加载源码  const std::string kernel_source_file = "kernels/vector_add.cl";  
const std::string kernel_binary_file = "vector_add.bin";  int main() {  cl_int err;  // 1. 获取平台  cl_platform_id platform;  clGetPlatformIDs(1, &platform, NULL);  // 2. 获取设备  cl_device_id device;  clGetDeviceIDs(platform, CL_DEVICE_TYPE_GPU, 1, &device, NULL);  // 3. 创建上下文  cl_context context = clCreateContext(NULL, 1, &device, NULL, NULL, &err);  // 4. 创建命令队列  cl_command_queue queue = clCreateCommandQueue(context, device, 0, &err);  // 5. 加载内核程序  cl_program program;  if (USE_BINARY) {  // 载入二进制  std::ifstream bin_file(kernel_binary_file, std::ios::binary);  if (!bin_file.is_open()) {  std::cerr << "Failed to open kernel binary: " << kernel_binary_file << std::endl;  return -1;  }  size_t bin_size;  bin_file.seekg(0, std::ios::end);  bin_size = bin_file.tellg();  bin_file.seekg(0, std::ios::beg);  std::vector<unsigned char> binary_data(bin_size);  bin_file.read((char*)binary_data.data(), bin_size);  bin_file.close();  const unsigned char* binaries[] = { binary_data.data() };  program = clCreateProgramWithBinary(context, 1, &device, &bin_size, binaries, NULL, &err);  if (err != CL_SUCCESS) {  std::cerr << "Failed to create program with binary" << std::endl;  return -1;  }  // 编译二进制(某些平台可能不需要)  err = clBuildProgram(program, 1, &device, NULL, NULL, NULL);  if (err != CL_SUCCESS) {  size_t log_size;  clGetProgramBuildInfo(program, device, CL_PROGRAM_BUILD_LOG, 0, NULL, &log_size);  std::vector<char> log(log_size);  clGetProgramBuildInfo(program, device, CL_PROGRAM_BUILD_LOG, log_size, log.data(), NULL);  std::cerr << "Build log:\n" << log.data() << std::endl;  return -1;  }  } else {  // 载入源码  std::ifstream kernel_file(kernel_source_file);  if (!kernel_file.is_open()) {  std::cerr << "Failed to open kernel file" << std::endl;  return -1;  }  std::string kernel_code((std::istreambuf_iterator<char>(kernel_file)), std::istreambuf_iterator<char>());  const char* kernel_source = kernel_code.c_str();  program = clCreateProgramWithSource(context, 1, &kernel_source, NULL, &err);  if (err != CL_SUCCESS) {  std::cerr << "Failed to create program with source" << std::endl;  return -1;  }  // 编译程序  if (clBuildProgram(program, 1, &device, NULL, NULL, NULL) != CL_SUCCESS) {  size_t log_size;  clGetProgramBuildInfo(program, device, CL_PROGRAM_BUILD_LOG, 0, NULL, &log_size);  std::vector<char> log(log_size);  clGetProgramBuildInfo(program, device, CL_PROGRAM_BUILD_LOG, log_size, log.data(), NULL);  std::cerr << "Build log:\n" << log.data() << std::endl;  return -1;  }  }  // 6. 创建核函数  cl_kernel kernel = clCreateKernel(program, "vector_add", &err);  if (err != CL_SUCCESS) {  std::cerr << "Failed to create kernel" << std::endl;  return -1;  }  // 7. 准备数据  const int N = 1024;  std::vector<float> A(N, 1.0f);  


三、CMakeLists.txt配置示例
cmake_minimum_required(VERSION 3.14)
project(OpenCLExample)# 查找OpenCL
find_package(OpenCL REQUIRED)# 添加可执行文件
add_executable(opencl_example src/main.cpp)# 添加源码路径(要根据实际路径调整)
target_include_directories(opencl_example PRIVATE ${OPENCL_INCLUDE_DIRS})
target_link_libraries(opencl_example PRIVATE ${OPENCL_LIBRARIES})# 复制kernel文件到构建目录(可选)
configure_file(kernels/vector_add.cl ${CMAKE_BINARY_DIR}/kernels/vector_add.cl COPYONLY)

四、总结
  • 开发时:用.cl源码工程方便调试。
  • 发布时:用OpenCL API在程序中生成二进制文件,存为.bin,并加载二进制,避免源码泄露。
  • CMake主要负责布局和资源管理,不涉及二进制生成的细节,但可以利用 configure_file() 复制资源。

OPENCL编译模式

在编译OpenCL程序时,特别是在使用CMake或其他构建工具,对OpenCL内核进行预编译或处理时,通常会涉及一些标志或参数。这些缩写(IL、BC、CL、CLS)代表不同的概念,主要与OpenCL内核的中间表示和二进制格式有关:


1. IL(Intermediate Language,中间语言)

  • 定义:OpenCL的内核可以被编译成一种中间表示(Intermediate Language),类似于LLVM IR或SPIR-V,方便在不同硬件和驱动之间进行移植和优化。
  • 作用:IL是一种平台无关的“中间码”,可以被存储和传输,然后再在目标设备上被编译成硬件特定的机器码。
  • 使用场景:通常在预编译、特定平台支持或多通用硬件环境中使用。

2. BC(Binary Code,二进制代码)

  • 定义:这是OpenCL内核被编译后生成的二进制格式,比如为特定GPU/CPU生成的专用机器代码。
  • 区别:比中间语言更接近设备底层,可以直接加载到设备上执行。
  • 用途:减少运行时编译时间,保护内核源码。

3. CL(OpenCL C代码)

  • 定义:这是OpenCL标准定义的C语言风格的内核源代码(.cl文件)。
  • 处理方式:需要在运行时由OpenCL驱动或API(如clBuildProgram())编译成设备可执行二进制或中间格式。
  • 扩展:当你用“CL”作为参数或标志,通常指操作的目标是OpenCL的源代码或相关操作。

4. CLS(OpenCL C Source)

  • 定义:即OpenCL的C源代码,也就是.cl文件,纯文本的源代码形式。
  • 作用:这是最原始的代码形式,用于开发和调试。

总结区别与用途:

表格

缩写全名类型描述
ILIntermediate Language中间语言(中间表示)跨平台、中立的中间码,方便后续优化和转换
BCBinary Code二进制代码设备专用、编译后生成的二进制形式,直接加载执行
CLOpenCL C Source源代码(.cl开发时的源代码,需要在运行时编译成可执行形式
CLSOpenCL C Source与CL类似,纯文本源代码通常指源代码文件,也可以理解为直接的好理解的源码文本

怎么在CMake或者编译流程中使用?

  • 源代码(.clCLS):用clCreateProgramWithSource加载,然后运行clBuildProgram编译成二进制。
  • 预编译二进制(.bc.bin, IL):用clCreateProgramWithBinary,直接加载预先编译好的二进制(推荐保护代码,提升加载速度)
  • 生成IL或BC:可以在不同平台用工具链(如XLA、SPIR-V工具)或在运行时由API自动生成。
  • 如果你的目标是保护源码,建议预编译成二进制(BC/IL),在加载时用clCreateProgramWithBinary()

RK系列芯片上怎么使用opencl

在RK(Rockchip)系列芯片上使用OpenCL,通常需要满足以下几个条件和步骤:


一、确认支持情况

  • 硬件支持:
    RK系列芯片(如RK3399、RK3566等)多配备ARM Mali GPU或Mali兼容的OpenCL驱动支持。
  • 软件支持:
    需要安装支持的OpenCL平台驱动(通常是Mali的OpenCL SDK或Rockchip的专用驱动包)。

二、获取和安装OpenCL驱动

  1. 获取官方驱动:
    • 你可以从Rockchip官网或者芯片供应商那里获取专用的OpenCL SDK或驱动包,通常包含动态库(libOpenCL.so)、头文件和示例。
  2. 实现或集成驱动:
    • 使用来自Rockchip的预编译OpenCL库,或者自己编译Mali的OpenCL实现(如果你拥有源代码和编译环境)。
  3. 配置环境:
    • 将驱动库(libOpenCL.so)放在系统库路径(如 /usr/lib/ 或 /usr/local/lib/)。
    • 可能需要设置环境变量,比如 LD_LIBRARY_PATH=/usr/local/lib

三、使用OpenCL开发

  1. 编写应用程序:

    • 你可以用标准OpenCL API(和之前讲解的一样)进行开发。
  2. 运行时环境:

    • 确保运行环境中有匹配的libOpenCL.so库,应用程序在启动时会识别到GPU的OpenCL平台。
  3. 验证支持:

    • 运行简单的OpenCL程序(如列出平台和设备)确认支持。
// 简单示例:列出平台和设备
cl_platform_id platform;
clGetPlatformIDs(1, &platform, NULL);cl_device_id device;
clGetDeviceIDs(platform, CL_DEVICE_TYPE_GPU, 1, &device, NULL);// 打印设备信息
char buffer[256];
clGetDeviceInfo(device, CL_DEVICE_NAME, sizeof(buffer), buffer, NULL);
printf("Device: %s\n", buffer);

四、特定驱动可能的注意事项

  • 驱动版本兼容:
    需要确保OpenCL驱动版本与你的硬件和软件环境匹配,否则可能无法正确加载或运行。

  • 硬件优化:
    利用Mali GPU的特性优化内核代码,以获得更好的性能。

  • 调试和调优:
    使用厂商提供的工具或OpenCL调试工具监测性能。


五、总结

表格

步骤说明
获取驱动从Rockchip或Mali提供商下载官方OpenCL SDK或驱动包
安装驱动将库文件放到系统路径,配置好环境变量
开发应用使用OpenCL API(如前述代码示例)编写程序
运行测试运行示例程序确认支持,开始GPU加速

相关文章:

Opencl

**OpenCL&#xff08;Open Computing Language&#xff09;**是一种用于异构平台&#xff08;包括CPU、GPU、FPGA、DSP等&#xff09;上的并行计算框架和编程标准。它由Khronos Group制定&#xff0c;旨在提供一种跨平台、统一的编程接口&#xff0c;使开发者可以利用不同硬件设…...

如何在 HTML 中添加按钮

原文&#xff1a;如何在 HTML 中添加按钮 | w3cschool笔记 &#xff08;请勿将文章标记为付费&#xff01;&#xff01;&#xff01;&#xff01;&#xff09; 在网页开发中&#xff0c;按钮是用户界面中不可或缺的元素之一。无论是用于提交表单、触发动作还是导航&#xff0…...

【优秀三方库研读】quill 开源库中的命名空间为什么要用宏封装

将命名空间封装成宏的作用与优势 QUILL_BEGIN_NAMESPACE 和 QUILL_END_NAMESPACE 这种宏封装是 C++ 库开发中的常见技巧,主要解决以下问题并提供显著优势: 1. 解决核心问题:命名空间嵌套与版本控制 问题场景: 库需要支持多版本共存(如 quill::v1, quill::v2),但希望默认…...

AlphaFold3运行错误及解决方法(1)

1. chemical_component_sets.pickle 运行alphafold3遇到下面的问题: FileNotFoundError: [Errno 2] No such file or directory: /xxx/xxx/anaconda3/envs/alphafold3/lib/python3.11/site-packages/alphafold3/constants/converters/chemical_component_sets.pickle搜索你的系…...

Linux--进程的程序替换

问题导入&#xff1a; 前面我们知道了&#xff0c;fork之后&#xff0c;子进程会继承父进程的代码和“数据”&#xff08;写实拷贝&#xff09;。 那么如果我们需要子进程完全去完成一个自己的程序怎么办呢&#xff1f; 进程的程序替换来完成这个功能&#xff01; 1.替换原理…...

调教 DeepSeek - 输出精致的 HTML MARKDOWN

【序言】 不知道是不是我闲的蛋疼&#xff0c;对百度AI 和 DeepSeek 的回答都不太满意。 DeepSeek 回答句子的引用链接&#xff0c;始终无法准确定位。有时链接只是一个域名&#xff0c;有时它给的链接是搜索串如: baidu.com/?q"搜索内容"。 百度AI 回答句子的引用…...

【笔记】Windows系统部署suna基于 MSYS2的Poetry 虚拟环境backedn后端包编译失败处理

基于 MSYS2&#xff08;MINGW64&#xff09;中 Python 的 Poetry 虚拟环境包编译失败处理笔记 一、背景 在基于 MSYS2&#xff08;MINGW64&#xff09;中 Python 创建的 Poetry 虚拟环境里&#xff0c;安装 Suna 开源项目相关包时编译失败&#xff0c;阻碍项目正常部署。 后端…...

GQA(Grouped Query Attention):分组注意力机制的原理与实践《一》

GQA&#xff08;Grouped Query Attention&#xff09;是近年来在大语言模型中广泛应用的一种注意力机制优化方法&#xff0c;最初由 Google 在 2023 年提出。它是对 Multi-Query Attention (MQA) 的扩展&#xff0c;旨在平衡模型性能与计算效率。 &#x1f31f; GQA 是什么&…...

【深度学习优化算法】02:凸性

【作者主页】Francek Chen 【专栏介绍】 ⌈ ⌈ ⌈PyTorch深度学习 ⌋ ⌋ ⌋ 深度学习 (DL, Deep Learning) 特指基于深层神经网络模型和方法的机器学习。它是在统计机器学习、人工神经网络等算法模型基础上&#xff0c;结合当代大数据和大算力的发展而发展出来的。深度学习最重…...

JAVA国际版一对一视频交友视频聊天系统源码支持H5+APP

全球畅连无界社交&#xff1a;JAVA国际版一对一视频交友系统源码&#xff08;H5APP双端覆盖&#xff09; 在全球化社交需求激增的今天&#xff0c;构建一个支持多语言、适配国际支付且功能丰富的视频交友平台&#xff0c;成为出海创业者和企业的核心诉求。JAVA国际版一对一视频…...

策略公开了:年化494%,夏普比率5.86,最大回撤7% | 大模型查询akshare,附代码

原创内容第907篇&#xff0c;专注智能量化投资、个人成长与财富自由。 这位兄弟的策略公开了&#xff0c;年化494%&#xff0c;夏普比率5.86&#xff0c;最大回撤7%&#xff0c;欢迎大家前往围观&#xff1a; http://www.ailabx.com/strategy/683ed10bdabe146c4c0b2293 系统代…...

【C++】string类的模拟实现(详解)

文章目录 上文链接一、整体框架二、构造函数1. default2. copy3. range 三、析构函数四、拷贝构造(1) 传统写法(2) 现代写法 五、赋值重载(1) 传统写法(2) 现代写法 六、获取元素1. operator[ ] 七、迭代器1. begin2. end 八、容量相关1. size2. reserve3. clear 九、修改操作1…...

业界宽松内存模型的不统一而导致的软件问题, gcc, linux kernel, JVM

当不同CPU厂商未能就统一的宽松内存模型&#xff08;Relaxed Memory Model&#xff09;达成一致&#xff0c;很多软件的可移植性会收到限制或损害&#xff0c;主要体现在以下几个方面&#xff1a; 1. 可能的理论限制 1.1. 并发程序的行为不一致 现象上&#xff0c;同一段多线程…...

多模态大语言模型arxiv论文略读(101)

ML-Mamba: Efficient Multi-Modal Large Language Model Utilizing Mamba-2 ➡️ 论文标题&#xff1a;ML-Mamba: Efficient Multi-Modal Large Language Model Utilizing Mamba-2 ➡️ 论文作者&#xff1a;Wenjun Huang, Jiakai Pan, Jiahao Tang, Yanyu Ding, Yifei Xing, …...

量化Quantization初步之--带量化(QAT)的XOR异或pyTorch版250501

量化(Quantization)这词儿听着玄&#xff0c;经常和量化交易Quantitative Trading (量化交易)混淆。 其实机器学习(深度学习)领域的量化Quantization是和节约内存、提高运算效率相关的概念&#xff08;因大模型的普及&#xff0c;这个量化问题尤为迫切&#xff09;。 揭秘机器…...

Linux Maven Install

在 CentOS&#xff08;例如 CentOS 7 或 CentOS 8&#xff09;中安装 Maven&#xff08;Apache Maven&#xff09;的方法主要有两种&#xff1a;使用包管理器&#xff08;简单但可能版本较旧&#xff09;&#xff0c;或者手动安装&#xff08;推荐&#xff0c;可获得最新版&…...

#Java篇:学习node后端之sql常用操作

学习路线 1、javascript基础&#xff1b; 2、nodejs核心模块 fs: 文件系统操作 path: 路径处理 http / https: 创建服务器或发起请求 events: 事件机制&#xff08;EventEmitter&#xff09; stream: 流式数据处理 buffer: 处理二进制数据 os: 获取操作系统信息 util: 工具方…...

电网“逆流”怎么办?如何实现分布式光伏发电全部自发自用?

2024年10月9日&#xff0c;国家能源局综合司发布了《分布式光伏发电开发建设管理办法&#xff08;征求意见稿&#xff09;》&#xff0c;意见稿规定了户用分布式光伏、一般工商业分布式光伏以及大型工商业分布式光伏的发电上网模式&#xff0c;当选择全部自发自用模式时&#x…...

如何查看电脑电池性能

检查电脑电池性能的方法如下&#xff1a; 按下winR键&#xff0c;输入cmd回车&#xff0c;进入命令行窗口 在命令行窗口输入powercfg /batteryreport 桌面双击此电脑&#xff0c;把刚刚复制的路径粘贴到文件路径栏&#xff0c;然后回车 回车后会自动用浏览器打开该报告 红…...

kubernetes》》k8s》》kubectl proxy 命令后面加一个

命令后面加一个& 在Linux终端中&#xff0c;如果在命令的末尾加上一个&符号&#xff0c;这表示将这个任务放到后台去执行 kubectl proxy 官网资料 是 Kubernetes 提供的一个命令行工具&#xff0c;用于在本地和 Kubernetes API Server 之间创建一个安全的代理通道。…...

深入理解Linux系统进程切换

目录 引言 一、什么是进程切换&#xff1f; 二、进程切换的触发条件 三、进程切换的详细步骤 1、保存当前进程上下文&#xff1a; 2、更新进程控制块(PCB)&#xff1a; 3、选择下一个进程&#xff1a; 4、恢复新进程上下文&#xff1a; 5、切换地址空间&#xff1a; 6…...

网络安全运维实训室建设方案

一、网络安全运维人才需求与实训困境 在数字化时代&#xff0c;网络安全已成为国家安全、社会稳定和经济发展的重要基石。随着信息技术的飞速发展&#xff0c;网络安全威胁日益复杂多样&#xff0c;从个人隐私泄露到企业商业机密被盗&#xff0c;从关键基础设施遭受攻击到社会…...

DBeaver 连接mysql报错:CLIENT_PLUGIN_AUTH is required

DBeaver 连接mysql报错&#xff1a;CLIENT_PLUGIN_AUTH is required 一、必须要看这个 >> &#xff1a;参考文献 二、补充 2.1 说明 MySQL5、6这些版本比较老&#xff0c;而DBeaver默认下载的是MySQL8的连接库&#xff0c;所以连接旧版本mysql报错&#xff1a;CLIEN…...

联通专线赋能,亿林网络裸金属服务器:中小企业 IT 架构升级优选方案

在当今数字化飞速发展的时代&#xff0c;中小企业面临着日益增长的业务需求与复杂多变的市场竞争环境。如何构建高效、稳定且具性价比的 IT 架构&#xff0c;成为众多企业突破发展瓶颈的关键所在。而亿林网络推出的 24 核 32G 裸金属服务器&#xff0c;搭配联通专线的千兆共享带…...

Web3时代的数据保护挑战与应对策略

随着互联网技术的飞速发展&#xff0c;我们正步入Web3时代&#xff0c;这是一个以去中心化、用户主权和数据隐私为核心的新时代。然而&#xff0c;Web3时代也带来了前所未有的数据保护挑战。本文将探讨这些挑战&#xff0c;并提出相应的应对策略。 数据隐私挑战 在Web3时代&a…...

Qwen3与MCP协议:重塑大气科学的智能研究范式

在气象研究领域&#xff0c;从海量数据的解析到复杂气候模型的构建&#xff0c;科研人员长期面临效率低、门槛高、易出错的挑战。而阿里云推出的Qwen3大模型与MCP协议的结合&#xff0c;正通过混合推理模式与标准化协同机制&#xff0c;为大气科学注入全新活力。本文将深入解析…...

CppCon 2015 学习:Benchmarking C++ Code

关于性能问题与调试传统 bug&#xff08;如段错误&#xff09;之间差异的分析。以下是对这一页内容的详细解释&#xff1a; 主题&#xff1a;传统问题&#xff08;如段错误&#xff09;调试流程清晰 问题类型&#xff1a;段错误&#xff08;Segmentation Fault&#xff09; …...

URL 结构说明+路由(接口)的认识

一、URL 结构说明 以这个为例&#xff1a;http://127.0.0.1:5000/zhouleifeng 1.组成部分: http://&#xff1a;协议 127.0.0.1&#xff1a;主机&#xff08;本地地址&#xff09; :5000&#xff1a;端口号&#xff08;Flask 默认 5000&#xff09; /zhouleifeng&#xff1a…...

省赛中药检测模型调优

目录 一、baseline性能二、baseline DETR head三、baseline RepC3K2四、baseline RepC3K2 SimSPPF五、baseline RepC3K2 SimSPPF LK-C2PSA界面1.引入库2.读入数据 总结 一、baseline性能 Epoch GPU_mem box_loss cls_loss dfl_loss Instances Size120/120 …...

linux 故障处置通用流程-36计+1计

通用标准处置快速索引 编号 通 用 标 准 处 置 索 引 001 Linux操作系统标准关闭 002 Linux操作系统标准重启 003 Linux操作系统强行关闭 004 Linux操作系统强行重启 005 检查Linux操作系统CPU负载 006 查询占用CPU资源最多的进程 007 检查Linux操…...