【2024第一期CANN训练营】3、AscendCL运行时管理

news2024/10/6 23:25:36

文章目录

  • 【2024第一期CANN训练营】3、AscendCL运行时管理
    • 1. 初始化与去初始化
    • 2. 资源申请与释放
      • 2.1 申请流程
      • 2.2 释放流程
      • 2.3 运行模式(可选)
    • 3. 数据传输
      • 3.1 接口调用流程
      • 3.2 主要数据传输场景
        • 1. Host内的数据传输
        • 2. 从Host到Device的数据传输
        • 3. 从Device到Host的数据传输
        • 4. 一个Device内的数据传输
        • 5. 两个Device间的数据传输
    • 4. Stream管理
      • 4.1 单线程单Stream
      • 4.2 单线程多Stream
      • 4.3 多线程多Stream
    • 5. 多Device切换
      • 5.1 多Device切换关键接口
      • 5.2 多Device切换流程
    • 6. 同步等待
      • 6.1 Event的同步等待
      • 6.2 Stream内任务的同步等待
      • 6.3 Stream间任务的同步等待
      • 6.4 Device的同步等待

【2024第一期CANN训练营】3、AscendCL运行时管理

本教程将介绍如何使用昇腾社区AscendCL应用开发接口进行运行时管理。并从AscendCL的初始化与去初始化、运行管理资源的申请与释放、数据传输、Stream管理、多Device切换以及同步等待等关键步骤来展开内容

以下教程涉及的所有代码都可以从样例介绍中获得

1. 初始化与去初始化

在使用AscendCL进行开发前,首先需要初始化AscendCL环境。这可以通过调用aclInit接口来完成。如果默认配置已满足需求,可以直接传入NULL。

aclError ret = aclInit(NULL);

完成所有AscendCL调用后,需要调用aclFinalize接口去初始化AscendCL。

ret = aclFinalize();

2. 资源申请与释放

运行管理资源包括Device、Context和Stream。申请和释放这些资源的顺序很重要。

2.1 申请流程

  1. 使用aclrtSetDevice显式指定运算的Device。
  2. 通过aclrtCreateContext创建Context。
  3. 使用aclrtCreateStream创建Stream。

示例代码:

// 初始化变量
int32_t deviceId=0 ;
aclrtContext context;
aclrtStream stream;

aclError ret = aclrtSetDevice(deviceId);
ret = aclrtCreateContext(&context, deviceId);
ret = aclrtCreateStream(&stream);

2.2 释放流程

  1. 使用aclrtDestroyStream销毁Stream。
  2. 通过aclrtDestroyContext销毁Context。
  3. 调用aclrtResetDevice重置Device。

示例代码:

ret = aclrtDestroyStream(stream);
ret = aclrtDestroyContext(context);
ret = aclrtResetDevice(deviceId);

2.3 运行模式(可选)

获取当前昇腾AI软件栈的运行模式,根据不同的运行模式,后续的接口调用方式不同

  • 如果查询结果为ACL_HOST,则数据传输时涉及申请Host上的内存。
  • 如果查询结果为ACL_DEVICE,则数据传输时仅需申请Device上的内存。
aclrtRunMode runMode;
extern bool g_isDevice;

ret = aclrtGetRunMode(&runMode);
g_isDevice = (runMode == ACL_DEVICE);

3. 数据传输

数据传输包含申请内存,将数据读入内存,内存复制三个环节

3.1 接口调用流程

  1. 申请内存
    • 在Host上申请内存可以使用C++标准库中的newmalloc,或者使用AscendCL提供的aclrtMallocHost接口。
    • 在Device上申请内存则需要使用aclrtMalloc接口。
  2. 将数据读入内存:用户需要自行管理数据读入内存的实现逻辑。
  3. 内存复制:通过内存复制实现数据传输,可以选择同步或异步内存复制。
    • 同步内存复制使用aclrtMemcpy接口
    • 异步内存复制使用aclrtMemcpyAsync接口,并配合aclrtSynchronizeStream接口实现同步等待。

3.2 主要数据传输场景

1. Host内的数据传输

