timemory 3.3.0
Modular C++ Toolkit for Performance Analysis and Logging. Profiling API and Tools for C, C++, CUDA, Fortran, and Python. The C++ template API is essentially a framework to creating tools: it is designed to provide a unifying interface for recording various performance measurements alongside data logging and interfaces to other tools.
tim::base::ring_buffer Struct Reference

Ring buffer implementation, with support for mmap as backend (Linux only). More...

#include "timemory/storage/ring_buffer.hpp"

+ Collaboration diagram for tim::base::ring_buffer:

Public Member Functions

 ring_buffer ()=default
 
 ring_buffer (bool _use_mmap)
 
 ring_buffer (size_t _size)
 
 ring_buffer (size_t _size, bool _use_mmap)
 
 ~ring_buffer ()
 
 ring_buffer (const ring_buffer &)
 
 ring_buffer (ring_buffer &&) noexcept=delete
 
ring_bufferoperator= (const ring_buffer &)
 
ring_bufferoperator= (ring_buffer &&) noexcept=delete
 
bool is_initialized () const
 Returns whether the buffer has been allocated. More...
 
size_t capacity () const
 Get the total number of bytes supported. More...
 
void init (size_t size)
 Creates new ring buffer. More...
 
void destroy ()
 Destroy ring buffer. More...
 
template<typename Tp >
std::pair< size_t, Tp * > write (Tp *in, std::enable_if_t< std::is_class< Tp >::value, int >=0)
 Write class-type data to buffer (uses placement new). More...
 
template<typename Tp >
std::pair< size_t, Tp * > write (Tp *in, std::enable_if_t<!std::is_class< Tp >::value, int >=0)
 Write non-class-type data to buffer (uses memcpy). More...
 
template<typename Tp >
Tp * request ()
 Request a pointer to an allocation. This is similar to a "write" except the memory is uninitialized. Typically used by allocators. If Tp is a class type, be sure to use a placement new instead of a memcpy. More...
 
void * request (size_t n)
 Request a pointer to an allocation for at least. More...
 
template<typename Tp >
std::pair< size_t, Tp * > read (Tp *out, std::enable_if_t< std::is_class< Tp >::value, int >=0) const
 Read class-type data from buffer (uses placement new). More...
 
template<typename Tp >
std::pair< size_t, Tp * > read (Tp *out, std::enable_if_t<!std::is_class< Tp >::value, int >=0) const
 Read non-class-type data from buffer (uses memcpy). More...
 
template<typename Tp >
Tp * retrieve ()
 Retrieve a pointer to the head allocation (read). More...
 
void * retrieve (size_t n)
 Retrieve a pointer to the head allocation of at least. More...
 
size_t count () const
 Returns number of bytes currently held by the buffer. More...
 
size_t free () const
 Returns how many bytes are availiable in the buffer. More...
 
bool is_empty () const
 Returns if the buffer is empty. More...
 
bool is_full () const
 Returns if the buffer is full. More...
 
size_t rewind (size_t n) const
 Rewind the read position n bytes. More...
 
void set_use_mmap (bool)
 explicitly configure to use mmap if avail More...
 
bool get_use_mmap () const
 query whether using mmap More...
 
std::string as_string () const
 

Friends

template<typename Tp >
struct data_storage::ring_buffer
 
std::ostream & operator<< (std::ostream &os, const ring_buffer &obj)
 

Detailed Description

Ring buffer implementation, with support for mmap as backend (Linux only).

Definition at line 49 of file ring_buffer.hpp.

Constructor & Destructor Documentation

◆ ring_buffer() [1/6]

tim::base::ring_buffer::ring_buffer ( )
default

◆ ring_buffer() [2/6]

tim::base::ring_buffer::ring_buffer ( bool  _use_mmap)
inlineexplicit

Definition at line 55 of file ring_buffer.hpp.

55{ set_use_mmap(_use_mmap); }
void set_use_mmap(bool)
explicitly configure to use mmap if avail

References set_use_mmap().

