gcc

Install

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 下载 
## 从这里 https://mirrors.aliyun.com/gnu/gcc/ 选一个版本,或使用其他源。
VERSION=7.3.0
wget https://mirrors.aliyun.com/gnu/gcc/gcc-${VERSION}/gcc-${VERSION}.tar.gz
# 解压
tar xzf gcc-${VERSION}.tar.gz
cd gcc-${VERSION}

# 下载依赖
./contrib/download_prerequisites

# 配置
./configure --prefix=/opt/gcc/${VERSION} --enable-languages=c,c++ --disable-multilib
# 编译
make -j$(nproc)
make install

# 如果不想污染源文件
mkdir gcc-build
cd gcc-build
../configure --prefix=/opt/gcc/${VERSION} --enable-languages=c,c++ --disable-multilib
## 另一个配置
../configure --enable-bootstrap --enable-languages=c,c++,objc,obj-c++,fortran,go,lto --enable-shared --enable-threads=posix --enable-checking=release --enable-multilib --with-system-zlib --enable-__cxa_atexit --disable-libunwind-exceptions --enable-gnu-unique-object --enable-linker-build-id --with-gcc-major-version-only --with-linker-hash-style=gnu --enable-plugin --enable-initfini-array --with-isl --enable-libmpx --enable-libsanitizer --enable-gnu-indirect-function --enable-libcilkrts --enable-libatomic --enable-libquadmath --enable-libitm --with-tune=generic --with-arch_32=x86-64

错误处理

7.3.0

1
2
3
4
5
6
7
8
9
10
# 错误
../../.././libsanitizer/sanitizer_common/sanitizer_platform_limits_posix.cc:157:10: fatal error: sys/ustat.h: No such file or directory
#include <sys/ustat.h>

# 解决方案
## 1. 不编译 sanitizer, configure 时添加参数 --disable-libsanitizer
vim libsanitizer/sanitizer_common/sanitizer_platform_limits_posix.cc
## 2. 注释第 157、250 行
// #include <sys/ustat.h>
// unsigned struct_ustat_sz = sizeof(struct ustat);

编译

1
2
3
4
5
6
7
gcc sample.cpp # 默认生成 a.out

# 指定输出文件
gcc -o sample.exe sample.cpp

# 指定 c++ std 版本
gcc -std=c++17 -o sample.exe sample.cpp

参数

1
2
3
-c	仅编译
-Dname=value 定义 name 值
-o file-name 输出

Flags

1
2
3
4
5
6
7
8
9
10
11
# warning
-Wall 开启警告(all: 应该开启的最小警告集合)
-Wextra 开启扩展警告
-Werror 所有警告视为错误
-Wno-error=... 关闭某项警告视为错误
-Wno-<warning-name> 关闭某项警告
-Wextra
-w 忽略所有警告

# 常用警告
-Werror=return-type 函数没有 return 视为错误

示例

1
2
3
4
5
6
export CFLAGS='-g -O3'
export CXXFLAGS='-ggdb3 -O0 -Wno-narrowing'
export CPPFLAGS='-DX=1 -DY=2 -Wno-narrowing'
export CCFLAGS='--asdf -Wno-narrowing'

make CXXFLAGS='-ggdb3 -O0 -Wno-narrowing' CPPFLAGS='-DX=1 -DY=2 -Wno-narrowing' CCFLAGS='--asdf -Wno-narrowing' all -j

注意

  • -w-Werror... 同时用会有冲突,-w 会短路 -Werror-w -Werror=...-Werror=... -w 都不行。

编译库

1
// common.h

查看查找链接库路径

1
2
gcc -Xlinker -v
g++ -Xlinker -v

链接

  • libraries 允许未定义的符号(undefined symbols)
  • executable 不允许有未定义的符号
  • 在代码中定义的符号(如函数名)还未使用到之前,链接器并不会把它加入到连接表中

修改系统默认库查找路径

1
export LIBRARY_PATH={path}:{path}

AUR 低版本 GCC

1
2
3
4
configure:4314: $? = 0
configure:4303: gcc -V >&5
gcc: error: unrecognized command-line option '-V'
gcc: fatal error: no input files

上面的报错可以忽略,只是尝试探测 gcc 版本,详见 这里

redis usage

登录

1
$ redis-cli -h <host> -p <port>

查询

集群信息

1
2
3
4
5
6
# 集群信息
> cluster info
# 节点信息
> cluster nodes
# 内存信息
> info memory

