Skip to content

Commit c7fdb54

Browse files
authored
Extend OrtAllocator API to get Allocator statistics (microsoft#24785)
### Description <!-- Describe your changes. --> Extend IAllocator to get Allocator statistics: - Add `OrtAllocator::GetStats` and `AllocatorGetStats` C-API. - Add `Ort::Allocator::GetStats` Cxx API to parse the string and return as map. - Add UT. ### Motivation and Context <!-- - Why is this change required? What problem does it solve? - If it fixes an open issue, please link to the issue here. --> Our system integrates multiple models for inference, each with varying memory demands. Providing a mechanism to retrieve detailed memory statistics would be useful for analyzing memory usage across models and devices more effectively.
1 parent 8b3326e commit c7fdb54

File tree

12 files changed

+144
-2
lines changed

12 files changed

+144
-2
lines changed

include/onnxruntime/core/framework/allocator.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,9 @@ class IAllocator {
101101
const OrtMemoryInfo& Info() const { return memory_info_; };
102102

103103
// Each implementation of IAllocator can override and provide their own implementation
104-
virtual void GetStats(AllocatorStats* /*stats*/) { return; }
104+
virtual void GetStats(AllocatorStats* stats) {
105+
*stats = {};
106+
}
105107

106108
static bool CalcMemSizeForArray(size_t nmemb, size_t size, size_t* out) noexcept {
107109
return CalcMemSizeForArrayWithAlignment(nmemb, size, 0, out);

include/onnxruntime/core/session/onnxruntime_c_api.h

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -340,6 +340,26 @@ typedef struct OrtAllocator {
340340
* those made during session initialization. This allows for separate memory management strategies for these allocations.
341341
*/
342342
void*(ORT_API_CALL* Reserve)(struct OrtAllocator* this_, size_t size); ///< Returns a pointer to an allocated block of `size` bytes
343+
344+
/**
345+
* @brief Function used to get the statistics of the allocator.
346+
*
347+
* Return a pointer to the OrtKeyValuePairs structure that contains the statistics of the allocator
348+
* and the user should call OrtApi::ReleaseKeyValuePairs.
349+
* Supported keys are:
350+
* - Limit: Bytes limit of the allocator. -1 if no limit is set.
351+
* - InUse: Number of bytes in use.
352+
* - TotalAllocated: The total number of allocated bytes by the allocator.
353+
* - MaxInUse: The maximum bytes in use.
354+
* - NumAllocs: Number of allocations.
355+
* - NumReserves: Number of reserves. (Number of calls to Reserve() in arena-based allocators)
356+
* - NumArenaExtensions: Number of arena extensions (Relevant only for arena based allocators)
357+
* - NumArenaShrinkages: Number of arena shrinkages (Relevant only for arena based allocators)
358+
* - MaxAllocSize: The max single allocation seen.
359+
*
360+
* NOTE: If the allocator does not implement this function, the OrtKeyValuePairs instance will be empty.
361+
*/
362+
ORT_API2_STATUS(GetStats, _In_ const struct OrtAllocator* this_, _Outptr_ OrtKeyValuePairs** out);
343363
} OrtAllocator;
344364

345365
typedef void(ORT_API_CALL* OrtLoggingFunction)(
@@ -5277,6 +5297,22 @@ struct OrtApi {
52775297
* \since Version 1.23
52785298
*/
52795299
ORT_API2_STATUS(GetTensorSizeInBytes, _In_ const OrtValue* ort_value, _Out_ size_t* size);
5300+
5301+
/** \brief Calls OrtAllocator::GetStats function
5302+
*
5303+
* Return a pointer to the OrtKeyValuePairs structure that contains the statistics of the allocator
5304+
* and the user should call OrtApi::ReleaseKeyValuePairs.
5305+
*
5306+
* NOTE: If the allocator does not implement this function, the OrtKeyValuePairs instance will be empty.
5307+
*
5308+
* \param[in] ort_allocator The allocator to get stats from
5309+
* \param[out] out A pointer to the OrtKeyValuePairs instance that contains the stats
5310+
*
5311+
* \snippet{doc} snippets.dox OrtStatus Return Value
5312+
*
5313+
* \since Version 1.23.
5314+
*/
5315+
ORT_API2_STATUS(AllocatorGetStats, _In_ const OrtAllocator* ort_allocator, _Outptr_ OrtKeyValuePairs** out);
52805316
};
52815317

52825318
/*

include/onnxruntime/core/session/onnxruntime_cxx_api.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2155,6 +2155,12 @@ struct AllocatorImpl : Base<T> {
21552155
MemoryAllocation GetAllocation(size_t size);
21562156
void Free(void* p);
21572157
ConstMemoryInfo GetInfo() const;
2158+
2159+
/** \brief Function that returns the statistics of the allocator.
2160+
*
2161+
* \return A pointer to a KeyValuePairs object that will be filled with the allocator statistics.
2162+
*/
2163+
KeyValuePairs GetStats() const;
21582164
};
21592165

21602166
} // namespace detail

include/onnxruntime/core/session/onnxruntime_cxx_inline.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -243,6 +243,12 @@ inline ConstMemoryInfo AllocatorImpl<T>::GetInfo() const {
243243
return ConstMemoryInfo{out};
244244
}
245245

246+
template <typename T>
247+
inline KeyValuePairs AllocatorImpl<T>::GetStats() const {
248+
OrtKeyValuePairs* out;
249+
ThrowOnError(GetApi().AllocatorGetStats(this->p_, &out));
250+
return KeyValuePairs(out);
251+
}
246252
} // namespace detail
247253

248254
inline AllocatorWithDefaultOptions::AllocatorWithDefaultOptions() {

onnxruntime/core/framework/allocator_stats.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,4 +51,4 @@ struct AllocatorStats {
5151
return ss.str();
5252
}
5353
};
54-
} // namespace onnxruntime
54+
} // namespace onnxruntime