◆ ring_buffer() [3/6]

tim::base::ring_buffer::ring_buffer ( size_t  _size)
inlineexplicit

Definition at line 56 of file ring_buffer.hpp.

56{ init(_size); }
void init(size_t size)
Creates new ring buffer.
Definition: ring_buffer.cpp:84

References init().

◆ ring_buffer() [4/6]

tim::base::ring_buffer::ring_buffer ( size_t  _size,
bool  _use_mmap 
)

Definition at line 52 of file ring_buffer.cpp.

53{
54 set_use_mmap(_use_mmap);
55 init(_size);
56}

References init(), and set_use_mmap().

◆ ~ring_buffer()

tim::base::ring_buffer::~ring_buffer ( )

Definition at line 59 of file ring_buffer.cpp.

59{ destroy(); }
void destroy()
Destroy ring buffer.

References destroy().

◆ ring_buffer() [5/6]

tim::base::ring_buffer::ring_buffer ( const ring_buffer rhs)

Definition at line 62 of file ring_buffer.cpp.

63: m_use_mmap{ rhs.m_use_mmap }
64, m_use_mmap_explicit{ rhs.m_use_mmap_explicit }
65{
66 init(rhs.m_size);
67}

References init().

◆ ring_buffer() [6/6]

tim::base::ring_buffer::ring_buffer ( ring_buffer &&  )
deletenoexcept

Member Function Documentation

◆ as_string()

std::string tim::base::ring_buffer::as_string ( ) const

Definition at line 216 of file ring_buffer.cpp.

217{
218 std::ostringstream ss{};
219 ss << std::boolalpha << "is_initialized: " << is_initialized()
220 << ", capacity: " << capacity() << ", count: " << count() << ", free: " << free()
221 << ", is_empty: " << is_empty() << ", is_full: " << is_full()
222 << ", pointer: " << m_ptr << ", read count: " << m_read_count
223 << ", write count: " << m_write_count;
224 return ss.str();
225}
bool is_full() const
Returns if the buffer is full.
bool is_empty() const
Returns if the buffer is empty.
size_t free() const
Returns how many bytes are availiable in the buffer.
bool is_initialized() const
Returns whether the buffer has been allocated.
Definition: ring_buffer.hpp:68
size_t capacity() const
Get the total number of bytes supported.
Definition: ring_buffer.hpp:71
size_t count() const
Returns number of bytes currently held by the buffer.

References capacity(), count(), free(), is_empty(), is_full(), and is_initialized().

◆ capacity()

size_t tim::base::ring_buffer::capacity ( ) const
inline

Get the total number of bytes supported.

Definition at line 71 of file ring_buffer.hpp.

71{ return m_size; }

Referenced by as_string(), tim::data_storage::ring_buffer< Tp >::as_string(), and tim::data_storage::ring_buffer< Tp >::capacity().

◆ count()

size_t tim::base::ring_buffer::count ( ) const
inline

Returns number of bytes currently held by the buffer.

Definition at line 116 of file ring_buffer.hpp.

116{ return (m_write_count - m_read_count); }

Referenced by as_string(), tim::data_storage::ring_buffer< Tp >::as_string(), tim::data_storage::ring_buffer< Tp >::count(), free(), is_empty(), is_full(), read(), and retrieve().

◆ destroy()

void tim::base::ring_buffer::destroy ( )

Destroy ring buffer.

Definition at line 164 of file ring_buffer.cpp.

165{
166 if(m_ptr && m_init)
167 {
168 m_init = false;
169#if defined(TIMEMORY_LINUX)
170 if(!m_use_mmap)
171 {
172 ::free(m_ptr);
173 }
174 else
175 {
176 // Truncate file to zero, to avoid writing back memory to file, on munmap.
177 if(ftruncate(m_fd, 0) < 0)
178 {
179 bool _cond = settings::verbose() > 0 || settings::debug();
181 "Ring buffer failed to truncate the file "
182 "descriptor %i\n",
183 m_fd);
184 }
185 // Unmap the mapped virtual memmory.
186 auto ret = munmap(m_ptr, m_size * 2);
187 // Close the backing file.
188 close(m_fd);
189 if(ret)
190 perror("munmap");
191 }
192#else
193 ::free(m_ptr);
194#endif
195 }
196 m_init = false;
197 m_size = 0;
198 m_read_count = 0;
199 m_write_count = 0;
200 m_ptr = nullptr;
201}
#define CONDITIONAL_PRINT_HERE(CONDITION,...)
Definition: macros.hpp:183

