Skip to content

Commit cc1692e

Browse files
committed
[perf] experiement with distributed slices for bookmark types
1 parent 274b95c commit cc1692e

9 files changed

Lines changed: 245 additions & 90 deletions

src/base/distributed_slice.hh

Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
/**
2+
* Copyright (c) 2025, Timothy Stack
3+
*
4+
* All rights reserved.
5+
*
6+
* Redistribution and use in source and binary forms, with or without
7+
* modification, are permitted provided that the following conditions are met:
8+
*
9+
* * Redistributions of source code must retain the above copyright notice, this
10+
* list of conditions and the following disclaimer.
11+
* * Redistributions in binary form must reproduce the above copyright notice,
12+
* this list of conditions and the following disclaimer in the documentation
13+
* and/or other materials provided with the distribution.
14+
* * Neither the name of Timothy Stack nor the names of its contributors
15+
* may be used to endorse or promote products derived from this software
16+
* without specific prior written permission.
17+
*
18+
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ''AS IS'' AND ANY
19+
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20+
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21+
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
22+
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23+
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
24+
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
25+
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26+
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27+
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28+
*/
29+
30+
#ifndef lnav_distributed_slice
31+
#define lnav_distributed_slice
32+
33+
#include <vector>
34+
35+
#define DS_STRINGIZE(A) #A
36+
37+
#if defined(__APPLE__)
38+
# define DIST_SLICE(id) \
39+
__attribute__((used, \
40+
section("__DATA,__ds_" DS_STRINGIZE( \
41+
id) ",regular,no_dead_strip")))
42+
# define DIST_SLICE_BEGIN(id) \
43+
__asm("section$start$__DATA$__ds_" DS_STRINGIZE(id))
44+
# define DIST_SLICE_END(id) \
45+
__asm("section$end$__DATA$__ds_" DS_STRINGIZE(id))
46+
#else
47+
# define DIST_SLICE(id) \
48+
__attribute__((used, section("ds_" DS_STRINGIZE(id))))
49+
# define DIST_SLICE_BEGIN(id) __asm("__start_ds_" DS_STRINGIZE(id))
50+
# define DIST_SLICE_END(id) __asm("__stop_ds_" DS_STRINGIZE(id))
51+
#endif
52+
53+
template<typename T>
54+
struct dist_slice_container {
55+
using iterator = T*;
56+
using const_iterator = const T*;
57+
58+
constexpr dist_slice_container(iterator start, iterator end)
59+
: ds_start(start), ds_end(end)
60+
{
61+
}
62+
63+
constexpr iterator begin() { return this->ds_start; }
64+
65+
constexpr const_iterator begin() const { return this->ds_start; }
66+
67+
constexpr iterator end() { return this->ds_end; }
68+
69+
constexpr const_iterator end() const { return this->ds_end; }
70+
71+
size_t size() const { return this->ds_end - this->ds_start; }
72+
73+
template<typename U>
74+
struct slice_indexed_array {
75+
using iterator = typename std::vector<U>::iterator;
76+
77+
explicit slice_indexed_array(const dist_slice_container& slices)
78+
: sia_slices(slices), sia_array(std::vector<U>(slices.size()))
79+
{
80+
}
81+
82+
U& operator[](const T* slice)
83+
{
84+
auto index = std::distance(this->sia_slices.begin(), slice);
85+
return this->sia_array[index];
86+
}
87+
88+
const U& operator[](const T* slice) const
89+
{
90+
auto index = std::distance(this->sia_slices.begin(), slice);
91+
return this->sia_array[index];
92+
}
93+
94+
iterator begin()
95+
{
96+
return this->sia_array.begin();
97+
}
98+
99+
iterator end()
100+
{
101+
return this->sia_array.end();
102+
}
103+
104+
void clear()
105+
{
106+
for (auto& elem : this->sia_array) {
107+
elem.clear();
108+
}
109+
}
110+
111+
private:
112+
const dist_slice_container& sia_slices;
113+
std::vector<U> sia_array;
114+
};
115+
116+
template<typename U>
117+
slice_indexed_array<U> create_array_indexed_by() const
118+
{
119+
return slice_indexed_array<U>(*this);
120+
}
121+
122+
private:
123+
iterator ds_start;
124+
iterator ds_end;
125+
};
126+
127+
#define DIST_SLICE_CONTAINER(T, id) \
128+
(+[]() -> auto { \
129+
extern T start DIST_SLICE_BEGIN(id); \
130+
extern T end DIST_SLICE_END(id); \
131+
\
132+
return dist_slice_container<T>(&start, &end); \
133+
})()
134+
135+
#endif

