linux内核使用许多likely和unlikely宏,这两个宏的内容为: #define likely(x) (__builtin_expect(!!(x), 1)) #define unlikely(x) (__builtin_expect(!!(x), 0)) 是用来告诉编译器,当前判断条件是否常用或者不常用。编译器根据提示,生成的二进制的代码流程会有相应改变,以达到让cpu尽可能的顺序执行的目的。gcc官方文档里说,使用-fprofile-arcs来进行实际的性能测试,说程序员对自己的程序的预测一般都是错误的。 然后我搜到是用gcov去做,使用了一下确实很直观。编译的时候,参数加上“-fprofile-arcs -ftest-coverage”。然后运行会生成 .gcda .gcno文件。用gcov source.c会生成相应代码的.gcov文件。vim编辑这些文件,就能看到源码形式的,每一行都执行了多少次。 这里说个疑问,前边说预测不准确,我当时看到那里还说真的是不准确。我之前就感觉明明会大概率走这个分支,然后加上提示后,速度却变慢了。但是当我用g

Continue

想到了三种方式,但是结果跟自己预想的不太一样,具体也没有细想。 第一种方式就是char*类型的strcmp比较,这种比较容易想到。但是效率中等。 第二种方式是使用类型__uint128_t,这个是gcc给出的类型,应该可移植性不太高,这种方式速度最快,char*用的时间是差不多1.5倍。 第三种方式是使用sse,并行试试,_mm_xor_si128和_mm_test_all_zeros进行比较,但是速度是最慢的,可能涉及类型的转换,看指令也非常的多。我还以为会是最快的。 除了第三种慢的意外,我第一种还对比了第一个char就不相等的情况,这种特殊情况我以为第一种会快,没想到还是慢,跟之前速度差不多。 我特意看了一下第二种方式的实现方式:通过两个xor,分别对比8字节,然后通过or操作把两个对比结果合并,然后通过test指令判断合并的结果是否为0,test指令会修改ZF标志位,最后通过jne判断ZF标志位进行跳转。

Continue

最近看c底层相关的指令,操作,编码,多线程,磁盘文件读写相关的看的比较多。不经理永远不知道这里边的东西有多少,还有很多不易理解的。由于时间仓猝,本来想好好整理一下,作为一个总结,但还是决定只是记一个笔记,而且内容来自搜索引擎,有一部分不是看的官方文档解释等,可能不正确,而且没有写全。以后无聊的时候想起来再搞吧(感觉用不到以后不会再搞了) 先说为什么写这个文章,我碰到一个问题。多线程读写文件,然后单线程读取,线上后读取结果不一致。之前有过分步的测试,是没有问题的。 然后我排查原因,先从读入手,发现数据有问题,然后转到看写。写先通过单独打印几条日志,和对写入的数据错误判断,在某个函数。这时候,我打开编译的debug模式,就是能打印更详细的日志,然后发现问题没了,数据正常了。然后我就玩起了编译参数。发现打印日志就没事,不打印就出问题了,然后我就想到是编译器给我优化过头了,那时候还是开始02的优化。因为那个函数不涉及多线程的操作。然后我去掉 -DNDEBUG参数,然后用assert()判断出是哪一行出了问题。然后实验了几个方法,发现都是可以的,于是网上搜索资料,大体比较了一下这集中

Continue

perf工具应该都听说过,我也试了一把,感觉很好很强大。 先使用sudo perf stat ./a.out命令,查看一下性能统计信息。结果展示为: Performance counter stats for './a.out': 2,740.62 msec task-clock # 3.903 CPUs utilized 39 context-switches # 0.014 K/sec 2 cpu-migrations # 0.001 K/sec 128,974 page-faults # 0.047 M/sec 10,394,863,898 cycles

Continue

Callgrind是valgrind的一个工具,能够分析程序运行效率,帮助找到程序瓶颈。 命令tool知道使用的valgrind的工具, valgrind --tool=callgrind ./a.out 运行完之后会生成一个callgrind.out.PID文件,然后执行下面命令进行分析 callgrind_annotate callgrind.out.PID 这个命令能够展示每个调用函数对应的执行指令的次数,展示已经排序,可以优先优化最顶部的函数。 cachegrind也是valgrind的一个工具,主要分析内存使用情况的,比如cpu cache的使用等。 简单使用命令: valgrind --tool=cachegrind ./a.out ==12810== ==12810== I refs: 13,413,053,205 ==12810== I1 misses: 3,851 ==12810== LLi misses: 3,552 ==12810== I1 miss ra

Continue