References tim::plotting::close(), CONDITIONAL_PRINT_HERE, tim::debug, free(), and tim::verbose.

Referenced by ~ring_buffer(), tim::data_storage::ring_buffer< Tp >::destroy(), init(), and operator=().

◆ free()

size_t tim::base::ring_buffer::free ( ) const
inline

Returns how many bytes are availiable in the buffer.

Definition at line 119 of file ring_buffer.hpp.

119{ return (m_size - count()); }

References count().

Referenced by as_string(), tim::data_storage::ring_buffer< Tp >::as_string(), destroy(), tim::data_storage::ring_buffer< Tp >::free(), tim::data_storage::ring_buffer< Tp >::is_full(), request(), and write().

◆ get_use_mmap()

bool tim::base::ring_buffer::get_use_mmap ( ) const
inline

query whether using mmap

Definition at line 134 of file ring_buffer.hpp.

134{ return m_use_mmap; }

◆ init()

void tim::base::ring_buffer::init ( size_t  size)

Creates new ring buffer.

Definition at line 84 of file ring_buffer.cpp.

85{
86 if(m_init)
87 throw std::runtime_error(
88 "tim::base::ring_buffer::init(size_t) :: already initialized");
89
90 m_init = true;
91
92 // Round up to multiple of page size.
93 _size += units::get_page_size() - ((_size % units::get_page_size() > 0)
94 ? (_size % units::get_page_size())
95 : units::get_page_size());
96
97 if((_size % units::get_page_size()) > 0)
98 {
99 std::ostringstream _oss{};
100 _oss << "Error! size is not a multiple of page size: " << _size << " % "
101 << units::get_page_size() << " = " << (_size % units::get_page_size());
102 throw std::runtime_error(_oss.str());
103 }
104
105 m_size = _size;
106 m_read_count = 0;
107 m_write_count = 0;
108
109 if(!m_use_mmap_explicit)
110 m_use_mmap = get_env("TIMEMORY_USE_MMAP", m_use_mmap);
111
112#if defined(TIMEMORY_LINUX)
113 if(!m_use_mmap)
114 {
115 m_ptr = malloc(m_size * sizeof(char));
116 return;
117 }
118 // Set file path depending on whether shared memory is compiled in or not.
119# ifdef SHM
120 char path[] = "/dev/shm/rb-XXXXXX";
121# else
122 char path[] = "/tmp/rb-XXXXXX";
123# endif /* SHM */
124
125 // Create a temporary file for mmap backing.
126 if((m_fd = mkstemp(path)) < 0)
127 destroy();
128
129 // Remove file from filesystem. Note the file is still open by the
130 // process.
131 // XXX there might be a security problem with this, if so, use umaks 0600.
132 if(unlink(path))
133 destroy();
134
135 // Resize file to buffer size.
136 if(ftruncate(m_fd, m_size) < 0)
137 destroy();
138
139 // Map twice the buffer size.
140 if((m_ptr = mmap(NULL, 2 * m_size, PROT_NONE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0)) ==
141 MAP_FAILED)
142 destroy();
143
144 // Map the temporary file into the first half of the above mapped space.
145 if(mmap(m_ptr, m_size, PROT_READ | PROT_WRITE, MAP_FIXED | MAP_SHARED, m_fd, 0) ==
146 MAP_FAILED)
147 destroy();
148
149 // Map the temporary file into the second half of the mapped space.
150 // This creates two consecutive copies of the same physical memory, thus
151 // allowing contiues reads and writes of the buffer.
152 if(mmap(static_cast<char*>(m_ptr) + m_size, m_size, PROT_READ | PROT_WRITE,
153 MAP_FIXED | MAP_SHARED, m_fd, 0) == MAP_FAILED)
154 destroy();
155#else
156 m_use_mmap = false;
157 m_ptr = malloc(m_size * sizeof(char));
158 (void) m_fd;
159#endif
160}
Tp get_env(const std::string &env_id, Tp _default, bool _store)

