《CUDA编程》3.简单CUDA程序的基本框架

news2025/1/21 4:48:21

本章将学习CUDA程序的基本框架,编写更加有用的CUDA程序

0 C++例子:数组相加

C++代码如下:

#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h> // 包含 time.h 头文件以使用 clock()

const double EPS = 1.0e-15;
const double a = 1.23;
const double b = 2.34;
const double c = 3.57;

void add(const double* x, const double* y, double* z, const int N) {
    for (int n = 0; n < N; ++n) {
        z[n] = x[n] + y[n];
    }
}

void check(const double* z, const int N) {
    bool has_error = false;
    for (int n = 0; n < N; ++n) {
        if (fabs(z[n] - c) > EPS) {//判断浮点数是否相等时,不能用==,而应该做差,判断差值是否小于某个值
            has_error = true;
        }
    }
    printf("Has error: %d\n", has_error);
}

int main(void) {
    const int N = 100000000;
    const int M = sizeof(double) *N;

    double* x = (double*)malloc(M);
    double* y = (double*)malloc(M);
    double* z = (double*)malloc(M);

    // 记录程序开始时间
    clock_t start = clock();

    for (int n = 0; n < N; ++n) {
        x[n] = a;
        y[n] = b;
    }

    add(x, y, z, N);
    check(z, N);

    // 记录程序结束时间
    clock_t end = clock();

    // 计算并打印程序运行时间
    double cpu_time_used = ((double)(end - start)) / CLOCKS_PER_SEC;
    printf("Time used: %f seconds\n", cpu_time_used);

    free(z);
    free(x);
    free(y);

    return 0;
}

输出结果为
在这里插入图片描述
其中

    const int N = 100000000; //定义数组的长度为10的8次方
    const int M = sizeof(double) *N; // 每个数组所需的字节数

    double* x = (double*)malloc(M); // 分配内存
    double* y = (double*)malloc(M); // 分配内存
    double* z = (double*)malloc(M); // 分配内存

会创建3个长度为10的8次方的一维数组,每个数组大约占用 800 MB 的内存,总共需要约 2.4 GB 的主机内存来存储这三个数组,同样的后面的CUDA程序也需要2.4GB的GPU内存,如果不足,请自行调整

1 CUDA的基本框架

一个典型的基本框架如下图所示:
请添加图片描述
现在我们根据上述框架,将刚刚的C++程序编写为CUDA程序:

#include <cuda.h>
#include <cuda_runtime.h>
#include <math.h>
#include <stdio.h>
#include <time.h> // 包含 time.h 头文件以使用 clock()

const double EPS = 1.0e-15;
const double a = 1.23;
const double b = 2.34;
const double c = 3.57;

// 希望 add 函数在 GPU 上执行
__global__ void add(const double *x, const double *y, double *z);
void check(const double* z, const int N);

int main(void) {
    const int N = 100000000; // 定义数组的长度为 10 的 8 次方
    const int M = sizeof(double) * N; // 每个数组所需的字节数

    // 分配host内存
    double* h_x = (double*)malloc(M); 
    double* h_y = (double*)malloc(M); 
    double* h_z = (double*)malloc(M); 

    // 记录程序开始时间
    clock_t start = clock();

    for (int n = 0; n < N; ++n) {
        h_x[n] = a;
        h_y[n] = b;
    }
	
	//分配device内存
    double* d_x, * d_y, * d_z;
    cudaMalloc((void**)&d_x, M);
    cudaMalloc((void**)&d_y, M);
    cudaMalloc((void**)&d_z, M);

    // 将数据从主机复制到设备上
    cudaMemcpy(d_x, h_x, M, cudaMemcpyHostToDevice);
    cudaMemcpy(d_y, h_y, M, cudaMemcpyHostToDevice);

    const int block_size = 128;
    // 计算网格尺寸,确保所有元素都能被处理
    const int grid_size = (N + block_size - 1) / block_size; 

    // 调用内核函数在设备中进行计算
    add <<<grid_size, block_size>> > (d_x, d_y, d_z);

    // 将计算结果从设备复制回主机
    cudaMemcpy(h_z, d_z, M, cudaMemcpyDeviceToHost);
    check(h_z, N);

    // 记录程序结束时间
    clock_t end = clock();

    // 计算并打印程序运行时间
    double cpu_time_used = ((double)(end - start)) / CLOCKS_PER_SEC;
    printf("Time used: %f seconds\n", cpu_time_used);
	
	// 释放内存
    free(h_x);
    free(h_y);
    free(h_z);
    cudaFree(d_x);
    cudaFree(d_y);
    cudaFree(d_z);
    return 0;
}

