Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
---

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

## 这是什么项目
Expand Down
41 changes: 41 additions & 0 deletions code/examples/vol3/01_container_cache_benchmark.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
// Standard: C++20
// 容器选择 · 内存局部性 benchmark:vector(连续内存)vs list(节点式)遍历耗时
// 两者遍历都是 O(n)、每次加法都是 O(1),但 vector 的连续内存吃满 cache,
// list 的每个节点都要单独访存——实测两边耗时差几倍,这就是「默认用 vector」的底层理由。
#include <chrono>
#include <cstdio>
#include <list>
#include <vector>

int main() {
constexpr int N = 1'000'000;
std::vector<int> v(N);
std::list<int> l;
for (int i = 0; i < N; ++i) {
v[i] = i;
l.push_back(i);
}

volatile long long sink = 0;

auto t0 = std::chrono::high_resolution_clock::now();
long long sv = 0;
for (auto x : v) {
sv += x;
}
sink += sv;
auto t1 = std::chrono::high_resolution_clock::now();

long long sl = 0;
for (auto x : l) {
sl += x;
}
sink += sl;
auto t2 = std::chrono::high_resolution_clock::now();

auto us_v = std::chrono::duration_cast<std::chrono::microseconds>(t1 - t0).count();
auto us_l = std::chrono::duration_cast<std::chrono::microseconds>(t2 - t1).count();
std::printf("vector 遍历 %lld us, list 遍历 %lld us, list 慢 %.2fx\n", us_v, us_l,
us_v ? (double)us_l / us_v : 0.0);
return 0;
}
35 changes: 35 additions & 0 deletions code/examples/vol3/40_iterator_categories.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
// Standard: C++20
// 迭代器 category:用 C++20 concept 在编译期给各种容器的迭代器「量等级」
// 五层强弱(input→forward→bidirectional→random_access→contiguous),是/否一目了然,
// 也能解释为什么 std::sort 用得了 vector 却用不了 list。
#include <array>
#include <forward_list>
#include <iostream>
#include <iterator>
#include <list>
#include <set>
#include <string>
#include <vector>

static const char* yn(bool v) {
return v ? "是" : "否";
}

template <class It> void print_row(const char* lbl) {
std::cout << lbl << " " << yn(std::input_iterator<It>) << " "
<< yn(std::forward_iterator<It>) << " " << yn(std::bidirectional_iterator<It>)
<< " " << yn(std::random_access_iterator<It>) << " "
<< yn(std::contiguous_iterator<It>) << '\n';
}

int main() {
std::cout << "类型 input forward bidi rand contig\n";
print_row<std::vector<int>::iterator>("vector<int>::iterator ");
print_row<std::array<int, 4>::iterator>("array<int,4>::iterator ");
print_row<std::string::iterator>("string::iterator ");
print_row<int*>("int* (裸指针) ");
print_row<std::list<int>::iterator>("list<int>::iterator ");
print_row<std::set<int>::iterator>("set<int>::iterator ");
print_row<std::forward_list<int>::iterator>("forward_list::iterator ");
return 0;
}
31 changes: 31 additions & 0 deletions code/examples/vol3/42_binary_vs_linear.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// Standard: C++20
// 二分 vs 线性查找:一千万个有序元素,最坏情况(目标在末尾)对比
// std::find(O(n))和 std::binary_search(O(log n))。
// 线性 find 扫到底落毫秒级,二分几次比较落微秒级——这是「有序」的红利(前提是真的有序)。
#include <algorithm>
#include <chrono>
#include <iostream>
#include <vector>