当前仅支持调用aclrtMemcpy接口执行同步Host内的内存复制任务,不支持调用aclrtMemcpyAsync接口执行异步Host内的内存复制功能

// 申请内存
uint64_t size = 1 * 1024 * 1024;
void* hostPtrA = NULL;
void* hostPtrB = NULL;
aclrtMallocHost(&hostPtrA, size);
aclrtMallocHost(&hostPtrB, size);

// 向内存中读入数据
ReadFile(fileName, hostPtrA, size);

// 同步内存复制
aclrtMemcpy(hostPtrB, size, hostPtrA, size, ACL_MEMCPY_HOST_TO_HOST);

// 释放资源
aclrtFreeHost(hostPtrA);
aclrtFreeHost(hostPtrB);
2. 从Host到Device的数据传输

既支持同步内存复制,又支持异步内存复制

  • 同步内存复制
// 申请内存
uint64_t size = 1 * 1024 * 1024;
void* hostPtrA = NULL;
void* devPtrB = NULL;
aclrtMallocHost(&hostPtrA, size);
aclrtMalloc(&devPtrB, size, ACL_MEM_MALLOC_NORMAL_ONLY);

// 向内存中读入数据
ReadFile(fileName, hostPtrA, size);

// 同步内存复制
aclrtMemcpy(devPtrB, size, hostPtrA, size, ACL_MEMCPY_HOST_TO_DEVICE);

// 释放资源
aclrtFreeHost(hostPtrA);
aclrtFree(devPtrB);
  • 异步内存复制
// 申请内存
uint64_t size = 1 * 1024 * 1024;
void* hostAddr = NULL;
void* devAddr = NULL;
aclrtMallocHost(&hostAddr, size + 64);
aclrtMalloc(&devAddr, size, ACL_MEM_MALLOC_NORMAL_ONLY);
aclrtStream stream = NULL;
aclrtCreateStream(&stream);

// 获取到64字节对齐的地址
char *hostAlignAddr =(char *)hostAddr + 64 - ((uintptr_t)hostAddr % 64);

// 向内存中读入数据
ReadFile(fileName, hostAlignAddr, size);

// 异步内存复制
aclrtMemcpyAsync(devAddr, size, hostAlignAddr, size, ACL_MEMCPY_HOST_TO_DEVICE, stream);
aclrtSynchronizeStream(stream);

// 释放资源
aclrtDestroyStream(stream);
aclrtFreeHost(hostAddr);
aclrtFree(devAddr);
3. 从Device到Host的数据传输

既支持同步内存复制,又支持异步内存复制

  • 同步内存复制
// 申请内存
uint64_t size = 1 * 1024 * 1024;
void* devPtrA = NULL;
void* hostPtrB = NULL;
aclrtMalloc(&devPtrA, size, ACL_MEM_MALLOC_NORMAL_ONLY);
aclrtMallocHost(&hostPtrB, size);

// 向内存中读入数据
ReadFile(fileName, devPtrA, size);

// 同步内存复制
aclrtMemcpy(hostPtrB, size, devPtrA, size, ACL_MEMCPY_DEVICE_TO_HOST);

// 释放资源
aclrtFree(devPtrA);
aclrtFreeHost(hostPtrB);
  • 异步内存复制
// 申请内存
uint64_t size = 1 * 1024 * 1024;
void* hostAddr = NULL;
void* devAddr = NULL;
aclrtMallocHost(&hostAddr, size + 64);
aclrtMalloc(&devAddr, size, ACL_MEM_MALLOC_NORMAL_ONLY);
aclrtStream stream = NULL;
aclrtCreateStream(&stream);

// 向内存中读入数据
ReadFile(fileName, devAddr, size);

// 获取到64字节对齐的地址
char *hostAlignAddr =(char *)hostAddr + 64 - ((uintptr_t)hostAddr % 64);

// 异步内存复制
aclrtMemcpyAsync(hostAlignAddr, size, devAddr, size, ACL_MEMCPY_DEVICE_TO_HOST, stream);
aclrtSynchronizeStream(stream);

