the hardware. The use of virtual memory also repre-
sents the negative aspect of this approach, that is, it
depends on virtual memory support being present in
the operating system.
The rest of the paper is organised as follows. In
section 2 we describe the standard approaches and vir-
tual memory in more detail, together with the MMU.
In section 3 we describe the implementation of the
virtual memory container in C++. In section 4 we
compare the performance of virtual memory con-
tainer to some other possible approaches to the prob-
lem. In section 5 we discuss the portability of our
implementation to other architectures and operating
systems.
2 BACKGROUND
2.1 Standard Approach to Resizable
Containers
Probably the most widely used container in standard
C++ is the std::vector. It is an example of a dy-
namic array with the ability to resize itself automat-
ically when one or more elements are inserted or
deleted. Storage for the elements is being handled
transparently by the container.
C++ standard (ISO, 2017) for example describes:
A vector is a sequence container that sup-
ports random access iterators. In addition,
it supports (amortized) constant time insert
and erase operations at the end; insert and
erase in the middle take linear time. Storage
management is handled automatically, though
hints can be given to improve efficiency. A
vector satisfies all of the requirements of a
container and of a reversible container, of a
sequence container, including most of the op-
tional sequence container requirements, of an
allocator-aware container, and, for an element
type other than bool, of a contiguous con-
tainer.
Since vector must be continuous, it can be used
to interact with C code which expects continuous ar-
rays of elements, it has minimal memory overhead,
and the speed of its random access iterators is unpar-
alleled among containers. This, along with its abil-
ity to grow automatically or per request, makes it a
workhorse for the data structures in general.
It is, however, limited by its implementation of dy-
namic resizing, which may catch an unprepared pro-
grammer off guard with its linear time performance,
invalidation of iterators, pointers and references to el-
ements, and the resulting lack of thread-safety even
in cases when elements are only added to the vector
and never modified. The usual implementation of the
algorithm to grow vector in size is to allocate a larger
block of memory, copy or move the contents from the
old to the new block, and deallocate the old block of
memory. When done automatically, the growth ra-
tio is usually exponential, e.g. doubling the memory
block size on each growth. Only when done manu-
ally, memory block of an exact required size may be
obtained for holding the vector’s elements.
When vector is grown, all the newly available
elements, that is, the elements with indices i, where
old_size ≤ i <new_size are also default initialized.
The creation of vector of fixed size itself includes the
growth operation and the overhead associated with it.
For parallel programming, vector in raw form
is ill suited, since it is not thread-safe in the slight-
est. Full read-only concurrent access is allowed, but
read-only access to one element while another ele-
ment is being modified is only allowed if vector
does not get resized by he modification, i.e. func-
tion push_back should not be used on a vetor when
it is being concurrently accessed by other threads.
When dynamic resizing is required, all element ac-
cess has to be protected, at least with a readers-writer
lock(Raynal, 2012).
2.2 Virtual Memory and MMU
Virtual memory is an abstraction of the physical mem-
ory that hides the arrangement of physical memory
and other resources from the programmer and sepa-
rates different processes’ address spaces (Bhattachar-
jee et al., 2017). The mapping between virtual and
physical memory addresses is carried out by the op-
erating system kernel and the memory management
unit (MMU), a dedicated address translation hard-
ware, built into the CPU (Gorman, 2004).
On 64-bit version of Linux each process can ac-
cess a total of 256 TiB of virtual memory. The upper
128 TiB are reserved for the kernel while the lower
128 TiB can be used by the program, as shown in Fig-
ure 1. A part of this user memory region is reserved
by the stack and executable code, but the vast majority
is freely allocatable. Not only physical memory can
be mapped into virtual memory, but also swap parti-
tion, normal files on disk, I/O devices, etc.
While up to 128 TiB of virtual memory can be al-
located, in practice only as much of it can be used as
there is physical memory in the system. For a fixed
size, densely populated data structures virtual mem-
ory presents no real improvement over direct access
ICSOFT 2021 - 16th International Conference on Software Technologies
482