int main() {
constexpr int kN = 10'000'000;
std::vector<int> v(kN);
for (int i = 0; i < kN; ++i)
v[i] = i; // 已升序

int target = kN - 1; // 最坏情况:在末尾

auto t1 = std::chrono::high_resolution_clock::now();
bool found_lin = std::find(v.begin(), v.end(), target) != v.end();
auto t2 = std::chrono::high_resolution_clock::now();
bool found_bin = std::binary_search(v.begin(), v.end(), target);
auto t3 = std::chrono::high_resolution_clock::now();

auto ns_lin = std::chrono::duration_cast<std::chrono::nanoseconds>(t2 - t1).count();
auto ns_bin = std::chrono::duration_cast<std::chrono::nanoseconds>(t3 - t2).count();

std::cout << "find (O(n)) " << found_lin << " 耗时 " << ns_lin << " ns\n";
std::cout << "binary_search (O(log n)) " << found_bin << " 耗时 " << ns_bin << " ns\n";
std::cout << "倍数差距: " << (ns_bin > 0 ? ns_lin / ns_bin : -1) << "x\n";
return 0;
}
14 changes: 14 additions & 0 deletions code/examples/vol3/44_accumulate_truncation.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// Standard: C++20
// std::accumulate 最坑的地方:返回类型 = 初始值类型。
// 初始值 0(int)会把 double 元素截断成 int 累加;0.0(double)才保留小数。
// 数学上 1.5+2.5+3.5+4.5 = 12.0,但 accumulate(v, 0) 给你 10。
#include <iostream>
#include <numeric>
#include <vector>

int main() {
std::vector<double> v{1.5, 2.5, 3.5, 4.5}; // 数学和 = 12.0
std::cout << "accumulate(v, 0): " << std::accumulate(v.begin(), v.end(), 0) << '\n';
std::cout << "accumulate(v, 0.0): " << std::accumulate(v.begin(), v.end(), 0.0) << '\n';
return 0;
}
23 changes: 23 additions & 0 deletions code/examples/vol3/46_parallel_asm.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
// Standard: C++17
// std::reduce 执行策略的代码生成对比:seq vs par
// 只编译看汇编(allow-x86-asm,不运行故无需 -ltbb 链接):
// seq 版是一段标量累加循环;
// par 版是一串 call __gnu_parallel / TBB 运行时——
// 这就是「libstdc++ 并行后端是 TBB」在汇编层的证据。
#include <execution>
#include <numeric>
#include <vector>

long sum_seq(const std::vector<long>& v) {
return std::reduce(std::execution::seq, v.begin(), v.end(), 0L);
}

long sum_par(const std::vector<long>& v) {
return std::reduce(std::execution::par, v.begin(), v.end(), 0L);
}

int main() {
std::vector<long> v(1000, 1);
volatile long sink = sum_seq(v) + sum_par(v);
return sink != 0;
}
49 changes: 49 additions & 0 deletions code/examples/vol3/50_string_view_benchmark.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
// Standard: C++20
// string_view 零拷贝传参 vs const string& 的性能对比
// 同一个 90 字节 payload(稳超 SSO),紧循环多次调用:
// const string& 路径每次都构造临时 string,string_view 路径零分配——差距来自这。
#include <chrono>
#include <cstdio>
#include <string>
#include <string_view>

static const char* kPayload =
"The quick brown fox jumps over the lazy dog - a non-trivial string payload.";

long count_vowels_ref(const std::string& s) {
long n = 0;
for (char c : s) {
if (c == 'a' || c == 'e' || c == 'i' || c == 'o' || c == 'u')
++n;
}
return n;
}

long count_vowels_sv(std::string_view sv) {
long n = 0;
for (char c : sv) {
if (c == 'a' || c == 'e' || c == 'i' || c == 'o' || c == 'u')
++n;
}
return n;
}

