Git Product home page Git Product logo

jared-zdc.github.io's Introduction

叩诚

行叩诚而不阿兮,遂见迎而逢举

RSS Feed

友情链接

Name Link Desc

最近更新

wiki

技术分析

现代处理器性能分析及优化

问题定位

jared-zdc.github.io's People

Contributors

jared-zdc avatar jaredzhu avatar

Watchers

 avatar

jared-zdc.github.io's Issues

在MacOS上交叉编译aarch64 linux内核源码

前言

由于笔者的日常工作都是基于MacOs的笔记本,日常的工作总需要切换跳转机到aarch64的机器上编译内核,因此想在本地修改直接本地交叉编译,也踩了不少坑。目前看网上也没有相关的踩坑的资料,这里仅以记录,希望对各位有帮助

安装交叉编译工具

直接使用github上一个同学做好的工具:

brew tap messense/macos-cross-toolchains
# install x86_64-unknown-linux-gnu toolchain
brew install x86_64-unknown-linux-gnu
# install aarch64-unknown-linux-gnu toolchain
brew install aarch64-unknown-linux-gnu

安装gcc

brew install gcc cpp
#修改默认gcc连接到gcc
cat ~/.zprofile
alias gcc="gcc-11"
alias g++="g++-11"
alias c++="c++-11"

编译内核

git clone https://github.com/torvalds/linux.git

#生成.config
make defconfig ARCH=arm64 cross_compile=aarch64-unknown-linux-gnu-

#编译内核
make Image ARCH=arm64 cross_compile=aarch64-unknown-linux-gnu- -j8

踩坑实录


问题一: ld: unknown option: --version

diff --git a/init/Kconfig b/init/Kconfig
index 5a0251b5f..90698ffe6 100644
--- a/init/Kconfig
+++ b/init/Kconfig
@@ -35,7 +35,7 @@ config GCC_VERSION
 
 config LD_VERSION
        int
-       default $(shell,$(LD) --version | $(srctree)/scripts/ld-version.sh)
+       default 13
 
 config CC_IS_CLANG
        def_bool $(success,echo "$(CC_VERSION_TEXT)" | grep -q clang)
diff --git a/scripts/lld-version.sh b/scripts/lld-version.sh
index d70edb4d8..f8b2954f4 100755
--- a/scripts/lld-version.sh
+++ b/scripts/lld-version.sh
@@ -6,7 +6,7 @@
 # Print the linker version of `ld.lld' in a 5 or 6-digit form
 # such as `100001' for ld.lld 10.0.1 etc.
 
-linker_string="$($* --version)"
+linker_string="$($* -v)"
 
 if ! ( echo $linker_string | grep -q LLD ); then
        echo 0

问题二: scripts/sorttable.c:27:10: fatal error: 'elf.h' file not found
在内核目录下建立一个目录:macos,将编译器sysroot目录下的相关头文件拷贝到macos目录中,主要拷贝以下文件,并保持目录级数相同:

cp sysroot/usr/include/asm-generic/bitsperlong.h asm-generic 
cp sysroot/usr/include/asm/bitsperlong.h asm
cp sysroot/usr/include/asm-generic/int-ll64.h asm-generic 
cp sysroot/usr/include/asm-generic/types.h asm-generic 
cp sysroot/usr/include/asm/types.h asm
cp sysroot/usr/include/asm/posix_types.h ./asm
cp sysroot/usr/include/asm-generic/posix_types.h ./asm-generic 
cp sysroot/usr/include/linux/elf-em.h ./linux/elf-em.h
cp sysroot/usr/include/elf.h .
cp sysroot/usr/include/features.h . 
cp sysroot/usr/include/stdc-predef.h .
cp sysroot/usr/include/gnu/stubs.h gnu/ 

freejared@B-T2HTMD6R-1928 include % ls
asm             asm-generic     elf.h           features.h      gnu             linux           stdc-predef.h

#编译指定路径 -I/macos
make Image ARCH=arm64 CROSS_COMPILE=aarch64-unknown-linux-gnu- HOSTCFLAGS="-I/macos"

问题三: scripts/extract-cert.c:21:10: fatal error: 'openssl/bio.h' file not found

#安装openssl
brew install openssl

#指定头文件路径 -I/usr/local/Cellar/openssl@3/3.0.3/include
make Image ARCH=arm64 CROSS_COMPILE=aarch64-unknown-linux-gnu- HOSTCFLAGS="-I/macos -I/usr/local/Cellar/openssl@3/3.0.3/include"

问题四:ld: library not found for -lcrypto

#指定库路径 -L/usr/local/opt/openssl/lib
make Image ARCH=arm64 CROSS_COMPILE=aarch64-unknown-linux-gnu- HOSTCFLAGS="-I/macos -I/usr/local/Cellar/openssl@3/3.0.3/include -L/usr/local/opt/openssl/lib"

问题五:error: member reference base type 'typeof (((struct tee_client_device_id )0)->uuid)' (aka 'unsigned char [16]') is not a structure or union uuid.b[15])

从错误提示上看,是由于macOS环境已经定义了uuid_t从而引发了重复定义的错误

修改uuid的定义方式:

diff --git a/scripts/mod/file2alias.c b/scripts/mod/file2alias.c
index 2417dd1dee33..3477058781a3 100644
--- a/scripts/mod/file2alias.c
+++ b/scripts/mod/file2alias.c
@@ -42,9 +42,15 @@ typedef struct {
 typedef struct {
        __u8 b[16];
 } uuid_le;
+
 typedef struct {
        __u8 b[16];
-} uuid_t;
+} compat_uuid_t;
+
+#ifdef __APPLE__
+#define uuid_t compat_uuid_t
+#endif
+
 #define        UUID_STRING_LEN         36

最终编译命令

make Image ARCH=arm64 CROSS_COMPILE=aarch64-unknown-linux-gnu- HOSTCFLAGS="-I/macos -I/usr/local/Cellar/openssl@3/3.0.3/include -L/usr/local/opt/openssl/lib" -j8

部分参考了Philon同学的经验:https://ixx.life/notes/cross-compile-linux-on-macos/

Intel PAUSE指令功效分析

PAUSE指令

Improves the performance of spin-wait loops. When executing a “spin-wait loop,” processors will suffer a severe performance penalty when exiting the loop because it detects a possible memory order violation. The PAUSE instruction provides a hint to the processor that the code sequence is a spin-wait loop. The processor uses this hint to avoid the memory order violation in most situations, which greatly improves processor performance. For this reason, it is recommended that a PAUSE instruction be placed in all spin-wait loops.

An additional function of the PAUSE instruction is to reduce the power consumed by a processor while executing a spin loop. A processor can execute a spin-wait loop extremely quickly, causing the processor to consume a lot of power while it waits for the resource it is spinning on to become available. Inserting a pause instruction in a spin-wait loop greatly reduces the processor’s power consumption.

翻译成人话的意思是,其功效主要是两个:

  • 提升抢锁性能
  • 节省功耗
    针对这两点,尝试使用一些testcase进行深入的测试分析,看看是否可以发现一些有意思的东西。

测试

之前在一个问题分析中有提到C库中Intel使用了rep nop(事实上就是PAUSE)来优化他的抢锁的性能,因此这里依然使用pthread_spin_lock的实现进行魔改来测试性能:

//根据参数选择不同的spin_lock
if(pause_type)
        mypthread_spin_lock = mypthread_pause_spin_lock;
    else    
        mypthread_spin_lock = mypthread_nopause_spin_lock;


//测试主体 N个线程
void *doWork() {
    uint32_t loop = thread_cnt;
    uint32_t type = pause_type;
    uint64_t iloop = idle_loop;

    while(!readyFlag) ;

    while(loop--) {

        mypthread_spin_lock(&lockValue);
        loop_counter ++;
        mypthread_spin_unlock(&lockValue);
        iloop = idle_loop;
        while(iloop--);
    }
}

从实际测试结果上来看,性能似乎并没有太大变化,甚至于nopause的性能还要略优于pause的性能:
image

从测试功耗结果的角度看,确实节省了不少功耗,32个线程测试时,pause场景比nopaus场景,整机功耗能够下降将近25W左右,也就是说Intel手册上说明的功耗降低是有意义的,但是性能却没有体现出来。

分析

由此可以推断,在pthread_spin_lock的场景下,pause实际上是没有起到提升锁性能的作用,仅仅是起到了降低功耗的作用,那么pause在什么样的场景下 可以提升抢锁的性能?

在不同的架构下,pause 指令的延时其实是不同的,甚至于在icelake阶段,pause的延时已经达到了140个cycle了。至于为什么对抢锁性能没有较大的提升,这里可能有两个推论:

  • 是否pause对原子的性能提升在实现上,更关注了CAS的性能,而忽略了FAA/SWAP的操作
  • 是否pause的作用仅仅是提供了一个idle loop的作用,减少了冲突导致的性能提升?

修改pthread_spin_lock为cas实现方式,测试结果如下:
image

很有意思的是,发现性能也没有明显的变化,查看intel trm的文档,其中有一句话描述:
image

也就是说,这个并不是直接提升了原子指令的性能,而是一个hint给到处理器,告诉处理器当前处于spin_loop阶段,处理器的能力可以让度出去,避免了大多数场景里面了一些内存序冲突的问题,这里我的理解是减少了对其它共享内存操作的可能性,减少了核间竞争的的开销,提升了整体cache的有效性,从而业务性能有比较大的提升。

此外,在测试过程中,发现pause指令并不是单纯的执行nop指令,更多的像整个流水线都让渡出去了,从perf的执行统计上看,IPC非常低:
image

也就是说 这个时候,基本另外的线程可以独占整个处理器,在性能上又有很大的提升。

进一步测试

在同一个物理核的超线程上面做redis的测试,确认IPC,在同物理核的另外一个线程上,运行测试程序,确认不同情况下,对redis的线程的影响:
nopause时, redis rps只能达到15万左右, 带了pause指令后, redis的性能可以达到16万吞吐量,这个还是计算并不密集的情况,比如在计算圆周率的场景下,pause指令对另外一个线程的吞吐量影响更大(IPC : 0.8->1.8):
image

总结

  • PAUSE指令并不是直接提升抢锁指令的性能,而是通过让渡CPU资源给同核心的另一个线程,提升整体运算利用率
  • PAUSE指令在多线程的时候,还能减少对共享内存的访问,减少同cache的竞争,从而提升其它业务核的性能
  • PAUSE指令能够极大的降低功耗