__global__ void add(const double* x, const double* y, double* z) {
    const int n = blockIdx.x * blockDim.x + threadIdx.x;
    z[n] = x[n] + y[n];
}

void check(const double* z, const int N) {
    bool has_error = false;
    for (int n = 0; n < N; ++n) {
        if (fabs(z[n] - c) > EPS) {
            has_error = true;
        }
    }
    printf("Has error: %d\n", has_error);
}

输出结果如下:
请添加图片描述

1.1 解释grid_size的计算

const int block_size = 128;
// 计算网格尺寸,确保所有元素都能被处理
const int grid_size = (N + block_size - 1) / block_size; 

其中

  • N:要处理的元素总数
  • block_size:每个block中的线程数,这里是128,可以根据任务进行调整,最大是1024

公式(N + block_size - 1) / block_size是确保N不能被block_size 整除,即确保有足够的线程块来处理元素,下面举例说明:

  1. 整除情况:例如N=1024,block_size=128,则1024/128=8,所以需要8个线程块,则grid_size设置为8
  2. 不能整除:例如N=1025,block_size=128,则1025/128=8余1,所以还需要多一个线程块来处理,但是grid_size是int类型,所以此时也是取8。
    为了避免这种情况,通过加 block_size - 1 来确保 N 在除以 block_size 时总是向上取整

1.2 隐形的device初始化

在CUAD的runtime的API中,没有明显的初始化设备的函数,因为在第一次调用一个设和设备的管理及版本查询功能无关的runtime的API时,device将自动初始化

1.3 device内存的分配与释放

在代码中,我们在hsot和device中分别定义了3个数组,也分别分配了内存和显存
在这里插入图片描述
大家发现第一个指针是一个双重指针,是因为cudaMalloc()的函数原型是
在这里插入图片描述

  • 第一个参数address待分配设备内存的指针,但内存本身就是一个指针,所以该参数其实是指针的指针,所以使用双重指针
  • size是待分配内存的字节数
  • 返回值是一个错误代号。如果调用成功,返回cudaSuccess,否则返回一个错误代码。

看下面这段代码
在这里插入图片描述
虽然double类型占用是8字节,但是为了程序的健壮性和可移植性(有的设备可能不是),所以这里还是使用sizeof(double)

1.4 host和device之间数据的传递

这里使用的是cudaMemcpy()函数,原型是:
在这里插入图片描述

  • dst:目标地址

  • src: 源地址

  • count:复制数据的字节数

  • kind:标志,取以下几个值
    –cudaMemcpyHostToHost,表示从主机复制到主机
    –cudaMemcpyHostToDevice,表示从主机复制到设备
    –cudaMemcpyDeviceToHost,表示从设备复制到主机
    –cudaMemcpyDeviceToDevice,表示从设备复制到设备
    –cudaMemcpyDefault,表示根据dst和src所指的地址自动判断,要求系统有统一寻址功能(unified virtual addressing)

  • 返回值是一个错误代号,如果调用成功,返回cudaSuccess

  • 该函数将一定字节数的数据从源地址所指的缓冲区复制到目标地址所指的缓冲区

所以下列代码的意义是:

cudaMemcpy(d_x, h_x, M, cudaMemcpyHostToDevice);
//将h_x指向的主机内存中的数据,复制到d_x指向的设备内存中去

1.5 核函数中数据与线程的对应

观察下列代码
在这里插入图片描述
使用了一个一维线程块,一共有【10的8次方/128】个线程块,每个线程块有128个线程

再观察下列函数:

这个是C++中写的函数
在这里插入图片描述
这个是CUDA程序中写在device中的函数
在这里插入图片描述
可以发现,将主机函数修改为设备中的核函数,只需要去掉循环即可。

因为在主机函数中,需要依次对数组的元素进行操作,所以会写一个循环。
但是在核函数中,我们使用“单指令— —多线程”的方式进行编写,即每个元素的操作都由单独的一个线程来计算,所以不需要循环,会进行并行执行

注意: 也可以使用循环,只需要设置为<<<1, 1>>>,就可以用一个线程去调用核函数计算

2 核函数的要求

2.1 编写核函数时要注意:

  • 核函数的返回类型必须是void
  • 必须使用__global__,也可以加上其他C++的限定符,如static,次序任意
  • 函数名无特殊要求,支持重载
  • 不支持可变数量的参数列表,参数的个数必须确定
  • 核函数不能成为一个类的成员,通常是用一个包装函数调用核函数,然后将包装函数定义为类的成员
  • 在计算能力3.5之前的,核函数之间不能相互调用
  • 可以向核函数传递非指针变量(如int N),其内容每个线程可见(可使用可访问)
  • 除非使用统一内存编程机制(将在第12章介绍),否则传给核函数的数组(指针)必须指向设备内存

2.2 核函数中if语句的必要性

在这里插入图片描述
该代码中没有使用if语句,但由于线程数是比元素总数多的,所以应该添加一个if语句,来防止数组越界,并且节约不必要的线程计算,修改代码如下:
在这里插入图片描述
运行结果如下,比不加if语句之前快,因为多余的线程没有执行计算操作
在这里插入图片描述

3 自定义设备函数

核函数可以调用不带执行配置的自定义函数,这样的自定义函数称为设备函数(device function),它是在设备中执行,并在设备中被调用的。

与之相比,核函数是在设备中执行,但在主机端被调用的。

3.1 标识符

  • 用__global__修饰的函数称为核函数,一般由主机调用,在设备中执行。如果使用动态并行,则也可以在核函数中调用自己或其他核函数。
  • 用__device__修饰的函数叫称为设备函数,只能被核函数或其他设备函数调用,在设备中执行。
  • 用__host__修饰的函数就是主机端的普通C++函数,在主机中被调用,在主机中执行。对于主机端的函数,该修饰符可省略。因为有时可以用__host__和__device__同时修饰一个函数,使得该函数既是一个C++中的普通函数,又是一个设备函数。这样做可以减少冗余代码。编译器将针对主机和设备分别编译该函数
  • 不能同时用__device__和__global__修饰一个函数,即不能将一个函数同时定义为设备函数和核函数。
  • 不能同时用__host__和__global__修饰一个函数,即不能将一个函数同时定义为主机函数和核函数。
  • 编译器决定把设备函数当作内联函数(inline function)或非内联函数,但可以用修饰符__noinline__建议一个设备函数为非内联函数(编译器不一定接受),也可以用修饰符__forceinline__建议一个设备函数为内联函数

3.2 例子: 为数组相加的核函数定义一个设备函数

(1)返回值

#include <cuda.h>
#include <cuda_runtime.h>
#include <math.h>
#include <stdio.h>
#include <time.h> // 包含 time.h 头文件以使用 clock()

const double EPS = 1.0e-15;
const double a = 1.23;
const double b = 2.34;
const double c = 3.57;

// 希望 add 函数在 GPU 上执行
__global__ void add(const double *x, const double *y, double *z,int N);
__device__ double add1_device(const double x, const double y);
void check(const double* z, const int N);