// 释放资源
aclrtDestroyStream(stream);
aclrtFreeHost(hostAddr);
aclrtFree(devAddr);
4. 一个Device内的数据传输
// 申请内存
uint64_t size = 1 * 1024 * 1024;
void* devPtrA = NULL;
void* devPtrB = NULL;
aclrtMalloc(&devPtrA, size, ACL_MEM_MALLOC_NORMAL_ONLY);
aclrtMalloc(&devPtrB, size, ACL_MEM_MALLOC_NORMAL_ONLY);

// 向内存中读入数据
ReadFile(fileName, devPtrA, size);

// 同步内存复制
aclrtMemcpy(devPtrB, size, devPtrA, size, ACL_MEMCPY_DEVICE_TO_DEVICE);

// 异步内存复制
aclrtStream stream;
aclrtCreateStream(&stream);
aclrtMemcpyAsync(devPtrB, size, devPtrA, size, ACL_MEMCPY_DEVICE_TO_DEVICE, stream);
aclrtSynchronizeStream(stream);

// 释放资源
aclrtFree(devPtrA);
aclrtFree(devPtrB);
5. 两个Device间的数据传输
  • Atlas 200/300/500 推理产品上,不支持该功能。
  • Atlas 200/500 A2推理产品,不支持该功能。
// AscendCL初始化
auto ret = aclInit(NULL);

// 查询Device 0和Device 1之间是否支持内存复制
int32_t canAccessPeer = 0;
ret = aclrtDeviceCanAccessPeer(&canAccessPeer, 0, 1);

// 1表示支持内存复制
if (canAccessPeer == 1) {
    // Device 0下的操作,包括内存申请、数据写入、内存复制等
	ret = aclrtSetDevice(0);		
	void *dev0;
	ret = aclrtMalloc(&dev0, 10, ACL_MEM_MALLOC_HUGE_FIRST_P2P);
	ret = aclrtMemset(dev0, 10, 1, 10);
    
	// Device 1下的操作
	ret = aclrtSetDevice(1);
	ret = aclrtDeviceEnablePeerAccess(0, 0);
	void *dev1;
	ret = aclrtMalloc(&dev1, 10, ACL_MEM_MALLOC_HUGE_FIRST_P2P);
	ret = aclrtMemset(dev1, 10, 0, 10);

    // 执行复制,将Device 0上的内存数据复制到Device 1上
    ret = aclrtMemcpy(dev1, 10, dev0, 10, ACL_MEMCPY_DEVICE_TO_DEVICE);
	ret = aclrtResetDevice(1);
    ret = aclrtSetDevice(0);
	ret = aclrtResetDevice(0);
	printf("P2P copy success\n");
} else {
    printf("current device doesn't support p2p feature\n");
}

// AscendCL去初始化
aclFinalize();

注意事项

  • 在进行数据传输时,需要确保内存申请和释放的正确性,避免内存泄漏。
  • 异步内存复制时,需要确保内存地址的64字节对齐。
  • 在使用AscendCL进行数据传输时,应该增加异常处理逻辑,以便于及时发现并解决问题。

4. Stream管理

在AscendCL应用开发中,Stream是任务队列的抽象,用于管理任务的并行执行。理解并有效管理Stream对于提升程序性能和资源利用率至关重要。AscendCL提供了以下几种Stream管理机制:

4.1 单线程单Stream

在单线程环境下,可以创建一个Stream来管理任务的执行。

#include "acl/acl.h"

// 显式创建一个Stream
aclrtStream stream;
aclrtCreateStream(&stream);

// 调用触发任务的接口,传入stream参数
aclrtMemcpyAsync(dstPtr, dstSize, srcPtr, srcSize, ACL_MEMCPY_HOST_TO_DEVICE, stream);

// 调用aclrtSynchronizeStream接口,阻塞应用程序运行,直到指定Stream中的所有任务都完成。
aclrtSynchronizeStream(stream);

// Stream使用结束后,显式销毁Stream
aclrtDestroyStream(stream);

4.2 单线程多Stream

在单线程环境下,可以创建多个Stream来并行执行不同的任务。

#include "acl/acl.h"
int32_t deviceId = 0;
uint32_t modelId1 = 0;
uint32_t modelId2 = 1;