References destroy(), and tim::get_env().

Referenced by ring_buffer(), tim::data_storage::ring_buffer< Tp >::init(), and operator=().

◆ is_empty()

bool tim::base::ring_buffer::is_empty ( ) const
inline

Returns if the buffer is empty.

Definition at line 122 of file ring_buffer.hpp.

122{ return (count() == 0); }

References count().

Referenced by as_string(), tim::data_storage::ring_buffer< Tp >::is_empty(), and read().

◆ is_full()

bool tim::base::ring_buffer::is_full ( ) const
inline

Returns if the buffer is full.

Definition at line 125 of file ring_buffer.hpp.

125{ return (count() == m_size); }

References count().

Referenced by as_string().

◆ is_initialized()

bool tim::base::ring_buffer::is_initialized ( ) const
inline

Returns whether the buffer has been allocated.

Definition at line 68 of file ring_buffer.hpp.

68{ return m_init; }

Referenced by as_string(), and tim::data_storage::ring_buffer< Tp >::is_initialized().

◆ operator=() [1/2]

ring_buffer & tim::base::ring_buffer::operator= ( const ring_buffer rhs)

Definition at line 71 of file ring_buffer.cpp.

72{
73 if(this == &rhs)
74 return *this;
75 destroy();
76 m_use_mmap = rhs.m_use_mmap;
77 m_use_mmap_explicit = rhs.m_use_mmap_explicit;
78 init(rhs.m_size);
79 return *this;
80}

References destroy(), and init().

◆ operator=() [2/2]

ring_buffer & tim::base::ring_buffer::operator= ( ring_buffer &&  )
deletenoexcept

◆ read() [1/2]

template<typename Tp >
std::pair< size_t, Tp * > tim::base::ring_buffer::read ( Tp *  out,
std::enable_if_t< std::is_class< Tp >::value, int >  = 0 
) const

Read class-type data from buffer (uses placement new).

Definition at line 259 of file ring_buffer.hpp.

260{
261 if(is_empty() || out == nullptr)
262 return { 0, nullptr };
263
264 auto _length = sizeof(Tp);
265
266 // Make sure we do not read out more than there is actually in the buffer.
267 if(_length > count())
268 throw std::runtime_error("ring buffer is empty");
269
270 // if read count is at the tail of buffer, bump to the end of buffer
271 auto _modulo = m_size - (m_read_count % m_size);
272 if(_modulo < _length)
273 m_read_count += _modulo;
274
275 // pointer in buffer
276 Tp* in = reinterpret_cast<Tp*>(read_ptr());
277
278 // Copy out for BYTE, nothing magic here.
279 *out = *in;
280
281 // Update read count.
282 m_read_count += _length;
283
284 return { _length, in };
285}

References count(), and is_empty().

◆ read() [2/2]

template<typename Tp >
std::pair< size_t, Tp * > tim::base::ring_buffer::read ( Tp *  out,
std::enable_if_t<!std::is_class< Tp >::value, int >  = 0 
) const

Read non-class-type data from buffer (uses memcpy).

Definition at line 289 of file ring_buffer.hpp.

