Skip to content

Commit 3477cd0

Browse files
tf-marissawcopybara-github
authored andcommitted
Add peak private footprint memory measurement tracking
PiperOrigin-RevId: 820398210
1 parent eae3e20 commit 3477cd0

File tree

6 files changed

+91
-6
lines changed

6 files changed

+91
-6
lines changed

tflite/profiling/memory_info.cc

Lines changed: 51 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,12 @@ limitations under the License.
1616

1717
#include <stddef.h>
1818

19+
#include <cstdint>
20+
#include <fstream>
21+
#include <iostream>
1922
#include <ostream>
23+
#include <sstream>
24+
#include <string>
2025

2126
#ifdef __linux__
2227
#include <malloc.h>
@@ -37,6 +42,38 @@ namespace memory {
3742

3843
const size_t MemoryUsage::kValueNotSet = 0;
3944

45+
namespace {
46+
47+
#if defined(__linux__)
48+
// Returns the current VM swap in kilobytes on Linux.
49+
int64_t GetCurrentVmSwapKb() {
50+
std::ifstream status_file("/proc/self/status");
51+
if (!status_file.is_open()) {
52+
return -1;
53+
}
54+
std::string line;
55+
while (std::getline(status_file, line)) {
56+
if (line.rfind("VmSwap:", 0) == 0) {
57+
std::stringstream ss(line);
58+
std::string key;
59+
int64_t value_kb;
60+
// The line format is "VmSwap: 1234 kB"
61+
// We can extract the key ("VmSwap:") and the numeric value ("1234").
62+
ss >> key >> value_kb;
63+
if (!ss.fail()) {
64+
return value_kb;
65+
} else {
66+
return -1; // Indicate parsing error
67+
}
68+
}
69+
}
70+
// If the VmSwap line is not found, it means 0 swap is being used.
71+
return 0;
72+
}
73+
#endif
74+
75+
} // namespace
76+
4077
bool MemoryUsage::IsSupported() {
4178
#if defined(__linux__) || defined(__APPLE__) || defined(_WIN32)
4279
return true;
@@ -50,6 +87,10 @@ MemoryUsage GetMemoryUsage() {
5087
rusage res;
5188
if (getrusage(RUSAGE_SELF, &res) == 0) {
5289
result.mem_footprint_kb = res.ru_maxrss;
90+
int64_t vm_swap_kb = GetCurrentVmSwapKb();
91+
if (vm_swap_kb >= 0) {
92+
result.private_footprint_bytes = (vm_swap_kb + res.ru_maxrss) * 1024;
93+
}
5394
}
5495
#if defined(__NO_MALLINFO__)
5596
result.total_allocated_bytes = -1;
@@ -71,19 +112,24 @@ MemoryUsage GetMemoryUsage() {
71112
if (status == KERN_SUCCESS) {
72113
result.mem_footprint_kb =
73114
static_cast<int64_t>(vm_info.phys_footprint / 1024.0);
115+
// TODO: b/421171145 - Consider subtracting shared_resident_kb.
116+
result.private_footprint_bytes = vm_info.phys_footprint;
74117
}
75118
struct mstats stats = mstats();
76119
result.total_allocated_bytes = stats.bytes_total;
77120
result.in_use_allocated_bytes = stats.bytes_used;
78121
#elif defined(_WIN32)
79-
PROCESS_MEMORY_COUNTERS process_memory_counters;
122+
PROCESS_MEMORY_COUNTERS_EX process_memory_counters;
80123
HANDLE process_handle = GetCurrentProcess();
81124
if (process_handle != nullptr &&
82-
GetProcessMemoryInfo(process_handle, &process_memory_counters,
125+
GetProcessMemoryInfo(process_handle,
126+
(PROCESS_MEMORY_COUNTERS*)&process_memory_counters,
83127
sizeof(process_memory_counters))) {
84128
result.mem_footprint_kb = process_memory_counters.WorkingSetSize / 1024;
129+
result.private_footprint_bytes = process_memory_counters.PrivateUsage;
85130
} else {
86131
result.mem_footprint_kb = -1;
132+
result.private_footprint_bytes = -1;
87133
}
88134
CloseHandle(process_handle);
89135
HANDLE process_heap = GetProcessHeap();
@@ -108,7 +154,9 @@ void MemoryUsage::AllStatsToStream(std::ostream* stream) const {
108154
<< mem_footprint_kb / 1000.0 << " MB, total non-mmapped heap size = "
109155
<< total_allocated_bytes / 1000.0 / 1000.0
110156
<< " MB, in-use heap size = "
111-
<< in_use_allocated_bytes / 1000.0 / 1000.0 << " MB";
157+
<< in_use_allocated_bytes / 1000.0 / 1000.0
158+
<< " MB, private footprint = "
159+
<< private_footprint_bytes / 1000.0 / 1000.0 << " MB";
112160
}
113161

114162
} // namespace memory

tflite/profiling/memory_info.h

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,8 @@ struct MemoryUsage {
3535
MemoryUsage()
3636
: mem_footprint_kb(kValueNotSet),
3737
total_allocated_bytes(kValueNotSet),
38-
in_use_allocated_bytes(kValueNotSet) {}
38+
in_use_allocated_bytes(kValueNotSet),
39+
private_footprint_bytes(kValueNotSet) {}
3940

4041
// The memory footprint (in kilobytes).
4142
//
@@ -85,13 +86,22 @@ struct MemoryUsage {
8586
// non-heap uses of memory such as thread stacks, globals, code, etc.
8687
size_t in_use_allocated_bytes;
8788

89+
// Private footprint (in kilobytes).
90+
//
91+
// For Linux this is the rusage::ru_maxrss + VmSwap.
92+
// For Mac this is the task_vm_info::phys_footprint.
93+
// For Windows this is the PrivateUsage.
94+
size_t private_footprint_bytes;
95+
8896
MemoryUsage operator+(MemoryUsage const& obj) const {
8997
MemoryUsage res;
9098
res.mem_footprint_kb = mem_footprint_kb + obj.mem_footprint_kb;
9199
res.total_allocated_bytes =
92100
total_allocated_bytes + obj.total_allocated_bytes;
93101
res.in_use_allocated_bytes =
94102
in_use_allocated_bytes + obj.in_use_allocated_bytes;
103+
res.private_footprint_bytes =
104+
private_footprint_bytes + obj.private_footprint_bytes;
95105
return res;
96106
}
97107

@@ -102,6 +112,8 @@ struct MemoryUsage {
102112
total_allocated_bytes - obj.total_allocated_bytes;
103113
res.in_use_allocated_bytes =
104114
in_use_allocated_bytes - obj.in_use_allocated_bytes;
115+
res.private_footprint_bytes =
116+
private_footprint_bytes - obj.private_footprint_bytes;
105117
return res;
106118
}
107119

tflite/profiling/memory_info_test.cc

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,27 +30,32 @@ TEST(MemoryUsage, AddAndSub) {
3030
mem1.mem_footprint_kb = 5;
3131
mem1.total_allocated_bytes = 7000;
3232
mem1.in_use_allocated_bytes = 2000;
33+
mem1.private_footprint_bytes = 1000;
3334

3435
mem2.mem_footprint_kb = 3;
3536
mem2.total_allocated_bytes = 7000;
3637
mem2.in_use_allocated_bytes = 4000;
38+
mem2.private_footprint_bytes = 500;
3739

3840
const auto add_mem = mem1 + mem2;
3941
EXPECT_EQ(8, add_mem.mem_footprint_kb);
4042
EXPECT_EQ(14000, add_mem.total_allocated_bytes);
4143
EXPECT_EQ(6000, add_mem.in_use_allocated_bytes);
44+
EXPECT_EQ(1500, add_mem.private_footprint_bytes);
4245

4346
const auto sub_mem = mem1 - mem2;
4447
EXPECT_EQ(2, sub_mem.mem_footprint_kb);
4548
EXPECT_EQ(0, sub_mem.total_allocated_bytes);
4649
EXPECT_EQ(-2000, sub_mem.in_use_allocated_bytes);
50+
EXPECT_EQ(500, sub_mem.private_footprint_bytes);
4751
}
4852

4953
TEST(MemoryUsage, GetMemoryUsage) {
5054
MemoryUsage result;
5155
EXPECT_EQ(MemoryUsage::kValueNotSet, result.mem_footprint_kb);
5256
EXPECT_EQ(MemoryUsage::kValueNotSet, result.total_allocated_bytes);
5357
EXPECT_EQ(MemoryUsage::kValueNotSet, result.in_use_allocated_bytes);
58+
EXPECT_EQ(MemoryUsage::kValueNotSet, result.private_footprint_bytes);
5459

5560
#if defined(__linux__) || defined(__APPLE__) || defined(_WIN32)
5661
// Just allocate some space in heap so that we have some meaningful
@@ -72,6 +77,7 @@ TEST(MemoryUsage, GetMemoryUsage) {
7277
EXPECT_GE(result.mem_footprint_kb, size / 1024);
7378
EXPECT_GE(result.total_allocated_bytes, size);
7479
EXPECT_GE(result.in_use_allocated_bytes, size);
80+
EXPECT_GE(result.private_footprint_bytes, size);
7581
#endif
7682
}
7783

tflite/profiling/memory_latency_logger.cc

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -80,10 +80,16 @@ void MemoryLatencyLogger::Stop(absl::string_view log_message) {
8080
<< " MB,";
8181
}
8282
if (mem_monitor_->GetCurrentInUseMemoryInMB() < 0) {
83-
message_stream << " current in-use: unknown";
83+
message_stream << " current in-use: unknown,";
8484
} else {
8585
message_stream << " current in-use: "
86-
<< mem_monitor_->GetCurrentInUseMemoryInMB() << " MB";
86+
<< mem_monitor_->GetCurrentInUseMemoryInMB() << " MB,";
87+
}
88+
if (mem_monitor_->GetPeakPrivateFootprintInMB() < 0) {
89+
message_stream << " peak private: unknown";
90+
} else {
91+
message_stream << " peak private: "
92+
<< mem_monitor_->GetPeakPrivateFootprintInMB() << " MB";
8793
}
8894
TFLITE_LOG(INFO) << message_stream.str();
8995
}

tflite/profiling/memory_usage_monitor.cc

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,11 @@ void MemoryUsageMonitor::Start() {
6262
if (current_in_use_bytes > peak_in_use_mem_bytes_) {
6363
peak_in_use_mem_bytes_ = current_in_use_bytes;
6464
}
65+
int64_t current_private_footprint_bytes =
66+
mem_info.private_footprint_bytes;
67+
if (current_private_footprint_bytes > peak_private_footprint_bytes_) {
68+
peak_private_footprint_bytes_ = current_private_footprint_bytes;
69+
}
6570
if (stop_signal_->HasBeenNotified()) break;
6671
sampler_->SleepFor(sampling_interval_);
6772
}

tflite/profiling/memory_usage_monitor.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,13 @@ class MemoryUsageMonitor {
8787
return BytesToMegabytes(peak_in_use_mem_bytes_);
8888
}
8989

90+
float GetPeakPrivateFootprintInMB() const {
91+
if (!is_supported_ || check_memory_thd_ != nullptr) {
92+
return kInvalidMemUsageMB;
93+
}
94+
return BytesToMegabytes(peak_private_footprint_bytes_);
95+
}
96+
9097
MemoryUsageMonitor(MemoryUsageMonitor&) = delete;
9198
MemoryUsageMonitor& operator=(const MemoryUsageMonitor&) = delete;
9299
MemoryUsageMonitor(MemoryUsageMonitor&&) = delete;
@@ -105,6 +112,7 @@ class MemoryUsageMonitor {
105112
std::unique_ptr<std::thread> check_memory_thd_ = nullptr;
106113
int64_t peak_mem_footprint_bytes_ = kInvalidMemUsageBytes;
107114
int64_t peak_in_use_mem_bytes_ = kInvalidMemUsageBytes;
115+
int64_t peak_private_footprint_bytes_ = kInvalidMemUsageBytes;
108116
};
109117

110118
} // namespace memory

0 commit comments

Comments
 (0)