src/bookmarks.cc

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -92,21 +92,24 @@ bookmark_metadata::clear()
9292
this->bm_annotations.la_pairs.clear();
9393
}
9494

95-
std::optional<bookmark_type_t*>
96-
bookmark_type_t::find_type(const std::string& name)
95+
const bookmark_type_t::type_container&
96+
bookmark_type_t::get_all_types()
9797
{
98-
return get_all_types()
99-
| lnav::itertools::find_if(
100-
[&name](const auto& elem) { return elem->bt_name == name; })
101-
| lnav::itertools::deref();
98+
static auto retval = DIST_SLICE_CONTAINER(bookmark_type_t, bm_types);
99+
100+
return retval;
102101
}
103102

104-
std::vector<bookmark_type_t*>&
105-
bookmark_type_t::get_all_types()
103+
std::optional<const bookmark_type_t*>
104+
bookmark_type_t::find_type(const std::string& name)
106105
{
107-
static std::vector<bookmark_type_t*> all_types;
106+
for (const auto& bmt : get_all_types()) {
107+
if (bmt.get_name() == name) {
108+
return &bmt;
109+
}
110+
}
108111

109-
return all_types;
112+
return std::nullopt;
110113
}
111114

112115
std::vector<string_fragment>
@@ -115,7 +118,7 @@ bookmark_type_t::get_type_names()
115118
std::vector<string_fragment> retval;
116119

