Barretenberg
The ZK-SNARK library at the core of Aztec
Loading...
Searching...
No Matches
backing_memory.hpp
Go to the documentation of this file.
1// === AUDIT STATUS ===
2// internal: { status: Complete, auditors: [Nishat], commit: 94f596f8b3bbbc216f9ad7dc33253256141156b2 }
3// external_1: { status: not started, auditors: [], commit: }
4// external_2: { status: not started, auditors: [], commit: }
5// =====================
6
7#pragma once
8
10#include "unistd.h"
11#include <atomic>
12#include <cstring>
13#include <fcntl.h>
14#include <filesystem>
15#include <memory>
16#ifndef __wasm__
17#include <sys/mman.h>
18#endif
19
20// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
21extern bool slow_low_memory;
22
23// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
24extern size_t storage_budget;
25
26// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
27extern std::atomic<size_t> current_storage_usage;
28
29// Parse storage size string (e.g., "500m", "2g", "1024k")
30size_t parse_size_string(const std::string& size_str);
31
32template <typename Fr> struct BackingMemory {
33 // Common raw data pointer used by all storage types
34 Fr* raw_data = nullptr;
35
36#ifndef __wasm__
37 // File-backed data substruct with cleanup metadata
39 size_t file_size;
40 std::string filename;
41 int fd;
43
45 {
46 if (raw_data_ptr != nullptr && file_size > 0) {
47 munmap(raw_data_ptr, file_size);
49 }
50 if (fd >= 0) {
51 close(fd);
52 }
53 if (!filename.empty()) {
54 std::filesystem::remove(filename);
55 }
56 }
57 };
59#endif
60 // Aligned memory data substruct
62
63 BackingMemory() = default;
64
65 BackingMemory(const BackingMemory&) = default;
67
68 BackingMemory(BackingMemory&& other) noexcept
69 : raw_data(other.raw_data)
70#ifndef __wasm__
71 , file_backed(std::move(other.file_backed))
72#endif
73 , aligned_memory(std::move(other.aligned_memory))
74 {
75 other.raw_data = nullptr;
76 }
77
79 {
80 if (this != &other) {
81 raw_data = other.raw_data;
82#ifndef __wasm__
83 file_backed = std::move(other.file_backed);
84#endif
85 aligned_memory = std::move(other.aligned_memory);
86 other.raw_data = nullptr;
87 }
88 return *this;
89 }
90
91 // Allocate memory, preferring file-backed if in low memory mode.
92 // Memory is NOT zeroed — callers that need zeroed memory must do so themselves.
93 static BackingMemory allocate(size_t size)
94 {
96#ifndef __wasm__
97 if (slow_low_memory) {
99 return memory;
100 }
101 }
102#endif
104 return memory;
105 }
106
107 ~BackingMemory() = default;
108
109 private:
110 // Use new Fr[] instead of std::make_shared<Fr[]>(n) to avoid serial
111 // value-initialization (zeroing). Polynomial's constructor handles
112 // zeroing in parallel where needed.
113 static void allocate_aligned(BackingMemory& memory, size_t size)
114 {
115 if (size == 0) {
116 memory.aligned_memory = nullptr;
117 memory.raw_data = nullptr;
118 return;
119 }
120 Fr* ptr = new Fr[size];
121 memory.aligned_memory = std::shared_ptr<Fr[]>(ptr, [](Fr* p) { delete[] p; });
122 memory.raw_data = ptr;
123 }
124
125#ifndef __wasm__
127 {
128 if (size == 0) {
129 return false;
130 }
131
132 if (size > std::numeric_limits<size_t>::max() / sizeof(Fr)) {
133 return false;
134 }
135
136 size_t required_bytes = size * sizeof(Fr);
137
138 // Check and update storage usage to enforce budget
139 size_t current_usage = current_storage_usage.load();
140 while (true) {
141 if (current_usage + required_bytes > storage_budget) {
142 return false;
143 }
144 if (current_storage_usage.compare_exchange_weak(current_usage, current_usage + required_bytes)) {
145 break;
146 }
147 }
148
149 size_t file_size = required_bytes;
150 static std::atomic<size_t> file_counter{ 0 };
151 size_t id = file_counter.fetch_add(1);
152
153 std::filesystem::path temp_dir;
154 try {
155 temp_dir = std::filesystem::temp_directory_path();
156 } catch (const std::exception&) {
157 temp_dir = std::filesystem::current_path();
158 }
159
160 std::string filename = temp_dir / ("poly-mmap-" + std::to_string(getpid()) + "-" + std::to_string(id));
161
162 int fd = open(filename.c_str(), O_CREAT | O_RDWR | O_TRUNC, 0644);
163 if (fd < 0) {
164 current_storage_usage.fetch_sub(required_bytes);
165 return false;
166 }
167
168 if (ftruncate(fd, static_cast<off_t>(file_size)) != 0) {
169 close(fd);
170 std::filesystem::remove(filename);
171 current_storage_usage.fetch_sub(required_bytes);
172 return false;
173 }
174
175 void* addr = mmap(nullptr, file_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
176 if (addr == MAP_FAILED) {
177 close(fd);
178 std::filesystem::remove(filename);
179 current_storage_usage.fetch_sub(required_bytes);
180 return false;
181 }
182
183 auto file_backed_data = std::make_shared<FileBackedData>();
184 file_backed_data->file_size = file_size;
185 file_backed_data->filename = filename;
186 file_backed_data->fd = fd;
187 file_backed_data->raw_data_ptr = static_cast<Fr*>(addr);
188
189 memory.raw_data = static_cast<Fr*>(addr);
190 memory.file_backed = std::move(file_backed_data);
191
192 return true;
193 }
194#endif
195};
size_t parse_size_string(const std::string &size_str)
std::atomic< size_t > current_storage_usage
bool slow_low_memory
size_t storage_budget
constexpr decltype(auto) get(::tuplet::tuple< T... > &&t) noexcept
Definition tuple.hpp:13
std::string to_string(bb::avm2::ValueTag tag)
Curve::ScalarField Fr
MemoryStore memory
~BackingMemory()=default
BackingMemory()=default
static bool try_allocate_file_backed(BackingMemory &memory, size_t size)
static void allocate_aligned(BackingMemory &memory, size_t size)
std::shared_ptr< Fr[]> aligned_memory
BackingMemory & operator=(const BackingMemory &)=default
static BackingMemory allocate(size_t size)
BackingMemory(BackingMemory &&other) noexcept
BackingMemory(const BackingMemory &)=default
BackingMemory & operator=(BackingMemory &&other) noexcept
std::shared_ptr< FileBackedData > file_backed