int main() {
constexpr int kIters = 50'000'000;
volatile long sink = 0;

auto t0 = std::chrono::steady_clock::now();
for (int i = 0; i < kIters; ++i)
sink += count_vowels_ref(kPayload);
auto t1 = std::chrono::steady_clock::now();
for (int i = 0; i < kIters; ++i)
sink += count_vowels_sv(kPayload);
auto t2 = std::chrono::steady_clock::now();

auto ms_ref = std::chrono::duration_cast<std::chrono::milliseconds>(t1 - t0).count();
auto ms_sv = std::chrono::duration_cast<std::chrono::milliseconds>(t2 - t1).count();
std::printf("const string& path: %lld ms\n", static_cast<long long>(ms_ref));
std::printf("string_view path: %lld ms\n", static_cast<long long>(ms_sv));
std::printf("ratio (ref/sv): %.2fx\n", ms_sv ? static_cast<double>(ms_ref) / ms_sv : 0.0);
return 0;
}
59 changes: 59 additions & 0 deletions code/examples/vol3/53_print_benchmark.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
// Standard: C++23
// std::print vs cout(sync 开/关) vs printf 的格式化性能对比
// 200 万条短行写到 /dev/null(排除终端 I/O 噪声,只测格式化与缓冲),
// 耗时打到 stderr——print 绕开 cout 的虚调用 + sync 开销,通常最快。
#include <chrono>
#include <cstdio>
#include <iostream>
#include <print>

constexpr int kIterations = 2'000'000;

static void report(const char* name, std::chrono::steady_clock::time_point t0,
std::chrono::steady_clock::time_point t1) {
auto us = std::chrono::duration_cast<std::chrono::microseconds>(t1 - t0).count();
std::fprintf(stderr, "%-22s %lld us\n", name, (long long)us);
}

void bench_cout_sync() {
std::ios::sync_with_stdio(true); // 默认
auto t0 = std::chrono::steady_clock::now();
for (int i = 0; i < kIterations; ++i) {
std::cout << "i=" << i << " sq=" << i * 2 << '\n';
}
report("cout (sync=true)", t0, std::chrono::steady_clock::now());
}

void bench_cout_nosync() {
std::ios::sync_with_stdio(false); // 常见的"加速 cout"写法
auto t0 = std::chrono::steady_clock::now();
for (int i = 0; i < kIterations; ++i) {
std::cout << "i=" << i << " sq=" << i * 2 << '\n';
}
report("cout (sync=false)", t0, std::chrono::steady_clock::now());
}

void bench_printf() {
auto t0 = std::chrono::steady_clock::now();
for (int i = 0; i < kIterations; ++i) {
std::printf("i=%d sq=%d\n", i, i * 2);
}
report("printf", t0, std::chrono::steady_clock::now());
}

void bench_print() {
auto t0 = std::chrono::steady_clock::now();
for (int i = 0; i < kIterations; ++i) {
std::print("i={} sq={}\n", i, i * 2);
}
report("print", t0, std::chrono::steady_clock::now());
}

int main() {
std::freopen("/dev/null", "w", stdout); // 排除终端 I/O 噪声
bench_cout_sync();
bench_printf();
bench_print();
bench_cout_nosync();
return 0;
}
21 changes: 21 additions & 0 deletions code/examples/vol3/54_regex_backtracking.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// Standard: C++17
// 病态回溯(catastrophic backtracking):模式 (a+)+b 喂 n 个 a(结尾不给 b),
// 回溯 NFA 对同一批 a 反复试各种分组组合,输入每长一点耗时翻几番——指数级。
// 注:n=28 实测约 22 秒(在线运行会超时),这里只跑到 n=24 展示增长趋势。
#include <chrono>
#include <iostream>
#include <regex>
#include <string>

int main() {
std::regex bad_re(R"((a+)+b)");
for (int n : {16, 20, 24}) {
std::string s(static_cast<std::size_t>(n), 'a'); // n 个 a,结尾没有 b
auto t0 = std::chrono::steady_clock::now();
bool m = std::regex_match(s, bad_re);
auto t1 = std::chrono::steady_clock::now();
auto ms = std::chrono::duration_cast<std::chrono::milliseconds>(t1 - t0).count();
std::cout << "n=" << n << " matched=" << std::boolalpha << m << " " << ms << " ms\n";
}
return 0;
}
15 changes: 15 additions & 0 deletions code/examples/vol3/59_cmath_nan.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// Standard: C++20
// NaN 不等于自己:IEEE 754 规定 NaN 与任何值(含自身)比较都返回 false。
// 所以判 NaN 永远只能用 std::isnan,绝不能用 ==——这是浮点世界最经典的坑。
#include <cmath>
#include <iostream>
#include <limits>