现代处理器性能分析及优化-C00

最近在youtube上无意中看到Denis Bakhvalov的有关处理器性能分析优化的课程以及书籍,个人觉得这本书对于想入或者初入这个领域的技术人员有比较大的启发作用,因此萌生了想要翻译Denis 教材《Performance analysis and tuning on modern CPU》的想法,并通过翻译这本书,梳理这些年在通用CPU性能分析优化的一些经验、总结以及教训,希望能够对想要了解这个领域的同学们起到一定的作用。

Denis简介

Denis头像
Denis是英特尔的高级开发人员,主要负责通过C++编译器为各种不同的体系结构程序进行编译优化。其工作及兴趣方向主要是编译器、性能优化相关的工程。从2008年开始,作为软件工程师,主要从事于开发桌面应用程序、嵌入式开发、性能分析和编译器开发。2016年开始撰写个人博客,分享关于性能分析、C/C++编译器以及CPU微架构相关的经验总结(笔者建议同学们有空可以浏览一下他的博客,里面提供了比较多的学习视频以及相关案例的讨论

此外,Denis也是一个积极生活倡导者,在工作之余,他经常会跟朋友踢足球,网球,跑步,还有下棋。除此之外,Denis是两个漂亮女儿的父亲(此处,笔者推荐一本书《软技能 代码之外的生存指南》,献给IT界的朋友们,希望各位在工作之余的生活足够多姿多彩)。

笔者简介

笔者在华为从事相关处理器性能分析优化工作9年左右,主要参与了鲲鹏处理器的研发,包括性能分析、性能优化、benchmark验证分析以及BringUp相关的工作,因此在该领域有一定的经验以及心得,但仍然在这个领域内还是一个小学生,希望能够跟这个领域内的大牛们一起探讨学习

nginx测试异常分析

总结

在Intel上测试nginx发现了一个比较奇怪的现象,服务端压测时候,负载总是无法压上去,反而客户端随着核数增加,负载几乎100%,并且主要集中在sys上。

# 服务端测试命令:
​
taskset -c 0-7 /usr/local/nginx/sbin/nginx -c /usr/local/nginx/conf/nginx.conf
​
# 客户端测试命令: 使用lo网口
# 短链接
taskset -c 8-31 /home/nginx-test/httpress-1.1.0/bin/Release/httpress -n 1000000 -c 200 -t 16  http://127.0.0.1:10000/index.html
​

image

分析

从当前跟随核数的特征以及top显示的结果上看,问题大概率出现在抢锁的问题上。通过perf采样,可以证实:

image

__inet_check_established函数主要实现如下:

image

从这个函数代码大概可以分析出这个函数的主要用途是 判断当前的socket状态是否是TCP_TIME_WAIT的状态,如果是的话,就需要通过twsk_unique获得一个端口:

int tcp_twsk_unique(struct sock *sk, struct sock *sktw, void *twp)
{
        const struct inet_timewait_sock *tw = inet_twsk(sktw);
        const struct tcp_timewait_sock *tcptw = tcp_twsk(sktw);
        struct tcp_sock *tp = tcp_sk(sk);
        int reuse = sock_net(sk)->ipv4.sysctl_tcp_tw_reuse;

        if (reuse == 2) {
                /* Still does not detect *everything* that goes through
                 * lo, since we require a loopback src or dst address
                 * or direct binding to 'lo' interface.
                 */
                bool loopback = false;
                if (tw->tw_bound_dev_if == LOOPBACK_IFINDEX)
                        loopback = true;
#if IS_ENABLED(CONFIG_IPV6)
                if (tw->tw_family == AF_INET6) {
                        if (ipv6_addr_loopback(&tw->tw_v6_daddr) ||
                            ipv6_addr_v4mapped_loopback(&tw->tw_v6_daddr) ||
                            ipv6_addr_loopback(&tw->tw_v6_rcv_saddr) ||
                            ipv6_addr_v4mapped_loopback(&tw->tw_v6_rcv_saddr))
                                loopback = true;
                } else
#endif

twsk_unique接口中,会根据tcp_tw_reuse配置来判断如何分配端口。

如果长时间找不到可以复用的端口,那么lock的时间就会变得很长,导致了锁冲突的产生。

但是很神奇的是,查看系统tcp_tw_reuse的配置,已经明确是打开了复用:

image

tcp_tw_reuse分析

既然tcp_tw_reuse配置已经打开,那为什么从热点上看,还有大量的连接在做hash_connect呢?

那么是否是有可能在这样大流量下的短链接本来就需要足够的端口来使用,资源已经严重不足了?

查看一下端口的使用情况以及限制:

image

不知道什么原因,对于系统对本地端口数量进行了限制,修改这个端口限制从40000-65535 到 0-65535后,性能提升了 100%

但是发现尽管性能提升了,但是客户端还是在大量的hash_connect抢锁, 所以似乎还有其它的问题导致了端口没有完全复用起来。

使用netstat | grep TIME_WAIT确定一下数量:

image

发现基本上已经达到极限了, 主要还是由于当前的吞吐量太高 ,已经端口复用已经无法承载了。

tmux + vimrc + build.sh备忘

tmux.conf

freejared@B-T2HTMD6R-1928 ~ % cat ~/.tmux.conf 
# Send prefix
set-option -g prefix C-a
unbind-key C-a
bind-key C-a send-prefix

# Use Alt-arrow keys to switch panes
bind -n M-Left select-pane -L
bind -n M-Right select-pane -R
bind -n M-Up select-pane -U
bind -n M-Down select-pane -D

# Shift arrow to switch windows

vimrc

# Set easier window split keys
bind-key v split-window -h
bind-key  h split-window -v

# Easy config reload
bind-key r source-file ~/.tmux.conf \; display-message "tmux.conf reloaded"
freejared@B-T2HTMD6R-1928 ~ % cat ~/.vimrc
"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
" cscope setting
"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
if has("cscope")
  set csprg=/usr/local/bin/cscope
  set csto=1
  set cst
  set nocsverb
  " add any database in current directory
  if filereadable("cscope.out")
      cs add cscope.out
  endif
  set csverb
endif


nmap fs :cs find s <C-R>=expand("<cword>")<CR><CR>
nmap fg :cs find g <C-R>=expand("<cword>")<CR><CR>
nmap fc :cs find c <C-R>=expand("<cword>")<CR><CR>
nmap ft :cs find t <C-R>=expand("<cword>")<CR><CR> 
nmap fe :cs find e <C-R>=expand("<cword>")<CR><CR>
nmap ff :cs find f <C-R>=expand("<cfile>")<CR><CR>  
nmap fi :cs find i ^<C-R>=expand("<cfile>")<CR>$<CR>
nmap fd :cs find d <C-R>=expand("<cword>")<CR><CR>

build.sh

freejared@B-T2HTMD6R-1928 Code % cat build.sh 
find . -name "*.h" -o -name "*.c" -o -name "*.cpp" > cscope.files
cscope -bkq -i cscope.files
ctags -R .
freejared@B-T2HTMD6R-1928 Code % 

现代处理器性能分析及优化-C01

一直以来,工程师都有一个观念:“性能为王”,以前是,现在是,以后更是。

根据《Data Never Sleeps 5.0》调查研究,世界上每天产生2.5万亿字节的数据,并且保持着每年25%的速度递增。在我们如今的社会生活中,信息产生的来源越来越多,信息交换的速度越来越快,推动了对更快软件(SW)和更快硬件(HW)的需求。简而言之,数据增长不仅对计算能力提出了更高的需求,而且对存储和网络系统也提出了更高需求。

在PC时代,开发人员通常直接在操作系统之上编程,有时候还不可避免的需要直接参与控制硬件。随着世界进入云时代,软件堆栈变得更深更复杂。通过对底层硬件的高度抽象,大多数开发人员往往无需对于实际硬件的把控,直接基于分布式、虚拟化、微服务以及更高层次的开发环境进行开发,效率极大提高的同时,却也带来了消极的一面,导致现代应用程序对于硬件的亲和性逐渐降低。

还有,幸亏于摩尔定律的有效性,软件程序员这几十年来一直“轻松愉快”。过去,一些软件供应商更愿意等待新一代硬件来加快应用程序的速度,而不花费人力资源来改进代码。但是,花无百日红,如今摩尔定律正在逐渐失效,通过查看图1,我们可以看到单线程性能增长正在放缓,想要再通过简单的ScalUp的方式提高性能,收益越来越小。

Figure 1: 40 Years of Microprocessor Trend Data. © Image by K. Rupp via karlrupp.net

因此当硬件性能提升不再提供显著的收益时,我们必须开始更多地关注代码运行的速度,分析软硬件运行方式,提高协同能力,并且对软件进行优化。

“Software today is massively inefficient; it’s become prime time again for software programmers to get really good at optimization.” - Marc Andreessen, the US entrepreneur and investor (a16z Podcast, 2020)

作者经验:在英特尔工作期间,我经常听到这样的故事:当英特尔客户应用程序运行性能不达标时,他们会立即下意识地开始指责英特尔的CPU性能不足。但是,当英特尔派遣我们的性能优化专家与他们合作并帮助他们改进应用程序时,客户的程序性能立刻提高5到10倍

笔者经验:笔者曾经也处理过类似的案例,在华为鲲鹏处理器上运行的一个加密货币的业务,仅仅因为优化了一下软件的算法指令,性能直接提升20倍以上。由此可见,很多时候,并非处理器性能不达标,而是软硬协同能力不足,并没有充分发挥处理器的能力

优化应用性能是一件具有挑战性的工作,通常需要大量的努力,但希望这本书能够给你提供一定的帮助!

为什么需要性能优化

现代CPU核心数量逐年递增。从2019年底开始,市场上已经有了高达100个核心的处理器(目前估计可以到200个核心以上了),这是非常令人兴奋的,但这并不意味着我们不必再关心性能了,甚至我们更需要关注性能,因为更多的核心有可能导致应用性能反而出现下降的可能。

一般来说,通用多线程应用程序的性能并不总是随我们分配给任务的CPU核心数量线性扩展。了解发生这种情况的原因以及修复这种情况的可能方法对于产品性能的未来增长至关重要。如果不对应用进行适当的性能分析和优化,这将会导致大量的性能和资金浪费,并扼杀项目产品。

根据paper《There’s plenty of room at the Top: What will drive computer performance after Moore’s law?》研究表明,至少在短期内,大多数应用程序的性能提升将来自软件栈的优化。但是遗憾的是,应用程序默认条件下很难无法获得最佳性能。该paper还提供了一个很好的示例,说明了在源代码级别上进行性能优化潜力极大。如下表1显示了各种优化方案的性能提升的比例,该测试程序是两个4096×4096矩阵进行相乘,通过应用多种优化手段,最终结果是程序运行速度相较原始程序快了60,000倍以上。提供这个例子的原因并不是为了挑Python或Java(它们是伟大的语言)毛病,而是为了打破软件默认具有“足够好”性能的信念。

Speedups from performance engineering a program that multiplies two 4096-by-4096 matrices running on a dual-socket Intel Xeon E5-2666 v3 system with a total of 60 GB of memory
Speedups from performance engineering a program that multiplies two 4096-by-4096 matrices running on a dual-socket Intel Xeon E5-2666 v3 system with a total of 60 GB of memory

一般情况下,以下因素会对应用软件的性能造成比较大的影响:

  • 软件限制,有人可能会问,“为什么处理器不能解决所有的问题?”, 现代CPU的处理速度如此之快,甚至每一代都有一定的提升,但是CPU仍然执行的是人们告诉它的程序,换句话说,程序员给定的程序是什么样的,它就按照什么样执行。比如程序员给定的是冒泡排序,它不会自动的选择更快的快速排序方案。它还是会盲目地执行它被告知要做的任何事情,这就导致了如果程序员给定的一个傻白甜的程序块,CPU也只能傻乎乎的执行,从而影响了整体应用的性能。有兴趣的同学可以看看这个新闻:打开游戏要运行 19.8 亿次 if 语句?黑客嘲讽 RockStar 游戏代码太烂了

  • 编译器限制,诚然,如今的编译器越来越智能,可以主动进行非常多的优化,因此很多开发人员也意识不到编译器做的事情,但是尽管如此,在一些涉及到更复杂的决策时,还是无法完美的达到最佳情况。这是由于编译器第一性要求是保证程序的正确性,在一些场景下,由于无法确定程序员的真实意图,编译器也无法激进的进行优化,不得不倾向于更保守的编译选择,因此在某些场景下,编译器也提供了一些特定的指令或选项,让开发人员指定优化方案,已达到更佳的性能。

  • 经验算法限制,开发人员有时候往往痴迷于算法的复杂性,默认觉得O(N log N)的算法一定比O()性能要强,比如考虑InsertionSort以及QuickSort,大负债工作量下,QuickSort是相对于InsertionSort具有一定的优势,但是假如工作负载较小时,InsertionSort性能反而优于QuickSort。在没有测试目标工作负载的情况下盲目相信 Big O 表示法,可能会导致开发人员走上错误的道路。所以,即使是最知名的算法,对于所有的可能的输入,性能不一定是实践中最高的。

笔者经验: 在性能优化中,往往这一块经常犯经验性的错误,比如在一个数据库性能优化中,我们往往觉得更快的存储设备性能越佳,但是在特定场景中,NVME盘的性能甚至比不过一个简单的SSD盘的性能

上述限制为我们的优化软件的性能留下了充足的空间。 从广义上讲,软件堆栈包括许多层,例如,固件、BIOS、操作系统、
库和应用程序的源代码。 但由于大多数较低的 SW 层不是在我们的直接控制下,因此我们优化的主要重点将放在应用可控的源代码上。 另一个重要的点是我们将经常接触的软件是编译器,通过编译器进行优化,获得性能的提升是非常有可能的,在本书中可以找到许多这样的例子。

作者经验: 即使不是编译器相关的专家也可以通过编译器对应用程序进行优化,并且根据我的经验,至少 90% 的优化可以在源代码级别完成,无需深入研究编译器源代码。 虽然,了解编译器的工作原理以及知道如何让它做你想做的事情在与性能相关的工作中总是有好处的。

笔者经验: 在实际优化的项目中,往往分析的第一顺序是应用程序源码本身,其次是编译器,库,操作系统以及更加底层的驱动,硬件优化等等,同时依次优化的难度递增,优化带来的收益相对而言也越小(这个不完全一定,笔者也遇到过硬件的一个配置直接极大的提升了整体软件的性能,比如打开硬件的预取等)。

此外,由于现代处理器多核多线程的原因,应用程序分布在多核之间并行运行,也会给应用程序带来通信相关的消耗以及资源竞争相关的问题。

值得一提的是,性能提升不仅来自优化软件。根据 paper《There’s plenty of room at the Top: What will drive computer performance after Moore’s law?》研究表明,算法(特别是对于机器学习等新问题领域)和效率更高的流水线硬件设计也可以带来较大的性能提升。 算法显然在应用程序的性能中起着重要作用,但我们不会在本书中讨论这个主题。 我们也不会讨论新的硬件设计架构,因为大多数时候,软件开发人员更多的是面向当前已有的处理器进行软件开发。但是,了解现代 CPU架构设计对于优化应用程序非常重要。

  • 笔者经验: 在很多CPU公司都需要有懂处理器设计的软件性能优化人员,并且多多益善,笔者曾经在华为为鲲鹏处理器招聘这方面的人才,相对硬件设计来说,既懂软件又懂硬件的人才实在太少了, 大家可以看看如下Intel的招聘要求:
Intel Performance Engineer

在后摩尔时代,代码优化能力的重要性越来越高,特别是需要根据运行它的硬件进行软硬协同优化,这一点对于工程师的要求也越来越高,也是区分码农与码神的关键因素。

本书中的方法主要是榨干程序的最后一点性能提升空间。通常这类方法的优化提升的空间不大,一般情况下不超过 10%,例如上表1中的第6/7行。但是,不要低估 10% 提升的重要性,它特别适用于在云环境中运行的大型分布式应用程序。根据google研究分析显示,2018年,谷歌在电力和冷却基础设施上的消耗几乎与其购买的一系列服务器的花费相当,而提升软件的性能就能够直接改善能源消耗,带来相应的收益显而易见。

“At such scale, understanding performance characteristics becomes critical – even small improvements in performance or utilization can translate into immense cost savings.” Kanev et al., 2015

谁需要性能优化

在高性能计算(HPC)、云服务、高频交易(HFT)、游戏开发和其它高速领域,性能至关重要,例如,谷歌报告说,搜索速度每慢2%,每个用户的搜索量就减少2%。又比如雅虎,页面加载速度每加快400毫秒,流量就增加了5-9%。在大部分游戏中,小的改进就可以产生重大的影响。这些例子证明,服务性能得越差,使用它的人就越少。

除了上述领域之外,如今的通用服务应用也需要它。例如,集成到Microsoft Visual Studio IDE中的Visual C++ IntelliSense11功能就有非常苛刻的性能要求。要使IntelliSense自动完成工作,他们必须在以毫秒为单位的时间内完成对整个源代码解析。如果需要几秒钟的时间来构建,估计没有人会使用这个功能。这样的功能必须及时响应用户的请求,并在用户键入新代码时提供有效的反应。只有在设计软件时就考虑了完整的性能需求,并贯彻到架构中,才能实现类似的功能。

  • 笔者经验: 没有性能的架构连玩具都不如 (某大型IC设计公司军规)*

相反的是,如果一个工具具有优良的性能,那么就会在非常多的领域或项目里面用到,比如像Unreal13和Unity14这样的游戏引擎被广泛用于建筑、3d可视化、电影制作和其他领域。它们的性能非常好,所以它们是需要进行2d和3d渲染、物理引擎、碰撞检测、声音、动画等的应用程序的不二选择。

“Fast tools don’t just allow users to accomplish tasks faster; they allow users to accomplish entirely new types of tasks, in entirely new ways.” - Nelson Elhage wrote in article15on his blog (2020).

不言而喻,人们本能的讨厌性能缓慢的软件。性能优良的特性可能成为产品的绝佳竞争优势,甚至是唯一因素!

性能优化是非常重要的和有价值的工作,但它可能非常耗时耗力。事实上,性能优化也是一个永无止境的游戏,总会发现有一些东西需要优化。但是不可避免的是,这也是一个边际效益递减的事情,项目或应用获得的收益在一定程度上也会逐渐降低。在这种情况下,进一步的优化将以非常高的工程成本来进行,这也是性能优化工作需要tradeoff的地方,需要在成本与优化收益之间取得一定的平衡。

在开始性能优化工作之前,我们需要明确这样做的必要性以及目标,避免盲目的进行性能优化,如果仅仅为了优化而进行的优化并不能为您的产品增加价值。正确的开始性能优化工作是需要树立一个明确的性能目标,并且说明为什么要这样做,这样做的收益是什么,明确衡量性能目标的指标数据,同学们可以在这两个paper 《Systems Performance: Enterprise and the Cloud》《Pro .NET Benchmarking. Apress》中阅读关于更多关于设定性能目标相关的知识。

此外,为了练习和掌握性能分析和优化的技能而拿起这本书的同学,欢迎你继续阅读。

什么是性能分析

是否有过这样的场景,你跟同事在争论某段代码的性能高低? 但是却缺乏非常有利的手段或者证据证明哪段代码确实性能更优。由于现在处理器的相关特性组件非常多,即使对代码小的调整也会引发显著的性能变化。这也是为什么本书中的第一个建议是: 测试为王 。 talk is cheap , show me the measure result.

  • 作者经验: 我看到很多人在尝试优化应用程序时都依赖直觉。但是通常,这样的优化对于应用很难起到正向的作用

没有经验的开发人员经常会根据直觉对源代码进行优化,希望能提高程序的性能。比如假定前一个i值没有使用的情况下,在代码中使用++i替换i++。但是通常情况下,此更改不会对生成的代码产生任何影响,因为只要有优化能力的编译器都会认识到i的前一个值没有被使用,因此也不会产生冗余的副本,从而影响性能。

许多很早流传在各个优化手册中的代码优化tips在很早以前是有效的,但是目前的编译器已经完全能够自动识别并且优化。此外,有些人倾向于过度使用传统的优化技巧,并奉为圭臬。比如是使用基于XOR的交换语句(XOR_swap_algorithm),但是实际上,简单的std::swap就可以产生更快的代码。这种优化可能不会提高应用程序的性能。依靠仔细的性能分析以及测试验证找到正确的修复位置,而不是靠直接和猜测,这样的优化才是有效的。

本书中的性能分析方式是基于收集有关程序如何执行的某些信息以及如何反应在CPU的特定事件统计上来引导分析的,并且最终在程序源代码中进行的任何更改都可以通过分析和解释收集的数据来体现。

定位出性能瓶颈的确切原因只是工程师工作的一半,就像门诊看病确定病因。而下半场确实要给它开具苦口良药。有时候,只是需要仅仅更改程序源代码中的一行代码就可以产生大幅的性能提升, 而性能分析优化就是为了找到这个关键的位置!

总结:

  • 性能分析不是主观臆测,而是通过数据的收集分析以及合理的测试得到的有效的结论
  • 有效的测试能够极大的帮助性能分析,而错误的测试却能导致分析南辕北辙

这本书主要讨论什么

这本书的编写是为了帮助开发人员更好地了解他们的应用程序的性能,学习如何发现低效率的代码块并消除它们。解决日常疑惑:为什么我的存档程序的性能比传统的慢两倍?为什么我的函数更改会导致性能下降两倍?客户抱怨我的应用程序太慢,我不知道从何说起?我是否优化了程序,使其充分发挥潜力?如何处理所有缓存未命中和分支错误预测?,希望在这本书的结尾,你能得到这些问题的答案。

以下是这本书的大纲:

  • 第二章讨论了如何进行正确的进行性能评估并分析其结果。其中介绍了性能测试和评估结果的最佳实践
  • 第三章和第四章提供了CPU微体系架构的基本知识和性能分析中的术语;如果您已经知道这一点,请随时跳过
  • 第五章探讨了几种最流行的性能分析方法及工具。它解释了分析性能分析工具的工作原理以及这些工具可以收集哪些数据
  • 第六章介绍了有关现代CPU为增强性能分析能力而提供的功能,详细介绍了这些特性是如何工作的,以及它们可以解决什么样的问题
  • 第七-九章包含典型性能问题的示例。可与自顶向下的微体系结构分析(见第6.1节)一起使用,结合阅读,这是本书最重要的概念之一
  • 第十章包含了前三章中涵盖的性能问题类别以外的一些优化主题,并且仍然比较重要,建议阅读
  • 第十一章讨论分析了多线程优化技术。它概述了优化多线程应用程序性能所面临的一些重要的问题,以及可用于分析多线程应用程序性能的工具。这个主题本身相当复杂,所以本章只关注硬件相关的问题,比如“false share”。

本书中提供的示例主要基于如下开源软件:

  • linux操作系统
  • 基于LLVM的Clang编译器(用于C和C++语言)
  • Linux perf(作为分析工具)

选择使用这些软件或者工具的原因不仅是因为提到的技术的流行,而且是因为它们的源代码是开放的,这可以让我们能够更好地了解其工作机制。这对于学习本书中提出的概念特别有用。我们有时还会将展示在性能优化这个领域中“大公司”的专有工具,例如英特尔®VTune TM Profiler。

这本书不包括什么

系统性能取决于不同的组件:CPU、操作系统、内存、I/O设备等。应用程序可以从调整系统的各种组件中受益。一般来说,工程师应该分析整个系统的性能。然而,影响系统性能的最大因素是它的CPU核心。这就是为什么本书主要侧重于从CPU的角度进行性能分析,当然偶尔也会涉及操作系统和内存子系统。

本书的描述的硬件架构并不会超出单个CPU结构,因此我们将不讨论分布式、NUMA和异构系统的优化技术。本书不讨论使用OpenCL和openMP等解决方案将计算卸载到加速器(GPU、FPGA等)相关的方案。

  • 笔者赘述: 但是实际上在真实世界的优化,这一部分优化的难度以及复杂性远远超过单颗cpu领域内的问题,后续笔者会根据实际情况在附录中补充一些关于numa,IO协同以及分布式的一些优化的案例*

本书围绕Intel x86 CPU架构展开的,暂不提供基于AMD、ARM或RISC-V芯片的具体调优方案。尽管如此,在其他章节中讨论的许多方法也能很好地适用于这些处理器。此外,Linux是本书的首选操作系统,但对于本书中的大多数示例,这并不重要,因为同样的技术在Windows和Mac操作系统上依然可以很好的使用。

本书中的所有代码片段都是用C、C++或x86汇编语言编写的,然而在很大程度上,本书中的技术或者想法依然可以应用于其他编程语言,如Rust、Go甚至Fortran。但是值得一提的是,本书对于JVM环境上的java类应用的优化,并不涉及。

  • 笔者赘述: 关于java类优化其实也非常重要,比如如今的微服务以及很多的中间件 kafka/zookeeper等都是基于java进行开发的,笔者在后续也会在章节中增加一些java相关的优化技术*

最后,作者假设读者可以拥有完全控制他们开发的软件的权限,包括他们选择使用的库和编译器。因此,本书并不包含关于如何优化商业程序,例如,优化SQL数据库查询等。(事实上这些技术依然可以使用在商业软件的技术分析上,只不过无法基于源代码级别进行优化)

章节概要

  • CPU在单线程性能上并没有像过去几年那样提升的那么多。这就是为什么性能优化比过去40年变得更加重要的原因。计算行业现在的变化比90年代以来的任何时候都要快得多。
  • 根据paper 《There’s plenty of room at the Top: What will drive computer performance after Moore’s law?》表明,将来性能提升的关键因素就是进行软件性能优化。性能优化的重要性不应该被低估。对于大型分布式应用程序,每一个小的性能提升都会带来巨大的成本节约。
  • 通常情况下,软件没有所谓的最佳性能。总会存在某些限制,阻止应用程序达到其全部性能潜力,包括硬件或者软件环境。CPU无法神奇地加速奇葩的慢算法,编译器也不能为每个程序生成最佳代码,并且由于硬件的特殊性,即使针对某个问题的最著名的算法也并不总是性能最高的,所有这些都为优化应用程序的性能留下了空间。
  • 对于某些类型的应用程序,高性能不仅仅表现的是一项优良特性,在某些方面,它使用户能够以一种新的方式解决新的问题。
  • 软件优化应以强大的业务需求为后盾。开发人员应设定可量化的目标和指标,这些目标和指标必须可以用于衡量优化进展情况。
  • 预测某段代码的性能几乎是不可能的,因为影响性能的因素太多了。在进行软件优化时,开发人员不应依赖直觉,而应使用仔细的进行性能分析,大胆揣测,小心求证。

gradle https代理访问

背景

由于公司的网络无法直接访问外网,在用gradle的时候,必须配置代理

代理配置

gradle的代理配置网上教程较多,这里基本上也是复制网上的基础配置,谨以此作为备案,以便后续查看

systemProp.http.auth.ntlm.domain=CHINA
systemProp.http.keepAlive=true
systemProp.http.proxyHost=your proxy host
systemProp.http.proxyPort=8080
systemProp.http.proxyUser=your acount if needed
systemProp.http.proxyPassword=your password if needed
systemProp.http.nonProxyHosts=*.xxxx.com|localhost|127.0.0.1
systemProp.https.proxyHost=your proxy host
systemProp.https.proxyPort=8080
systemProp.https.proxyUser=your acount if needed
systemProp.https.proxyPassword=your password if needed
systemProp.https.nonProxyHosts=*.xxxx.com|localhost|127.0.0.1

在gradle工程中,最好在以下两个文件中,均添加以上配置:

  • ./gradle.properties
  • ./gradle/wrapper/gradle-wrapper.properties

证书导入

由于gradle的maven源经常是https协议的,这样会导致https的证书错误:

sun.security.validator.ValidatorException: PKIX path validation failed: java.security.cert.CertPathValidatorException: signature check failed
javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path validation failed: java.security.cert.CertPathValidatorException: signature check failed

因此需要根据build.gradle中访问的网址将需要的证书逐个导入到jdk中:

keytool -import -alias your_alias_name -keystore $JAVA_HOME/lib/security/cacerts  -file  your_cer_file

证书可以通过使用浏览器访问该网站,然后导出到文件中,编码选择base64编码即可

这里需要注意,证书有时候是多级认证的,因此需要逐个导出,此外,根据工程访问的repo源不同,需要的证书有可能不一样,因此都需要逐个导出,并导入到编译环境中,否则无法访问相关文件

最后在gradle.properties(gradle-wrapper.properties)文件中指明cacerts路径:

systemProp.javax.net.ssl.trustStore=/home/paas/jdk-11.0.2/lib/security/cacerts
systemProp.javax.net.ssl.trustStorePassword=changeit

内核中 “__context__” 含义浅析备忘

背景

在分析Linux RCU lock的时候,发现每次在rcu_read_lock/rcu_read_unlock的时候,都会成对出现__acquire/release接口,这两个接口的定义都是__context,但是在内核中并没有找到出处。

备忘

查阅了相关资料,原来这个东西是给Linux代码静态检查工具使用的,为了保证lock/unlock必须成对出现使用的。

宏名称 宏定义 检查点
__bitwise attribute((bitwise)) 确保变量是相同的位方式(比如 bit-endian, little-endiandeng)
__user attribute((noderef, address_space(1))) 指针地址必须在用户地址空间
__kernel attribute((noderef, address_space(0))) 指针地址必须在内核地址空间
__iomem attribute((noderef, address_space(2))) 指针地址必须在设备地址空间
__safe attribute((safe)) 变量可以为空
__force attribute((force)) 变量可以进行强制转换
__nocast attribute((nocast)) 参数类型与实际参数类型必须一致
__acquires(x) attribute((context(x, 0, 1))) 参数x 在执行前引用计数必须是0,执行后,引用计数必须为1
__releases(x) attribute((context(x, 1, 0))) 与 __acquires(x) 相反
__acquire(x) context(x, 1) 参数x 的引用计数 + 1
__release(x) context(x, -1) 与 __acquire(x) 相反
__cond_lock(x,c) ((c) ? ({ __acquire(x); 1; }) : 0) 参数c 不为0时,引用计数 + 1, 并返回1

其中 __acquires(x) 和 __releases(x), __acquire(x) 和 __release(x) 必须配对使用, 否则 Sparse 会给出警告

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.