此bounty已结束。回答此问题可获得+100声望奖励。赏金宽限期22小时后结束getsoubl希望引起更多的注意这个问题。
我对C++17中引入的单调缓冲区和非同步内存池的组合有疑问。下面是一段代码(源代码C++ Weekly - Ep 248)
#include <spdlog/spdlog.h>
#include <array>
#include <cassert>
#include <iostream>
#include <memory_resource>
#include <string>
#include <vector>
// Prints if new/delete gets used.
class print_alloc : public std::pmr::memory_resource {
public:
print_alloc(std::string name, std::pmr::memory_resource* upstream)
: m_name(std::move(name)), m_upstream(upstream) {
assert(upstream);
}
private:
std::string m_name;
std::pmr::memory_resource* m_upstream;
void* do_allocate(std::size_t bytes, std::size_t alignment) override {
spdlog::trace("[{} (alloc)] Size: {} Alignment: {} ...", m_name, bytes,
alignment);
auto result = m_upstream->allocate(bytes, alignment);
spdlog::trace("[{} (alloc)] ... Address: {}", m_name, result);
return result;
}
std::string format_destroyed_bytes(std::byte* p, const std::size_t size) {
std::string result = "";
bool in_string = false;
auto format_char = [](bool& in_string, const char c, const char next) {
auto format_byte = [](const char byte) {
return fmt::format(" {:02x}", static_cast<unsigned char>(byte));
};
if (std::isprint(static_cast<int>(c))) {
if (!in_string) {
if (std::isprint(static_cast<int>(next))) {
in_string = true;
return fmt::format(" \"{}", c);
} else {
return format_byte(c);
}
} else {
return std::string(1, c);
}
} else {
if (in_string) {
in_string = false;
return '"' + format_byte(c);
}
return format_byte(c);
}
};
std::size_t pos = 0;
for (; pos < std::min(size - 1, static_cast<std::size_t>(32)); ++pos) {
result += format_char(in_string, static_cast<char>(p[pos]),
static_cast<char>(p[pos + 1]));
}
result += format_char(in_string, static_cast<char>(p[pos]), 0);
if (in_string) {
result += '"';
}
if (pos < (size - 1)) {
result += " <truncated...>";
}
return result;
}
void do_deallocate(void* p, std::size_t bytes,
std::size_t alignment) override {
spdlog::trace(
"[{} (dealloc)] Address: {} Dealloc Size: {} Alignment: {} Data: {}",
m_name, p, bytes, alignment,
format_destroyed_bytes(static_cast<std::byte*>(p), bytes));
m_upstream->deallocate(p, bytes, alignment);
}
bool do_is_equal(
const std::pmr::memory_resource& other) const noexcept override {
return this == &other;
}
};
template <typename Container, typename... Values>
auto create_container(auto* resource, Values&&... values) {
Container result{resource};
result.reserve(sizeof...(values));
(result.emplace_back(std::forward<Values>(values)), ...);
return result;
};
int main() {
spdlog::set_level(spdlog::level::trace);
print_alloc default_alloc{"Rogue PMR Allocation!",
std::pmr::null_memory_resource()};
std::pmr::set_default_resource(&default_alloc);
print_alloc oom{"Out of Memory", std::pmr::null_memory_resource()};
std::array<std::uint8_t, 32768> buffer{};
std::pmr::monotonic_buffer_resource underlying_bytes(buffer.data(),
buffer.size(), &oom);
print_alloc monotonic{"Monotonic Array", &underlying_bytes};
std::pmr::unsynchronized_pool_resource unsync_pool(&monotonic);
print_alloc pool("Pool", &unsync_pool);
for (int i = 0; i < 10; ++i) {
spdlog::debug("Starting Loop Iteration");
auto vec = create_container<std::pmr::vector<std::pmr::string>>(
&pool, "Hello", "World", "Hello Long String", "Another Long String");
spdlog::trace("Emplacing Long String");
vec.emplace_back("a different long string");
spdlog::trace("Emplacing Long String");
vec.emplace_back("a different long string 1");
spdlog::trace("Emplacing Long String");
vec.emplace_back("a different long string");
spdlog::trace("Emplacing Long String");
vec.emplace_back("a different long string");
spdlog::trace("Emplacing Short String");
vec.emplace_back("bob");
spdlog::trace("Emplacing Short String");
vec.emplace_back("was");
spdlog::trace("Erasing First Element");
vec.erase(vec.begin());
spdlog::trace("Erasing First Element");
vec.erase(vec.begin());
spdlog::trace("Erasing First Element");
vec.erase(vec.begin());
spdlog::debug("Finishing Loop Iteration");
// vec.push_back("Hello Long World");
}
spdlog::debug("Exiting Main");
}
Demo
基于单调缓冲区的implementation,在释放期间不做任何事情。缓冲区只增长。
检查打印输出,如果这个程序,在每次迭代中,使用相同的内存区域。这不符合单调缓冲区的实现。我错过了什么。在每次迭代中,从单调缓冲区中检索的分配块被重用
1条答案
按热度按时间k3fezbri1#
检查打印输出,如果这个程序,在每次迭代中,使用相同的内存区域。这不符合单调缓冲区的实现。我错过了什么。在每次迭代中,从单调缓冲区中检索的分配块被重用
你所说的是
unsynchronized_pool_resource
和monotonic_buffer_resources
组合的预期结果。你说monotonic_buffer_resources
应该分配新的内存,永远不要重用以前分配的内存-它做!您所看到的行为是unsynchorinized_pool_resource
的结果,unsynchorinized_pool_resource
是一种旨在从其底层资源(在本例中为monotonic_buffer_resources
)中池化和重用内存的资源。当内存被释放时,池资源保留该内存以供将来分配,而不是立即将其返回给其资源。monotonic_buffer_resource
仍然表现单调-unsynchronized_pool_resource
的池化行为显示了这些迭代中的内存重用。结合这两个允许非常有效的内存使用。您的程序可以从
monotonic_buffer_resource
获得快速(和可预测)的内存分配,并从unsynchronized_pool_resource
获得内存重用的效率。希望这能让你明白。
演示
这里有一个简单的例子,利用这两个:
当运行时,会看到这样的输出:
这里我们可以看到如果我们 * 只 * 使用
monotonic_buffer_resource
的情况:输出是你所假设的,你在每次迭代中看到一个不同的内存地址-因为
monotonic_buffer_resource
从它的缓冲区分配新的内存块,从不重用以前分配的内存。