现代C++ 教程:高速上手C++11/14/17/20正则表达式描述了一种字符串匹配的模式。一般使用正则表达式主要是实现下面三个需求: 1. 检查一个串是否包含某种形式的子串; 2. 将匹配的子串替换; 3. 从某个串中取出符合条件的子串。 正则表达式是由普通字符(例如 a 到 z)以及特殊字符组成的文字模式。模式描述在搜索文本时要 匹配的一个或多个字符串。正则表达式作为一个模板,将某个字符模式与所搜索的字符串进行匹配。 57 6.1 正则表达式简介 特殊字符是正则表达式里有特殊含义的字符,也是正则表达式的核心匹配语法。参见下表: 特别字 符 描述 $ 匹配输入字符串的结尾位置。 (,) 标记一个子表达式的开始和结束位置。子表达式可以获取供以后使用。 * 匹配前面的子表达式零次或多次。 + 匹配前面的子表达式一次或多次。 . 匹配除换行符 \n 之外的任何单字符。 [ 标记一个中括号表达式的开始。 ? 匹配前面的子表达式零次或一次,或指明一个非贪婪限定符。 。例如,n 匹配字符 n。\n 匹配换行符。序列 \\ 匹配 '\' 字符,而 \( 则匹配 '(' 字符。 ˆ 匹配输入字符串的开始位置,除非在方括号表达式中使用,此时它表示不接受该字符集 合。 { 标记限定符表达式的开始。 \| 指明两项之间的一个选择。 限定符 限定符用来指定正则表达式的一个给定的组件必须要出现多少次才能满足匹配。见下表: 字符 描述 * 匹配前面的子表达式零次或多次。例如,foo*0 码力 | 83 页 | 2.42 MB | 1 年前3
C++高性能并行编程与优化 - 课件 - 03 现代 C++ 进阶:模板元编程• 但是却没有出错,这是因为模板没有被调用,所以不会被实际编译! • 而只有当 main 调用了这个函数,才会被编译,才会报错! • 用一个假模板实现延迟编译的技术,可以加快编译的速度,用于代理模式等。 模板函数:一个例子 • 比如,要打印任意一个 vector : 模板函数:配合运算符重载 • 实现用 std::cout << a 打印任意 vector : 模板函数:大家学废了吗! const & ) • 同理, auto const & 可以定义常引用: 自动类型推导:函数返回引用 • 当然,函数的返回类型也可以是 auto & 或者 auto const & 。比如懒汉单例模式: 理解右值:即将消失的,不长时间存在于内存中的值 • 引用又称为左值( l-value )。左值通常对应着一个长时间存在于内 存中的变量。 • 除了左值之外,还有右值( r-value )。右值通常是一个表达式,代 decltype(auto) p = func(); • 会自动推导为 func() 的返回类型。 • 和下面这种方式等价: • decltype(func()) p = func(); • 在代理模式中,用于完美转发函数返回值。比如: • decltype(auto) at(size_t i) const { • return m_internal_class.at(i); } using0 码力 | 82 页 | 12.15 MB | 1 年前3
C++高性能并行编程与优化 - 课件 - 16 现代 CMake 模块化项目管理指南,他们 分别在各自的目录下有自己的 CMakeLists.txt 。 二、根项目的 CMakeLists.txt 配置 • 在根项目的 CMakeLists.txt 中,设置了默 认的构建模式,设置了统一的 C++ 版本 等各种选项。然后通过 project 命令初始 化了根项目。 • 随后通过 add_subdirectory 把两个子项 目 pybmain 和 biology src/*.cpp) • 疑问 1 :都是按照通配符批量匹配文件,有什么区别? • GLOB : src/main.cpp (√) src/test/main.cpp ( × ) • GLOB_RECURSE : src/main.cpp (√) src/test/main.cpp (√) • 区别在于 GLOB_RECURSE 允许 * 匹配嵌套的目录。 • 疑问 2 :加了 CONFIGURE_DEPENDS IMPORTED Targets 章节是在介绍现代 的用法,而 Result Variables 章节是在介绍 古代的用法,我们尽量用现代的那种就行。 官方文档: find_package 的两种模式 指定使用哪种模式 • find_package(TBB MODULE REQUIRED) • 只会寻找 FindTBB.cmake ,搜索路径: 1. ${CMAKE_MODULE_PATH} (默认为0 码力 | 56 页 | 6.87 MB | 1 年前3
C++高性能并行编程与优化 - 课件 - 08 CUDA 开启的 GPU 编程编译器生成 GPU 指令码。最后再链接成同一个文件 ,看起来好像只编译了一次一样,实际上你的代码会被预处理很 多次。 • 他在 GPU 编译模式下会定义 __CUDA_ARCH__ 这个宏,利用 #ifdef 判断该宏是否定义,就可以判断当前是否处于 GPU 模式 ,从而实现一个函数针对 GPU 和 CPU 生成两份源码级不同的 代码。 __CUDA_ARCH__ 是个版本号 • 其实 __CUDA_ARCH__ CPU 再进行调用,这是 CUDA 特有的能力。 常用于这种情况:需要从 GPU 端动态计算出 blockDim 和 gridDim ,而又不希望导回数据到 CPU 导致强制同步影响性能。 这种模式被称为动态并行( dynamic parallelism ), OpenGL 有一 个 glDispatchComputeIndirect 的 API 和这个很像,但毕竟没有 CUDA 可以直接在 blockDim ),都能自动根据给定的 n 区间循环,不会越界,也不会漏掉几个元 素。 • 这样一个 for 循环非常符合 CPU 上常见 的 parallel for 的习惯,又能自动匹配不同 的 blockDim ,看起来非常方便。 从线程到板块 • 核函数内部,用之前说到的 blockDim.x + blockIdx.x + threadIdx.x 来获取线程在整个 网格中编号。0 码力 | 142 页 | 13.52 MB | 1 年前3
C++高性能并行编程与优化 - 课件 - 07 深入浅出访存优化uint64_t address; • char data[64]; • }; • CacheEntry cache[512]; • 当 CPU 读取一个地址时: • 缓存会查找和该地址匹配的条目。如果找到,则给 CPU 返 回缓存中的数据。如果找不到,则向主内存发送请求,等读 取到该地址的数据,就创建一个新条目。 • 在 x86 架构中每个条目的存储 64 字节的数据,这个条目 uint64_t address; • char data[64]; • }; • CacheEntry cache[512]; • 当 CPU 写入一个地址时: • 缓存会查找和该地址匹配的条目。如果找到,则修改缓存 中该地址的数据。如果找不到,则创建一个新条目来存储 CPU 写的数据,并标记为脏( dirty )。 • 当读和写创建的新条目过多,缓存快要塞不下时,他会把 最 4KB ?原来现在操作系统管理内存是用分页 ( page ),程序的内存是一页一页贴在地址空间中的, 有些地方可能不可访问,或者还没有分配,则把这个页设 为不可用状态,访问他就会出错,进入内核模式。 • 因此硬件出于安全,预取不能跨越页边界,否则可能会触 发不必要的 page fault 。所以我们选用页的大小,因为本 来就不能跨页顺序预取,所以被我们切断掉也无所谓。 • 另外,我们可以用0 码力 | 147 页 | 18.88 MB | 1 年前3
C++高性能并行编程与优化 - 课件 - 02 现代 C++ 入门:RAII 内存管理这样就可以在编译期提前发现错误: 解决方案:要么定义 • 如果需要允许用户拷贝你的 Vector 类对象 ,我们还是需要实现一下的。 • 发现了吗?其实不管是 size/resize 这样的 get/set 模式也好;自定义的拷贝构造函数 也好; RAII 保证异常安全也好;都是在为 面向对象思想的“封装:不变性”服务。 • 即:保证任何单个操作前后,对象都是处于 正确的状态,从而避免程序读到错误数据 • 因为他们的业务需求大多是:打开数据库,增删改查学生数据,打开一个窗口,写入一个 文件,正则匹配是不是电邮地址,应答 HTTP 请求等。 • 这些业务往往都是在和资源打交道,从而基本都是刚刚说的要删除拷贝函数的那一类,解 决这种需求,几乎总是在用 shared_ptr的模式,于是 Java 和 Python 干 脆简化:一切非基础类型的对象都是浅拷贝,引用计数由垃圾回收机制自动管理。 多线程等概念作为语言基本元素存在。这些在我们的 业务里面是非常重要的,所以不可替代。 • (试图升华文章中心主旨) 扩展阅读关键字 • 限于篇幅,此处放出一些扩展知识供学有余力的同学研究: 1. P-IMPL 模式 2. 虚函数与纯虚函数 3. 拷贝如何作为虚函数 4. std::unique_ptr::release() 5. std::enable_shared_from_this 6. dynamic_cast 0 码力 | 96 页 | 16.28 MB | 1 年前3
Hello 算法 1.1.0 C++ 版通过索引遍历数组 for (int i = 0; i < size; i++) { count += nums[i]; } } 6. 查找元素 在数组中查找指定元素需要遍历数组,每轮判断元素值是否匹配,若匹配则输出对应索引。 因为数组是线性数据结构,所以上述查找操作被称为“线性查找”。 // === File: array.cpp === /* 在数组中查找指定元素 */ int find(int 载机制。 ‧ 缓存行:缓存不是单个字节地存储与加载数据,而是以缓存行为单位。相比于单个字节的传输,缓存行 的传输形式更加高效。 ‧ 预取机制:处理器会尝试预测数据访问模式(例如顺序访问、固定步长跳跃访问等),并根据特定模式 将数据加载至缓存之中,从而提升命中率。 ‧ 空间局部性:如果一个数据被访问,那么它附近的数据可能近期也会被访问。因此,缓存在加载某一数 据时,也会加载其附近的数据,以提高命中率。 占用空间:链表元素比数组元素占用空间更多,导致缓存中容纳的有效数据量更少。 ‧ 缓存行:链表数据分散在内存各处,而缓存是“按行加载”的,因此加载到无效数据的比例更高。 ‧ 预取机制:数组比链表的数据访问模式更具“可预测性”,即系统更容易猜出即将被加载的数据。 ‧ 空间局部性:数组被存储在集中的内存空间中,因此被加载数据附近的数据更有可能即将被访问。 总体而言,数组具有更高的缓存命中率,因此它在操作0 码力 | 379 页 | 18.47 MB | 1 年前3
Hello 算法 1.0.0 C++版通过索引遍历数组 for (int i = 0; i < size; i++) { count += nums[i]; } } 6. 查找元素 在数组中查找指定元素需要遍历数组,每轮判断元素值是否匹配,若匹配则输出对应索引。 因为数组是线性数据结构,所以上述查找操作被称为“线性查找”。 // === File: array.cpp === /* 在数组中查找指定元素 */ int find(int 载机制。 ‧ 缓存行:缓存不是单个字节地存储与加载数据,而是以缓存行为单位。相比于单个字节的传输,缓存行 的传输形式更加高效。 ‧ 预取机制:处理器会尝试预测数据访问模式(例如顺序访问、固定步长跳跃访问等),并根据特定模式 将数据加载至缓存之中,从而提升命中率。 ‧ 空间局部性:如果一个数据被访问,那么它附近的数据可能近期也会被访问。因此,缓存在加载某一数 据时,也会加载其附近的数据,以提高命中率。 占用空间:链表元素比数组元素占用空间更多,导致缓存中容纳的有效数据量更少。 ‧ 缓存行:链表数据分散在内存各处,而缓存是“按行加载”的,因此加载到无效数据的比例更高。 ‧ 预取机制:数组比链表的数据访问模式更具“可预测性”,即系统更容易猜出即将被加载的数据。 ‧ 空间局部性:数组被存储在集中的内存空间中,因此被加载数据附近的数据更有可能即将被访问。 总体而言,数组具有更高的缓存命中率,因此它在操作0 码力 | 378 页 | 17.59 MB | 1 年前3
Hello 算法 1.2.0 简体中文 C++ 版通过索引遍历数组 for (int i = 0; i < size; i++) { count += nums[i]; } } 6. 查找元素 在数组中查找指定元素需要遍历数组,每轮判断元素值是否匹配,若匹配则输出对应索引。 因为数组是线性数据结构,所以上述查找操作被称为“线性查找”。 // === File: array.cpp === /* 在数组中查找指定元素 */ int find(int 载机制。 ‧ 缓存行:缓存不是单个字节地存储与加载数据,而是以缓存行为单位。相比于单个字节的传输,缓存行 的传输形式更加高效。 ‧ 预取机制:处理器会尝试预测数据访问模式(例如顺序访问、固定步长跳跃访问等),并根据特定模式 将数据加载至缓存之中,从而提升命中率。 ‧ 空间局部性:如果一个数据被访问,那么它附近的数据可能近期也会被访问。因此,缓存在加载某一数 据时,也会加载其附近的数据,以提高命中率。 占用空间:链表元素比数组元素占用空间更多,导致缓存中容纳的有效数据量更少。 ‧ 缓存行:链表数据分散在内存各处,而缓存是“按行加载”的,因此加载到无效数据的比例更高。 ‧ 预取机制:数组比链表的数据访问模式更具“可预测性”,即系统更容易猜出即将被加载的数据。 ‧ 空间局部性:数组被存储在集中的内存空间中,因此被加载数据附近的数据更有可能即将被访问。 总体而言,数组具有更高的缓存命中率,因此它在操作0 码力 | 379 页 | 18.48 MB | 10 月前3
Hello 算法 1.2.0 繁体中文 C++ 版透過索引走訪陣列 for (int i = 0; i < size; i++) { count += nums[i]; } } 6. 查詢元素 在陣列中查詢指定元素需要走訪陣列,每輪判斷元素值是否匹配,若匹配則輸出對應索引。 因為陣列是線性資料結構,所以上述查詢操作被稱為“線性查詢”。 // === File: array.cpp === /* 在陣列中查詢指定元素 */ int find(int 制。 ‧ 快取行:快取不是單個位元組地儲存與載入資料,而是以快取行為單位。相比於單個位元組的傳輸,快 取行的傳輸形式更加高效。 ‧ 預取機制:處理器會嘗試預測資料訪問模式(例如順序訪問、固定步長跳躍訪問等),並根據特定模式 將資料載入至快取之中,從而提升命中率。 ‧ 空間區域性:如果一個數據被訪問,那麼它附近的資料可能近期也會被訪問。因此,快取在載入某一資 料時,也會載入其附近的資料,以提高命中率。 佔用空間:鏈結串列元素比陣列元素佔用空間更多,導致快取中容納的有效資料量更少。 ‧ 快取行:鏈結串列資料分散在記憶體各處,而快取是“按行載入”的,因此載入到無效資料的比例更高。 ‧ 預取機制:陣列比鏈結串列的資料訪問模式更具“可預測性”,即系統更容易猜出即將被載入的資料。 ‧ 空間區域性:陣列被儲存在集中的記憶體空間中,因此被載入資料附近的資料更有可能即將被訪問。 總體而言,陣列具有更高的快取命中率,因此它在操0 码力 | 379 页 | 18.79 MB | 10 月前3
共 24 条
- 1
- 2
- 3