290{
291 if(is_empty() || out == nullptr)
292 return { 0, nullptr };
293
294 auto _length = sizeof(Tp);
295
296 using Up = typename std::remove_const<Tp>::type;
297
298 // Make sure we do not read out more than there is actually in the buffer.
299 if(_length > count())
300 throw std::runtime_error("ring buffer is empty");
301
302 // if read count is at the tail of buffer, bump to the end of buffer
303 auto _modulo = m_size - (m_read_count % m_size);
304 if(_modulo < _length)
305 m_read_count += _modulo;
306
307 // pointer in buffer
308 Tp* in = reinterpret_cast<Tp*>(read_ptr());
309
310 // Copy out for BYTE, nothing magic here.
311 Up* _out = const_cast<Up*>(out);
312 memcpy(_out, in, _length);
313
314 // Update read count.
315 m_read_count += _length;
316
317 return { _length, in };
318}

References count(), and is_empty().

◆ request() [1/2]

template<typename Tp >
Tp * tim::base::ring_buffer::request

Request a pointer to an allocation. This is similar to a "write" except the memory is uninitialized. Typically used by allocators. If Tp is a class type, be sure to use a placement new instead of a memcpy.

Definition at line 230 of file ring_buffer.hpp.

231{
232 if(m_ptr == nullptr)
233 return nullptr;
234
235 auto _length = sizeof(Tp);
236
237 // Make sure we don't put in more than there's room for, by writing no
238 // more than there is free.
239 if(_length > free())
240 throw std::runtime_error("heap-buffer-overflow :: ring buffer is full. read data "
241 "to avoid data corruption");
242
243 // if write count is at the tail of buffer, bump to the end of buffer
244 auto _modulo = m_size - (m_write_count % m_size);
245 if(_modulo < _length)
246 m_write_count += _modulo;
247
248 // pointer in buffer
249 Tp* _out = reinterpret_cast<Tp*>(write_ptr());
250
251 // Update write count
252 m_write_count += _length;
253
254 return _out;
255}

References free().

◆ request() [2/2]

void * tim::base::ring_buffer::request ( size_t  n)

Request a pointer to an allocation for at least.

Parameters
nbytes.

Definition at line 229 of file ring_buffer.cpp.

230{
231 if(m_ptr == nullptr)
232 return nullptr;
233
234 // Make sure we don't put in more than there's room for, by writing no
235 // more than there is free.
236 if(_length > free())
237 throw std::runtime_error("heap-buffer-overflow :: ring buffer is full. read data "
238 "to avoid data corruption");
239
240 // if write count is at the tail of buffer, bump to the end of buffer
241 auto _modulo = m_size - (m_write_count % m_size);
242 if(_modulo < _length)
243 m_write_count += _modulo;
244
245 // pointer in buffer
246 void* _out = write_ptr();
247
248 // Update write count
249 m_write_count += _length;
250
251 return _out;
252}

References free().

◆ retrieve() [1/2]

template<typename Tp >
Tp * tim::base::ring_buffer::retrieve

Retrieve a pointer to the head allocation (read).

Definition at line 322 of file ring_buffer.hpp.

323{
324 if(m_ptr == nullptr)
325 return nullptr;
326
327 auto _length = sizeof(Tp);
328
329 // Make sure we don't put in more than there's room for, by writing no
330 // more than there is free.
331 if(_length > count())
332 throw std::runtime_error("ring buffer is empty");
333
334 // if read count is at the tail of buffer, bump to the end of buffer
335 auto _modulo = m_size - (m_read_count % m_size);
336 if(_modulo < _length)
337 m_read_count += _modulo;
338
339 // pointer in buffer
340 Tp* _out = reinterpret_cast<Tp*>(read_ptr());
341
342 // Update write count
343 m_read_count += _length;
344
345 return _out;
346}

References count().

◆ retrieve() [2/2]

void * tim::base::ring_buffer::retrieve ( size_t  n)

Retrieve a pointer to the head allocation of at least.

Parameters
nbytes (read).

Definition at line 256 of file ring_buffer.cpp.

257{
258 if(m_ptr == nullptr)
259 return nullptr;
260
261 // Make sure we don't put in more than there's room for, by writing no
262 // more than there is free.
263 if(_length > count())
264 throw std::runtime_error("ring buffer is empty");
265
266 // if read count is at the tail of buffer, bump to the end of buffer
267 auto _modulo = m_size - (m_read_count % m_size);
268 if(_modulo < _length)
269 m_read_count += _modulo;
270
271 // pointer in buffer
272 void* _out = read_ptr();
273
274 // Update write count
275 m_read_count += _length;
276
277 return _out;
278}