// 显式创建一个Stream
aclrtContext context;
aclrtStream stream1, stream2;

// 创建Context
aclrtCreateContext(&context, deviceId);

// 创建stream1
aclrtCreateStream(&stream1);
// 调用触发任务的接口,例如异步模型推理,任务下发在stream1
aclmdlDataset *input1, *output1;
aclmdlExecuteAsync(modelId1, input1, output1, stream1);

// 创建stream2
aclrtCreateStream(&stream2);
// 调用触发任务的接口,例如异步模型推理,任务下发在stream2
aclmdlDataset *input2, *output2;
aclmdlExecuteAsync(modelId2, input2, output2, stream2);

// 流同步
aclrtSynchronizeStream(stream1);
aclrtSynchronizeStream(stream2);

// 释放资源
aclrtDestroyStream(stream1);
aclrtDestroyStream(stream2);
aclrtDestroyContext(context);

4.3 多线程多Stream

在多线程环境下,每个线程可以创建自己的Stream来执行任务。

#include "acl/acl.h"

void runThread(aclrtStream stream) {
    int32_t deviceId = 0;
    aclrtContext context;

    // 创建Context
    aclrtCreateContext(&context, deviceId);

    // 显式创建一个Stream
    aclrtStream threadStream;
    aclrtCreateStream(&threadStream);

    // 释放资源
    aclrtDestroyStream(threadStream);
    aclrtDestroyContext(context);
}

// 创建2个线程,每个线程对应一个Stream
aclrtStream stream1, stream2;
std::thread t1(runThread, stream1);
std::thread t2(runThread, stream2);

// 显式调用join函数确保结束线程
t1.join();
t2.join();

注意事项

  • 在创建Stream之前,确保已经创建了Context。
  • 在多Stream场景下,注意使用aclrtSynchronizeStream接口来同步任务的执行。
  • 在多线程环境下,确保每个线程正确地创建和销毁自己的Stream和Context。
  • 在程序结束前,确保释放所有Stream和Context资源,避免内存泄漏。

5. 多Device切换

在AscendCL应用开发中,多Device切换是一个重要的特性,它允许开发者在多个昇腾AI处理器(Device)之间高效地切换和管理任务。

下图为:同步等待流程_多Device场景

img

5.1 多Device切换关键接口

  1. aclrtSetCurrentContext:用于切换当前线程的Context,比使用aclrtSetDevice接口效率更高。

  2. aclrtSynchronizeDevice:用于等待特定Device上的所有计算任务结束。

5.2 多Device切换流程

  1. 初始化AscendCL:在使用AscendCL进行任何操作之前,需要先初始化AscendCL。

    aclError ret = aclInit(NULL);
    if (ret != ACL_ERROR_NONE) {
        // 错误处理
    }
    
  2. 创建Context:在多Device环境中,每个Device都有一个Context。需要为每个Device创建一个Context。

    aclrtContext context[DEVICE_NUM];
    for (int i = 0; i < DEVICE_NUM; ++i) {
        ret = aclrtCreateContext(&context[i], i);
        if (ret != ACL_ERROR_NONE) {
            // 错误处理
        }
    }
    
  3. 切换Context和Device:在执行任务时,使用aclrtSetCurrentContext接口切换到相应的Context,从而在对应的Device上执行任务。

    // 假设我们要在Device 1上执行任务
    aclrtSetCurrentContext(context[1]);
    // 执行任务...
    
  4. 等待Device任务完成:在需要等待特定Device上的任务完成时,使用aclrtSynchronizeDevice接口。

    // 等待Device 2上的任务完成
    aclrtSynchronizeDevice(2);
    
  5. 执行任务:在每个Device上执行相应的任务,如模型推理或算子执行。

    // 模型推理示例
    aclmdlExecuteAsync(modelId, input, output, stream);
    
  6. 释放资源:在任务完成后,释放Context和销毁所有资源。

    for (int i = 0; i < DEVICE_NUM; ++i) {
        aclrtDestroyContext(context[i]);
    }
    aclFinalize();
    