onnxruntime/core/session/allocator_adapters.cc

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
#include "allocator_adapters.h"
55
#include "core/framework/error_code_helper.h"
6+
#include "core/session/abi_key_value_pairs.h"
67
#include "core/session/inference_session.h"
78
#include "core/session/ort_env.h"
89
#include "core/session/ort_apis.h"
@@ -26,6 +27,16 @@ OrtAllocatorImplWrappingIAllocator::OrtAllocatorImplWrappingIAllocator(onnxrunti
2627
OrtAllocator::Reserve =
2728
[](OrtAllocator* this_, size_t size) { return static_cast<OrtAllocatorImplWrappingIAllocator*>(this_)->Reserve(size); };
2829
}
30+
OrtAllocator::GetStats =
31+
[](const OrtAllocator* this_, OrtKeyValuePairs** stats) noexcept -> OrtStatusPtr {
32+
API_IMPL_BEGIN
33+
auto kvp = std::make_unique<OrtKeyValuePairs>();
34+
auto stats_map = static_cast<const OrtAllocatorImplWrappingIAllocator*>(this_)->Stats();
35+
kvp->Copy(stats_map);
36+
*stats = reinterpret_cast<OrtKeyValuePairs*>(kvp.release());
37+
return nullptr;
38+
API_IMPL_END
39+
};
2940
}
3041

