Skip to content

Commit aff4729

Browse files
feature: vol3 migration ends and aligned up mostly passages (#79)
* feat: migrate the rest passages vol3 requested * feat: add dev docs * migrate the PR77 optimizations * add english version docs * chore: untrack internal notebook migration plan * fix: align reading_time_minutes with estimated (CI content quality)
1 parent dd5eeae commit aff4729

203 files changed

Lines changed: 45757 additions & 8997 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
---
2121

2222
<!-- COVERAGE_START -->
23-
![English Coverage](https://img.shields.io/badge/en_coverage-95%25-green.svg) 440/461 docs translated
23+
![English Coverage](https://img.shields.io/badge/en_coverage-100%25-green.svg) 492/492 docs translated
2424
<!-- COVERAGE_END -->
2525

2626
## 这是什么项目
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
// Standard: C++20
2+
// 容器选择 · 内存局部性 benchmark:vector(连续内存)vs list(节点式)遍历耗时
3+
// 两者遍历都是 O(n)、每次加法都是 O(1),但 vector 的连续内存吃满 cache,
4+
// list 的每个节点都要单独访存——实测两边耗时差几倍,这就是「默认用 vector」的底层理由。
5+
#include <chrono>
6+
#include <cstdio>
7+
#include <list>
8+
#include <vector>
9+
10+
int main() {
11+
constexpr int N = 1'000'000;
12+
std::vector<int> v(N);
13+
std::list<int> l;
14+
for (int i = 0; i < N; ++i) {
15+
v[i] = i;
16+
l.push_back(i);
17+
}
18+
19+
volatile long long sink = 0;
20+
21+
auto t0 = std::chrono::high_resolution_clock::now();
22+
long long sv = 0;
23+
for (auto x : v) {
24+
sv += x;
25+
}
26+
sink += sv;
27+
auto t1 = std::chrono::high_resolution_clock::now();
28+
29+
long long sl = 0;
30+
for (auto x : l) {
31+
sl += x;
32+
}
33+
sink += sl;
34+
auto t2 = std::chrono::high_resolution_clock::now();
35+
36+
auto us_v = std::chrono::duration_cast<std::chrono::microseconds>(t1 - t0).count();
37+
auto us_l = std::chrono::duration_cast<std::chrono::microseconds>(t2 - t1).count();
38+
std::printf("vector 遍历 %lld us, list 遍历 %lld us, list 慢 %.2fx\n", us_v, us_l,
39+
us_v ? (double)us_l / us_v : 0.0);
40+
return 0;
41+
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
// Standard: C++20
2+
// 迭代器 category:用 C++20 concept 在编译期给各种容器的迭代器「量等级」
3+
// 五层强弱(input→forward→bidirectional→random_access→contiguous),是/否一目了然,
4+
// 也能解释为什么 std::sort 用得了 vector 却用不了 list。
5+
#include <array>
6+
#include <forward_list>
7+
#include <iostream>
8+
#include <iterator>
9+
#include <list>
10+
#include <set>
11+
#include <string>
12+
#include <vector>
13+
14+
static const char* yn(bool v) {
15+
return v ? "" : "";
16+
}
17+
18+
template <class It> void print_row(const char* lbl) {
19+
std::cout << lbl << " " << yn(std::input_iterator<It>) << " "
20+
<< yn(std::forward_iterator<It>) << " " << yn(std::bidirectional_iterator<It>)
21+
<< " " << yn(std::random_access_iterator<It>) << " "
22+
<< yn(std::contiguous_iterator<It>) << '\n';
23+
}
24+
25+
int main() {
26+
std::cout << "类型 input forward bidi rand contig\n";
27+
print_row<std::vector<int>::iterator>("vector<int>::iterator ");
28+
print_row<std::array<int, 4>::iterator>("array<int,4>::iterator ");
29+
print_row<std::string::iterator>("string::iterator ");
30+
print_row<int*>("int* (裸指针) ");
31+
print_row<std::list<int>::iterator>("list<int>::iterator ");
32+
print_row<std::set<int>::iterator>("set<int>::iterator ");
33+
print_row<std::forward_list<int>::iterator>("forward_list::iterator ");
34+
return 0;
35+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
// Standard: C++20
2+
// 二分 vs 线性查找:一千万个有序元素,最坏情况(目标在末尾)对比
3+
// std::find(O(n))和 std::binary_search(O(log n))。
4+
// 线性 find 扫到底落毫秒级,二分几次比较落微秒级——这是「有序」的红利(前提是真的有序)。
5+
#include <algorithm>
6+
#include <chrono>
7+
#include <iostream>
8+
#include <vector>
9+
10+
int main() {
11+
constexpr int kN = 10'000'000;
12+
std::vector<int> v(kN);
13+
for (int i = 0; i < kN; ++i)
14+
v[i] = i; // 已升序
15+
16+
int target = kN - 1; // 最坏情况:在末尾
17+
18+
auto t1 = std::chrono::high_resolution_clock::now();
19+
bool found_lin = std::find(v.begin(), v.end(), target) != v.end();
20+
auto t2 = std::chrono::high_resolution_clock::now();
21+
bool found_bin = std::binary_search(v.begin(), v.end(), target);
22+
auto t3 = std::chrono::high_resolution_clock::now();
23+
24+
auto ns_lin = std::chrono::duration_cast<std::chrono::nanoseconds>(t2 - t1).count();
25+
auto ns_bin = std::chrono::duration_cast<std::chrono::nanoseconds>(t3 - t2).count();
26+
27+
std::cout << "find (O(n)) " << found_lin << " 耗时 " << ns_lin << " ns\n";
28+
std::cout << "binary_search (O(log n)) " << found_bin << " 耗时 " << ns_bin << " ns\n";
29+
std::cout << "倍数差距: " << (ns_bin > 0 ? ns_lin / ns_bin : -1) << "x\n";
30+
return 0;
31+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
// Standard: C++20
2+
// std::accumulate 最坑的地方:返回类型 = 初始值类型。
3+
// 初始值 0(int)会把 double 元素截断成 int 累加;0.0(double)才保留小数。
4+
// 数学上 1.5+2.5+3.5+4.5 = 12.0,但 accumulate(v, 0) 给你 10。
5+
#include <iostream>
6+
#include <numeric>
7+
#include <vector>
8+
9+
int main() {
10+
std::vector<double> v{1.5, 2.5, 3.5, 4.5}; // 数学和 = 12.0
11+
std::cout << "accumulate(v, 0): " << std::accumulate(v.begin(), v.end(), 0) << '\n';
12+
std::cout << "accumulate(v, 0.0): " << std::accumulate(v.begin(), v.end(), 0.0) << '\n';
13+
return 0;
14+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
// Standard: C++17
2+
// std::reduce 执行策略的代码生成对比:seq vs par
3+
// 只编译看汇编(allow-x86-asm,不运行故无需 -ltbb 链接):
4+
// seq 版是一段标量累加循环;
5+
// par 版是一串 call __gnu_parallel / TBB 运行时——
6+
// 这就是「libstdc++ 并行后端是 TBB」在汇编层的证据。
7+
#include <execution>
8+
#include <numeric>
9+
#include <vector>
10+
11+
long sum_seq(const std::vector<long>& v) {
12+
return std::reduce(std::execution::seq, v.begin(), v.end(), 0L);
13+
}
14+
15+
long sum_par(const std::vector<long>& v) {
16+
return std::reduce(std::execution::par, v.begin(), v.end(), 0L);
17+
}
18+
19+
int main() {
20+
std::vector<long> v(1000, 1);
21+
volatile long sink = sum_seq(v) + sum_par(v);
22+
return sink != 0;
23+
}
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
// Standard: C++20
2+
// string_view 零拷贝传参 vs const string& 的性能对比
3+
// 同一个 90 字节 payload(稳超 SSO),紧循环多次调用:
4+
// const string& 路径每次都构造临时 string,string_view 路径零分配——差距来自这。
5+
#include <chrono>
6+
#include <cstdio>
7+
#include <string>
8+
#include <string_view>
9+
10+
static const char* kPayload =
11+
"The quick brown fox jumps over the lazy dog - a non-trivial string payload.";
12+
13+
long count_vowels_ref(const std::string& s) {
14+
long n = 0;
15+
for (char c : s) {
16+
if (c == 'a' || c == 'e' || c == 'i' || c == 'o' || c == 'u')
17+
++n;
18+
}
19+
return n;
20+
}
21+
22+
long count_vowels_sv(std::string_view sv) {
23+
long n = 0;
24+
for (char c : sv) {
25+
if (c == 'a' || c == 'e' || c == 'i' || c == 'o' || c == 'u')
26+
++n;
27+
}
28+
return n;
29+
}
30+
31+
int main() {
32+
constexpr int kIters = 50'000'000;
33+
volatile long sink = 0;
34+
35+
auto t0 = std::chrono::steady_clock::now();
36+
for (int i = 0; i < kIters; ++i)
37+
sink += count_vowels_ref(kPayload);
38+
auto t1 = std::chrono::steady_clock::now();
39+
for (int i = 0; i < kIters; ++i)
40+
sink += count_vowels_sv(kPayload);
41+
auto t2 = std::chrono::steady_clock::now();
42+
43+
auto ms_ref = std::chrono::duration_cast<std::chrono::milliseconds>(t1 - t0).count();
44+
auto ms_sv = std::chrono::duration_cast<std::chrono::milliseconds>(t2 - t1).count();
45+
std::printf("const string& path: %lld ms\n", static_cast<long long>(ms_ref));
46+
std::printf("string_view path: %lld ms\n", static_cast<long long>(ms_sv));
47+
std::printf("ratio (ref/sv): %.2fx\n", ms_sv ? static_cast<double>(ms_ref) / ms_sv : 0.0);
48+
return 0;
49+
}
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
// Standard: C++23
2+
// std::print vs cout(sync 开/关) vs printf 的格式化性能对比
3+
// 200 万条短行写到 /dev/null(排除终端 I/O 噪声,只测格式化与缓冲),
4+
// 耗时打到 stderr——print 绕开 cout 的虚调用 + sync 开销,通常最快。
5+
#include <chrono>
6+
#include <cstdio>
7+
#include <iostream>
8+
#include <print>
9+
10+
constexpr int kIterations = 2'000'000;
11+
12+
static void report(const char* name, std::chrono::steady_clock::time_point t0,
13+
std::chrono::steady_clock::time_point t1) {
14+
auto us = std::chrono::duration_cast<std::chrono::microseconds>(t1 - t0).count();
15+
std::fprintf(stderr, "%-22s %lld us\n", name, (long long)us);
16+
}
17+
18+
void bench_cout_sync() {
19+
std::ios::sync_with_stdio(true); // 默认
20+
auto t0 = std::chrono::steady_clock::now();
21+
for (int i = 0; i < kIterations; ++i) {
22+
std::cout << "i=" << i << " sq=" << i * 2 << '\n';
23+
}
24+
report("cout (sync=true)", t0, std::chrono::steady_clock::now());
25+
}
26+
27+
void bench_cout_nosync() {
28+
std::ios::sync_with_stdio(false); // 常见的"加速 cout"写法
29+
auto t0 = std::chrono::steady_clock::now();
30+
for (int i = 0; i < kIterations; ++i) {
31+
std::cout << "i=" << i << " sq=" << i * 2 << '\n';
32+
}
33+
report("cout (sync=false)", t0, std::chrono::steady_clock::now());
34+
}
35+
36+
void bench_printf() {
37+
auto t0 = std::chrono::steady_clock::now();
38+
for (int i = 0; i < kIterations; ++i) {
39+
std::printf("i=%d sq=%d\n", i, i * 2);
40+
}
41+
report("printf", t0, std::chrono::steady_clock::now());
42+
}
43+
44+
void bench_print() {
45+
auto t0 = std::chrono::steady_clock::now();
46+
for (int i = 0; i < kIterations; ++i) {
47+
std::print("i={} sq={}\n", i, i * 2);
48+
}
49+
report("print", t0, std::chrono::steady_clock::now());
50+
}
51+
52+
int main() {
53+
std::freopen("/dev/null", "w", stdout); // 排除终端 I/O 噪声
54+
bench_cout_sync();
55+
bench_printf();
56+
bench_print();
57+
bench_cout_nosync();
58+
return 0;
59+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
// Standard: C++17
2+
// 病态回溯(catastrophic backtracking):模式 (a+)+b 喂 n 个 a(结尾不给 b),
3+
// 回溯 NFA 对同一批 a 反复试各种分组组合,输入每长一点耗时翻几番——指数级。
4+
// 注:n=28 实测约 22 秒(在线运行会超时),这里只跑到 n=24 展示增长趋势。
5+
#include <chrono>
6+
#include <iostream>
7+
#include <regex>
8+
#include <string>
9+
10+
int main() {
11+
std::regex bad_re(R"((a+)+b)");
12+
for (int n : {16, 20, 24}) {
13+
std::string s(static_cast<std::size_t>(n), 'a'); // n 个 a,结尾没有 b
14+
auto t0 = std::chrono::steady_clock::now();
15+
bool m = std::regex_match(s, bad_re);
16+
auto t1 = std::chrono::steady_clock::now();
17+
auto ms = std::chrono::duration_cast<std::chrono::milliseconds>(t1 - t0).count();
18+
std::cout << "n=" << n << " matched=" << std::boolalpha << m << " " << ms << " ms\n";
19+
}
20+
return 0;
21+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
// Standard: C++20
2+
// NaN 不等于自己:IEEE 754 规定 NaN 与任何值(含自身)比较都返回 false。
3+
// 所以判 NaN 永远只能用 std::isnan,绝不能用 ==——这是浮点世界最经典的坑。
4+
#include <cmath>
5+
#include <iostream>
6+
#include <limits>
7+
8+
int main() {
9+
double nan = std::numeric_limits<double>::quiet_NaN();
10+
std::cout << std::boolalpha;
11+
std::cout << "NaN == NaN? " << (nan == nan) << '\n';
12+
std::cout << "NaN != NaN? " << (nan != nan) << '\n';
13+
std::cout << "isnan(NaN)? " << std::isnan(nan) << " (判 NaN 的正确写法)\n";
14+
return 0;
15+
}

0 commit comments

Comments
 (0)