int main(void) {
    const int N = 100000000; // 定义数组的长度为 10 的 8 次方
    const int M = sizeof(double) * N; // 每个数组所需的字节数

    // 在 host 中分配内存
    double* h_x = (double*)malloc(M); // 分配内存
    double* h_y = (double*)malloc(M); // 分配内存
    double* h_z = (double*)malloc(M); // 分配内存

    // 记录程序开始时间
    clock_t start = clock();

    for (int n = 0; n < N; ++n) {
        h_x[n] = a;
        h_y[n] = b;
    }

    double* d_x, * d_y, * d_z;
    cudaMalloc((void**)&d_x, M);
    cudaMalloc((void**)&d_y, M);
    cudaMalloc((void**)&d_z, M);

    // 将主机上的数据复制到设备上
    cudaMemcpy(d_x, h_x, M, cudaMemcpyHostToDevice);
    cudaMemcpy(d_y, h_y, M, cudaMemcpyHostToDevice);

    const int block_size = 128;
    // 计算网格尺寸,确保所有元素都能被处理
    const int grid_size = (N + block_size - 1) / block_size; 

    // 调用内核函数来执行加法
    add <<<grid_size, block_size>> > (d_x, d_y, d_z,N);

    // 将计算结果从设备复制回主机
    cudaMemcpy(h_z, d_z, M, cudaMemcpyDeviceToHost);
    check(h_z, N);

    // 记录程序结束时间
    clock_t end = clock();

    // 计算并打印程序运行时间
    double cpu_time_used = ((double)(end - start)) / CLOCKS_PER_SEC;
    printf("Time used: %f seconds\n", cpu_time_used);

    free(h_x);
    free(h_y);
    free(h_z);
    cudaFree(d_x);
    cudaFree(d_y);
    cudaFree(d_z);

    return 0;
}

//返回值
__device__ double add1_device(const double x, const double y) {
    return x + y;
}

__global__ void add(const double* x, const double* y, double* z,int N) {
    const int n = blockIdx.x * blockDim.x + threadIdx.x;//会飘红,不影响运行
    // 添加边界检查,确保索引不越界
    if (n < N) {
        z[n] = add1_device(x[n], y[n]);
    }
}

void check(const double* z, const int N) {
    bool has_error = false;
    for (int n = 0; n < N; ++n) {
        if (fabs(z[n] - c) > EPS) {
            has_error = true;
        }
    }
    printf("Has error: %d\n", has_error);
}

(2)指针

#include <cuda.h>
#include <cuda_runtime.h>
#include <math.h>
#include <stdio.h>
#include <time.h> // 包含 time.h 头文件以使用 clock()

const double EPS = 1.0e-15;
const double a = 1.23;
const double b = 2.34;
const double c = 3.57;

// 希望 add 函数在 GPU 上执行
__global__ void add(const double *x, const double *y, double *z,int N);
__device__ void add2_device(const double x, const double y,double *z);
void check(const double* z, const int N);

int main(void) {
    const int N = 100000000; // 定义数组的长度为 10 的 8 次方
    const int M = sizeof(double) * N; // 每个数组所需的字节数

    // 在 host 中分配内存
    double* h_x = (double*)malloc(M); // 分配内存
    double* h_y = (double*)malloc(M); // 分配内存
    double* h_z = (double*)malloc(M); // 分配内存

    // 记录程序开始时间
    clock_t start = clock();

    for (int n = 0; n < N; ++n) {
        h_x[n] = a;
        h_y[n] = b;
    }

    double* d_x, * d_y, * d_z;
    cudaMalloc((void**)&d_x, M);
    cudaMalloc((void**)&d_y, M);
    cudaMalloc((void**)&d_z, M);

    // 将主机上的数据复制到设备上
    cudaMemcpy(d_x, h_x, M, cudaMemcpyHostToDevice);
    cudaMemcpy(d_y, h_y, M, cudaMemcpyHostToDevice);

    const int block_size = 128;
    // 计算网格尺寸,确保所有元素都能被处理
    const int grid_size = (N + block_size - 1) / block_size; 

    // 调用内核函数来执行加法
    add <<<grid_size, block_size>> > (d_x, d_y, d_z,N);

    // 将计算结果从设备复制回主机
    cudaMemcpy(h_z, d_z, M, cudaMemcpyDeviceToHost);
    check(h_z, N);

    // 记录程序结束时间
    clock_t end = clock();

    // 计算并打印程序运行时间
    double cpu_time_used = ((double)(end - start)) / CLOCKS_PER_SEC;
    printf("Time used: %f seconds\n", cpu_time_used);

    free(h_x);
    free(h_y);
    free(h_z);
    cudaFree(d_x);
    cudaFree(d_y);
    cudaFree(d_z);

    return 0;
}

