感谢大家的支持,另外还要特别感谢老兵哥在“timer frequency”问题上的支持,那个问题还有待研究,周末了,祝大家愉快。
++++++++++++++++++++++++++++++++++++++++++++++++++++++
今天在封装一个"内存映射文件"读取的类。编写WINDOWS版本的时候发现CreateFile()函数中,可以在参数dwFlagsAndAttributes中指定FILE_FLAG_SEQUENTIAL_SCAN来优化顺序读的情况。那么,在LINUX中如何优化顺序读呢?
查了一下资料,发现madvise()这个函数可以对映射的内存提出使用建议,从而提高内存。
以下是我进行对比测试的代码,可以明显的发现:使用MADV_SEQUENTIAL标志比使用MADV_RANDOM标志更快。
以下是代码:
=============================================================
//test_advise.cpp
//测试madvise()函数的差异
#include <stdio.h>
#include <sys/mman.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <time.h>
#include <sys/time.h>
#ifndef P
#define P(format, ...) do \
{ \
fprintf(stdout, "%s %s %d " format "\n", __FILE__, __FUNCTION__, __LINE__, ##__VA_ARGS__); \
fflush(stdout); \
} \
while (0);
#endif
static int operator-(struct timeval& lsh, struct timeval& rsh)
{
if (lsh.tv_sec==rsh.tv_sec)
{
return lsh.tv_usec - rsh.tv_usec;
}
else
{
return (lsh.tv_sec-rsh.tv_sec)*1000000 + (lsh.tv_usec - rsh.tv_usec);
}
}
#define FILE_LEGNTH (200*1024*1024) /*200MB的文件*/
/// 创建一个大的空文件
void CreateFile()
{
int fd = open("test.dat", O_CREAT | O_RDWR);
if (fd<1)
{
P("open error");
return;
}
lseek(fd, FILE_LEGNTH-1, SEEK_SET);
write(fd, "\0", 1);
close(fd);
fd = -1;
}
/// 顺序读
void SequenceRead()
{
int fd = open("test.dat", O_RDWR | O_EXCL);
if (-1==fd)
{
P("open error");
return;
}
void* p = mmap(NULL, FILE_LEGNTH, PROT_READ, MAP_SHARED, fd, 0);
if (MAP_FAILED==p)
{
P("map error");
return;
}
close(fd);
fd = -1;
//内存使用建议
if (-1==madvise(p, FILE_LEGNTH, MADV_WILLNEED | MADV_SEQUENTIAL))
{
P("madvise error");
return;
}
//下面开始逐个字符读取
struct timeval start;
struct timeval end;
int i;
unsigned char* pTemp = (unsigned char*)p;
int nSum = 0;
gettimeofday(&start, NULL);
for (i=0; i<FILE_LEGNTH; i++)
{
nSum += *pTemp;
pTemp++;
}
gettimeofday(&end, NULL);
//
if (-1==munmap(p, FILE_LEGNTH))
{
P("unmap error");
return;
}
//显示占用时间
struct tm stTime;
localtime_r(&start.tv_sec, &stTime);
char strTemp[40];
strftime(strTemp, sizeof(strTemp)-1, "%Y-%m-%d %H:%M:%S", &stTime);
printf("start=%s.%07d\n", strTemp, (int)start.tv_usec);
//
localtime_r(&end.tv_sec, &stTime);
strftime(strTemp, sizeof(strTemp)-1, "%Y-%m-%d %H:%M:%S", &stTime);
printf("end =%s.%07d\n", strTemp, (int)end.tv_usec);
printf("spend=%d 微秒\n", end-start);
}
/// 随机读
void RandomRead()
{
int fd = open("test.dat", O_RDWR | O_EXCL);
if (-1==fd)
{
P("open error");
return;
}
void* p = mmap(NULL, FILE_LEGNTH, PROT_READ, MAP_SHARED, fd, 0);
if (MAP_FAILED==p)
{
P("map error");
return;
}
close(fd);
fd = -1;
//内存使用建议
if (-1==madvise(p, FILE_LEGNTH, MADV_RANDOM))
{
P("madvise error");
return;
}
//下面开始逐个字符读取
struct timeval start;
struct timeval end;
int i;
unsigned char* pTemp = (unsigned char*)p;
int nSum = 0;
gettimeofday(&start, NULL);
for (i=0; i<FILE_LEGNTH; i++)
{
nSum += *pTemp;
pTemp++;
}
gettimeofday(&end, NULL);
//
if (-1==munmap(p, FILE_LEGNTH))
{
P("unmap error");
return;
}
//显示占用时间
struct tm stTime;
localtime_r(&start.tv_sec, &stTime);
char strTemp[40];
strftime(strTemp, sizeof(strTemp)-1, "%Y-%m-%d %H:%M:%S", &stTime);
printf("start=%s.%07d\n", strTemp, (int)start.tv_usec);
//
localtime_r(&end.tv_sec, &stTime);
strftime(strTemp, sizeof(strTemp)-1, "%Y-%m-%d %H:%M:%S", &stTime);
printf("end =%s.%07d\n", strTemp, (int)end.tv_usec);
printf("spend=%d 微秒\n", end-start);
}
int main()
{
CreateFile();
RandomRead();
SequenceRead();
return 1;
}
/*
g++ -o test_advise.o -c test_advise.cpp -g -Wall -Werror
g++ -o test_advise test_advise.o
*/
=============================================================
需要注意的是,当没有使用madvise的时候,相当于使用madvise(... MADV_NORMAL),这种情况下仍然会进行预读,性能和使用madvise(... MADV_SEQUENTIAL)差不多。我猜想,使用MADV_SEQUENTIAL标志的时候,对于实际的物理内存的占用将是最小的,关于这点,希望内核高手能够给我些指点,谢谢!