环境:
在window上的wsl2中使用64位的ubuntu20.04(VMware虚拟机中的ubuntu同样适用),使用的交叉编译工具为:arm-fsl-linux-gnueabi,32位文件系统。
一、gperftools交叉编译安装
首先到github上下载gperftools安装包,网址:gperftools-master.zip
打开ubuntu终端,开始操作:
建议进入超级用户模式,在ubuntu终端输入:su
然后输入密码,回车即可。
不然很多操作没有权限,要一直在命令行前面加上:sudo
前置工作:使能交叉编译环境,我的是source一下交叉编译工具链:
source /opt/fsl-framebuffer/2.4.4/environment-setup-cortexa9hf-neon-fsl-linux-gnueabi
把刚下载完之的压缩包,拷贝进你的ubuntu软件安装的目录中,根据个人目录拷贝即可。(例如:本人目录 /home/john/download/)
step0 进入拷贝压缩包的目录:
cd /home/john/download
step1 解压文件:
unzip gperftools-master.zip
step2 进入文件目录:
cd gperftools-master
step3 执行autogen脚本:
./autogen.sh
step4 配置configure文件:(这一步非常重要,指定交叉编译工具和文件安装路径)
我的配置选项:
./configure --prefix=/home/john/download/gperftools-master/build --host=arm-fsl-linux-gnueabi
可以根据自己的需求去选择配置选项,如果只需要tcmalloc最小系统,可参考如下配置选项:
./configure \
--disable-cpu-profiler --disable-heap-profiler \
--disable-heap-checker \
--disable-debugalloc --disable-libtool-lock \
--disable-deprecated-pprof \
--enable-minimal \ -- 最小系统安装(完整的tcmalloc内存池功能,没有调试功能)
--prefix=/home/john/download/gperftools-master/build \ -- 指定安装路径
--host=arm-fsl-linux-gnueabi -- 指定交叉编译工具链前缀
step5 编译:
make
step6 安装:
make install
安装完成,进入安装目录查看:cd build
库文件、头文件已经成功在安装路径生成,以下就是加入到工程中编译使用即可。
二、检测tcmalloc是否交叉编译安装成功 、实例验证
写一个最简单的程序测试一下:
#include <stdio.h>
#include <gperftools/tcmalloc.h>
int main()
{
char *ptr = (char *)tc_malloc(sizeof(char));
tc_free(ptr);
printf("tcmalloc works fine\n");
return 0;
}
方法一:创建一个工程,把动态库和头文件添加到工程中(工程结构如下图所示)
创建一个根目录test(Linux称目录,windows称文件夹)
根目录下创建一个lib目录用来存放动态库libtcmalloc.so(在安装路径下的lib文件夹中拷贝过来)
根目录下创建一个include目录用来存放gperftools头文件(在安装路径下的include文件夹中拷贝过来)
根目录下创建一个test.c源文件,把上面的代码拷贝进去(包含头文件 <gperftools/tcmalloc.h>)
根目录下创建一个Makefile文件,手动指定动态库路径和头文件路径,-ltcmalloc链接,内容如下:
注意:编译之前记得使能交叉编译环境,因为Makefile中使用 $(CC) 是当前环境变量中的编译器
All:test
CFLAGS = -I./include -L./lib -ltcmalloc
test:test.c
$(CC) -o test test.c $(CFLAGS)
.PHONY:clean
clean:
rm test
进入工程目录:cd test
编译程序:make
拷贝到开发板上:
执行程序:./test
执行成功,证明交叉编译tcmalloc成功了!
方法二:把动态库和头文件添加到交叉编译工具链中(找到自己的交叉编译工具链的库和头文件存放路径,分别拷贝进去)
把tcmalloc动态库添加到交叉编译工具的库文件路径:
cp /home/john/download/gperftools/build/lib/libtcmalloc.so /opt/fsl-framebuffer/2.4.4/sysroots/cortexa9hf-neon-fsl-linux-gnueabi/usr/lib
把tcmalloc头文件添加到交叉编译工具的头文件路径
cp -rf /home/john/download/gperftools/build/include/*/opt/fsl-framebuffer/2.4.4/sysroots/cortexa9hf-neon-fsl-linux-gnueabi/usr/include
创建一个根目录test(Linux称目录,windows称文件夹)
根目录下创建一个test.c源文件,把上面的代码拷贝进去(包含头文件 <gperftools/tcmalloc.h>)
根目录下创建一个Makefile文件,编译时只要加上 -ltcmalloc 即可,内容如下:
注意:编译之前记得使能交叉编译环境,因为Makefile中使用 $(CC) 是当前环境变量中的编译器
All:test
CFLAGS = -ltcmalloc
test:test.c
$(CC) -o test test.c $(CFLAGS)
.PHONY:clean
clean:
rm test
进入工程目录:cd test
编译程序:make
拷贝到开发板上:
执行程序:./test
执行成功,证明交叉编译tcmalloc成功了!
三、tcmalloc性能简单测试
感受下使用内存池tcmalloc和不使用内存池tcmalloc编译代码运行的区别:
tcmalloc内存池的使用,不需要改动应用层任何代码,只需要包含头文件和链接动态库,底层就会自动hook住malloc,free等内存操作的函数了。使用tcmalloc和不使用tcmalloc分别去编译以下代码,执行看一下结果的差异就能看出来了。
测试代码引用另一位博主的
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <time.h>
#include <sys/time.h>
#include <pthread.h>
#include <gperftools/tcmalloc.h>
#define LOOP_NUM (1024 * 1024)
#define THREAD_NUM 20
static void *fun_thread(void *arg)
{
struct timeval start,end;
gettimeofday(&start,NULL);
for (uint32_t i=0;i<LOOP_NUM;i++)
{
uint8_t *data = (uint8_t *)malloc(i);
free(data);
}
gettimeofday(&end,NULL);
printf("time use:%ld usec \n",(end.tv_sec-start.tv_sec)*1000000+end.tv_usec-start.tv_usec);
return NULL;
}
int main(void)
{
pthread_t th[THREAD_NUM];
uint8_t i;
for (i=0;i<THREAD_NUM;i++)
{
pthread_create(&th[i],NULL,fun_thread,NULL);
}
for (i=0;i<THREAD_NUM;i++)
{
pthread_join(th[i],NULL);
}
return 0;
}
不使用tcmalloc的执行结果:(不使用的时候把包含头文件<gperftools/tcmalloc.h>这行注释了)
使用tcmalloc的执行结果:
从两个程序运行结果可以明显看出:不使用内存池tcmalloc功能的程序,分配和释放同样多的内存空间,需要的时间更长,而且长很多。据个人对内存的理解,是由于前期分配大量的小块空间,导致内存空间中有很多内存碎片(内存不连续,空间比较分散),后期分配大块的空间时,没有足够连续的空间可以分配,所以要等待系统回收之后再分配。而内存池对内存空间有着自己一套分配和回收的方案,比裸用malloc和free高效很多。