Aleph-w 3.0
A C++ Library for Data Structures and Algorithms
Loading...
Searching...
No Matches
rope_benchmark.cc
Go to the documentation of this file.
1/*
2 Aleph_w
3
4 Data structures & Algorithms
5 https://github.com/lrleon/Aleph-w
6
7 This file is part of Aleph-w library
8
9 Copyright (c) 2002-2026 Leandro Rabindranath Leon
10
11 Permission is hereby granted, free of charge, to any person obtaining a copy
12 of this software and associated documentation files (the "Software"), to deal
13 in the Software without restriction, including without limitation the rights
14 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
15 copies of the Software, and to permit persons to whom the Software is
16 furnished to do so, subject to the following conditions:
17
18 The above copyright notice and this permission notice shall be included in all
19 copies or substantial portions of the Software.
20
21 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
22 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
24 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
26 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
27 SOFTWARE.
28*/
29
63#include <tpl_rope.H>
64
65#include <chrono>
66#include <cstdlib>
67#include <iomanip>
68#include <iostream>
69#include <random>
70#include <string>
71#include <string_view>
72
73using namespace Aleph;
74
75namespace
76{
77using Clock = std::chrono::steady_clock;
78
79double elapsed_ms(const Clock::time_point start)
80{
81 return std::chrono::duration<double, std::milli>(Clock::now() - start).count();
82}
83
84void print_row(const std::string & label, const double ms, const size_t final_size)
85{
86 std::cout << " " << std::left << std::setw(28) << label << std::right
87 << std::setw(10) << std::fixed << std::setprecision(2) << ms << " ms"
88 << " (final size " << final_size << ")\n";
89}
90
93void bench_string_append(const int chunk_count)
94{
95 const auto start = Clock::now();
96 std::string s;
97 for (int i = 0; i < chunk_count; ++i)
98 {
99 const char c = static_cast<char>('a' + (i % 26));
100 s += std::string_view(&c, 1);
101 }
102 print_row("std::string::operator+=", elapsed_ms(start), s.size());
103}
104
106void bench_rope_concat(const int chunk_count)
107{
108 const auto start = Clock::now();
110 for (int i = 0; i < chunk_count; ++i)
111 {
112 const char c = static_cast<char>('a' + (i % 26));
113 r = r.concat(Rope<char>{std::string_view(&c, 1)});
114 }
115 print_row("Rope::concat (1 char/call)", elapsed_ms(start), r.size());
116}
117
127void bench_rope_concat_chunked(const int total_chars, const int chunk_size)
128{
129 const auto start = Clock::now();
131 if (total_chars <= 0 or chunk_size <= 0)
132 {
133 print_row("Rope::concat (" + std::to_string(chunk_size) + " chars/call)",
134 elapsed_ms(start), r.size());
135 return;
136 }
137 std::string chunk(static_cast<size_t>(chunk_size), 'a');
138 for (int built = 0; built < total_chars; )
139 {
140 const int remaining = total_chars - built;
141 const int piece_size = remaining < chunk_size ? remaining : chunk_size;
142 const std::string_view piece(chunk.data(), static_cast<size_t>(piece_size));
143 r = r.concat(Rope<char>{piece});
144 built += piece_size;
145 }
146 print_row("Rope::concat (" + std::to_string(chunk_size) + " chars/call)",
147 elapsed_ms(start), r.size());
148}
149
153void bench_string_edits(const std::string & base, const int edit_count)
154{
155 const auto start = Clock::now();
156 std::string s = base;
157 const std::string marker = "<<edit>>";
158 std::mt19937 rng(0xEDEDu);
159 for (int i = 0; i < edit_count; ++i)
160 {
161 std::uniform_int_distribution<size_t> pos_dist(0, s.size());
162 const size_t pos = pos_dist(rng);
163 s.insert(pos, marker);
164 s.erase(pos, marker.size());
165 }
166 print_row("std::string insert+erase", elapsed_ms(start), s.size());
167}
168
169void bench_rope_edits(const Rope<char> & base, const int edit_count)
170{
171 const auto start = Clock::now();
172 Rope<char> r = base;
173 const Rope<char> marker{std::string_view("<<edit>>")};
174 std::mt19937 rng(0xEDEDu);
175 for (int i = 0; i < edit_count; ++i)
176 {
177 std::uniform_int_distribution<size_t> pos_dist(0, r.size());
178 const size_t pos = pos_dist(rng);
179 r = r.insert(pos, marker);
180 r = r.erase(pos, marker.size());
181 }
182 print_row("Rope insert+erase", elapsed_ms(start), r.size());
183}
184} // namespace
185
186int main(int argc, char * argv[])
187{
188 // Defaults large enough to actually reach the crossover where Rope's
189 // typical O(log n) structural edits beat std::string's O(n) ones (see [2]
190 // below); smaller sizes finish faster but may not show it.
191 int chunk_count = 2000000;
192 int edit_count = 5000;
193
194 for (int i = 1; i < argc; ++i)
195 {
196 const std::string_view arg = argv[i];
197 if (arg == "--chunks" and i + 1 < argc)
198 chunk_count = std::atoi(argv[++i]);
199 else if (arg == "--edits" and i + 1 < argc)
200 edit_count = std::atoi(argv[++i]);
201 else if (arg == "--help")
202 {
203 std::cout << "Usage: rope_benchmark [--chunks N] [--edits N]\n";
204 return 0;
205 }
206 }
207 if (chunk_count < 0)
208 chunk_count = 0;
209 if (edit_count < 0)
210 edit_count = 0;
211
212 std::cout << "\n=== Aleph::Rope vs std::string benchmark ===\n\n";
213
214 std::cout << "[1] Large concatenation: " << chunk_count
215 << " single-character appends\n";
216 bench_string_append(chunk_count);
217 bench_rope_concat(chunk_count);
218 std::cout << " (Rope's documented weak case -- every successful "
219 "absorption re-copies the whole receiving leaf; see below "
220 "for the pattern it is actually built for.)\n";
221
222 constexpr int chunk_size = 1000;
223 // Overflow-safe ceiling division: `chunk_count + chunk_size - 1` (the
224 // usual `(a + b - 1) / b` idiom) can overflow `int` for a large
225 // `--chunks` value before the division ever runs; `(chunk_count - 1) /
226 // chunk_size + 1` reaches the same result without ever adding two
227 // positive values that could each be close to INT_MAX.
228 const int chunked_calls =
229 chunk_count == 0 ? 0 : (chunk_count - 1) / chunk_size + 1;
230 std::cout << "\n[1b] Same total size via " << chunked_calls
231 << " chunked concatenations (" << chunk_size
232 << " chars/call, Rope's intended "
233 "usage pattern)\n";
234 bench_rope_concat_chunked(chunk_count, chunk_size);
235
236 std::cout << "\n[2] Structural editing: " << edit_count
237 << " random middle insert+erase pairs on a "
238 << chunk_count << "-character base sequence\n";
239 const std::string base_string(static_cast<size_t>(chunk_count), 'x');
240 const Rope<char> base_rope{std::string_view(base_string)};
243
244 std::cout << "\nDone.\n";
245 return 0;
246}
int main()
Immutable, structurally-shared rope over a sequence of Char.
Definition tpl_rope.H:176
Rope concat(const Rope &other) const
Return a new rope that is *this followed by other.
Definition tpl_rope.H:708
static mt19937 rng
std::chrono::steady_clock Clock
size_t blossom_maximum_cardinality_matching(const GT &g, DynDlist< typename GT::Arc * > &matching, SA sa=SA())
Alias of compute_maximum_cardinality_general_matching().
Definition Blossom.H:466
size_t piece_size(std::string_view s) noexcept
size_t chunk_count(const size_t n, const size_t chunk_size) noexcept
size_t chunk_size(const size_t n, const size_t num_threads, const size_t min_chunk=64)
Calculate optimal chunk size based on data size and thread count.
Main namespace for Aleph-w library functions.
Definition ah-arena.H:89
and
Check uniqueness with explicit hash + equality functors.
gsl_rng * r
Immutable, structurally-shared rope (Aleph::Rope) for large character sequences.