{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "34447ed0",
   "metadata": {},
   "source": [
    "\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c7996ea0",
   "metadata": {},
   "source": [
    "# AscendC编程范式介绍与API介绍\n",
    "在上一节中,我们通过Hello World示例体验了核函数从编写到调用的完整过程。从本节开始,我们将深入Ascend C编程的核心思想与工具集,系统学习其编程范式,特别是矢量编程范式的构建理念与关键组件,并全面了解Ascend C提供的编程接口(API)分类与用途。这些理论知识将为后续实际开发高性能矢量算子奠定坚实基础。\n",
    "\n",
    "本节学习大纲如下:\n",
    "- Ascend C编程范式\n",
    "- Ascend C编程接口概述\n",
    "---"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "799432da",
   "metadata": {},
   "source": [
    "## 1. Ascend C编程范式\n",
    "\n",
    "根据[抽象硬件架构](../01_basic_overview/01.04_ascend_c_op_dev_basic_concepts.ipynb),为了充分发挥硬件的极致算力,AICore内部采用了先进的流水线并行执行架构。多个执行单元并行工作,以一种高效协同、流水作业的方式完成完整的算子执行过程。理解这种硬件工作模式,是掌握Ascend C编程范式的关键。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a7b85f60",
   "metadata": {},
   "source": [
    "如下所示的流水线并行示意图清晰地阐明了这一概念。可以更直观地理解流水并行的概念。示意图中,从输入数据到输出数据需要经过3个阶段任务的处理(T1、T2、T3),多个执行单元并行处理,每个执行单元只会专注于一个任务的处理,会处理所有的数据分片;执行单元完成对某个数据分片的处理后,将其加入到通信队列,下一个执行单元空闲时就会从队列中取出数据继续处理;可以类比为生产流水线中的工人只完成某一项固定工序,完成后就交由下一项工序负责人继续处理。所有产品在工序间流转,从而实现整体处理的高效并行。\n",
    "\n",
    "<img src=\"./images/02.03/pipeline_parallelism_schematic.png\" alt=\"pipeline_parallelism_schematic\"  width=\"700px\" >"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "bcc2aae6",
   "metadata": {},
   "source": [
    "如何清晰地定义各工序?如何安全、高效地在工序间传递半成品数据?如何管理整个流水线共享的资源?\n",
    "\n",
    "Ascend C编程范式正是为解决这些问题而设计的流水线式的编程范式。 它将一个Ascend C算子核函数内的实现,逻辑上划分为多个流水任务(对应流水线上的工序)。任务之间通过专门的队列(TQue) 进行通信和同步,确保数据像在传送带上一样,从一个任务平稳地交付到下一个任务。同时,通过统一的资源管理模块(TPipe),对任务间共享的内存、事件等资源进行生命周期管理和调度,保障整条流水线顺畅、无冲突地运转。\n",
    "\n",
    "因此,基于Ascend C编程范式进行开发,意味着遵循一套与硬件架构深度匹配的固定流程:开发者首先将算子的计算过程解耦为若干流水任务,然后定义连接它们的队列,最后实现每个任务的具体计算逻辑。这套范式屏蔽了底层硬件的并行同步细节,使开发者能够**快速搭建起算子实现的代码框架**,并天然地发挥出AI Core的流水线并行优势。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "9f723bc5",
   "metadata": {},
   "source": [
    "### 1.1 典型算子编程范式\n",
    "\n",
    "在Ascend C的编程实践中,根据算子计算逻辑与数据流复杂度的不同,主要衍生出三种典型的计算范式,它们构成了构建算子的核心模板:\n",
    "- 基本的矢量编程范式:这是最基础、最常用的范式。它将计算流程清晰地划分为三个基本的流水任务:CopyIn(数据搬入)、Compute(矢量计算)、CopyOut(结果搬出)。该范式结构简单,是理解流水线编程思想的起点。\n",
    "\n",
    "    <img src=\"./images/02.03/vector_programming_paradigm.png\" alt=\"vector_programming_paradigm\"  width=\"500px\" >\n",
    "\n",
    "- 基本的矩阵编程范式:适用于处理需要数据分片或归约的矩阵运算。它在矢量范式的基础上进行了扩展,任务流程为:CopyIn(数据搬入)、Split(数据分片)、Compute(子矩阵计算)、Aggregate(结果聚合)、CopyOut(结果搬出)。它在数据拆分与合并阶段提供了更精细的控制。\n",
    "\n",
    "    <img src=\"./images/02.03/cube_programming_paradigm.png\" alt=\"cube_programming_paradigm\"  width=\"500px\" >\n",
    "\n",
    "- 复杂的矢量/矩阵编程范式:用于实现包含多个计算阶段或数据依赖关系更复杂的算子。其核心思想是通过将多个矢量/矩阵的CopyOut与CopyIn任务首尾相连,组合成更长的流水线,例如 CopyIn->Compute->CopyOut/In->Compute->CopyOut,从而实现复杂的数据流图。\n",
    "\n",
    "    <img src=\"./images/02.03/fused_operator_programming_paradigm.png\" alt=\"fused_operator_programming_paradigm\"  width=\"500px\" >"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "9d59652d",
   "metadata": {},
   "source": [
    "\n",
    "### 1.2 矢量编程范式详解\n",
    "对于初次接触Ascend C的开发者而言,掌握基本的矢量编程范式是构建一切复杂算子的基石。它完整包含了流水线编程的核心要素(任务划分、队列通信、资源管理),且结构最为直观。因此,接下来我们将以该范式为蓝本,深入剖析其每个任务的具体职责、实现方法以及各核心组件(TPipe, TQue, TBuf)在其中是如何协同工作的。理解了这一范式,便能触类旁通,为学习更复杂的编程模式打下坚实基础。\n",
    "\n",
    "基本的矢量编程范式将一个算子的核心执行过程,明确划分为三个基本流水任务:CopyIn、Compute和CopyOut。这三个任务通过TQue进行数据接力,并由TPipe统一管理与分配所需的内存及同步资源,构成一个清晰、高效的流水线。\n",
    "\n",
    "<img src=\"./images/02.03/vector_programming_paradigm.png\" alt=\"vector_programming_paradigm\"  width=\"700px\" >\n",
    "\n",
    "各流水任务的具体职责如下:\n",
    "\n",
    "- **CopyIn**负责搬入操作:\n",
    "\n",
    "    将输入数据从Global Memory搬运到Local Memory(VECIN用于表达矢量计算搬入数据的存放位置),完成搬运后执行入队列(EnQue)操作,将数据递交给下游任务;\n",
    "\n",
    "- **Compute**负责矢量指令计算操作:\n",
    "\n",
    "    从上游队列执行出队列(DeQue)操作以获取数据,在Local Memory中进行核心的矢量计算,计算完成后将结果执行入队列(EnQue)操作,传递给下游任务;\n",
    "\n",
    "- **CopyOut**负责搬出操作:\n",
    "\n",
    "    从上游队列执行出队列(DeQue)操作获取计算结果,将计算结果从Local Memory(VECOUT用于表达矢量计算搬出数据的存放位置)搬运到Global Memory,,完成算子的输出。\n",
    "\n",
    "\n",
    "通过这一分解可以看出,矢量编程范式不仅定义了流水任务的划分,更规定了数据流的方向(Global→Local→Compute→Local→Global)与任务间通信与同步的协同机制(通过TQue的EnQue/DeQue)。\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "937ca439",
   "metadata": {},
   "source": [
    "理解基础矢量编程范式的任务划分与数据流后,我们进入其具体实现层面。从编码角度看,实现一个基础的矢量算子遵循一个清晰的设计流程:\n",
    "- 1.内存初始化、创建队列资源(Init):通过TPipe分配任务间队列及临时变量所需内存。\n",
    "- 2.数据搬入(CopyIn):输入数据从Global内存搬运到Local内存\n",
    "- 3.核心计算(Compute):使用Local内存数据执行矢量计算\n",
    "- 4.结果搬出(CopyOut):计算结果从Local内存搬运到Global内存\n",
    "\n",
    "<img src=\"./images/02.03/basic_task_design_of_vector_operator_programming.png\" alt=\"basic_task_design_of_vector_operator_programming\"  width=\"550px\" >"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "edf4a5e2",
   "metadata": {},
   "source": [
    "在Ascend C流水线编程范式中,任务的划分、通信与资源管理依赖于一组精心设计的数据结构。理解核心组件TPipe、TQue以及TBuf、TPosition在范式中的角色与协作关系,从而形成一个完整的实践逻辑:\n",
    "\n",
    "- **GlobalTensor和LocalTensor(数据的载体)**\n",
    "\n",
    "    Ascend C使用GlobalTensor和LocalTensor作为**数据的基本操作单元**,它是各种指令API直接调用的对象,也是数据的载体。\n",
    "\n",
    "    **GlobalTensor**:对应于Global Memory中的数据。\n",
    "\n",
    "    **LocalTensor**:对应于Local Memory中的数据,是任务内部计算和任务间传递的主要载体。\n",
    "\n",
    "- **TPipe(内存管理)**\n",
    "\n",
    "    任务间数据传递使用到的内存统一由内存管理模块Pipe进行管理,一个Kernel函数必须且只能初始化一个TPipe对象。Pipe作为片上内存管理者,通过**InitBuffer**接口,对外提供**Queue**内存初始化功能,开发者可以通过该接口为指定的Queue分配内存。\n",
    "\n",
    "- **TQue(任务间的通信与同步队列)**\n",
    "\n",
    "    不同的流水任务之间存在数据依赖,需要进行数据传递,Ascend C中使用**Queue**队列完成任务之间的数据通信和同步,Queue提供了**EnQue**、**DeQue**、**AllocTensor**、**FreeTensor**等基础API。\n",
    "\n",
    "    - Queue队列内存初始化完成后,需要使用内存时,通过调用**AllocTensor**来为LocalTensor分配内存,当创建的LocalTensor完成相关计算无需再使用时,再调用**FreeTensor**来回收LocalTensor的内存。\n",
    "\n",
    "    <img src=\"./images/02.03/memory_management_schematic.png\" alt=\"memory_management_schematic\"  width=\"500px\">\n",
    "\n",
    "    - 任务A将处理完成的数据放入(EnQue)TQue,任务B从同一个TQue中取出(DeQue)数据继续处理,构成了数据在流水线上的流动。\n",
    "\n",
    "    <img src=\"./images/02.03/queue.png\" alt=\"queue\"  width=\"700px\">\n",
    "\n",
    "- **TBuf(任务内的临时变量内存管理)**\n",
    "\n",
    "    在任务内部进行计算时,可能会用到一些临时变量。这些临时变量占用的内存同样通过Pipe进行管理。为TBuf分配的内存空间**只能参与计算,无法执行Queue队列的入队出队操作**。通过Pipe的**InitBuffer**接口为TBuf进行内存初始化操作,之后即可通过**Get**获取指定长度的Tensor参与计算。\n",
    "\n",
    "- **TPosition(抽象逻辑位置)**\n",
    "\n",
    "    Ascend C管理不同层级的物理内存时,用一种抽象的逻辑位置(TPosition)来**表达各级别的存储,代替了片上物理存储的概念**,从而让开发者无需感知复杂的物理内存层级。在矢量编程中,主要使用:\n",
    "    - **GM**:Global Memory,对应AI Core的外部存储\n",
    "    - **VECIN**:搬入数据的存放位置。\n",
    "    - **VECOUT**:搬出数据的存放位置。\n",
    "    - **VECCALC**:计算需要临时变量的存放位置\n",
    "\n",
    "- **double buffer机制**\n",
    "\n",
    "    执行于AI Core上的指令队列主要包括如下几类,即Vector指令队列、Cube指令队列和MTE指令队列。**不同指令队列间的相互独立性和可并行执行性,是DoubleBuffer优化机制的基石**。\n",
    "\n",
    "    矢量计算CopyIn、CopyOut过程使用MTE指令队列(MTE2、MTE3),Compute过程使用Vector指令队列(V),意味着CopyIn、CopyOut过程和Compute过程是可以并行的。\n",
    "\n",
    "    如下图所示,考虑一个完整的数据搬运和计算过程,CopyIn过程将数据从Global Memory搬运到Local Memory,Vector计算单元完成计算后,经过CopyOut过程将计算结果搬回Global Memory。在此过程中,数据搬运与Vector计算串行执行,Vector计算单元无可避免存在资源闲置问题。\n",
    "    \n",
    "    <img src=\"./images/02.03/data_moving_and_vector_computing_process.png\" alt=\"data_moving_and_vector_computing_process\"  width=\"300px\">\n",
    "\n",
    "    为减少Vector等待时间,DoubleBuffer机制将待处理的数据一分为二,比如Tensor1、Tensor2。如下图所示,当Vector对Tensor1中数据进行Compute时,Tensor2可以执行CopyIn的过程;而当Vector切换到计算Tensor2时,Tensor1可以执行CopyOut的过程。由此,数据的进出搬运和Vector计算**实现并行执行**,Vector闲置问题得以有效缓解。\n",
    "    \n",
    "    <img src=\"./images/02.03/double_buffer_mechanism.png\" alt=\"double_buffer_mechanism\"  width=\"300px\">\n",
    "\n",
    "    总体来说,DoubleBuffer是基于MTE指令队列与Vector指令队列的独立性和可并行性,通过将数据搬运与Vector计算并行执行以隐藏数据搬运时间并降低Vector指令的等待时间,最终提高Vector单元的利用效率,可以通过**为队列申请内存时设置内存块的个数**来实现数据并行,简单代码示例如下:\n",
    "\n",
    "    ```\n",
    "    pipe.InitBuffer(inQueue, 2, 256);\n",
    "    ```\n",
    "\n",
    "    多数情况下,采用DoubleBuffer能有效提升Vector的时间利用率,缩减算子执行时间。然而,DoubleBuffer机制缓解Vector闲置问题并不代表它总能带来整体的性能提升。例如:\n",
    "\n",
    "    -   当数据搬运时间较短,而Vector计算时间显著较长时,由于数据搬运在整个计算过程中的时间占比较低,DoubleBuffer机制带来的性能收益会偏小。\n",
    "    -   又如,当原始数据较小且Vector可一次性完成所有计算时,强行使用DoubleBuffer会降低Vector计算资源的利用率,最终效果可能适得其反。\n",
    "\n",
    "    因此,DoubleBuffer的性能收益需综合考虑Vector算力、数据量大小、搬运与计算时间占比等多种因素。\n",
    "\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "23d9b9fa",
   "metadata": {},
   "source": [
    "基于以上概念,矢量编程范式中三个流水任务的协作与具体操作如下:\n",
    "\n",
    "- Stage 1: **CopyIn**任务\n",
    "\n",
    "    - 使用**DataCopy**接口将输入GlobalTensor拷贝到LocalTensor。\n",
    "\n",
    "    - 使用**EnQue**将该LocalTensor放入VECIN位置的Queue中,通知Compute任务数据就绪。\n",
    "\n",
    "- Stage 2: **Compute**任务\n",
    "\n",
    "    - 使用**DeQue**从VECIN的Queue中取出LocalTensor。\n",
    "\n",
    "    - 使用**Ascend C矢量指令API**(例如Add)进行矢量计算。\n",
    "\n",
    "    - 使用**EnQue**将结果LocalTensor放入VECOUT位置的Queue中,通知CopyOut任务。\n",
    "\n",
    "- Stage 3: **CopyOut**任务\n",
    "\n",
    "    - 使用**DeQue**从VECOUT的Queue中取出结果LocalTensor。\n",
    "\n",
    "    - 使用**DataCopy**接口将该LocalTensor拷贝到输出GlobalTensor。\n",
    "    \n",
    "<img src=\"./images/02.03/vector_programming_paradigm_and_api.png\" alt=\"vector_programming_paradigm_and_api\"  width=\"700px\">"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "bc4b7962",
   "metadata": {},
   "source": [
    "对应伪码实现如下:\n",
    "```\n",
    "AscendC::TPipe pipe;                                // 创建全局的资源管理   \n",
    "AscendC::TQue<AscendC::TPosition::VECIN, 1> queIn;  // 创建CopyIn阶段的队列\n",
    "AscendC::TQue<AscendC::TPosition::VECOUT, 1> queOut;// 创建CopyOut阶段的队列\n",
    "// Init 阶段\n",
    "pipe.InitBuffer(queIn, 2, 1024);                    // 开启DoubleBuffer,将待处理的数据一分为二,实现流水并行\n",
    "pipe.InitBuffer(queOut, 2, 1024);\n",
    "for-loop {\n",
    "    // CopyIn 阶段\n",
    "   {\n",
    "    auto tensor = queIn.AllocTensor<half>();       // 从Que上申请资源, 长度1024\n",
    "    AscendC::DataCopy(tensor, gm, 1024);           // 搬运数据从GM到VECIN\n",
    "    queIn.EnQue(tensor); \n",
    "    }\n",
    "    // Compute阶段\n",
    "   {\n",
    "    auto tensor = queIn.DeQue<half>();\n",
    "    auto tensorOut = queOut.AllocTensor<half>();\n",
    "    AscendC::Abs(tensorOut, tensor, 1024);        // 计算\n",
    "    queIn.FreeTensor(tensor);\n",
    "    queOut.EnQue(tensorOut);\n",
    "    }\n",
    "    // CopyOut 阶段\n",
    "   {\n",
    "    auto tensor = queOut.DeQue<half>();\n",
    "    AscendC::DataCopy(gmOut, tensor, 1024);       // 搬运数据从VECOUT到GM\n",
    "    queOut.FreeTensor(tensor);                    // 释放资源\n",
    "    }\n",
    "}\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "6c38223f",
   "metadata": {},
   "source": [
    "---\n",
    "## 2. Ascend C编程接口概述\n",
    "\n",
    "Ascend C提供一组类库API,开发者使用标准C++语法和类库API进行编程。Ascend C编程类库API示意图如下所示,分为:\n",
    "- **基础数据结构**:kernel API中使用到的基础数据结构,比如GlobalTensor和LocalTensor。\n",
    "- **基础API**:实现对硬件能力的抽象,开放芯片的能力,保证完备性和兼容性。\n",
    "- **高阶API**:实现一些常用的计算算法,用于提高编程开发效率,通常会调用多种基础API实现。高阶API包括数学库、Matmul、Softmax等API。高阶API可以保证兼容性。\n",
    "- **Utils API**(公共辅助函数):丰富的通用工具类,涵盖标准库、平台信息获取、运行时编译及日志输出等功能,支持开发者高效实现算子开发与性能优化。\n",
    "\n",
    "更多详细的接口说明请参考 [Ascend C API 文档](https://www.hiascend.com/document/detail/zh/CANNCommunityEdition/900beta1/API/ascendcopapi/atlasascendc_api_07_0003.html) 。\n",
    "\n",
    "<img src=\"./images/02.03/ascend_c_library.png\" alt=\"ascend_c_library\"  width=\"700px\">"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "397c31b1",
   "metadata": {},
   "source": [
    "### 2.1 基础API\n",
    "\n",
    "实现基础功能的API,包括计算类、数据搬运、内存管理和任务同步等。使用基础API自由度更高,可以通过API组合实现自己的算子逻辑。基础API是对计算能力的表达。\n",
    "\n",
    "根据功能的不同,主要分为以下几类:\n",
    "- **标量计算API**,实现调用Scalar计算单元执行计算的功能。\n",
    "- **矢量计算API**,实现调用Vector计算单元执行计算的功能。\n",
    "- **矩阵计算API**,实现调用Cube计算单元执行计算的功能。\n",
    "- **数据搬运API**,计算API基于Local Memory数据进行计算,所以数据需要先从Global Memory搬运至Local Memory,再使用计算API完成计算,最后从Local Memory搬出至Global Memory。执行搬运过程的接口称之为数据搬运API,比如DataCopy接口。\n",
    "- **资源管理API**,用于分配管理内存,比如AllocTensor、FreeTensor接口;\n",
    "- **同步控制API**,完成任务间的通信和同步,比如EnQue、DeQue接口。不同的API指令间有可能存在依赖关系,从抽象硬件架构可知,不同的指令异步并行执行,为了保证不同指令队列间的指令按照正确的逻辑关系执行,需要向不同的组件发送同步指令。同步控制API内部即完成这个发送同步指令的过程,开发者无需关注内部实现逻辑,使用简单的API接口即可完成。\n",
    "\n",
    "根据对数据操作方法的不同,分为以下几类:\n",
    "- **连续计算API**:支持Tensor前n个数据计算。针对源操作数的连续n个数据进行计算并连续写入目的操作数,解决一维tensor的连续计算问题。\n",
    "- **高维切分API**:支持Repeat和Stride。功能灵活的计算API,提供与Built-In API完全对等编程能力,充分发挥硬件优势,支持对每个操作数的DataBlock Stride,Repeat Stride,Mask等参数的操作。\n",
    "\n",
    "下图以矢量加法Add为例,展示了连续计算API和高维切分API的特点。\n",
    "\n",
    "<img src=\"./images/02.03/characteristics_of_several_computing_methods_of_computing_api.png\" alt=\"characteristics_of_several_computing_methods_of_computing_api\"  width=\"700px\">"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "aae2a2e4",
   "metadata": {},
   "source": [
    "### 2.2 高阶API\n",
    "\n",
    "高阶API基于单核对常见算法进行抽象和封装,实现了一些常用的计算算法,旨在提高编程开发效率。高阶API一般通过调用多种基础API实现。高阶API包括数学计算、矩阵计算、激活函数等API。\n",
    "\n",
    "如下图所示,实现一个矩阵乘操作,使用基础API需要的步骤较多,需要关注格式转换、数据切分等逻辑;使用高阶API则无需关注这些逻辑,可以快速实现功能。\n",
    "\n",
    "\n",
    "<img src=\"./images/02.03/high_level_api.png\" alt=\"high_level_api\"  width=\"700px\">"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "dda309c1",
   "metadata": {},
   "source": [
    "### 2.3 Utils API\n",
    "\n",
    "Ascend C开发提供了丰富的通用工具类,涵盖标准库、平台信息获取、上下文构建、运行时编译及日志输出等功能,支持开发者高效实现算子开发与性能优化。\n",
    "\n",
    "- **C++标准库API**:提供算法、数学函数、容器函数等C++标准库函数。\n",
    "- **平台信息获取API**:提供获取平台信息的功能,比如获取硬件平台的核数等信息。\n",
    "- **RTC API**:Ascend C运行时编译库,通过aclrtc接口,在程序运行时,将中间代码动态编译成目标机器码,提升程序运行性能。\n",
    "- **log API**:提供Host侧打印Log的功能。开发者可以在算子的TilingFunc代码中使用ASC_CPU_LOG_XXX接口来输出相关内容。\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "200993ba",
   "metadata": {},
   "source": [
    "---\n",
    "## 课后练习\n",
    "\n",
    "请根据本节课程学习内容完成以下题目进行自测。\n",
    "\n",
    "1. (单选题)根据课程介绍,Ascend C主要包含三种典型的算子编程范式。其中,作为理解流水线编程思想起点、结构最直观的范式是:\n",
    "\n",
    "    A. 基本的矩阵编程范式(Cube编程范式)\n",
    "\n",
    "    B. 基本的矢量编程范式(Vector编程范式)\n",
    "\n",
    "    C. 复杂的矢量/矩阵编程范式(融合算子编程范式)\n",
    "\n",
    "    D. 多核并行编程范式\n",
    "\n",
    "2. (单选题)在Ascend C中,通过为队列(如queIn)调用pipe.InitBuffer(queIn, 2, 1024)来设置“2个内存块”。这主要是为了实现以下哪种机制?\n",
    "\n",
    "    A. 内存冗余备份,提高数据安全性\n",
    "\n",
    "    B. DoubleBuffer机制,实现数据搬运与矢量计算的并行执行\n",
    "\n",
    "    C. 内存分页管理,提高内存访问效率\n",
    "\n",
    "    D. 任务优先级调度,确保高优先级任务先执行\n",
    "\n",
    "3. (单选题)在矢量编程范式中,CopyIn任务主要负责什么?\n",
    "\n",
    "    A. 将数据从Local Memory搬运到Global Memory\n",
    "\n",
    "    B. 进行矢量计算\n",
    "\n",
    "    C. 将数据从Global Memory搬运到Local Memory,并放入队列\n",
    "\n",
    "    D. 从队列中取出数据进行计算\n",
    "\n",
    "4. (单选题)Ascend C中用于管理任务间通信与同步的核心组件是?\n",
    "\n",
    "    A. TPipe\n",
    "\n",
    "    B. TBuf\n",
    "\n",
    "    C. TQue\n",
    "\n",
    "    D. TPosition\n",
    "\n",
    "5. (单选题)Ascend C的API中,以下哪一类API主要用于对硬件能力的抽象,开放芯片计算、搬运、同步等基础功能?\n",
    "\n",
    "    A. 高阶API\n",
    "\n",
    "    B. Utils API\n",
    "\n",
    "    C. 基础API\n",
    "\n",
    "    D. 数学库API\n",
    "\n",
    "**执行以下代码获取答案。**"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "2a0eb740",
   "metadata": {
    "vscode": {
     "languageId": "cpp"
    }
   },
   "outputs": [],
   "source": [
    "!cat ./answer/02.03/02.03_answer.txt"
   ]
  }
 ],
 "metadata": {
  "language_info": {
   "name": "python"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}