一些零散的和编译相关的语法/flash-attn涉及语法扫盲

news2024/11/17 8:29:12
  1. #pragma once:一个编译指令,用于防止头文件被多次包含。当编译器遇到#pragma once时,它会确保该头文件在一个编译单元(一个.cpp文件及其包含的所有文件)中只会被包含一次。即使该文件被间接包含多次,编译器也会忽略多余的包含操作,从而加快编译速度并减少编译错误
    • #pragma once需要编译器追踪文件路径以确保文件只被包含一次,这增加了实现的复杂度
    • C和C++以及它们的标准早期并未包括#pragma once这种指令,所以为了保持与旧版代码的兼容性,和确保跨不同平台和编译器的代码能正常运行,编译器通常不会默认强制使用#pragma once
  2. __VA_ARGS__:在宏定义中,是用来表示可变参数的预处理器标记。
    • 调用BOOL_SWITCH时传递了一个lambda表达式:BOOL_SWITCH(flag, BoolConst, [&] { return some_function<BoolConst>(); });
    • BOOL_SWITCH定义如下:其中__VA_ARGS__就代表宏的可变参数,__VA_ARGS__()表示将这些可变参数当作一个可调用对象(通常是一个函数或lambda表达式)来调用
      #define BOOL_SWITCH(COND, CONST_NAME, ...)      \
        [&] {                                         \
          if (COND) {                                 \
            constexpr static bool CONST_NAME = true;  \
            return __VA_ARGS__();                     \
          } else {                                    \
            constexpr static bool CONST_NAME = false; \
            return __VA_ARGS__();                     \
          }                                           \
        }()
      
    • 则调用BOOL_SWITCH后展开后的代码就是:这里__VA_ARGS__()就代表了[&] { return some_function<BoolConst>(); }这个lambda函数的调用,()的作用就是让__VA_ARGS__所代表的lambda函数立即执行
      [&] {
        if (flag) {
          constexpr static bool BoolConst = true;
          return [&] { return some_function<BoolConst>(); }();
        } else {
          constexpr static bool BoolConst = false;
          return [&] { return some_function<BoolConst>(); }();
        }
      }()
      
  3. 函数模板:允许用户编写可以处理多种数据类型的通用函数,而无需为每种类型都分别编写函数实现。函数模板的基本定义形式如下:
    template<typename T>
    void function_name(T param){
    	// 函数体
    }
    
    • typename T表示T是一个类型参数,可以用它来代表函数中使用的任何数据类型
    • 当调用一个函数模板时,编译器根据用户提供的参数类型自动推断出模板参数,并生成一个具体的函数实例。即,模版是一种编译时的机制,它不执行具体的代码,直到用户实际调用它时才会生成具体的代码
  4. 在flash-attention的flash_fwd_kernel.h文件中的combine_attn_seqk_parallel函数解释中,G老师提到了“代码中使用了大量的模板和元编程技术,使得函数能够灵活应对不同的配置参数”
    • 模板:是一种让程序可以编写通用代码的机制。用户可以在编写模板时不指定具体类型,而是在使用模板时指定,这样函数代码就可以处理多种类型
    • 元编程:允许程序在编译时而不是运行时生成或修改代码,这通常通过使用模板来实现,使得代码能够在编译时进行类型推导和其他计算。元编程是一种“编写程序来生成程序”的方法,元编程使得程序能够在编译时或运行时检查、生成或修改其他代码
      • static_assert是C++中一种元编程技术,用于在编译时检查条件。下面这行代码检查如果kMaxSplits大于128,编译器就会在编译阶段报错
        static_assert(kMaxSplits <= 128, "kMaxSplits must be <= 128");
        
      • 如下面是一个简单的使用bash脚本编写的元程序示例,同时也是一个生成式编程的例子
        #!/bin/bash
        # metaprogram
        echo '#!/bin/bash' >program
        for ((I=1; I<=992; I++)) do
            echo "echo $I" >>program
        done
        chmod +x program
        
    • 是什么:是在C和C++中由预处理器处理的指令。通过#define关键字定义,允许为代码中的表达式、常量或代码片段取别名,甚至定义具有参数的宏。宏会在预处理阶段展开,在编译前会被预处理器替换为其定义的内容
    • 举例:下面的SQUARE(x)是一个宏函数,接收参数x并将其替换为(x)*(x)
      #define SQUARE(x) ((x) * (x))
      
    • 条件编译:可使用预处理器的条件控制语句(如#ifdefifndef),将宏用于条件编译
      #ifdef DEBUG
      printf("Debug mode is enabled\n");
      #endif 
      
    • 缺点:
      • 无类型检查:宏在预处理阶段展开,而非编译时,所以编译器无法对宏参数进行类型检查。如上面的SQUARE("test")传入一个字符串,但预处理器仍会将“test”替换到宏定义中,导致无法预测的行为
      • 调试困难:由于宏是在预处理阶段替换的,调试时代码看起来会和原始代码不同,可能让问题更难追踪
    • 宏和const的区别
      • const是编译时常量,且具有类型信息。编译器可在编译阶段进行类型检查以确保传入的值类型正确
      • const变量可在调试时查看其值,而宏常量会在预处理阶段被替换,调试工具很难捕捉它们
      • const遵循C++的作用域规则,只在声明的作用域内可见,而宏是全局的。若多个文件中定义相同名称的宏,会导致冲突和错误
        • 常见作用域:【1】局部作用域(在函数、代码块等局部范围内声明的变量,只能在该范围内访问)、【2】类作用域(类的成员变量或函数,只能在该类的对象和成员函数中使用)、【3】命名空间作用域(在命名空间内声明的变量或函数,只能在同一命名空间或通过作用域解析符访问)
        • 全局:全局作用域中的标识符可在整个程序中访问。宏定义(#define)是一种预处理器指令,作用类似于全局替换。宏一旦定义,就会在整个源文件中全局替换
        • 原则上可以在b.cpp中include a.cpp来访问a.cpp中定义的宏,但这并不是推荐的做法(被include的.cpp文件可能会被编译多次,导致符号重复定义问题从而导致编译错误)。对于这种跨源文件使用的宏,一般会将宏定义放入一个共享的头文件(.h)中,并在各个源文件中include这个头文件
    • 下面是flash-attention中用宏进行模板化的CUDA内核函数声明的操作例子,感觉很新颖很灵活,用户可通过这种方式在不同的配置或类型下灵活定义内核函数,而不需要手动写大量重复的代码
      // Use a macro to clean up kernel definitions
      #define DEFINE_FLASH_FORWARD_KERNEL(kernelName, ...) \  // 这行代码定义了一个宏DEFINE_FLASH_FORWARD_KERNEL,它接受两个参数kernelName(内核函数名字)和__VA_ARGS__(这是个可变参数宏,允许传入不定数量的参数)
      template<typename Kernel_traits, __VA_ARGS__> \  // 这行代码使用模板的语法,Kernel_traits常用于在CUDA中描述内核函数的特性(如线程布局、块布局等)
      __global__ void kernelName(KERNEL_PARAM_MODIFIER const Flash_fwd_params params)  // 这行代码声明了一个CUDA内核函数
      
      • 使用:
      // 使用宏
      DEFINE_FLASH_FORWARD_KERNEL(MyKernel, typename T) 
      
      // 上面的宏展开后会变成:
      template<typename Kernel_traits, __VA_ARGS__>
      __global__ void MyKernel(KERNEL_PARAM_MODIFIER const Flash_fwd_params params) 
      // 这就定义了一个名为MyKernel的CUDA内核函数,它接受模板参数Kernel_traits和T,并可以在调用时传入Flash_fwd_params结构体
      
  5. 编译和调试
    • 编译是把源代码编程二进制obj的过程(链接后成为可执行文件),会检查有无简单的语法问题(要不然编译器不认识)
    • 调试的话,先要提前生成二进制代码,所以需要先进行编译和链接,然后到断点后,调试器会帮你加int3中断,就停住了。
    • 调试是在程序运行后,根据运行状况来检查错误,是对已经存在的二进制文件进行调试,目的在于查找性能瓶颈和跟踪软件bug;编译器是在程序没有运行的时候帮你检查错误,目的在于把代码编译(再汇编,这里不太严谨,具体可看这篇)成二进制文件,即可执行的程序
      请添加图片描述
    • vscode每次调试都要重新编译项目,这是因为在项目运行时更改了系统时间,导致vs编译日志认为文件需要重新编译。解决办法见这篇
  6. pybind11库:
    • 是什么:是一个轻量级的头文件库,用于在C++和python之间创建绑定。它允许用户将C++函数、类等轻松暴露给python,使得python可以调用C++代码,而不用编写繁琐的python C API代码。
    • 使用说明:在CPP文件中使用pybind11将C++函数绑定为python函数;然后在py文件中import刚才绑定的库,并使用
    • 示例:
      • C++文件:
      #include <pybind11/pybind11.h>
      
      int add(int i, int j){
      	return i+j;
      }
      
      PYBIND11_MODULE(example, m){
      	m.def("add", &add, "A function which adds two numbers");
      }
      
      • python中使用:
      import example
      result = example.add(2, 3)
      print(result)
      
  7. 内联函数inline
    • 是什么:在编译阶段,编译器会尝试将内联函数的调用语句替换成函数体本身,从而避免实际的函数调用(参数传递、函数跳转、函数返回),如下例编译器会将square(5)替换成5*5,这样就避免了函数调用的过程
      inline int square(int x) {
          return x * x;
      }
      
      int main() {
          int result = square(5);
      }
      
    • 使用场景:内联函数通常用于一些短小、性能敏感、执行频繁的函数,以避免重复进行函数调用的开销。模板函数通常会被定义为内联函数,它们的实例化会在编译期展开
    • 代码膨胀:内联函数会导致代码膨胀,因为函数体被复制到被一个调用点,这会增加可执行文件的体积
  8. extern__shared__关键字
    • 在C/C++中,extern关键字用来声明一个全局变量或函数,表示这个变量或函数是在其他文件或作用域中定义的
    • 在CUDA中,当extern__shared__关键字一起使用时,表示动态分配的共享内存,所以shared memory的大小是在运行时确定的而不是编译时(shared memory不是固定的?)。在调用CUDA内核时,可通过第三个参数<<<gridDim, blockDim, sharedMemSize>>>(...);来指定共享内存的大小。如下的代码,在调用内核时制定了共享内存大小,然后smem_[]就会在运行时变成大小为1024字节的数组
      __gloval__ void myKernel(...){
      	extern __shared__ char smem_[];  // 声明动态shared memory
      	// 使用shared memory
      }
      
      size_t sharedMemSize = 1024;
      myKernel<<<gridDim, blockDim, sharedMemSize>>>(...);
      
    • __shared__关键字在CUDA中表示共享内存,供同一block(=SM,含多个warp)内的所有线程共享访问
  9. constexpr关键字:
    是C++11引入的关键字,表示常量表达式(constant expression),作用是让变量或函数的值在编译时就能计算完成并确定其值,而不是在运行时。在性能敏感的代码中,constexpr是一个非常有效的工具
    • constexprconst的区别:前者要求值在编译时计算得出;后者表示变量一旦初始化后就不能改变,但它不一定在编译时计算
  10. using语法
    using是C++中的一种类型别名声明语法,通常用于为复杂的类型创建别名,使代码更加简洁和可读。用法就是:using 新类型名 = 原类型名;。如using Element = elem_type;的作用就是为elem_type创建一个别名,这个新的别名叫做Element。使用了这个using后,代码中的Element就相当于elem_type

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

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

相关文章

JavaWeb的小结03

第2章-第3节 一、知识点 Cookie、Session、Filter过滤器、Listener。 二、目标 理解Cookie和Session的区别。 掌握Cookie和Session的基本用法。 理解Filter过滤器的作用。 三、内容分析 重点 理解Cookie和Session的区别。 掌握Cookie和Session的基本用法。 理解Filter过…

minio简单使用

文章目录 简介官方地址Linux下载安装安装服务启动关闭帮助命令 java开发minio依赖包新建项目pom配置文件配置类Service测试类运行测试 Api使用前言针对桶的操作查看某个桶是否存在创建一个桶返回桶列表删除一个桶 针对文件的操作上传文件到桶中(本地文件上传)上传文件到桶中(基…

(Linux驱动学习 - 9).设备树下platform的LED驱动

一.platform相关结构体与函数 1.匹配列表 - struct of_device_id struct of_device_id {char name[32];char type[32];/* compatible 很重要&#xff0c;需要与设备树节点的 compatible 属性一致&#xff0c;才能匹配 */char compatible[128]; const void *data; }; …

dfs 判重Sequence one——hdu 2610

目录 前言 搜索算法判重 map判重 set判重 Sequence one 问题描述 输入 输出 数据范围 样例 问题分析 重构dfs参数 递减&#xff0c;不重复 去重的优化 最终代码 前言 搜索算法判重 搜索算法判重有很多种方法&#xff0c;常见的有两种&#xff0c;map判重和set判重…

模运算和快速幂

文章目录 模运算快速幂 模运算 模运算是大数运算中的常用操作。如果一个数太大&#xff0c;无法直接输出&#xff0c;或者不需要直接输出&#xff0c;则可以对它取模&#xff0c;缩小数值再输出。取模可以防止溢出&#xff0c;这是常见的操作。 取模运算一般要求a和m的符号一…

VCI_VBDSP使用教程-服务站

VCI_VBDSP使用教程-服务站 VBDSP软件压缩包请点击下载&#xff1a;(备注&#xff1a;将VBDSP软件压缩包做一个下载连接&#xff0c;放到此处) 教程视频&#xff1a;https://www.bilibili.com/video/BV19eHpeeEiz/?spm_id_from333.999.0.0&vd_source224b4434f72960113bc97…

数组的定义与使用(二)

2. 数组是引用类型 2.1初识JVM的内存分布 内存是一段连续的存储空间&#xff0c;主要用来存储程序运行时数据的。比如&#xff1a; 程序运行时代码需要加载到内存程序运行产生的中间数据要存放在内存程序中的常量也要保存有些数据可能需要长时间储存&#xff0c;有些数据当方…

PCL 平面点云边界特征提取(alpha shapes)

目录 一、概述 1.1原理 1.2实现步骤 1.3应用场景 二、代码实现 2.1关键函数 2.1.1 点云边界提取 2.1.2 可视化点云与边界 2.2完整代码 三、实现效果 PCL点云算法汇总及实战案例汇总的目录地址链接&#xff1a; PCL点云算法与项目实战案例汇总&#xff08;长期更新&a…

07:(寄存器开发)串口通信

串口通信 1、串口简介2、串口通讯协议3、硬件外设4、发送数据5、使用轮询的方式接收数据&#xff08;USART1&#xff09;6、使用中断的方式接收数据7、串口进行printf重定向 1、串口简介 串口通讯&#xff08;Serial Communication&#xff09;是一种设备间非常常用的串行通讯方…

后端增删改查的基本应用——一个简单的货物管理系统

最终效果&#xff0c;如图所示&#xff1a; 如果想要进行修改操作&#xff0c;可点击某栏修改选项&#xff0c;会在本表格下方弹出修改的具体操作界面&#xff08;点击前隐藏&#xff09;&#xff0c;并且目前的信息可复现在修改框内。 本篇文章通过该项目将后端和前端结合起来…

java内存控制

Java 内存控制是一个相对复杂但至关重要的主题&#xff0c;它涉及到如何高效地管理Java应用程序中的内存资源。在Java中&#xff0c;内存管理主要由Java虚拟机&#xff08;JVM&#xff09;负责&#xff0c;包括内存的分配和回收。尽管如此&#xff0c;作为开发者&#xff0c;我…

2025年5月高项,从0备考信息系统项目管理师 | 备考经验全攻略分享

在逐步摸索备考信息系统项目管理师的过程中&#xff0c;我总结了很多关于班课资料和学习经验&#xff0c;现在与大家分享。&#xff08;全文约3k字&#xff0c;阅读用时约5min&#xff09; 这篇分享帖不仅告诉你关于备考信息系统项目管理师实用的班课资料&#xff0c;还有学习…

Win11 23H2 10月正式版:22631.4317 镜像免费下载!

今日&#xff0c;系统之家小编给您带来2024年10月最新更新的Windows11 23H2正式版系统下载&#xff0c;该版本系统基于微软官方最新Windows11 23H2 22631.4317专业版展开离线制作&#xff0c;没有病毒残留&#xff0c;且能完美支持新老机型&#xff0c;安装后&#xff0c;系统版…

【概率论】泊松分布

泊松分布 若 &#xff0c;则 归一性 例子 泊松分布多出现在当X表示一定时间或一定空间内出现的事件的个数这种场合&#xff0c;如在一定时间内某交通路口所发生的事故的个数。 将泊松分布假设为二项分布 假设条件: &#xff08;1&#xff09;泊松分布一般为一段时间或一…

★ 算法OJ题 ★ 二分查找算法

Ciallo&#xff5e;(∠・ω< )⌒☆ ~ 今天&#xff0c;塞尔达将和大家一起做几道二分查找算法算法题 ~ ❄️❄️❄️❄️❄️❄️❄️❄️❄️❄️❄️❄️❄️❄️ 澄岚主页&#xff1a;椎名澄嵐-CSDN博客 算法专栏&#xff1a;★ 优选算法100天 ★_椎名澄嵐的博客-CSDN博客…

STM32 SPI串行总线

目录 STM32的SPI通信原理 SPI串行总线概述 SPI串行总线互连方式 STM32F1 SPI串行总线的工作原理 SPI串行总线的特征 SPI串行总线的内部结构 SPI串行总线时钟信号的相位和极性 STM32的SPI接口配置 STM32的SPI接口数据发送与接收过程 SPI的HAL 驱动函数 STM32的SPI通信…

靶标弹孔检测系统源码分享

靶标弹孔检测系统源码分享 [一条龙教学YOLOV8标注好的数据集一键训练_70全套改进创新点发刊_Web前端展示] 1.研究背景与意义 项目参考AAAI Association for the Advancement of Artificial Intelligence 项目来源AACV Association for the Advancement of Computer Vision …

apt update报错:ModuleNotFoundError: No module named ‘apt_pkg‘(可能是默认python版本被改坏了)

文章目录 错误信息分析1. 确保 apt_pkg 模块已安装2. 检查 Python 版本3. 重新配置 Python4. 修复损坏的依赖5. 检查环境变量 尝试 错误信息 (base) rootkyai:/ky/tml/ky_ai_get_server_info# apt update 获取:1 file:/var/cuda-repo-cross-aarch64-ubuntu2004-11-4-local InR…

【Python】如何让SQL Server像MySQL一样拥有慢查询日志(Slow Query Log慢日志)

如何让SQL Server像MySQL一样拥有慢查询日志&#xff08;Slow Query Log慢日志&#xff09; SQL Server一直以来被人诟病的一个问题是缺少了像MySQL的慢日志功能&#xff0c;程序员和运维无法知道数据库过去历史的慢查询语句。 因为SQLServer默认是不捕获过去历史的长时间阻塞…

inBuilder低代码平台新特性推荐-第二十五期

今天来给大家带来的是inBuilder低代码平台社区版中的特性推荐系列第二十五期——选人组件扩展&#xff01; 一、概述 inBuilder低代码平台社区版的开发过程中&#xff0c;选人组件支持tab页中增加扩展页面&#xff0c;由二开人员根据业务场景实现自定义取数接口和页面展示形式…