注意事项

  • 在多Device环境中,确保为每个Device创建了对应的Context。
  • 使用aclrtSetCurrentContext接口切换Context时,确保当前线程没有其他Device的任务在执行。
  • 在等待Device任务完成时,确保没有其他任务依赖于这些任务的结果,否则可能会导致死锁。
  • 在程序结束前,确保释放所有Context资源,并调用aclFinalize进行AscendCL的清理。

6. 同步等待

在AscendCL应用开发中,同步等待是一个关键的概念,它确保了在异步计算场景下任务的正确执行顺序和资源的正确管理

AscendCL提供了以下同步机制:

  1. Event的同步等待:使用aclrtSynchronizeEvent接口,阻塞应用程序运行,等待Event完成。
  2. Stream内任务的同步等待:使用aclrtSynchronizeStream接口,阻塞应用程序运行,直到指定Stream中的所有任务都完成。
  3. Stream间任务的同步等待:使用aclrtStreamWaitEvent接口,阻塞指定Stream的运行,直到指定的Event完成。
  4. Device的同步等待:使用aclrtSynchronizeDevice接口,阻塞应用程序运行,直到正在运算中的Device完成运算。

6.1 Event的同步等待

#include "acl/acl.h"

// 创建一个Event
aclrtEvent event;
aclrtCreateEvent(&event);

// 显式创建一个Stream
aclrtStream stream;
aclrtCreateStream(&stream);

// 在stream末尾添加了一个event
aclrtRecordEvent(event, stream);

// 阻塞应用程序运行,等待event发生,也就是stream执行完成
aclrtSynchronizeEvent(event);

// 显式销毁资源
aclrtDestroyStream(stream);
aclrtDestroyEvent(event);

6.2 Stream内任务的同步等待

#include "acl/acl.h"

// 显式创建一个Stream
aclrtStream stream;
aclrtCreateStream(&stream);

// 调用触发任务的接口,传入stream参数
aclrtMemcpyAsync(dstPtr, dstSize, srcPtr, srcSize, ACL_MEMCPY_HOST_TO_DEVICE, stream);

// 调用aclrtSynchronizeStream接口,阻塞应用程序运行,直到指定Stream中的所有任务都完成
aclrtSynchronizeStream(stream);

// Stream使用结束后,显式销毁Stream
aclrtDestroyStream(stream);

6.3 Stream间任务的同步等待

#include "acl/acl.h"

// 创建一个Event
aclrtEvent event;
aclrtCreateEvent(&event);

// 创建stream1
aclrtStream s1;
aclrtCreateStream(&s1);

// 创建stream2
aclrtStream s2;
aclrtCreateStream(&s2);

// 在s1末尾添加了一个event
aclrtRecordEvent(event, s1);

// 阻塞s2运行,直到指定event发生,也就是s1执行完成
aclrtStreamWaitEvent(s2, event);

// 显式销毁资源
aclrtDestroyStream(s2);
aclrtDestroyStream(s1);
aclrtDestroyEvent(event);

6.4 Device的同步等待

#include "acl/acl.h"

// 指定device
aclrtSetDevice(0);

// 创建context
aclrtContext ctx;
aclrtCreateContext(&ctx, 0);

// 创建stream
aclrtStream stream;
aclrtCreateStream(&stream);

// 阻塞应用程序运行,直到正在运算中的Device完成运算
aclrtSynchronizeDevice();

// 资源销毁
aclrtDestroyStream(stream);
aclrtDestroyContext(ctx);
aclrtResetDevice(0);

注意事项

  • 在使用同步等待机制时,确保正确地创建和销毁Event和Stream资源。
  • 在多Stream环境中,使用Event和aclrtStreamWaitEvent接口来实现Stream间的同步等待。
  • 在多Device环境中,使用aclrtSynchronizeDevice接口来等待特定Device上的任务完成。
  • 在程序结束前,确保释放所有资源,避免内存泄漏。。

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

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

相关文章

【深度学习】滴滴出行-交通场景目标检测

