#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/time.h>
#include <unistd.h>
#include <glog/logging.h>
#include <gflags/gflags.h>
// 读文件类
class CFileReader
{
public:
CFileReader()
: _buffer(NULL)
{
}
~CFileReader()
{
free(_buffer);
}
bool open(const char* filepath)
{
// 以O_DIRECT方式打开文件
int fd = ::open(filepath, O_RDONLY | O_DIRECT);
if (-1 == fd)
{
LOG(ERROR) << "open " << filepath << " error: " << strerror(errno);
return false;
}
// 取得文件大小,以便一次性将文件读取出来
struct stat st;
if (-1 == fstat(fd, &st))
{
LOG(ERROR) << "stat " << filepath << " error: " << strerror(errno);
close(fd);
return false;
}
// 分配足以容纳整个文件的Buffer
// 由于以O_DIRECT方式读取,所以需要按页对齐
size_t size = st.st_size + (getpagesize() - st.st_size%getpagesize());
posix_memalign((void**)&_buffer, getpagesize(), size);
if (NULL == _buffer)
{
LOG(ERROR) << "malloc failed";
close(fd);
return false;
}
// 将整个文件读取_buffer中
int bytes_read = read(fd, _buffer, size);
if (-1 == bytes_read)
{
LOG(ERROR) << "read " << filepath << " error: " << strerror(errno);
close(fd);
free(_buffer);
_buffer = NULL;
return false;
}
else if (bytes_read != size)
{
// 两组测试输出数据:
// FileSize(212000000) => AlignSize(212000768) -> RealSize(212000000)
// FileSize(2120000000) => AlignSize(2120003584) -> RealSize(2120000000)
printf("FileSize(%d) => AlignSize(%d) -> RealSize(%d)
", st.st_size, size, bytes_read);
}
return true;
}
// 从文件中读取一个节点数据
// offset:偏移量
// return:返回指向记录的指针
template <class P>
const P* get_record(uint64_t offset) const
{
return reinterpret_cast<P*>(_buffer + offset);
}
// 取得文件所有的记录
template <class P>
const P** get_all_record() const
{
return reinterpret_cast<P**>(_buffer);
}
private:
char* _buffer;
};
// 用于计时
class TimeWatcher
{
public:
TimeWatcher(const std::string& tip)
: _tip(tip)
{
struct timeval now;
gettimeofday(&now, NULL);
_now_msec = (now.tv_sec * 1000) + (now.tv_usec / 1000);
}
~TimeWatcher()
{
struct timeval now;
gettimeofday(&now, NULL);
time_t cur_msec = (now.tv_sec * 1000) + (now.tv_usec / 1000);
LOG(INFO) << _tip << " spend " << cur_msec - _now_msec << "ms";
}
private:
std::string _tip;
time_t _now_msec;
};
struct User
{
int32_t age;
int32_t hight;
int32_t weight;
char bitmap[50*4];
};
// 用于生成测试文件
bool make_test_file(const char* filepath, int num_records)
{
int fd = open(filepath, O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR);
if (-1 == fd)
{
LOG(ERROR) << "open " << filepath << " error: " << strerror(errno);
return false;
}
User user;
TimeWatcher time_watcher("write");
for (int i=0; i<num_records; ++i)
{
user.age = i;
user.hight = i + i;
user.weight = i * i;
if (-1 == write(fd, &user, sizeof(user)))
{
LOG(ERROR) << "write " << filepath << " error: " << strerror(errno);
close(fd);
return false;
}
}
close(fd);
return true;
}
// 模拟随机读取
void random_read(const CFileReader& file_reader, int num_random)
{
int *index = new int[num_random];
for (int i=0; i<num_random; ++i)
{
srandom(time(NULL));
index[i] = random() % num_random;
}
TimeWatcher time_watcher("randmon read");
for (int i=0; i<num_random; ++i)
{
file_reader.get_record<struct User>(index[i]);
}
}
// 执行测试
void test()
{
int num_records1 = 1000000;
int num_records2 = 10000000;
std::string file1 = "./file_1000000";
std::string file2 = "./file_10000000";
if (make_test_file(file1.c_str(), 1000000)
&& make_test_file(file2.c_str(), 10000000))
{
printf("to read, press ENTER to continue ...
");
getchar();
CFileReader file_reader1;
{
TimeWatcher time_watcher("open");
if (!file_reader1.open(file1.c_str()))
{
return;
}
}
random_read(file_reader1, 1000000);
random_read(file_reader1, 3000000);
CFileReader file_reader2;
{
TimeWatcher time_watcher("open");
if (!file_reader2.open(file2.c_str()))
{
return;
}
}
random_read(file_reader2, 1000000);
random_read(file_reader2, 3000000);
}
}
int main(int argc, char* argv[])
{
test();
return 0;
}