117120
for (const auto& bt : get_all_types()) {
118-
retval.emplace_back(bt->get_name());
121+
retval.emplace_back(bt.get_name());
119122
}
120123
std::stable_sort(
121124
retval.begin(), retval.end(), [](const auto& lhs, const auto& rhs) {

src/bookmarks.hh

Lines changed: 20 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -33,11 +33,13 @@
3333
#define bookmarks_hh
3434

3535
#include <map>
36+
#include <memory>
3637
#include <string>
3738
#include <unordered_set>
3839
#include <utility>
3940
#include <vector>
4041

42+
#include "base/distributed_slice.hh"
4143
#include "base/intern_string.hh"
4244
#include "base/lnav_log.hh"
4345
#include "tlx/container/btree_set.hpp"
@@ -117,20 +119,11 @@ public:
117119

118120
tlx::btree_set<LineType> bv_tree;
119121

120-
std::size_t size() const
121-
{
122-
return this->bv_tree.size();
123-
}
122+
std::size_t size() const { return this->bv_tree.size(); }
124123

125-
void clear()
126-
{
127-
this->bv_tree.clear();
128-
}
124+
void clear() { this->bv_tree.clear(); }
129125

130-
bool empty() const
131-
{
132-
return this->bv_tree.empty();
133-
}
126+
bool empty() const { return this->bv_tree.empty(); }
134127

135128
/**
136129
* Insert a bookmark into this vector, but only if it is not already in the
@@ -181,25 +174,24 @@ public:
181174
*/
182175
class bookmark_type_t {
183176
public:
184-
using type_iterator = std::vector<bookmark_type_t*>::iterator;
177+
using type_container = dist_slice_container<bookmark_type_t>;
185178

186-
static type_iterator type_begin() { return get_all_types().begin(); }
179+
static const type_container& get_all_types();
187180

188-
static type_iterator type_end() { return get_all_types().end(); }
189-
190-
static std::optional<bookmark_type_t*> find_type(const std::string& name);
191-
192-
static std::vector<bookmark_type_t*>& get_all_types();
181+
static std::optional<const bookmark_type_t*> find_type(
182+
const std::string& name);
193183

194184
static std::vector<string_fragment> get_type_names();
195185

196186
template<typename T, std::size_t N>
197187
explicit bookmark_type_t(const T (&name)[N])
198188
: bt_name(string_fragment::from_const(name))
199189
{
200-
get_all_types().push_back(this);
201190
}
202191

192+
bookmark_type_t(const bookmark_type_t&) = delete;
193+
bookmark_type_t(const bookmark_type_t&&) = delete;
194+
203195
const string_fragment& get_name() const { return this->bt_name; }
204196

205197
private:
@@ -248,7 +240,14 @@ bookmark_vector<LineType>::prev(LineType start) const
248240
*/
249241
template<typename LineType>
250242
struct bookmarks {
251-
using type = std::map<const bookmark_type_t*, bookmark_vector<LineType>>;
243+
using type = bookmark_type_t::type_container::slice_indexed_array<
244+
bookmark_vector<LineType>>;
245+
246+
static type create_array()
247+
{
248+
return bookmark_type_t::get_all_types()
249+
.create_array_indexed_by<bookmark_vector<LineType>>();
250+
}
252251
};
253252

254253
#endif

src/bottom_status_source.cc

Lines changed: 11 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -133,24 +133,19 @@ bottom_status_source::update_marks(listview_curses* lc)
133133
status_field& sf = this->bss_fields[BSF_HITS];
134134
auto retval = false;
135135

136-
if (bm.find(&textview_curses::BM_SEARCH) != bm.end()) {
137-
const auto& bv = bm[&textview_curses::BM_SEARCH];
136+
const auto& bv = bm[&textview_curses::BM_SEARCH];
138137

139-
if (!bv.empty() || !tc->get_current_search().empty()) {
140-
auto vl = tc->get_selection();
141-
if (vl) {
142-
auto lb = bv.bv_tree.find(vl.value());
143-
if (lb != bv.bv_tree.end()) {
144-
retval = sf.set_value(" Hit %'d of %'d for ",
145-
(lb - bv.bv_tree.begin()) + 1,
146-
tc->get_match_count());
147-
} else {
148-
retval = sf.set_value(" %'d hits for ",
149-
tc->get_match_count());
150-
}
138+
if (!bv.empty() || !tc->get_current_search().empty()) {
139+
auto vl = tc->get_selection();
140+
if (vl) {
141+
auto lb = bv.bv_tree.find(vl.value());
142+
if (lb != bv.bv_tree.end()) {
143+
retval = sf.set_value(" Hit %'d of %'d for ",
144+
(lb - bv.bv_tree.begin()) + 1,
145+
tc->get_match_count());
146+
} else {
147+
retval = sf.set_value(" %'d hits for ", tc->get_match_count());
151148
}
152-
} else {
153-
retval = sf.clear();
154149
}
155150
} else {
156151
retval = sf.clear();

src/listview_curses.cc

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -31,13 +31,16 @@
3131

3232
#include <chrono>
3333
#include <cmath>
34+
#include <optional>
3435

3536
#include "listview_curses.hh"
3637

3738
#include <sys/time.h>
3839
#include <time.h>
3940

4041
#include "base/func_util.hh"
42+
#include "base/itertools.enumerate.hh"
43+
#include "base/itertools.hh"
4144
#include "base/keycodes.hh"
4245
#include "base/lnav_log.hh"
4346
#include "config.h"
@@ -1247,17 +1250,29 @@ listview_curses::layout_for_row(vis_line_t row) const
12471250
{
12481251
auto above_height_avail
12491252
= height - retval.lr_desired_row_height - this->lv_tail_space;
1250-
auto curr_above_row = row - 1_vl;
1251-
while (curr_above_row >= 0_vl && above_height_avail > 0_vl) {
1253+
// scan forwards since that will be nicer to the line_buffer cache
1254+
auto highest_above_row = std::max(0_vl, row - above_height_avail);
1255+
for (auto curr_above_row = highest_above_row; curr_above_row < row;
1256+
++curr_above_row)
1257+
{
12521258
auto curr_above_height
12531259
= this->height_for_row(curr_above_row, height, width);
1254-
1260+
retval.lr_above_line_heights.emplace_back(curr_above_height);
1261+
}
1262+
std::reverse(retval.lr_above_line_heights.begin(),
1263+
retval.lr_above_line_heights.end());
1264+
auto above_limit = std::optional<size_t>();
1265+
for (const auto& [index, curr_above_height] :
1266+
lnav::itertools::enumerate(retval.lr_above_line_heights))
1267+
{
12551268
above_height_avail -= curr_above_height;
12561269
if (above_height_avail < 0_vl) {
1270+
above_limit = index;
12571271
break;
12581272
}
1259-
curr_above_row -= 1_vl;
1260-
retval.lr_above_line_heights.emplace_back(curr_above_height);
1273+
}
1274+
if (above_limit) {
1275+
retval.lr_above_line_heights.resize(above_limit.value());
12611276
}
12621277
}
12631278
{

0 commit comments

Comments
 (0)