//指针
__device__ void add2_device(const double x, const double y, double* z) {
    *z=x+y;
}

__global__ void add(const double* x, const double* y, double* z,int N) {
    const int n = blockIdx.x * blockDim.x + threadIdx.x;//会飘红,不影响运行
    // 添加边界检查,确保索引不越界
    if (n < N) {
        add2_device(x[n], y[n], &z[n]);
    }
}

void check(const double* z, const int N) {
    bool has_error = false;
    for (int n = 0; n < N; ++n) {
        if (fabs(z[n] - c) > EPS) {
            has_error = true;
        }
    }
    printf("Has error: %d\n", has_error);
}

(3)引用

#include <cuda.h>
#include <cuda_runtime.h>
#include <math.h>
#include <stdio.h>
#include <time.h> // 包含 time.h 头文件以使用 clock()

const double EPS = 1.0e-15;
const double a = 1.23;
const double b = 2.34;
const double c = 3.57;

// 希望 add 函数在 GPU 上执行
__global__ void add(const double *x, const double *y, double *z,int N);
__device__ void add3_device(const double x, const double y,double &z);
void check(const double* z, const int N);

int main(void) {
    const int N = 100000000; // 定义数组的长度为 10 的 8 次方
    const int M = sizeof(double) * N; // 每个数组所需的字节数

    // 在 host 中分配内存
    double* h_x = (double*)malloc(M); // 分配内存
    double* h_y = (double*)malloc(M); // 分配内存
    double* h_z = (double*)malloc(M); // 分配内存

    // 记录程序开始时间
    clock_t start = clock();

    for (int n = 0; n < N; ++n) {
        h_x[n] = a;
        h_y[n] = b;
    }

    double* d_x, * d_y, * d_z;
    cudaMalloc((void**)&d_x, M);
    cudaMalloc((void**)&d_y, M);
    cudaMalloc((void**)&d_z, M);

    // 将主机上的数据复制到设备上
    cudaMemcpy(d_x, h_x, M, cudaMemcpyHostToDevice);
    cudaMemcpy(d_y, h_y, M, cudaMemcpyHostToDevice);

    const int block_size = 128;
    // 计算网格尺寸,确保所有元素都能被处理
    const int grid_size = (N + block_size - 1) / block_size; 

    // 调用内核函数来执行加法
    add <<<grid_size, block_size>> > (d_x, d_y, d_z,N);

    // 将计算结果从设备复制回主机
    cudaMemcpy(h_z, d_z, M, cudaMemcpyDeviceToHost);
    check(h_z, N);

    // 记录程序结束时间
    clock_t end = clock();

    // 计算并打印程序运行时间
    double cpu_time_used = ((double)(end - start)) / CLOCKS_PER_SEC;
    printf("Time used: %f seconds\n", cpu_time_used);

    free(h_x);
    free(h_y);
    free(h_z);
    cudaFree(d_x);
    cudaFree(d_y);
    cudaFree(d_z);

    return 0;
}

//指针
__device__ void add3_device(const double x, const double y, double& z) {
    z=x+y;
}

__global__ void add(const double* x, const double* y, double* z,int N) {
    const int n = blockIdx.x * blockDim.x + threadIdx.x;//会飘红,不影响运行
    // 添加边界检查,确保索引不越界
    if (n < N) {
        add3_device(x[n], y[n], z[n]);
    }
}