案例5&#xff1a;滴滴出行-交通场景目标检测 相关知识点&#xff1a;目标检测、开源框架的配置和使用&#xff08;mmdetection, mmcv&#xff09; 1 任务目标 1.1 任务和数据简介 本次案例将使用深度学习技术来完成城市交通场景下的目标检测任务&#xff0c;案例所使用的数…

「全栈」低代码时代开启!页面开发、数据处理、复杂逻辑统统一站搞定!

数字化浪潮的推进让企业对应用开发效率有着愈发严苛的要求。 传统的开发模式&#xff0c;无论是前端开发还是后端处理&#xff0c;都普遍面临周期长、成本高、响应慢、迭代难等问题&#xff0c;由于部分企业长期未进行创新改革&#xff0c;导致每次在新增系统功能时&#xff0…

【源码阅读】Mybatis底层源码分析(详细Debug查看附代码)

一、搭建测试代码框架 &#xff08;代码已提交到github->测试代码&#xff0c;建议结合代码根据本文debug一遍更有利于理解&#xff0c;帮忙点个Star 哈&#xff0c;本人在这里谢谢了&#xff09; 二、猜想Mybatis是如何设计的 从上面的案例中&#xff0c;可以大致可以猜测…

VMware虚拟机硬盘容量扩容方法

扩容后不会影响原文件。亲测有效&#xff0c;高效便捷 - 在关机状态下&#xff0c;先在VM上直接扩容硬盘容量&#xff0c;输入扩容后的硬盘最大容量 注意&#xff0c;如果想在原硬盘上增加容量&#xff0c;需要将原来的快照都删除 - 输入最大磁盘大小 运行虚拟机进入系统&…

【代码】伪标签图像随机生成

这段代码将生成2-4个大小不同的圆形和1-2个大小不同的椭圆形&#xff0c;并确保它们之间以及与背景边界之间不会发生重叠 限制圆形的半径不超过150&#xff0c;第13行 import cv2 import random import os这段代码将生成2-4个大小不同的圆形和1-2个大小不同的椭圆形&#xff0…

代码随想录算法训练营第八天|344.反转字符串、541. 反转字符串II、卡码网:54.替换数字、151.翻转字符串里的单词、卡码网:55.右旋转字符串

题目&#xff1a;344.反转字符串 文章链接&#xff1a;代码随想录 视频链接&#xff1a;LeetCode:344.反转字符串 题目链接&#xff1a;力扣题目链接 图释&#xff1a; // 反转字符串 void reverseString(vector<char>& s) {// 直接使用反转的库函数 reverserev…

数字化转型之于国家:为三驾马车更新马达

随着国民经济和社会发展第十四个五年规划的开启&#xff0c;中国也进入了全面建设社会主义现代化国家的新发 展阶段&#xff0c;未来要在坚持“创新、协调、绿色、开放、共享”的新发展理念下&#xff0c;在质量效益明显提升的基础上实 现经济持续健康发展。持续的发展意味着…

水库大坝安全监测中需要注意的事项

随着经济和社会的发展&#xff0c;水资源的需求也在不断增加。因此&#xff0c;建设水库已成为保障水资源的主要方式之一。然而&#xff0c;随着水库规模的增大和工程的复杂性的增加&#xff0c;水库大坝的安全问题也日益引起重视。为此&#xff0c;需要对水库大坝进行安全监测…

【爬虫】– 抓取原创力文档数据

使用RPA工具&#xff0c;实现针对于原创力中不可下载文档的抓取&#xff0c;可延用于其他类似文库 1 使用工具、环境 影刀RPA、WPS Office、谷歌浏览器&#xff08;非指定&#xff09; 2 代码流程 3 关键点 此方案只适合抓取非VIP即可预览全文的文档&#xff0c;抓取下来的数…

计算机网络 谢希仁(001-1)

计算机网络-方老师 总时长 24:45:00 共50个视频&#xff0c;6个模块 此文章包含1.1到1.4的内容 简介 1.1计算机网络的作用 三网融合&#xff08;三网合一&#xff09; 模拟信号就是连续信号 数字信号是离散信号 1.2互联网概述 以前2兆带宽就要98 现在几百兆带宽也就几百块 …

海川润泽AI机器视觉仪系列产品,助推“人工智能+”打开新质生产力的大门