int main() {
double nan = std::numeric_limits<double>::quiet_NaN();
std::cout << std::boolalpha;
std::cout << "NaN == NaN? " << (nan == nan) << '\n';
std::cout << "NaN != NaN? " << (nan != nan) << '\n';
std::cout << "isnan(NaN)? " << std::isnan(nan) << " (判 NaN 的正确写法)\n";
return 0;
}
50 changes: 50 additions & 0 deletions code/examples/vol3/60_random_distributions.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
// Standard: C++20
// <random> 的分布演示:mt19937 引擎喂给三种分布,各跑一百万个样本看统计量。
// normal(0,1) 看均值/标准差 + 直方图(钟形),bernoulli(0.7) 看 true 占比,
// uniform_real(0,1) 看均值是否逼近 0.5——分布对象自动处理好取模偏差,这是 rand()%N 做不到的。
#include <cmath>
#include <cstdio>
#include <random>
#include <vector>

int main() {
std::mt19937 eng(2024);

// 正态分布: 均值 0, 标准差 1
std::normal_distribution<double> norm(0.0, 1.0);
constexpr int N = 1000000;
double sum = 0, sum2 = 0;
std::vector<long long> hist(10, 0); // [-5, 5) 分 10 桶,每桶宽 1.0
for (int i = 0; i < N; ++i) {
double x = norm(eng);
sum += x;
sum2 += x * x;
int b = int(x + 5.0);
if (b >= 0 && b < 10)
++hist[b];
}
double mean = sum / N;
double stddev = std::sqrt(sum2 / N - mean * mean);
std::printf("normal(0,1) %d 样本: 均值=%.4f 标准差=%.4f\n", N, mean, stddev);
std::printf("直方图(每桶宽1.0, [-5,5)):\n");
for (int i = 0; i < 10; ++i) {
std::printf(" [%+.0f,%+.0f) %lld\n", i - 5.0, i - 4.0, hist[i]);
}

// 伯努利: p=0.7
std::bernoulli_distribution bern(0.7);
long long trues = 0;
for (int i = 0; i < N; ++i)
if (bern(eng))
++trues;
std::printf("bernoulli(0.7) %d 样本: true 占比=%.4f\n", N, double(trues) / N);

// 均匀实数: [0, 1)
std::uniform_real_distribution<double> ureal(0.0, 1.0);
double usum = 0;
for (int i = 0; i < N; ++i)
usum += ureal(eng);
std::printf("uniform_real(0,1) %d 样本: 均值=%.4f (期望 0.5)\n", N, usum / N);

return 0;
}
25 changes: 25 additions & 0 deletions code/examples/vol3/61_optional_constexpr.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// Standard: C++20
// C++20 起 optional 的绝大多数操作(构造/emplace/reset/value_or/operator*)都是 constexpr,
// 能在编译期求值、塞进 static_assert——模板元编程/编译期查表/consteval 里
// 「可能没值的盒子」也能用,不用再手搓 union。
#include <iostream>
#include <optional>

constexpr int compute() {
std::optional<int> o;
o.emplace(7);
int v = *o;
o.reset();
return v + 35; // 42
}

int main() {
static_assert(compute() == 42); // 编译期就定下来
constexpr std::optional<int> empty;
static_assert(empty.value_or(99) == 99); // value_or 也 constexpr

std::cout << "constexpr compute() = " << compute() << '\n';
std::cout << "empty.value_or(99) = " << empty.value_or(99) << '\n';
std::cout << "C++20 constexpr optional: OK\n";
return 0;
}
Loading
Loading