目录
- 前言
- 程序设计方法
- ODD->DOD
- Cache的3C与3R
- 面向数据设计需要思考的问题
- AOS
- SOA
- DOTS面向数据设计原则
前言
本文是Metaverse大衍神君的《DOTS之路》系列课程的学习笔记
程序设计方法
- Instructional Programming 指令化编程
- 脱离指令打孔输入后,伴随着机器汇编语言发展起来的
- Functional Programming 函数化编程
- 伴随着PASCAL语言出现,以小函数模块化组合的编程方式
- 限制小、易于调试
- 主要用在数学和科学计算领域
- 如今的机器学习、AI领域,函数编程依然焕发着活力
- Procedural Programming 过程化编程
- 源于命令式的编程方式
- 基于过程调用的概念,包含要执行的计算步骤
- 任何给定的过程都可以在程序执行过程中的任何时刻调用
- Object-Oriented Programming/Design 面向对象编程/设计(OOP/OOD)
- 以对象为概念的多范式模型
- 包含字段形式的数据与过程形式的代码
- 通常以类为基础,强调数据的封装、类的继承与数据的多态特征
- Data-Oriented Design(DOD) 面向数据设计
- 伴随着现代CPU多核并行计算、多级缓存、大缓存的设计而流行起来
- DOD并不是一种编程范式,DOTS式面向数据编程的一种范式
ODD->DOD
- 面向对象设计->面向数据设计
- 面向对象设计
- 核心在于:抽象、封装、继承
- 对人类来说更直观、易于理解
- 对现代CPU来说处理效率并不高效
- 面向数据设计
- 侧重于数据
- 需要考虑需要什么数据以及如何在内存中更好的构造数据
- 以便CPU处理数据系统时,能更有效的访问数据
- 面向对象设计
- DOD本质:面向内存/缓存友好的设计
- CPU缓存层级结构
- CPU 1 cycle
- L1 Cache 1~5 cycle
- L1D 数据缓存
- L1I 指令缓存
- L2 Cache 5~20 cycle
- CPU核内多个指令处理单元所共享
- L3 Cache 20~40 cycle
- CPU多个核所共享
- 负责与内存以及显卡中的显存交换数据
- Main Memory 40~100 cycle
- CPU缓存层级结构
- CPU在执行指令程序时,通过Prefetching来获取指令与数据
- CacheLine缓存行
- 每次访问的单位会根据系统与架构的不同而有所差异
- 一般32或64个字节
- 即使请求一个字节大小,实际上会得到CacheLine大小的缓存行数据
- 在Cache缓存内
- 可以将n个缓存行大小的缓存通过Direct-Map直接映射到同一逻辑缓存行
- 逻辑缓存行可以对应n个物理行,来帮助最小化缓存行的抖动
(抖动:挪动指针到每个物理缓存行的头)
先看右侧下方的图:
- CPU逻辑处理单元完成一条程序指令(其完成时间定义为CPU指令的cycle)
- Fetch获得L1I指令缓存中的指令
- Decode解码
- Excution 执行
- 执行完成后回写到L1D中
左侧的图:
- CPU处理指令时,从不同的缓存Cache中获取数据的时间开销也不同
- 获取的数据在某一级缓存没有命中时,向下一级缓存获取时
- 花费的时间开销可能是数倍时间
- 甚至是数量级的差异的时间开销
Cache的3C与3R
-
Compulsory misses:首次读取数据时,不可避免的Miss
-
Capacity misses:缓存空间不足,连续使用期间访问数据过多的话,无法保存所有活动的数据
-
Conflict misses:发生访问冲突时,由于数据映射到相同的缓存行,导致缓存的抖动
-
Rearrange 充血排列(代码、数据):更改布局以增加数据空间的局部性
-
Reduce减少(大小、缓存行读取):更小更只能的格式、压缩
-
Reuse重用(Cache lines):增加数据的时间(和空间)的局部性
面向数据设计需要思考的问题
- 哪些数据需要捆绑在一起,是一个概念还是有隐藏的含义
- 数据布局是如何设计的,AOS/SOA?支持 SIMD ?
- 目标平台内存的最小访问单位是多少,CPU各级缓存有多大?
- 数据多久需要一次,一帧一次,还是一帧多次,还是几乎不更新?
- 如何访问数据,随机/连续的还是stride或其他burst方式
- 总在修改数据还是只是读取数据,修改所有的内容,还是只修改一部分?
- 哪些数据设计对带宽的延迟影响大?
AOS
struct innerStruct
{
float x;
float y;
};
SOA
struct InnerArray
{
float x[LEN];
float y[LEN];
};
DOTS面向数据设计原则
- 先设计,后编码
- 为高效使用内存与缓存而设计
- 为Blittable Data设计
- 为普通情况设计
- 拥抱迭代