Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
125 changes: 62 additions & 63 deletions benchmarks/allocator_memory_cost_benchmark.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,24 +16,18 @@
#include <vector>

#include "umpire/Allocator.hpp"
#include "umpire/CLI11/CLI11.hpp"
#include "umpire/ResourceManager.hpp"
#include "umpire/Umpire.hpp"
#include "umpire/resource/MemoryResourceRegistry.hpp"
#include "umpire/strategy/QuickPool.hpp"
#include "umpire/CLI11/CLI11.hpp"

namespace {
const std::string hwm_name{"Memory Usage HSM"};

std::string pretty(std::size_t n)
{
const char* scale[] = {
" bytes",
" kilobytes",
" megabytes",
" gigabytes",
" terabytes"
};
const char* scale[] = {" bytes", " kilobytes", " megabytes", " gigabytes", " terabytes"};

int suffix{0};
double count{static_cast<double>(n)};
Expand Down Expand Up @@ -71,7 +65,7 @@ std::size_t get_memory_stat(const std::string& name)
return rval;
}

};
}; // namespace

struct MemoryStatSnapshot {
void take_snapshot()
Expand All @@ -92,11 +86,14 @@ struct MemoryStatSnapshot {
};

class AllocatorCostBenchmark {
public:
public:
friend std::ostream& operator<<(std::ostream& os, const AllocatorCostBenchmark& acb);

AllocatorCostBenchmark(const std::string& a, std::size_t n, bool no_intro, bool _show_process_mem_info) :
allocator_to_use{a}, number_of_allocs{n}, no_introspection{no_intro}, show_process_mem_info{_show_process_mem_info}
AllocatorCostBenchmark(const std::string& a, std::size_t n, bool no_intro, bool _show_process_mem_info)
: allocator_to_use{a},
number_of_allocs{n},
no_introspection{no_intro},
show_process_mem_info{_show_process_mem_info}
{
is_umpire_allocator = (allocator_to_use != "malloc");

Expand All @@ -108,15 +105,18 @@ class AllocatorCostBenchmark {

if (allocator_to_use == "Host") {
umpire_allocator = resource_allocator;
}
else if (allocator_to_use == "Quick") {
} else if (allocator_to_use == "HostFast") {
umpire_allocator = umpire::ResourceManager::getInstance().getAllocator("HOST_FAST");
} else if (allocator_to_use == "HostStats") {
umpire_allocator = umpire::ResourceManager::getInstance().makeResource("HOST_STATS_BENCHMARK",
umpire::Tracking::StatisticsOnly);
} else if (allocator_to_use == "Quick") {
if (no_introspection) {
umpire_allocator = umpire::ResourceManager::getInstance().makeAllocator<umpire::strategy::QuickPool, false>
("QUICK_POOL", resource_allocator);
}
else {
umpire_allocator = umpire::ResourceManager::getInstance().makeAllocator<umpire::strategy::QuickPool, true>
("QUICK_POOL", resource_allocator);
umpire_allocator = umpire::ResourceManager::getInstance().makeAllocator<umpire::strategy::QuickPool, false>(
"QUICK_POOL", resource_allocator);
} else {
umpire_allocator = umpire::ResourceManager::getInstance().makeAllocator<umpire::strategy::QuickPool, true>(
"QUICK_POOL", resource_allocator);
}
}
}
Expand All @@ -143,7 +143,6 @@ class AllocatorCostBenchmark {

for (std::size_t i = 0; i < number_of_allocs; ++i)
deallocate(slots[i]);

}

void run_time_benchmark()
Expand All @@ -155,16 +154,16 @@ class AllocatorCostBenchmark {
struct AllocatorValidator : public CLI::Validator {
AllocatorValidator()
{
func_ = [](const std::string &str) {
if (str != "Quick" && str != "Host" && str != "malloc") {
return std::string("Invalid Allocator name, must be Host, Quick, or malloc");
func_ = [](const std::string& str) {
if (str != "Quick" && str != "Host" && str != "HostFast" && str != "HostStats" && str != "malloc") {
return std::string("Invalid Allocator name, must be Host, HostFast, HostStats, Quick, or malloc");
} else
return std::string();
};
}
};

private:
private:
const std::string allocator_to_use;
const std::size_t number_of_allocs;
const bool no_introspection;
Expand All @@ -184,8 +183,7 @@ class AllocatorCostBenchmark {

if (is_umpire_allocator) {
ptr = umpire_allocator.allocate(n);
}
else {
} else {
ptr = ::malloc(n);
}

Expand All @@ -196,48 +194,49 @@ class AllocatorCostBenchmark {
{
if (is_umpire_allocator) {
umpire_allocator.deallocate(ptr);
}
else {
} else {
::free(ptr);
}
}

};

std::ostream& operator<<(std::ostream& os, const AllocatorCostBenchmark& acb)
{
os << acb.number_of_allocs << " 4-byte allocs from " << acb.allocator_to_use;
if (acb.is_umpire_allocator)
os << (acb.no_introspection ? std::string{"{Intro OFF}"} : std::string{"{Intro ON}"});
if (acb.is_umpire_allocator) {
if (acb.allocator_to_use == "HostFast") {
os << "{Untracked}";
} else if (acb.allocator_to_use == "HostStats") {
os << "{StatsOnly}";
} else {
os << (acb.no_introspection ? std::string{"{Intro OFF}"} : std::string{"{Intro ON}"});
}
}

if (acb.show_process_mem_info) {
os << std::endl;
for ( auto& m : acb.mstat.stat ) {
for (auto& m : acb.mstat.stat) {
const std::size_t total{m.second};
std::unordered_map<std::string,std::size_t>::const_iterator base_it = acb.mstat_baseline.stat.find(m.first);
std::unordered_map<std::string, std::size_t>::const_iterator base_it = acb.mstat_baseline.stat.find(m.first);
const std::size_t baseline{base_it->second};
const std::size_t cost{total - baseline};

os
<< m.first << " "
<< "Total{" << pretty(total) << "}, "
<< "Baseline{" << pretty(baseline) << "}, "
<< "Cost{" << pretty(cost) << "}, "
<< "or {" << pretty(cost / acb.number_of_allocs) << "}/allocation"
<< std::endl;
os << m.first << " "
<< "Total{" << pretty(total) << "}, "
<< "Baseline{" << pretty(baseline) << "}, "
<< "Cost{" << pretty(cost) << "}, "
<< "or {" << pretty(cost / acb.number_of_allocs) << "}/allocation" << std::endl;
}
}
else {
std::unordered_map<std::string,std::size_t>::const_iterator total_it = acb.mstat.stat.find(hwm_name);
std::unordered_map<std::string,std::size_t>::const_iterator base_it = acb.mstat_baseline.stat.find(hwm_name);
} else {
std::unordered_map<std::string, std::size_t>::const_iterator total_it = acb.mstat.stat.find(hwm_name);
std::unordered_map<std::string, std::size_t>::const_iterator base_it = acb.mstat_baseline.stat.find(hwm_name);
const std::size_t total{total_it->second};
const std::size_t baseline{base_it->second};
const std::size_t cost{total - baseline};
double allocs = static_cast<double>(acb.number_of_allocs);
os << " uses " << pretty(cost) << " of memory, costing "
<< pretty(cost / acb.number_of_allocs) << ", and "
<< std::chrono::duration_cast<std::chrono::nanoseconds>(acb.elapsed_time).count() / allocs << " nanoseconds per allocation"
<< std::endl;
os << " uses " << pretty(cost) << " of memory, costing " << pretty(cost / acb.number_of_allocs) << ", and "
<< std::chrono::duration_cast<std::chrono::nanoseconds>(acb.elapsed_time).count() / allocs
<< " nanoseconds per allocation" << std::endl;
}

return os;
Expand All @@ -251,18 +250,19 @@ int main(int argc, char* argv[])

std::string allocator;
app.add_option("-a,--use-allocator", allocator,
"Specify Allocator to use: 'Host', 'Quick', or 'malloc'.\n"
"When 'malloc' is specified, use the raw system call (no Umpire).\n"
"When 'Host' is specified, use HOST Umpire resource allocator.\n"
"When 'Quick' is specified, use the Umpire Quickpool allocator.\n"
)
->required()
->check(valid_allocator);
"Specify Allocator to use: 'Host', 'Quick', or 'malloc'.\n"
"When 'malloc' is specified, use the raw system call (no Umpire).\n"
"When 'Host' is specified, use HOST Umpire resource allocator.\n"
"When 'HostFast' is specified, use the untracked HOST_FAST allocator.\n"
"When 'HostStats' is specified, use a statistics-only HOST resource allocator.\n"
"When 'Quick' is specified, use the Umpire Quickpool allocator.\n")
->required()
->check(valid_allocator);

std::size_t allocations;
app.add_option("-n,--num-allocations", allocations, "Specify number of allocations to perform")
->required()
->check(CLI::Range(0, 100000000));
->required()
->check(CLI::Range(0, 100000000));

bool no_introspection{false};
app.add_flag("--no_introspection", no_introspection, "Disable introspection");
Expand All @@ -281,11 +281,10 @@ int main(int argc, char* argv[])
AllocatorCostBenchmark bm{allocator, allocations, no_introspection, show_process_mem_info};

if (measure_time_overhead) {
bm.run_time_benchmark();
}
else if (measure_space_overhead) {
bm.run_space_benchmark();
std::cout << bm;
bm.run_time_benchmark();
} else if (measure_space_overhead) {
bm.run_space_benchmark();
std::cout << bm;
}

return 0;
Expand Down
15 changes: 15 additions & 0 deletions docs/sphinx/cookbook/no_introspection.rst
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,13 @@ for allocations that come from a particular :class:`umpire::Allocator`, you can
turn off the introspection and avoid the overhead of tracking the associated
metadata.

For host allocations, Umpire also provides a built-in ``HOST_FAST`` allocator
that uses host memory with tracking disabled:

.. code-block:: C++

auto host_fast = rm.getAllocator("HOST_FAST");

.. warning::
Disabling introspection means that allocations from this Allocator cannot
be used for operations, or size and location queries.
Expand All @@ -31,6 +38,14 @@ allocations made from the pool, so the
:end-before: _sphinx_tag_tut_getsize_end
:language: C++

If you only need allocator statistics such as current size, high watermark, and
allocation count, but do not need pointer lookup or Umpire operations, create a
resource or allocator with ``umpire::Tracking::StatisticsOnly``:

.. code-block:: C++

auto host_stats = rm.makeResource("HOST_STATS", umpire::Tracking::StatisticsOnly);


The complete example is included below:

Expand Down
6 changes: 6 additions & 0 deletions src/umpire/Allocator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ Allocator::Allocator(strategy::AllocationStrategy* allocator) noexcept
strategy::mixins::AllocateNull{},
m_allocator{allocator},
m_tracking{allocator->isTracked()},
m_statistics_tracking{allocator->isStatisticsOnlyTracked()},
m_thread_safe{(dynamic_cast<umpire::strategy::ThreadSafeAllocator*>(allocator) != nullptr)}
{
m_thread_safe_mutex =
Expand Down Expand Up @@ -97,6 +98,11 @@ bool Allocator::isTracked() const noexcept
return m_allocator->isTracked();
}

Tracking Allocator::getTracking() const noexcept
{
return m_allocator->getTracking();
}

const std::string& Allocator::getStrategyName() const noexcept
{
return m_allocator->getStrategyName();
Expand Down
3 changes: 3 additions & 0 deletions src/umpire/Allocator.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@

#include "camp/camp.hpp"
#include "camp/resource.hpp"
#include "umpire/Tracking.hpp"
#include "umpire/strategy/AllocationStrategy.hpp"
#include "umpire/strategy/mixins/AllocateNull.hpp"
#include "umpire/strategy/mixins/Inspector.hpp"
Expand Down Expand Up @@ -181,6 +182,7 @@ class Allocator : private strategy::mixins::Inspector, strategy::mixins::Allocat
Platform getPlatform() noexcept;

bool isTracked() const noexcept;
Tracking getTracking() const noexcept;

const std::string& getStrategyName() const noexcept;

Expand All @@ -206,6 +208,7 @@ class Allocator : private strategy::mixins::Inspector, strategy::mixins::Allocat
umpire::strategy::AllocationStrategy* m_allocator;

bool m_tracking{true};
bool m_statistics_tracking{false};

/*!
* \brief Implementation to conditionally make Allocator thread-safe
Expand Down
16 changes: 16 additions & 0 deletions src/umpire/Allocator.inl
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ inline void* Allocator::do_allocate(std::size_t bytes)

if (m_tracking) {
registerAllocation(ret, bytes, m_allocator);
} else if (m_statistics_tracking) {
m_allocator->registerAllocationStatistics(ret, bytes);
}

umpire::event::record<umpire::event::allocate>(
Expand Down Expand Up @@ -93,6 +95,8 @@ inline void* Allocator::do_named_allocate(const std::string& name, std::size_t b

if (m_tracking) {
registerAllocation(ret, bytes, m_allocator, name);
} else if (m_statistics_tracking) {
m_allocator->registerAllocationStatistics(ret, bytes);
}

umpire::event::record<umpire::event::named_allocate>(
Expand All @@ -116,6 +120,8 @@ inline void* Allocator::do_resource_allocate(std::size_t bytes, camp::resources:

if (m_tracking) {
registerAllocation(ret, bytes, m_allocator);
} else if (m_statistics_tracking) {
m_allocator->registerAllocationStatistics(ret, bytes);
}

umpire::event::record<umpire::event::allocate_resource>(
Expand All @@ -138,6 +144,11 @@ inline void Allocator::do_deallocate(void* ptr)
if (!deallocateNull(ptr)) {
m_allocator->deallocate(ptr, record.size);
}
} else if (m_statistics_tracking) {
const auto size = m_allocator->deregisterAllocationStatistics(ptr);
if (!deallocateNull(ptr)) {
m_allocator->deallocate(ptr, size);
}
} else {
if (!deallocateNull(ptr)) {
m_allocator->deallocate(ptr);
Expand All @@ -162,6 +173,11 @@ inline void Allocator::do_resource_deallocate(void* ptr, camp::resources::Resour
if (!deallocateNull(ptr)) {
m_allocator->deallocate_resource(ptr, r, record.size);
}
} else if (m_statistics_tracking) {
const auto size = m_allocator->deregisterAllocationStatistics(ptr);
if (!deallocateNull(ptr)) {
m_allocator->deallocate_resource(ptr, r, size);
}
} else {
if (!deallocateNull(ptr)) {
m_allocator->deallocate_resource(ptr, r);
Expand Down
Loading
Loading