性能指标
- 带宽MB/s,关心消息量,不管消息数
- 吞吐量QPS,TPS
- 延迟
- 资源使用率
test
sysctl -A | grep tcp.*mem
# 查看网络缓冲区sysctl -A | grep range
# 查看端口范围ntptime
# 查看ntp时间ntpq -pn
# ntp同步情况,service ntp start启动nc
# netcat $ nc ip port | [pipe]tcpdump
和Wiretshark
来性能分析tcpcopy
可以用来进行压力测试strace
prove
perf
# 查看程序的性能热点 e.g. perf record bin/memcached footprint ptmalloc 1000000 && perf reportnetstat -tpna | grep :prot
列出某服务客户端地址,netstat
或lsof
找出进程发起的连接nm,查看name mangilng
正确使用TCP
SO_REUSERADDR
- ignore
SIGPIPE
TCP_NODELAY
read
netcat
消息格式
Google Protocol Buffers
网络神话
Reactor和Proactor
- Reactor模式指“non-blocking IO + IO multiplexing”,Proactor模式是另一个。
- Recator的基本结构是事件循环(event loop)和事件驱动(event-driven)和事件回调实现业务逻辑,提高并发度和吞吐量,适合IO密集型横须
- CPU密集型程序应该使用线程池,即blocking_queue实现的任务队列
线程分配
- 当线程很廉价时,创造多于CPU数目的线程,一线程处理一个TCP连接,通常使用阻塞Blocking IO
- 当线程很宝贵时,创造跟CPU数目一样多的线程,一个线程处理多个TCP连接上的IO,即”non-blocking IO + IO multiplexing”
- 进程指fork的产物,线程指pthread_create的宝贵的原生NPTL Pthread线程,每个线程由clone产生,对应内核的task_struct
- 两种场合必须使用单线程:程序fork和限制程序的CPU占用率。此外,只有单线程能使用fork
多线程的性能优势
- 如果很少CPU负载让IO跑满,或很少IO负载就让CPU跑满,那么多线程没有性能优势
- 多线程适用场景:
- 多个CPU可用。
- 线程间共享数据,或者也可进程间共享
- 可修改的共享数据,或者也可进程间shared memory
- 提供非均质服务,如处理优先级事件
- lantency和throughput一样重要,即IO和CPU同样重要
- 异步操作
- scale up,能享受到多线程的优势
- 可预测性能。超过临界点会性能下降,线程数量不随负载变化
- 多线程划分责任和功能
C++编译链接模型精要
一些记录
- C++三大约束:C兼容、零开销(与Rust的零成本抽象区别?)原则和值语义
- 笼统地表示编译过程可分为:preprocessor/compiler/assembler/linker四个步骤,C++没有import或using(C++20可能出现),include头文件将导致当需要使用一个函数时预处理阶段必不可少的导入了一堆不相关的函数
- 一些编译选项:-Wall、-Wextra、-Werror、-Wconversion、-Wshadow
- 使用前向生命可以减少include,CCS规定不能重载&&、||、,三个操作符,Google规定不能重载一元
operator&()
,这样class就不能前向生命了 - 判断一个C++可执行文件是release还是debug模式可以通过看class template的短函数有没有被inline展开,在nm命令中查看可以发现,开启-O2编译的程序是看不到inline函数的任何信息的
1
2
3
4class Foo; // 前向生命
void bar(Foo &foo) {
Foo *p = &foo; // 取foo地址,重载了意思就变了
} - C++的链接模型相比C多了一些内容,name mangling和vague linkage,即一个符号多份互不冲突。C中不允许重复定义
一些名词
- 一次定义原则(ODR),C是的,C++不是的
C++设计
- POD和非POD
- 三/五原则:POD类型需要拷贝赋值非平凡类型时,就需要析构函数,而需要析构函数的类也需要拷贝和赋值操作(需要拷贝操作的类也需要赋值操作),否则使用编译器合成的将导致未定义行为或多重析构的危险
- 程序迁移工具:Porting Advisor,从X86(CISC)到arm kunpeng(RISC)
- 数据库测试工具:BenchmarkSQL安装ant -> 创建配置文件 -> 配置数据库连接 -> 场景配置 -> 数据准备 -> 执行测试
- 数据库连接:db、driver、conn、user/password
- 场景配置:warehouse仓库数量、loadWorkers数据并发数、Terminals并发用户数、runMins测试时间、runTxnsPerTerminal、limitTxns。。。
- 大数据测试工具套件:HiBench:评估框架速度、吞吐量和系统资源利用率
- 测试类别:micro、ml、sql、websearch、graph、streaming
C++并发相关错误类型
不必要的阻塞
- 死锁:指第一个线程等待第二个线程执行后才能继续,而第二个线程又在等待第一个线程,构成一个线程等待循环状态。
- 活锁:当第一个线程等待第二个线程,第二个线程又在等待第一个线程时,活锁类似死锁。活锁死锁区别在于等待过程不是阻塞状态而是不断循环检测状态,如spinlock。
- 在I/O或外部输入上的阻塞:当线程阻塞是因为等待某外部输入而无法继续执行,可能外部输入永远都不到来
竞争条件
- 数据竞争:指没有同步好对某个共享内存的并发访问
- 破坏不变量:表现为悬挂指针(线程删除了被访问的数据)、随机存储损坏(线程为局部更新造成读取数据不一致)或者双闲状态(重复释放?)
- 生存期问题:线程超时访问的某些数据已经被删除、销毁或访问的内存已经被另一个对象重用
需要思考的问题
- 哪些数据需要保护,防止并行访问?
- 如何保证数据是被保护的?
- 此时其他线程执行到代码的何处?
- 该线程用了哪些信号量?其他线程用了哪些信号量?
- 该线程各操作间有先后顺序要求吗?
- 线程载入的数据是否有效,是否被其他线程修改?
假如测试一个队列需要考虑的不同应用场景
- 一个线程在自身队列调用push()或pop()验证
- 空队列上一个线程调用push()另一个线程pop()
- 空队列和满队列上分别多个线程push()
- 空队列和慢队列上分别多线程pop()
- 空队列和满队列上分别多线程push()和一个线程pop()
- 空队列和满队列分别多线程push()和pop()