3月5日&#xff0c;第十四届全国人民代表大会第二次会议开幕。国务院总理李强在政府工作报告&#xff0c;提出大力推进现代化产业体系建设&#xff0c;加快发展新质生产力。深入推进数字经济创新发展&#xff0c;制定支持数字经济高质量发展政策&#xff0c;积极推进数字产业化…

AI+权重衰退

AI权重衰退 1权重衰退2代码实现 2丢弃法 1权重衰退 AI权重衰退是指在人工智能&#xff08;AI&#xff09;领域中的一种技术或方法&#xff0c;用于训练机器学习模型时对权重进行惩罚或调整&#xff0c;以避免过拟合现象的发生。 在机器学习中&#xff0c;过拟合是指模型在训练…

Debug追踪

2.2 Debug追踪 使用IDEA的断点调试功能&#xff0c;查看程序的运行过程 在有效代码行&#xff0c;点击行号右边的空白区域&#xff0c;设置断点&#xff0c;程序执行到断点将停止&#xff0c;我们可以手动来运行程序 点击Debug运行模式 程序停止在断点上不再执行&#xff0c…

C/C++程序设计实验报告3 | 数组实验

本文整理自博主本科大一《C/C程序设计》专业课的课内实验报告&#xff0c;适合C语言初学者们学习、练习。 编译器&#xff1a;gcc 10.3.0 ---- 注&#xff1a; 1.虽然课程名为C程序设计&#xff0c;但实际上当时校内该课的内容大部分其实都是C语言&#xff0c;C的元素最多可能只…

多重背包(二进制优化)

[Acwing 5.多重背包] 有 N N N 种物品和一个容量是 V V V 的背包。 第 i i i 种物品最多有 s i s_i si​ 件&#xff0c;每件体积是 v i v_i vi​&#xff0c;价值是 w i w_i wi​。 求解将哪些物品装入背包&#xff0c;可使物品体积总和不超过背包容量&#xff0c;且…

BUUCTF-----[GXYCTF2019]禁止套娃

题目 目录扫描&#xff0c;扫到.git泄露&#xff0c;使用工具查看到index.php的源码 <?php include "flag.php"; echo "flag在哪里呢&#xff1f;<br>"; if(isset($_GET[exp])){if (!preg_match(/data:\/\/|filter:\/\/|php:\/\/|phar:\/\//i,…

出海品牌必备指南:海外网红营销5大底层逻辑解析

随着全球化的推进&#xff0c;品牌出海已经成为许多企业拓展市场的重要策略之一。在这个过程中&#xff0c;海外网红营销成为品牌吸引目标受众、提升知名度的有效工具。然而&#xff0c;要在海外市场取得成功&#xff0c;并不仅仅是找准网红合作伙伴&#xff0c;更需要深入了解…

全新2024快递平台系统 独立版快递信息查询小程序源码 cps推广营销流量主+前端 同城快递平台

内容目录 一、详细介绍二、效果展示1.部分代码2.效果图展示 三、学习资料下载 一、详细介绍 快递代发快递代寄寄件小程序可以对接易达云洋一级总代 快递小程序&#xff0c;接入云洋/易达物流接口&#xff0c;支持选择快递公司&#xff0c;三通一达&#xff0c;极兔&#xff0c…

鸿蒙Harmony应用开发—ArkTS声明式开发(容器组件:Counter)

计数器组件&#xff0c;提供相应的增加或者减少的计数操作。 说明&#xff1a; 该组件从API Version 7开始支持。后续版本如有新增内容&#xff0c;则采用上角标单独标记该内容的起始版本。 子组件 可以包含子组件。 接口 Counter() 从API version 9开始&#xff0c;该接口…

GIT共享 跨仓库操作 子模块

初级代码游戏的专栏介绍与文章目录-CSDN博客 有些文件想在多个项目共享&#xff0c;但是又不能放在一个GIT仓库里&#xff0c;这要用到子模块&#xff08;submodule&#xff09;&#xff0c;说难不难&#xff0c;就是有些细节要注意。 不过说真的&#xff0c;共享向来不是个好主…