导出数据

redis-cli

redis-shake

1
2
./redis-shake.linux -type=dump -conf=redis-shake.conf

redis-shake.conf 配置参考这里

搭建集群

1
2
3
4
5
6
nohup redis-server --cluster-enabled yes --port 6379 --cluster-config-file nodes1.conf &
nohup redis-server --cluster-enabled yes --port 6378 --cluster-config-file nodes2.conf &
nohup redis-server --cluster-enabled yes --port 6377 --cluster-config-file nodes3.conf &

sleep 3 # 等服务节点起来
redis-cli --cluster create 127.0.0.1:6379 127.0.0.1:6378 127.0.0.1:6377

nodes1.conf、nodes1.conf、nodes1.conf 可以不存在,运行后 redis 自动创建。不同的 redis 实例,需要绑定不同的配置文件,默认为 node.conf。

c++ 最佳实践

工程

工具

网络

服务治理

配置中心

定义全局变量

1
2
3
4
5
6
7
8
9
// global.h
namespace {
extern int i;
}

// global.cpp
namespace {
int i = 1024;
}

随机数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
// random_util.h
#include "random"
#include "climits"
namespace utils {
extern std::random_device gRandomDev;
extern std::mt19937 gRandomGenerator;
extern std::uniform_int_distribution<std::mt19937::result_type> gRandomDist; // (0, INT_MAX)

uint32_t RandomInt(const int &min = 0, const int &max = 0);
}

// random_util.cpp
#include "random_util.h"
namespace ranker::utils {
std::random_device gRandomDev;
std::mt19937 gRandomGenerator(gRandomDev());
std::uniform_int_distribution<std::mt19937::result_type> gRandomDist(0, INT_MAX);

uint32_t RandomInt(const int &min, const int &max) {
if (min == max && min == 0) {
return (uint32_t) gRandomDist(gRandomGenerator);
} else {
std::uniform_int_distribution<std::mt19937::result_type> dist(min, max);
return (uint32_t) dist(gRandomGenerator);
}
}
}

/*
满足泊松分布的随机数
std::poisson_distribution<> d{/*mean=*/4};
d(gRandomGenerator);
*/

c++ - holes

容易 Core Dump 的情形

容器重新分配内存导致的非法内存访问

1
2
3
4
5
6
7
// 定义结构
struct Biz {...};
// 定义容器
std::vector<Biz> bizes;
bizes.emplace_back(); // 添加一个元素
auto p_biz = &bizes.back(); // 保存指针
bizes.emplace_back(); // 添加一个元素. 该操作可能会使指针 p_biz 失效, 因为内存重新分配

模板

类似需要在编译期展开的代码实现,不能放在 cpp 文件中,而应该放在 header 中

静态变量初始化

1
2
3
4
5
6
7
8
// s.h
#ifndef S_H
#define S_H
class S {
static int v;
};
int S::v = 0;
#endif

上面的代码在链接时会报错。声明保护(gragma once 或使用 ifndef 判断)可以解决单个编译单元(单个源文件)的重复声明错误,但不能避免多个编译单元在链接时的重复定义错误。要解决这个问题,需要把静态变量的声明和初始化放在不同的文件里面。

1
2
3
4
5
6
7
8
9
10
// s.h
#ifndef S_H
#define S_H
class S {
static int v;
};
#endif

// s.cpp
int S::v = 0;

undefined reference to

  • 未连接库

  • 编译库的编译器版本不一致,ABI 不兼容

    • 尤其注意,多个库之间使用的 gcc/++ 不一致
  • find_package 的顺序,在某些情况下也有可能会导致这个问题

    • 被依赖先调用 find_package

编译 C 库使用 C++ 语法

1
gcc -lstdc++ code.c

c++ - perf

工具

gprof、perf、gperftools、valgrind。

Perf

perf 工具默认生成记录文件 perf.data,读写路径是 pwd。

安装

1
2
3
4
5
# ubuntu
sudo apt install linux-tools -y

# centos
sudo yum install perf -y

工具

top

系统性能分析工具。

参数

1
2
-e 指定 event,多个 event 用 , 分隔
-s 指定分类聚合列, 默认是函数,还有 comm(命令)、pid 等

使用

1
2
3
4
# 所有进程
$ perf top
# 指定进程
$ perf top -p <pid>

stat

运行命令 / 指定 pid,并收集性能统计数据。