References count().

◆ rewind()

size_t tim::base::ring_buffer::rewind ( size_t  n) const

Rewind the read position n bytes.

Definition at line 282 of file ring_buffer.cpp.

283{
284 if(n > m_read_count)
285 n = m_read_count;
286 m_read_count -= n;
287 return n;
288}

Referenced by tim::data_storage::ring_buffer< Tp >::rewind().

◆ set_use_mmap()

void tim::base::ring_buffer::set_use_mmap ( bool  _v)

explicitly configure to use mmap if avail

Definition at line 205 of file ring_buffer.cpp.

206{
207 if(m_init)
208 throw std::runtime_error("tim::base::ring_buffer::set_use_mmap(bool) cannot be "
209 "called after initialization");
210 m_use_mmap = _v;
211 m_use_mmap_explicit = true;
212}

Referenced by ring_buffer().

◆ write() [1/2]

template<typename Tp >
std::pair< size_t, Tp * > tim::base::ring_buffer::write ( Tp *  in,
std::enable_if_t< std::is_class< Tp >::value, int >  = 0 
)

Write class-type data to buffer (uses placement new).

Definition at line 166 of file ring_buffer.hpp.

167{
168 if(in == nullptr || m_ptr == nullptr)
169 return { 0, nullptr };
170
171 auto _length = sizeof(Tp);
172
173 // Make sure we don't put in more than there's room for, by writing no
174 // more than there is free.
175 if(_length > free())
176 throw std::runtime_error("heap-buffer-overflow :: ring buffer is full. read data "
177 "to avoid data corruption");
178
179 // if write count is at the tail of buffer, bump to the end of buffer
180 auto _modulo = m_size - (m_write_count % m_size);
181 if(_modulo < _length)
182 m_write_count += _modulo;
183
184 // pointer in buffer
185 Tp* out = reinterpret_cast<Tp*>(write_ptr());
186
187 // Copy in.
188 new((void*) out) Tp{ std::move(*in) };
189
190 // Update write count
191 m_write_count += _length;
192
193 return { _length, out };
194}

References free().

◆ write() [2/2]

template<typename Tp >
std::pair< size_t, Tp * > tim::base::ring_buffer::write ( Tp *  in,
std::enable_if_t<!std::is_class< Tp >::value, int >  = 0 
)

Write non-class-type data to buffer (uses memcpy).

Definition at line 198 of file ring_buffer.hpp.

199{
200 if(in == nullptr || m_ptr == nullptr)
201 return { 0, nullptr };
202
203 auto _length = sizeof(Tp);
204
205 // Make sure we don't put in more than there's room for, by writing no
206 // more than there is free.
207 if(_length > free())
208 throw std::runtime_error("heap-buffer-overflow :: ring buffer is full. read data "
209 "to avoid data corruption");
210
211 // if write count is at the tail of buffer, bump to the end of buffer
212 auto _modulo = m_size - (m_write_count % m_size);
213 if(_modulo < _length)
214 m_write_count += _modulo;
215
216 // pointer in buffer
217 Tp* out = reinterpret_cast<Tp*>(write_ptr());
218
219 // Copy in.
220 memcpy((void*) out, in, _length);
221
222 // Update write count
223 m_write_count += _length;
224
225 return { _length, out };
226}

References free().

Friends And Related Function Documentation

◆ data_storage::ring_buffer

template<typename Tp >
friend struct data_storage::ring_buffer
friend

Definition at line 52 of file ring_buffer.hpp.

◆ operator<<

std::ostream & operator<< ( std::ostream &  os,
const ring_buffer obj 
)
friend

Definition at line 138 of file ring_buffer.hpp.

139 {
140 return os << obj.as_string();
141 }
const std::string std::ostream * os

The documentation for this struct was generated from the following files: