C++文件输入输出性能优化技巧
- 使用内存映射文件:
- 原理:内存映射文件将文件内容直接映射到进程的虚拟地址空间,使得对文件的访问就像访问内存一样,减少了系统调用开销。
- 示例代码:
#include <iostream>
#include <windows.h>
#include <fcntl.h>
#include <io.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <stdio.h>
int main() {
const char* filename = "test.txt";
int fd = _open(filename, _O_RDONLY);
if (fd == -1) {
perror("open");
return 1;
}
struct stat fileStat;
if (fstat(fd, &fileStat) == -1) {
perror("fstat");
_close(fd);
return 1;
}
char* mappedMemory = (char*)mmap(nullptr, fileStat.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
if (mappedMemory == MAP_FAILED) {
perror("mmap");
_close(fd);
return 1;
}
// 访问映射的内存
std::cout.write(mappedMemory, fileStat.st_size);
if (munmap(mappedMemory, fileStat.st_size) == -1) {
perror("munmap");
}
_close(fd);
return 0;
}
- 缓冲区优化:
- 使用标准库缓冲区:C++标准库的
std::ifstream
和std::ofstream
已经有内置缓冲区。可以通过设置缓冲区大小来优化,例如std::ifstream ifs("file.txt", std::ios::binary); char buffer[4096]; ifs.rdbuf()->pubsetbuf(buffer, sizeof(buffer));
。
- 自定义缓冲区:自己实现缓冲区逻辑,比如在读取文件时,预先分配一块较大内存作为缓冲区,按块读取文件内容,减少系统调用次数。在写入文件时,先将数据写入缓冲区,达到一定阈值后再一次性写入文件。
- 异步I/O:
- 原理:在C++中可以使用
std::async
结合文件I/O操作实现异步。这样主线程可以在I/O操作进行时继续执行其他任务,提高整体效率。
- 示例代码:
#include <iostream>
#include <fstream>
#include <future>
void writeFileAsync(const std::string& filename) {
std::ofstream ofs(filename);
for (int i = 0; i < 1000000; ++i) {
ofs << "Line " << i << std::endl;
}
}
int main() {
std::future<void> futureTask = std::async(std::launch::async, writeFileAsync, "async_output.txt");
// 主线程可以执行其他任务
std::cout << "Main thread is doing other work while file is being written asynchronously." << std::endl;
futureTask.get();
return 0;
}
- 以二进制模式读写:
- 原因:以二进制模式读写文件避免了文本模式下可能的字符转换开销,特别是在处理非文本数据(如图片、音频等)时,效率更高。例如
std::ifstream ifs("data.bin", std::ios::binary);
。
文件输入输出异常处理机制设计
- 文件不存在异常处理:
- 检查文件是否存在:在打开文件前,使用
std::filesystem::exists
(C++17及以上)或boost::filesystem::exists
(C++17以下可使用Boost库)检查文件是否存在。
- 示例代码:
#include <iostream>
#include <fstream>
#include <filesystem>
int main() {
const std::string filename = "nonexistent_file.txt";
if (!std::filesystem::exists(filename)) {
std::cerr << "File " << filename << " does not exist." << std::endl;
return 1;
}
std::ifstream ifs(filename);
if (!ifs) {
std::cerr << "Failed to open file." << std::endl;
return 1;
}
return 0;
}
- 磁盘空间不足异常处理:
- 捕获异常:在进行文件写入操作时,可能会因为磁盘空间不足抛出
std::ios::failure
异常。可以使用try - catch
块捕获异常。
- 示例代码:
#include <iostream>
#include <fstream>
int main() {
std::ofstream ofs("large_file.txt");
try {
for (int i = 0; i < 1000000000; ++i) {
ofs << "A very long string to fill the disk space if possible." << std::endl;
if (!ofs) {
throw std::ios::failure("Disk space may be running out or other I/O error.");
}
}
} catch (const std::ios::failure& e) {
std::cerr << "I/O error: " << e.what() << std::endl;
// 可以尝试释放一些磁盘空间或采取其他恢复措施
}
return 0;
}
- 保证数据完整性:
- 事务处理思想:在写入文件时,可以将一系列相关的写入操作视为一个事务。例如,先写入临时文件,在所有操作完成后,再将临时文件重命名为目标文件。这样如果在写入临时文件过程中出现异常,目标文件不会被破坏。
- 示例代码:
#include <iostream>
#include <fstream>
#include <filesystem>
int main() {
const std::string tempFilename = "temp_file.txt";
const std::string finalFilename = "final_file.txt";
std::ofstream tempOfs(tempFilename);
if (!tempOfs) {
std::cerr << "Failed to open temp file." << std::endl;
return 1;
}
try {
// 写入操作
tempOfs << "Some data" << std::endl;
tempOfs.close();
std::filesystem::rename(tempFilename, finalFilename);
} catch (const std::exception& e) {
std::cerr << "Error: " << e.what() << std::endl;
// 如果出现异常,删除临时文件
std::filesystem::remove(tempFilename);
}
return 0;
}
- 错误日志记录:在捕获到异常时,记录详细的错误信息到日志文件中,包括异常类型、发生时间、文件名等,以便后续排查问题。可以使用第三方日志库如
spdlog
等。
#include <iostream>
#include <fstream>
#include <spdlog/spdlog.h>
int main() {
try {
std::ifstream ifs("nonexistent_file.txt");
if (!ifs) {
throw std::ios::failure("File not found");
}
} catch (const std::ios::failure& e) {
spdlog::error("I/O exception: {}", e.what());
}
return 0;
}