【发布时间】:2014-10-27 04:34:47
【问题描述】:
我有一个由文件映射内存支持的循环缓冲区(缓冲区的大小范围为 8GB-512GB)。
我正在从头到尾按顺序写入(8 个实例)该内存,此时它会循环回到开头。
它工作正常,直到它需要执行两个文件映射并在内存中循环,此时 IO 性能完全被破坏并且无法恢复(即使在几分钟后)。我不太清楚。
using namespace boost::interprocess;
class mapping
{
public:
mapping()
{
}
mapping(file_mapping& file, mode_t mode, std::size_t file_size, std::size_t offset, std::size_t size)
: offset_(offset)
, mode_(mode)
{
const auto aligned_size = page_ceil(size + page_size());
const auto aligned_file_size = page_floor(file_size);
const auto aligned_file_offset = page_floor(offset % aligned_file_size);
const auto region1_size = std::min(aligned_size, aligned_file_size - aligned_file_offset);
const auto region2_size = aligned_size - region1_size;
if (region2_size)
{
const auto region1_address = mapped_region(file, read_only, 0, (region1_size + region2_size) * 2).get_address();
const auto region2_address = reinterpret_cast<char*>(region1_address) + region1_size;
region1_ = mapped_region(file, mode, aligned_file_offset, region1_size, region1_address);
region2_ = mapped_region(file, mode, 0, region2_size, region2_address);
}
else
{
region1_ = mapped_region(file, mode, aligned_file_offset, region1_size);
region2_ = mapped_region();
}
size_ = region1_.get_size() + region2_.get_size();
offset_ = aligned_file_offset;
}
auto offset() const -> std::size_t { return offset_; }
auto size() const -> std::size_t { return size_; }
auto data() const -> const void* { return region1_.get_address(); }
auto data() -> void* { return region1_.get_address(); }
auto flush(bool async = true) -> void
{
region1_.flush(async);
region2_.flush(async);
}
auto mode() const -> mode_t { return mode_; }
private:
std::size_t offset_ = 0;
std::size_t size_ = 0;
mode_t mode_;
mapped_region region1_;
mapped_region region2_;
};
struct loop_mapping::impl final
{
std::tr2::sys::path file_path_;
file_mapping file_mapping_;
std::size_t file_size_;
std::size_t map_size_ = page_floor(256000000ULL);
std::shared_ptr<mapping> mapping_ = std::shared_ptr<mapping>(new mapping());
std::shared_ptr<mapping> prev_mapping_;
bool write_;
public:
impl(std::tr2::sys::path path, bool write)
: file_path_(std::move(path))
, file_mapping_(file_path_.string().c_str(), write ? read_write : read_only)
, file_size_(page_floor(std::tr2::sys::file_size(file_path_)))
, write_(write)
{
REQUIRE(file_size_ >= map_size_ * 3);
}
~impl()
{
prev_mapping_.reset();
mapping_.reset();
}
auto data(std::size_t offset, std::size_t size, boost::optional<bool> write_opt) -> void*
{
offset = offset % page_floor(file_size_);
REQUIRE(size < file_size_ - map_size_ * 3);
const auto write = write_opt.get_value_or(write_);
REQUIRE(!write || write_);
if ((write && mapping_->mode() == read_only) || offset < mapping_->offset() || offset + size >= mapping_->offset() + mapping_->size())
{
auto new_mapping = std::make_shared<loop::mapping>(file_mapping_, write ? read_write : read_only, file_size_, page_floor(offset), std::max(size + page_size(), map_size_));
if (mapping_)
mapping_->flush((new_mapping->offset() % file_size_) < (mapping_->offset() % file_size_));
if (prev_mapping_)
prev_mapping_->flush(false);
prev_mapping_ = std::move(mapping_);
mapping_ = std::move(new_mapping);
}
return reinterpret_cast<char*>(mapping_->data()) + offset - mapping_->offset();
}
}
-
// 8 processes to 8 different files 128GB each.
loop_mapping loop(...);
for (auto n = 0; true; ++n)
{
auto src = get_new_data(5000000/8);
auto dst = loop.data(n * 5000000/8, 5000000/8, true);
std::memcpy(dst, src, 5000000/8); // This becomes very slow after loop around.
std::this_thread::sleep_for(std::chrono::seconds(1));
}
有什么想法吗?
目标系统:
- 1x 3TB 希捷 Constellation ES.3
- 2x Xeon E5-2400(6 核,2.6Ghz)
- 6x 8GB DDR3 1600Mhz ECC
- Windows Server 2012
【问题讨论】:
-
您能否为您发布的代码添加更多解释?是发布的代码块中的慢速部分,还是提供的代码本身是慢速部分?
-
没有任何代码本身很慢。当我尝试写入映射内存时,它会变慢。我将添加一个简单的示例。
-
您可能需要为文件预先分配磁盘空间,方法是在末尾写入至少一个字节或使用 SetFileValidData(需要管理员权限)。
-
它听起来就像你在交换到你的进程地址空间时挣扎。仅仅因为文件被映射并不意味着它被提交到物理 RAM;它只是意味着它有一个映射的逻辑地址。每个“项目”(256000000,字节)为 244.14 MB,我可以很容易地看到这种情况发生。如果读取的目标是也在同样必须交换到物理存储的页面上,情况会更加复杂。您是否进行过进程评估以查看由此产生的页面错误(触发从物理存储读取到地址空间的未命中)?
-
@ronag 这个 IO 性能下降对我来说听起来就像内核低效地刷新修改的页面一样。我已经看到了。您期望顺序 IO,但您(部分)以高达 100 倍的性能损失获得随机 IO。如果您手动执行,那将永远不会发生。 AFAIK 操作系统同步文件缓冲区。也许您可以通过其他方式与其他进程同步并使用文件 IO 或使用内存共享部分传输数据。
标签: c++ windows boost interprocess file-mapping