Valgrind可以模拟cpu执行你的程序,然后给出内存使用或者程序错误信息。之前只使用过gdb来调试程序逻辑错误,现在准备多看几个,包括性能方面的调试。 安装直接使用的apt源安装的,使用也比较简单。直接valgrind ./a.out允许程序,运行过程中会给出程序建议。 这个程序我有一个一百万长度的uint64的数组,提示了"Invalid write of size 8"的错误,Warning: client switching stacks? SP change: 0x6c55ef0 --> 0x64b4c60 to suppress, use: --max-stackframe=8000144 or greater 我搜了一下发现说是栈空间消耗太大,我改成calloc,两个错误提示都没了。 ==15130== HEAP SUMMARY: ==15130== in use at exit: 457,122,934 bytes in 4,659,095 blocks ==15130== total heap usage: 4,65

Continue

写个uint64_t的程序,涉及大小端的转换。 uint64_t x = 0x0123456789ABCDEF; On a 32-bit little-endian processor, it will appear in memory as EF CD AB 89 67 45 23 01 On a 64-bit little-endian processor, it will appear in memory as EF CD AB 89 67 45 23 01. On a 32-bit big-endian processor, it will appear in memory as 01 23 45 67 89 AB CD EF. On a 64-bit big-endian processor, it will appear in memory as 01 23 45 67 89 AB CD EF. 转换涉及#include <endian.h> uint64_t htobe64(uint64_t host_64bits); uint64_t htole6

Continue

零拷贝不是一个新技术了,之前一直接触不到这么底层的技术,最近看的比较多,所以从代码上研究了一下。 在应用程序做数据传输等操作涉及系统调用,而为了提高性能,就是从减少系统调用次数和减少内核空间和用户空间的数据拷贝次数入手的。 具体的我也没看代码,都是从网上总结学来的。 像mmap方式,是减少了内核空间和用户空间的数据拷贝,使用映射还是指针的能够共享内核空间。但涉及比如把一个文件内容通过网络发送的操作,还涉及内核空间的数据拷贝。 sendfile和splice就是解决内核空间的数据copy的,我看linux手册是page buffer指针的复制,所以没有做数据的copy。指针是通过pipe buffer存储的。 ssize_t sendfile(int out_fd, int in_fd, off_t *offset, size_t count); ssize_t sendfile64(int out_fd, int in_fd, loff_t *offset, size_t count); 这俩的区别是sendfile64适合传送大文件,offset类型也决定了

Continue

照着别人写的js库,重新写了一个c版本的,也学了一下其中的算法。为什么需要松圆盘采样这个算法进行采样,是因为存随机其实也不是纯随机,随机分布不均匀。而这样生成的伪随机(psuedorandom)数列,很大程度保证了随机的均匀性。 开始以为很难,边写边学发现其实原理不太难。 基本思想就是,初始点可以给定或者随机。 第二步根据初始点,按照一定角度生成不同方向的新点,其中这个角度是关键。通过三角函数,保证生成的新点到初始点的距离为r。 第三步判断新生成点跟周围两个区域的点的距离,保证距离大于r。如果点不合法,则改变角度,重新生成。有一个重试次数保证重新生成上限,如果达到上限还没有找到一个新点,就说明这个点有问题,进行删除。如果合法,生成的点作为一个选择点并插入队列,作为下一次判断的初始点 第四步,队列中有其他生成点,继续上边第二第三步,直到没有生成点可以用。 期间我看js版本生成随机数的时候用的自己写的伪随机生成数,然后我就看了一下用的库,发现里边用了一个Thomas Wang写的 hash生成随机数,又简单看了一下。都是位操作,也懒得找原理看。先记一下,以后用到再看原理吧。

Continue

在setup.py里只需要写很少的代码,所有配置都放在setup.cfg里。如果参数通过setup()传入,以setup.cfg里的配置为准 #!/usr/bin/env python from setuptools import setup setup( setup_requires=['pbr'], pbr=True, ) setup.cfg里配置跟ini文件差不多。 [files]定义代码包里文件的安装目录,其中packages指定要安装的包;namespace_packages制定有命名空间的包;data_files指定要安装的文件的源地址和目的地址; [entry_points]指定模块入口点的运行脚本和模块。主要定义一些控制台脚本,pbr会自动生成这些脚本,做到脚本的跨平台。等号后边就相当于脚本执行调用的函数 随便看了两眼pbr源码: console_scripts就是两行,先import,后执行。 wsgi_scripts比较多,从代码来看,可以直接当脚本启动一个server或者,返回一个app提供给wsgi调用

Continue