void check(const double* z, const int N) {
    bool has_error = false;
    for (int n = 0; n < N; ++n) {
        if (fabs(z[n] - c) > EPS) {
            has_error = true;
        }
    }
    printf("Has error: %d\n", has_error);
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2164951.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Tesla T4 P2P测试

Tesla T4 P2P测试 一.测试环境二.测试步骤1.获取设备信息2.查看PCIE拓扑结构3.选择9B、9E这二张4.查看逻辑设备ID5.设置环境变量(需要用逻辑设备ID,通过UUID跟smi看到的物理ID关联)6.不同地址的原子操作2.P2P与非P2P的性能差异3.GPU带宽测试 Tesla T4 P2P测试 通过物理ID找到逻…

一场大模型面试,三个小时,被撞飞了

去华为面试大模型&#xff0c;一点半去五点半回&#xff0c;已经毫无力气。 1️⃣一轮面试—1小时 因为一面都是各个业务的主管&#xff0c;所以专业性很强&#xff0c;面试官经验很丰富&#xff0c;建议大家还是需要十分熟悉所学内容&#xff0c;我勉强通过一面。 2️⃣二轮…

9_24_statusBar

statusBar&#xff08;状态栏&#xff09; 状态栏就是一个窗口最先面的一行&#xff0c;一般有三个作用&#xff1a; • 永久信息&#xff0c;例如版本号&#xff0c;机构名称 • 进度消息&#xff0c;如进度条提示&#xff0c;百分比提示 • 实时消息&#xff0c;当前程序状态…

神经网络的初步学习

文章目录 概要基础概念简单的神经网络图神经元模型权重求和激活函数输出 多层感知机前向传播&#xff1a;激活函数&#xff1a;误差计算&#xff08;损失函数&#xff09;&#xff1a;反向传播&#xff1a;1. **激活函数&#xff08;Activation Function&#xff09;**2. **非线…

mini-lsm通关笔记Week2Overview

Week 2 Overview: Compaction and Persistence 在上周&#xff0c;您已经实现了LSM存储引擎的所有必要结构&#xff0c;并且您的存储引擎已经支持读写接口。在本周中&#xff0c;我们将深入探讨SST文件的磁盘组织&#xff0c;并研究在系统中实现性能和成本效益的最佳方法。我们…

Skyeye 云这几年的经历

前言 我是 17 年毕业的&#xff0c;之前也是在学校的实验室 (做开发的) 待了两年多时间&#xff0c;期间学了不少东西&#xff0c;学的东西也算是与时俱进了。最近两年也算是开源中国的常客了&#xff0c;每周都会保持自己项目的一个更新进度。 项目地址&#xff1a;skyeye-o…

【SpringCloud】优雅实现远程调用 - OpenFeign

目录 优雅实现远程调用-OpenFeignRestTemplate存在问题OpenFeign介绍Spring Cloud Feign 快速上手引入依赖添加注解编写OpenFeign的客户端远程调用测试 OpenFeign参数传递传递单个参数传递多个参数传递对象传递JSON 最佳实践Feign 继承方式创建⼀个Module引入依赖编写接口打Jar…

ESP32,制作一个遥控点火玩具

最近想做一个遥控点火玩具&#xff0c;过年的时候可以让娃拿出手机遥控点炮&#xff0c;绝对能成为全村最亮的仔。 实际也挺简单&#xff0c;使用的东西有ESP32开发板&#xff0c;1838T红外接收器&#xff0c;一个继电器&#xff0c;一个钨丝。 原理图如下。 GPIO17接红外ou…

夹耳式蓝牙耳机哪个牌子最好?夹耳式耳机推荐性价比排行榜

耳夹式耳机既不堵耳孔、也不需要包覆耳廓&#xff0c;佩戴时看起来更像是一个“耳环”&#xff0c;固定方式也类似“夹耳朵”。不过&#xff0c;它并不是真的夹住了耳朵肉&#xff0c;而是半夹、半挂——依靠耳廓边缘厚、里面薄&#xff0c;且有一定的弯折面的特殊构造&#xf…

Arm Cortex-R52+ Generic Timer分析

目录 1.Generic Timer初识 2.R52的Generic Timer 3.如何配置Timer中断 4.小结 1.Generic Timer初识 Arm Cortex-R52内部实现了Generic Timer(通用计时器)&#xff0c;它可以基于递增计数来产生中断和事件流。 事实上&#xff0c;该计时器和Armv8-R AArch32中的定义完全一…

Python数据分析与可视化:从基础到高级应用

一、引言 在当今数据驱动的时代&#xff0c;数据的分析和可视化变得至关重要。Python作为一种功能强大且广泛使用的编程语言&#xff0c;在数据分析和可视化领域拥有丰富的库和工具。通过Python&#xff0c;数据分析师和科学家能够高效地处理数据、提取有价值的信息并以直观的方…

【网络安全】更改参数实现试用计划延长

未经许可,不得转载。 文章目录 正文目标:example.com,电子商务网站,允许企业在线创建商店。该平台提供了广泛的功能,如商店设计、创建产品等。 正文 在界面 example.com/start-your-trial/ 上,可以创建为期 15 天的试用商店。填写完所有信息后,我点击了“Sign Up”按钮…

BERT训练环节(代码实现)

1.代码实现 #导包 import torch from torch import nn import dltools #加载数据需要用到的声明变量 batch_size, max_len 1, 64 #获取训练数据迭代器、词汇表 train_iter, vocab dltools.load_data_wiki(batch_size, max_len) #其余都是二维数组 #tokens, segments, vali…

一带一路区块链赛项样题解析(中)

一带一路区块链赛项样题解析 (模块二) 标题任务一 按要求完成智能合约开发 1、学籍信息合约(Roll)接口编码(6分) (1)编写学籍信息合约中的RollInfo 实体接口,完成RollInfo实体通用数据的初始化,实现可追溯的学籍信息上链功能;(2分) // SPDX-License-Identifie…

FPGA IP 和 开源 HDL 一般去哪找?

在FPGA开发的世界中&#xff0c;IP核和HDL模块是构建复杂数字系统的基石。它们如同乐高积木&#xff0c;让开发者能够快速搭建和重用经过验证的电路功能。但你是否曾感到迷茫&#xff0c;不知道从哪里寻找这些宝贵的资源&#xff1f;本文将为你揭开寻找FPGA IP核和HDL模块资源的…

探索MemGPT:AI界的新宠儿

文章目录 探索MemGPT&#xff1a;AI界的新宠儿1. 背景介绍2. MemGPT是什么&#xff1f;3. 如何安装MemGPT&#xff1f;4. 简单的库函数使用方法5. 场景应用场景一&#xff1a;创建持久聊天机器人场景二&#xff1a;文档分析场景三&#xff1a;多会话聊天互动 6. 常见Bug及解决方…

【2.使用VBA自动填充Excel工作表】

目录 前言什么是VBA如何使用Excel中的VBA简单基础入门控制台输出信息定义过程&#xff08;功能&#xff09;定义变量常用的数据类型Set循环For To 我的需求开发过程效果演示文件情况测试填充源文件测试填充目标文件 全部完整的代码sheet1中的代码&#xff0c;对应A公司工作表Us…

社区来稿丨一个真正意义上的实时多模态智能体框架,TEN Framework 为构建下一代 AI Agent 而生

本文由 RTE 开发者社区成员通过社区网站投稿提供&#xff0c;如果你也有与实时互动&#xff08;Real-Time Engagement&#xff0c;RTE&#xff09;相关的项目分享&#xff0c;欢迎访问网站 rtecommunity.dev 发布&#xff0c;优秀项目将会在公众号发布分享。 自从 OpenAI 展示了…

大数据毕业设计选题推荐-手机销售数据分析系统-Hive-Hadoop-Spark

✨作者主页&#xff1a;IT毕设梦工厂✨ 个人简介&#xff1a;曾从事计算机专业培训教学&#xff0c;擅长Java、Python、PHP、.NET、Node.js、GO、微信小程序、安卓Android等项目实战。接项目定制开发、代码讲解、答辩教学、文档编写、降重等。 ☑文末获取源码☑ 精彩专栏推荐⬇…

PINN机器学习登上Science正刊!热门buff叠满!11个创新思路get到就能发

今天我们来聊聊物理信息机器学习PIML。PINN大家都熟悉吧&#xff0c;毕竟研究热度就没下去过&#xff0c;这个热点其实就是PIML的一种典型代表。 PIML是一种融合了物理学与机器学习的创新技术&#xff0c;通过引入物理学的先验知识&#xff0c;来改进和优化机器学习模型的性能…