C++高性能并行编程与优化 - 课件 - 15 C++ 系列课:字符与字符串,内存管理与对象生命周期 ASCII 码 第 1 章 计算机如何表达字符 https://zh.wikipedia.org/wiki/ASCII 计算机如何表达字符 • 众所周知,计算机只能处理二进制 整数,字符要怎么办呢? • 于是就有了 ASCII 码表,他规定, 每个英文字符(包括大小写字母、 数字、特殊符号)都对应着一个整 数。在计算机里只要存储这个的整 数,就能代表这个字符了。 char 在 x86 架构是有符号的 (char = signed char) ,而在 arm 架构上则认为是无符号的 (char = unsigned char) ,因为他 认为“ arm 的指令集处理无符号 8 位整数更高效”,所以擅自把 char 魔改成无 符号的…… • 顺便一提, C++ 标准保证 char , signed char , unsigned char 是三个完全 不同的类型, 没什么好神秘的,他就是一个普通的字符。 • 仅仅只是 printf 和 scanf 这些特定的函数会对 % 特殊处理而已。 • 而 \ 比较厉害,他是编译器内部专门为他“开了个后门”。 • 编译器检测到字符串中出现 \ 就会把下一个字符特殊处理。 • 而 % ,编译器并不会特殊处理 % ,是 printf 函数内部在运行时处理了 % 的下一个字符。 • % 就像你和同学随手“拉钩”定下的约定,这是 printf 约定俗成的。0 码力 | 162 页 | 40.20 MB | 1 年前3
C++高性能并行编程与优化 - 课件 - 17 由浅入深学习 map 容器通过实战案例来学习 STL 算法库 7. C++ 标准输入输出流 & 字符串格式化 8. traits 技术,用户自定义迭代器与算法 9. allocator ,内存管理与对象生命周期 10. C++ 异常处理机制的前世今生 我们都要认真鞋习哦 我们都要认真鞋习哦 第一章:读取与写入 我负责监督你鞋习 ! 我负责监督你鞋习 ! map 查找元素的两个接口 • map 提供了两个查找元素的接口,一曰 key” 元素 • val = m.at(“key”); // 读取键值为 “ key” 的元素,如果不存在,抛出异常 • 所以 [] 和 at() 唯一的区别,在于键值不存在这一特殊情况的处理方式。 • [] 默默创建。 • at() 抛出异常。 读取 map 元素 • mapm; • val = m[“key”]; • 读取键值为 “ key” it->second; 读取 map 元素 • map m; • val = m.at(“key”); • 读取键值为 “ key” 的元素,如果不存在,那就抛出异常,导致程序异常退出。等价于: • it = m.find(“key”); • if (it == m.end()) { • throw std::out_of_range(“ 找不到键值” ); 0 码力 | 90 页 | 8.76 MB | 1 年前3
C++高性能并行编程与优化 - 课件 - 02 现代 C++ 入门:RAII 内存管理解构函数是显式的,离开作用域自动销毁,毫不含 糊(有好处也有坏处,对高性能计算而言利大于 弊) 如果没有解构函数,则每个带有返回的分 支都要手动释放所有之前的资源 : RAII :异常安全( exception-safe ) C++ 标准保证当异常发生时,会调用已创建对象的解构函数 。 因此 C++ 中没有(也不需要) finally 语句。 如果此处不关闭,则可等 待稍后垃圾回收时关闭。 虽然最后还是关了,但如 典型的例子包括,图形学某知名应用中, 可以简化函数具有多个返回值的处理。 • 和 std::tuple 相比,最大的好处是每个属性都有名字 ,不容易搞错。举个例子: • auto [hit, pos, ...] = intersect(...) • 每增加一个属性都要全部改一次代码。 • 更加 fancy 的写法: 编译器默认生成的构造函数:初始化列表(妙用,处理函数的复杂类型参 数) • 还有,函数的参数,如果是很复杂的类型 如果需要允许用户拷贝你的 Vector 类对象 ,我们还是需要实现一下的。 • 发现了吗?其实不管是 size/resize 这样的 get/set 模式也好;自定义的拷贝构造函数 也好; RAII 保证异常安全也好;都是在为 面向对象思想的“封装:不变性”服务。 • 即:保证任何单个操作前后,对象都是处于 正确的状态,从而避免程序读到错误数据 (如空悬指针)的情况。 三五法则:拷贝赋值函数0 码力 | 96 页 | 16.28 MB | 1 年前3
C++高性能并行编程与优化 - 课件 - 03 现代 C++ 进阶:模板元编程multiply(float) ,你也去 定义好几个重载吗?定义为 multiply(Numeric *) 的话 依然会违背你们的开 - 闭原则:比如 3.14f * 3 ,两 端是不同的类型,怎么处理所有可能类型的排列组合 ? 不如放弃类和方法的概念,欣然接受全局函数和重载 。 模板函数:定义 • 使用 template• 其中 T 可以变成任意类型。 • 调用时 twice value() : 3 optional : value() 会检测是否为空,空则抛出异常 • 当 ret 没有值时(即 nullopt ), ret.value() 会抛出一个异 常,类型为 std::bad_optional_access 。 optional : operator*() 不检测是否为空,不会抛出异常 • 除了 ret.value() 之外还可以用 *ret 获 取 optional optional 容器中的值,不过他不会 去检测是否 has_value() ,也不会抛出 异常,更加高效,但是要注意安全。 • 请确保在 has_value() 的分支内使用 *ret ,否则就是不安全的。 • 如果 optional 里的类型是结构体,则 也可以用 ret->xxx 来访问该结构体的 属性。 optional : operator bool() 和 has_value() 0 码力 | 82 页 | 12.15 MB | 1 年前3
C++高性能并行编程与优化 - 课件 - 08 CUDA 开启的 GPU 编程上的编译器(通常是系统自带的编译 器比如 gcc 和 msvc )生成 CPU 部分的指令码。然后送到真 正的 GPU 编译器生成 GPU 指令码。最后再链接成同一个文件 ,看起来好像只编译了一次一样,实际上你的代码会被预处理很 多次。 • 他在 GPU 编译模式下会定义 __CUDA_ARCH__ 这个宏,利用 #ifdef 判断该宏是否定义,就可以判断当前是否处于 GPU 模式 ,从而实现一个函数针对 GPU Hello, world! 打印了三遍! • 原来,三重尖括号里的第二个参数决定着启动 kernel 时所用 GPU 的线程数量。 • GPU 是为并行而生的,可以开启很大数量的 线程,用于处理大吞吐量的数据。 获取线程编号 • 可以通过 threadIdx.x 获取当前线程的编 号,我们打印一下试试看。 • 这是 CUDA 中的特殊变量之一,只有在 核函数里才可以访问。 • 可以看到线程编号从 ret 的值并没有被改写成 功。 分析返回的错误代码 • CUDA 的函数,如 cudaDeviceSynchronize() 。 • 他们出错时,并不会直接终止程序,也不会抛出 C++ 的异常,而是返回一个错误代码,告诉你出的具体什么 错误,这是出于通用性考虑。 • 这个错误代码的类型是 cudaError_t ,其实就是个 enum 类型,相当于 int 。 • 可以通过 cudaGetErrorName0 码力 | 142 页 | 13.52 MB | 1 年前3
C++高性能并行编程与优化 - 课件 - 13 C++ STL 容器全解之 vectoroperator[] • 值得注意的是, [] 运算符在索引超出数组大 小时并不会直接报错,这是为了性能的考虑。 • 如果你不小心用 [] 访问了越界的索引,可能 会覆盖掉别的变量导致程序行为异常,或是访 问到操作系统未映射的区域导致奔溃。 • int &operator[](size_t i) noexcept; • int const &operator[](size_t i) const noexcept; vector 容器: at • 为了防止不小心越界,可以用 a.at(i) 替代 a[i] , at 函数会检测索引 i 是否越界,如果他 发现索引 i >= a.size() 则会抛出异常 std::out_of_range 让程序提前终止(或者被 try-catch 捕获),配合任意一款调试器,就可 以很快速地定位到出错点。 • 不过 at 需要额外检测下标是否越界,虽然更安0 码力 | 90 页 | 4.93 MB | 1 年前3
C++高性能并行编程与优化 - 课件 - 06 TBB 开启的并行编程之旅Git 2.x (作业上传到 GitHub ) CUDA Toolkit 10.0 以上( GPU 专题) 第 0 章:从并发到并行 摩尔定律:停止增长了吗? • 晶体管的密度的确仍在指数增长,但处理器主 频却开始停止增长了,甚至有所下降。 • 很长时间之前我们就可以达到 2GHz ( 2001 年 8 月),根据 2003 年的趋势,在 2005 年 初我们就应该研发出 10GHz 的芯片。 神话与现实: 2 * 3GHz < 6GHz • 一个由双核组成的 3GHz 的 CPU 实际上提供了 6GHz 的处理能力,是吗? • 显然不是。甚至在两个处理器上同时运行两个线程也不见得可以获得两倍的性能。相似的 ,大多数多线程的应用不会比双核处理器的两倍快。他们应该比单核处理器运行的快,但 是性能毕竟不是线性增长。 • 为什么无法做到呢?首先,为了保证缓存一致性以及其他握手协议需要运行时间开销。在 • 并发:单核处理器,操作系统通过时间片调 度算法,轮换着执行着不同的线程,看起来 就好像是同时运行一样,其实每一时刻只有 一个线程在运行。目的:异步地处理多个不 同的任务,避免同步造成的阻塞。 • 并行:多核处理器,每个处理器执行一个线 程,真正的同时运行。目的:将一个任务分 派到多个核上,从而更快完成任务。 举个例子 • 并发:某互联网公司购置了一台单核处理 器的服务器,他正同时处理0 码力 | 116 页 | 15.85 MB | 1 年前3
C++高性能并行编程与优化 - 课件 - 04 从汇编角度看编译器优化rsi, rdi, rsp, rbp, r8, r9, r10, r11, ..., r15 • 其中 r8 到 r15 是 64 位 x86 新增的寄存器,给了汇编程序员更大的空间,降低了编译 器处理寄存器翻车( register spill )的压力。 • 因此 64 位比 32 位机器相比,除了内存突破 4GB 限制外,也有一定性能优势。 8 位, 16 位, 32 位, 64 位版本 size_t 在 64 位系统上相当于 uint64_t size_t 在 32 位系统上相当于 uint32_t 从而不需要用 movslq 从 32 位符号扩展 到 64 位,更高效。而且也能处理数组大 小超过 INT_MAX 的情况,推荐始终用 size_t 表示数组大小和索引。 浮点作为参数和返回: xmm 系列寄存器 xmm0 = xmm0 + xmm1 参数分别通过 xmm0 为什么需要 SIMD ?单个指令处理四个数据 • 这种单个指令处理多个数据的技术称为 SIMD ( single-instruction multiple-data )。 • 他可以大大增加计算密集型程序的吞吐量。 • 因为 SIMD 把 4 个 float 打包到一个 xmm 寄存器里同时运算,很像数学中矢量的逐元 素加法。因此 SIMD 又被称为矢量,而原始的一次只能处理 1 个 float 的方式,则称为0 码力 | 108 页 | 9.47 MB | 1 年前3
C++高性能并行编程与优化 - 课件 - 01 学 C++ 从 CMake 学起可以自动检测源文件和头文件之间的依赖关系,导出到 Makefile 里。 • make 的语法非常简单,不像 shell 或 python 可以做很多判断等。 • CMake 具有相对高级的语法,内置的函数能够处理 configure , install 等常见需求。 • 不同的编译器有不同的 flag 规则,为 g++ 准备的参数可能对 MSVC 不适用。 • CMake 可以自动检测当前的编译器,需要添加哪些 。然后用一个小程序,自动在编译前把引号 内的文件名 hello.h 的内容插入到记号所在的位置,这样不就只用编辑 hello.h 一次了嘛 ~ • 后来,这个编译前替换的步骤逐渐变成编译器的了一部分,称为预处理阶段, #define 定 义的宏也是这个阶段处理的。 • 此外,在实现的文件 hello.cpp 中导入声明的文件 hello.h 是个好习惯,可以保证当 hello.cpp 被修改时,比如改成 hello(int) 声明了该类的头文件,像这样递归 地 #include 即可: 预处理后变成: 头文件进阶 - 递归地使用头文件(续) • 但是这样造成一个问题,就是如果多个头文件都引用了 MyClass.h ,那么 MyClass 会被 重复定义两遍: • 解决方案:在头文件前面加上一行: #pragma once • 这样当预处理器第二次读到同一个文件时,就会自动跳过 • 通常头文件都不想被重复导入,因此建议在每个头文件前加上这句话0 码力 | 32 页 | 11.40 MB | 1 年前3
基于 Rust Arrow Flight 的物联网和时序数据传输及转换工具 霍琳贺),专为物联网、工业互联网、金融、 IT 运维监控等场景设计并优化,具有极强的弹性伸缩能力。同时它还带有内建的缓存、流式计算、数据订阅等 系统功能,能大幅减少系统设计的复杂度,降低研发和运营成本,是一个极简的时序数据处理平台。 采用关系型数据库模型 需要建库、建表, 为提升写入和查询效率,要求一个数据采集点一张表 为实现多表聚合,引入超级表概念 子表通过超级表创建,带有标签,通过标签实现多表 大量设备大量数据归集存储,存储压力大 • 数据总线 / 消息队列消息接入,定制化程度要求高 • 数据业务逻辑自定义需求强 • 一定的实时数据分析能力 taosX - 功能路线图 集群运维 数据接入 流式处理 流式处理 数据分享 开放平台 • Backup/Restore • Replication • Migration • Data Sources • IoT Protocols • Streaming taosX - 集群运维 • 数据库复制 • 全量 / 增量备份 • 数据导入 / 导出 • 数据库迁移 • 异地容灾 taosX - 数据接入 Comming Soon taosX - 流式处理 taosX - Transformer • Parse {"parse": {"field1": { "cast": { "as": "timestamp", "with": "%e-%b-%Y0 码力 | 29 页 | 2.26 MB | 1 年前3
共 26 条
- 1
- 2
- 3