3142
void* OrtAllocatorImplWrappingIAllocator::Alloc(size_t size) {
@@ -44,6 +55,26 @@ const OrtMemoryInfo* OrtAllocatorImplWrappingIAllocator::Info() const {
4455
return &i_allocator_->Info();
4556
}
4657

58+
std::unordered_map<std::string, std::string> OrtAllocatorImplWrappingIAllocator::Stats() const {
59+
AllocatorStats stats{};
60+
i_allocator_->GetStats(&stats);
61+
62+
// Allocators which does not implement GetStats() will return empty stats
63+
std::unordered_map<std::string, std::string> entries;
64+
if (stats.num_allocs > 0 || stats.bytes_limit != 0) {
65+
entries.insert_or_assign("Limit", std::to_string(stats.bytes_limit));
66+
entries.insert_or_assign("InUse", std::to_string(stats.bytes_in_use));
67+
entries.insert_or_assign("TotalAllocated", std::to_string(stats.total_allocated_bytes));
68+
entries.insert_or_assign("MaxInUse", std::to_string(stats.max_bytes_in_use));
69+
entries.insert_or_assign("NumAllocs", std::to_string(stats.num_allocs));
70+
entries.insert_or_assign("NumReserves", std::to_string(stats.num_reserves));
71+
entries.insert_or_assign("NumArenaExtensions", std::to_string(stats.num_arena_extensions));
72+
entries.insert_or_assign("NumArenaShrinkages", std::to_string(stats.num_arena_shrinkages));
73+
entries.insert_or_assign("MaxAllocSize", std::to_string(stats.max_alloc_size));
74+
}
75+
return entries;
76+
}
77+
4778
onnxruntime::AllocatorPtr OrtAllocatorImplWrappingIAllocator::GetWrappedIAllocator() {
4879
return i_allocator_;
4980
}

onnxruntime/core/session/allocator_adapters.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66
#include "core/framework/allocator.h"
77
#include "core/session/onnxruntime_cxx_api.h"
88

9+
#include <string>
10+
911
namespace onnxruntime {
1012

1113
// Since all allocators are of type 'OrtAllocator' and there is a single
@@ -29,6 +31,8 @@ struct OrtAllocatorImplWrappingIAllocator final : public OrtAllocatorImpl {
2931
const OrtMemoryInfo* Info() const;
3032
void* Reserve(size_t size);
3133

34+
std::unordered_map<std::string, std::string> Stats() const;
35+
3236
ORT_DISALLOW_COPY_AND_ASSIGNMENT(OrtAllocatorImplWrappingIAllocator);
3337

3438
onnxruntime::AllocatorPtr GetWrappedIAllocator();

onnxruntime/core/session/default_cpu_allocator_c_api.cc

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
#include "core/framework/utils.h"
55
#include "core/framework/allocator.h"
6+
#include "core/session/abi_key_value_pairs.h"
67
#include "core/session/allocator_adapters.h"
78
#include "core/session/onnxruntime_cxx_api.h"
89
#include "core/session/ort_apis.h"
@@ -17,6 +18,15 @@ struct OrtDefaultCpuAllocator : onnxruntime::OrtAllocatorImpl {
1718
[](OrtAllocator* this_, void* p) { static_cast<OrtDefaultCpuAllocator*>(this_)->Free(p); };
1819
OrtAllocator::Info =
1920
[](const OrtAllocator* this_) { return static_cast<const OrtDefaultCpuAllocator*>(this_)->Info(); };
21+
OrtAllocator::Reserve =
22+
[](OrtAllocator* this_, size_t size) { return static_cast<OrtDefaultCpuAllocator*>(this_)->Alloc(size); };
23+
OrtAllocator::GetStats =
24+
[](const OrtAllocator* /*this_*/, OrtKeyValuePairs** stats) noexcept -> OrtStatusPtr {
25+
// Default allocator does not support stats, return an empty OrtKeyValuePairs.
26+
auto kvp = std::make_unique<OrtKeyValuePairs>();
27+
*stats = reinterpret_cast<OrtKeyValuePairs*>(kvp.release());
28+
return nullptr;
29+
};
2030
Ort::ThrowOnError(OrtApis::CreateCpuMemoryInfo(OrtDeviceAllocator, OrtMemTypeDefault, &cpu_memory_info));
2131
}
2232

onnxruntime/core/session/onnxruntime_c_api.cc

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1567,6 +1567,12 @@ ORT_API_STATUS_IMPL(OrtApis::AllocatorGetInfo, _In_ const OrtAllocator* ptr, _Ou
15671567
API_IMPL_END
15681568
}
15691569

1570+
ORT_API_STATUS_IMPL(OrtApis::AllocatorGetStats, _In_ const OrtAllocator* ptr, _Outptr_ OrtKeyValuePairs** out) {
1571+
API_IMPL_BEGIN
1572+
return ptr->GetStats(ptr, out);
1573+
API_IMPL_END
1574+
}
1575+
15701576
template <typename T>
15711577
ORT_STATUS_PTR OrtGetNumSequenceElements(const OrtValue* p_ml_value, size_t* out) {
15721578
auto& data = p_ml_value->Get<T>();
@@ -3024,6 +3030,7 @@ static constexpr OrtApi ort_api_1_to_23 = {
30243030
&OrtApis::GetEpApi,
30253031
// End of Version 22 - DO NOT MODIFY ABOVE (see above text for more information)
30263032
&OrtApis::GetTensorSizeInBytes,
3033+
&OrtApis::AllocatorGetStats,
30273034
};
30283035

30293036
// OrtApiBase can never change as there is no way to know what version of OrtApiBase is returned by OrtGetApiBase.

onnxruntime/core/session/ort_apis.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -601,4 +601,5 @@ ORT_API(const OrtEpApi*, GetEpApi);
601601

602602
ORT_API_STATUS_IMPL(GetTensorSizeInBytes, _In_ const OrtValue* ort_value, _Out_ size_t* size);
603603

604+
ORT_API_STATUS_IMPL(AllocatorGetStats, _In_ const OrtAllocator* ptr, _Outptr_ OrtKeyValuePairs** out);
604605
} // namespace OrtApis

0 commit comments

Comments
 (0)