工具链与基本概念aimrt_cli 工具用法示例123aimrt_cli gen -p [config.yaml] -o [output_folder]# 示例:aimrt_cli gen -p helloworld.yaml -o ./helloworld/
配置驱动:通过 YAML 定义项目结构/编译选项/部署模式
工程生成:自动创建 CMake 工程、模块模板、配置文件
安全机制:输出目录需为空,避免文件覆盖冲突
配置文档示例:直接看示例可能无法理解,快进到后续示例中我们自行编写的配置文件,对照学习
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103# 基础信息配置(必填项)base_info: # 项目 ...
前言Sphinx介绍Sphinx是一种文档生成工具,主要用于生成和管理过程文档。它支持以Python为基础的丰富插件,可以输出为HTML,PDF,Latex等格式。
reStructuredText 介绍reStructuredText (简称reST)是一种轻量级标记语言,文件名后缀为.rst。是 Sphinx 使用的默认纯文本标记语言
markdown介绍Markdown 也是一种轻量级标记语言,文件名后缀通常为.md。
环境准备Python安装Linux环境
确保已安装Python
1python3 --version
如果未安装,使用包管理器安装:
Ubuntu/Debian系统:
1sudo apt install python3 python3-pip
Red Hat/CentOS系统:
1sudo yum install python3 python3-pip
Windows环境
访问Python官网:https://www.python.org,下载最新版本安装包。
在安装时,确保选中“Add Python to PATH”选项。 ...
C++20协程入门什么是协程?简单来说:协程是一个可以暂停和恢复的函数。
如果从传统函数的角度来看,暂停意味着线程会停止执行。那么,协程与普通函数的区别在哪里呢?关键在于:
普通函数是线程紧密相关的,函数的状态依赖于线程栈;
协程是线程无关的,它的状态独立于任何线程,可以在不同的线程间切换。
为了更好地理解这一点,我们可以先回顾一下函数调用的机制。每当调用一个普通函数时,当前线程的栈会保存这个函数的状态(例如函数参数、局部变量等)。这一过程通过栈顶指针的移动来完成。例如,函数 Foo() 调用 Bar() 的过程如下:
在这个过程中:
从 地址3 到 地址2 的内存空间分配给 Foo() 用来保存状态,栈顶指针指向 地址2;
当调用 Bar() 时,栈顶指针移到 地址1,此时从 地址2 到 地址1 的内存空间分配给 Bar() 用于保存状态;
当 Bar() 执行完毕,栈顶指针回到 地址2,Bar() 的状态被销毁,内存空间被回收。
可以看出,普通函数的状态完全依赖于线程栈,一旦线程结束或切换,函数的状态就不再存在,因此我们说普通函数是线程相关的。
而协程不一样,协程的状态是 ...
该篇内容基于小彭老师访存优化课程,添加了自己的理解和备注
第0章:前言将会用到的工具代码:
ticktock.h封装TBB计时工具
12345678910111213#pragma once// #include <chrono>// #define TICK(x) auto bench_##x = std::chrono::steady_clock::now();// #define TOCK(x) std::cout << #x ": " <<// std::chrono::duration_cast<std::chrono::duration<double>>(std::chrono::steady_clock::now()// - bench_##x).count() << "s" << std::endl;#include <tbb/tick_count.h>#define TICK(x) auto bench_##x = tbb::tic ...
第0章:从并发到并行停滞的摩尔定律
你醒啦?免费午餐结束了!
摩尔定律具体内容我们就不再提,从上图可以看到晶体管的密度的确仍在指数增长,但处理器主频却开始停止增长了,甚至有所下降。
很长时间之前我们就可以达到2GHz(2001年8月),根据2003年的趋势,在2005年初我们就应该研发出10GHz的芯片。
可为何直到今天也生产不出10GHz的芯片?
狭义的摩尔定律没有失效。但晶体管数量的增加,不再用于继续提升单核频率,转而用于增加核心数量。单核性能不再指数增长!
指望靠单核性能的增长带来程序性能提升的时代一去不复返了,现在要我们动动手为多核优化一下老的程序,才能搭上摩尔定律的顺风车。
神话与现实:2 * 3GHz VS 6GHz既然现在的CUP都提供了多核,那么一个由双核组成的3GHz的CPU实际上提供了6GHz的处理能力,是吗?
显然不是。甚至在两个处理器上同时运行两个线程也不见得可以获得两倍的性能。相似的,大多数多线程的应用不会比双核处理器的两倍快。他们应该比单核处理器运行的快,但是性能毕竟不是线性增长。
为什么无法做到呢?首先,为了保证缓存一致性以及其他握手协议需要运行时间开销。 ...
!!本文内容搬运小彭老师现代C++大典内容,仅为个人学习备份使用,请大家支持原作者链接:✝️小彭大典✝️数据结构与复杂度不论什么语言,容器(或者用学校里的话说:数据结构)的正确使用,能够在复杂度层面上,大幅提升性能。
C++ 中也是如此,有数组(vector),字典(map),还有上一课讲过的集合(set)。
今天我们要介绍的就是 C++ 的字典容器 map,以及 C++11 引入的另一个字典容器 unordered_map,他们的区别我们最后会详细讨论。我们先学习较为简单的 map,随后学习 unordered_map 时也可以举一反三、融会贯通。
介绍完这两个标准库自带的字典容器后,我们还将介绍一些常用的第三方库容器,例如 absl::flat_hash_map、tbb::concurrent_hash_map、google::dense_hash_map、robin_hood::unordered_map、tsl::robin_pg_map 等,需要根据应用场景选择适合的容器。
map/set 家族都是高效查找的专家:
vector 容器用 std::find 查 ...
一、文件、目录组织规范1. 推荐的目录组织方式目录组织格式:
项目名/include/项目名/模块名.h
项目名/include/项目名/模块名.h
项目名/src/模块名.cpp
CMakeLists.txt内容:
1target_include_directories(项目名 PUBLIC include)
源码文件(.h)中写:
12#include <项目名/模块名.h> 项目名::函数名();
头文件中写:
1234#pragma oncenamespace 项目名 {void 函数名();}
实现文件(.cpp)中写:
1234#include <项目名/模块名.h>namespace 项目名 {void 函数名() { 函数实现 }}
划分子项目
大型的项目,往往会划分为几个子项目。
即使你只有一个子项目,也建议你先创建一个子目录,方便以后追加新的子项目。
上面的案例中,我们在根目录下,创 ...
GPU编程
未读第8章:板块与共享内存为什么需要区分出板块的概念?之前说到实际的线程数量就是板块数量(gridDim)乘以每板块线程数量(blockDim)。
那么为什么中间要插一个板块呢?感觉很不直观,不如直接说线程数量不就好了?
这还得从 GPU 的硬件架构说起。
SM(Streaming Multiprocessors)与板块(block)GPU 是由多个流式多处理器(SM)组成的。每个 SM 可以处理一个或多个板块。
SM 又由多个流式单处理器(SP)组成。每个 SP 可以处理一个或多个线程。
每个 SM 都有自己的一块共享内存(shared memory),他的性质类似于 CPU 中的缓存——和主存相比很小,但是很快,用于缓冲临时数据。还有点特殊的性质,我们稍后会讲。
通常板块数量总是大于 SM 的数量,这时英伟达驱动就会在多个 SM 之间调度你提交的各个板块。正如操作系统在多个 CPU 核心之间调度线程那样……
不过有一点不同,GPU 不会像 CPU 那样做时间片轮换——板块一旦被调度到了一个 SM 上,就会一直执行,直到他执行完退出,这样的好处是不存在保存和切换上下文(寄存器,共享内 ...
GPU编程
未读第七章:原子操作经典案例:数组求和如何并行地对数组进行求和操作?
首先让我们试着用串行的思路来解题。
12345678910111213141516171819202122232425262728293031323334353637#include <cuda_runtime.h>#include <cstdio>#include <vector>#include "CudaAllocator.h"#include "helper_cuda.h"#include "ticktock.h"__global__ void parallel_sum(int *sum, int const *arr, int n) { for (int i = blockDim.x * blockIdx.x + threadIdx.x; i < n; i += blockDim.x * gridDim.x) { sum[0] += arr[i]; } ...
GPU编程
未读第六章:thrust库使用 CUDA 官方提供的 thrust::universal_vector虽然自己实现 CudaAllocator 很有趣,也帮助我们理解了底层原理。但是既然 CUDA 官方已经提供了 thrust 库,那就用他们的好啦。
123456789101112131415161718192021222324252627282930313233343536#include <cuda_runtime.h>#include <thrust/universal_vector.h> // trusth库#include <cstdio>#include "helper_cuda.h"template <class Func>__global__ void parallel_for(int n, Func func) { for (int i = blockDim.x * blockIdx.x + threadIdx.x; i < n; i += blockDim.x * gri ...