record

运行命令 / 指定 pid,并收集分析数据至 perf.data。

1
2
3
4
5
# 指定 pid
$ pid=$(pgrep <program-name>)

# 查看报告
$ perf report --show-total-period

report

读取 perf.data,展示分析结果。

1
2
3
4
5
$ perf report 

# 参数
--show-total-period 展示总耗时列
-a 统计系统范围内数据

diff

对比两个 perf.data 内容的区别,使用场景比如对比改动前后的变化。

archive

用来打包 perf.data 中使用到的环境数据,用于离线分析。

1
2
3
4
5
# 打包
$ perf archieve

# 分析
$ tar xvf perf.data.tar.bz2 -C ~/.debug

使用

1
2
3
4
5
6
7
8
9
10
11
12
# 查看系统全部耗时
$ perf top

# pid
$ pid=$(pgrep <program-name>)

# 记录数据
$ perf record -e cpu-clock -F 99 -p $pid -g -- sleep 60 # 记录 60 秒数据

# 展示记录数据的分析结果
$ perf report --show-total-period
$ perf report --show-total-period$ -i <perf.data> # 指定记录文件

火焰图

使用 这里 生成火焰图。

1
2
3
4
5
# 下载工具
$ git clone https://github.com/brendangregg/FlameGraph

# 生成火焰图
$ sudo perf script | FlameGraph/stackcollapse-perf.pl | FlameGraph/flamegraph.pl > flamegraph.svg

参考

问题

1

性能分析

指标

  • 通量(Throughput,或吞吐量)
  • 时延(Latency)

通量

性能瓶颈排查

  • 处理器占用率

    • 使用 top 命令查看
    • 如果处理器占用率未满,排查网络、任务调度、IO 空洞问题
    • 如果处理器占满,使用 perf、ftrace 等工具进一步排查
  • 流控(网卡)

    • 查看网卡丢包,可以通过增加缓冲区降低丢包率

      1
      2
      3
      4
      5
      6
      7
      8
      wlan0     Link encap:以太网  硬件地址 7c:7a:91:xx:xx:xx  
      inet 地址:192.168.0.103 广播:192.168.0.255 掩码:255.255.255.0
      inet6 地址: fe80::7e7a:91ff:fefe:5a1a/64 Scope:Link
      UP BROADCAST RUNNING MULTICAST MTU:1500 跃点数:1
      接收数据包:113832 错误:0 丢弃:0 过载:0 帧数:0
      发送数据包:81183 错误:0 丢弃:0 过载:0 载波:0
      碰撞:0 发送队列长度:1000
      接收字节:47850861 (47.8 MB) 发送字节:18914031 (18.9 MB)
  • 任务调度缺陷

    • 线程数远小于核心数,不能充分利用多核
  • IO 空洞

    • 同步等待 IO 操作,可以通过异步解决

参考

性能分析 - dstat

安装

1
2
# centos
sudo yum install -y dstat

命令

1
$ dstat

参数

默认参数为 -cdngy

1
2
3
4
5
6
-c cpu 信息
-C cpu 信息,指定核心,如 -C 0,2,4
-d disk 信息
-n net 信息
-g page 信息
-y system 信息

输出

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# cpu 相关
usr 用户消耗 cpu 时间占比
sys 系统消耗 cpu 时间占比
idl cpu 空闲时间占比
wai io 等待消耗的 cpu 占比
hiq 硬中断
siq 软中断

# system 相关
int 中断次数
csw 上下文切换次数

# disk
read 读取磁盘总数
write 写入磁盘总数

# net
recv 网络设备结束数据总数
send 网络设备发送数据总数

# page 相关(系统分页活动)
in 换入次数
out 换出次数

性能分析 - vmstat

安装

1
# centos

命令

1
$ vmstat <period> <count>

输出

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
r 运行队列,分配到 CPU 的进程数
b 堵塞进程数
swpd 使用虚拟内存
free 空闲物理内存
buff 缓冲占用内存
cache 缓存占用内存
si 每秒从磁盘读取虚拟内存大小
so 每秒写入磁盘虚拟内存大小
bi 每秒从块设备接受块数量
bo 每秒发送的块数量
in 每秒 cpu 中断次数,包括时间中断
cs 每秒上下文切换次数
us 用户 cpu 时间
sy 系统 cpu 时间
id 空闲 cpu 时间
wa 等待 io cpu 时间
st 从虚拟机窃取时间