Skip to content

Commit 92a8b22

Browse files
visitorckwakpm00
authored andcommitted
lib/min_heap: introduce non-inline versions of min heap API functions
Patch series "Enhance min heap API with non-inline functions and optimizations", v2. Add non-inline versions of the min heap API functions in lib/min_heap.c and updates all users outside of kernel/events/core.c to use these non-inline versions. To mitigate the performance impact of indirect function calls caused by the non-inline versions of the swap and compare functions, a builtin swap has been introduced that swaps elements based on their size. Additionally, it micro-optimizes the efficiency of the min heap by pre-scaling the counter, following the same approach as in lib/sort.c. Documentation for the min heap API has also been added to the core-api section. This patch (of 10): All current min heap API functions are marked with '__always_inline'. However, as the number of users increases, inlining these functions everywhere leads to a increase in kernel size. In performance-critical paths, such as when perf events are enabled and min heap functions are called on every context switch, it is important to retain the inline versions for optimal performance. To balance this, the original inline functions are kept, and additional non-inline versions of the functions have been added in lib/min_heap.c. Link: https://lkml.kernel.org/r/[email protected] Link: https://lore.kernel.org/[email protected] Link: https://lkml.kernel.org/r/[email protected] Signed-off-by: Kuan-Wei Chiu <[email protected]> Suggested-by: Andrew Morton <[email protected]> Cc: Adrian Hunter <[email protected]> Cc: Arnaldo Carvalho de Melo <[email protected]> Cc: Ching-Chun (Jim) Huang <[email protected]> Cc: Coly Li <[email protected]> Cc: Ian Rogers <[email protected]> Cc: Ingo Molnar <[email protected]> Cc: Jiri Olsa <[email protected]> Cc: Jonathan Corbet <[email protected]> Cc: Kent Overstreet <[email protected]> Cc: Kuan-Wei Chiu <[email protected]> Cc: "Liang, Kan" <[email protected]> Cc: Mark Rutland <[email protected]> Cc: Matthew Sakai <[email protected]> Cc: Matthew Wilcox (Oracle) <[email protected]> Cc: Namhyung Kim <[email protected]> Cc: Peter Zijlstra <[email protected]> Signed-off-by: Andrew Morton <[email protected]>
1 parent dabddd6 commit 92a8b22

File tree

9 files changed

+167
-46
lines changed

9 files changed

+167
-46
lines changed

drivers/md/bcache/Kconfig

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ config BCACHE
55
select BLOCK_HOLDER_DEPRECATED if SYSFS
66
select CRC64
77
select CLOSURES
8+
select MIN_HEAP
89
help
910
Allows a block device to be used as cache for other devices; uses
1011
a btree for indexing and the layout is optimized for SSDs.

drivers/md/dm-vdo/Kconfig

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ config DM_VDO
77
select DM_BUFIO
88
select LZ4_COMPRESS
99
select LZ4_DECOMPRESS
10+
select MIN_HEAP
1011
help
1112
This device mapper target presents a block device with
1213
deduplication, compression and thin-provisioning.

fs/bcachefs/Kconfig

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ config BCACHEFS_FS
2424
select XXHASH
2525
select SRCU
2626
select SYMBOLIC_ERRNAME
27+
select MIN_HEAP
2728
help
2829
The bcachefs filesystem - a modern, copy on write filesystem, with
2930
support for multiple devices, compression, checksumming, etc.

include/linux/min_heap.h

Lines changed: 86 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ struct min_heap_callbacks {
4040

4141
/* Initialize a min-heap. */
4242
static __always_inline
43-
void __min_heap_init(min_heap_char *heap, void *data, int size)
43+
void __min_heap_init_inline(min_heap_char *heap, void *data, int size)
4444
{
4545
heap->nr = 0;
4646
heap->size = size;
@@ -50,33 +50,33 @@ void __min_heap_init(min_heap_char *heap, void *data, int size)
5050
heap->data = heap->preallocated;
5151
}
5252

53-
#define min_heap_init(_heap, _data, _size) \
54-
__min_heap_init((min_heap_char *)_heap, _data, _size)
53+
#define min_heap_init_inline(_heap, _data, _size) \
54+
__min_heap_init_inline((min_heap_char *)_heap, _data, _size)
5555

5656
/* Get the minimum element from the heap. */
5757
static __always_inline
58-
void *__min_heap_peek(struct min_heap_char *heap)
58+
void *__min_heap_peek_inline(struct min_heap_char *heap)
5959
{
6060
return heap->nr ? heap->data : NULL;
6161
}
6262

63-
#define min_heap_peek(_heap) \
64-
(__minheap_cast(_heap) __min_heap_peek((min_heap_char *)_heap))
63+
#define min_heap_peek_inline(_heap) \
64+
(__minheap_cast(_heap) __min_heap_peek_inline((min_heap_char *)_heap))
6565

6666
/* Check if the heap is full. */
6767
static __always_inline
68-
bool __min_heap_full(min_heap_char *heap)
68+
bool __min_heap_full_inline(min_heap_char *heap)
6969
{
7070
return heap->nr == heap->size;
7171
}
7272

73-
#define min_heap_full(_heap) \
74-
__min_heap_full((min_heap_char *)_heap)
73+
#define min_heap_full_inline(_heap) \
74+
__min_heap_full_inline((min_heap_char *)_heap)
7575

7676
/* Sift the element at pos down the heap. */
7777
static __always_inline
78-
void __min_heap_sift_down(min_heap_char *heap, int pos, size_t elem_size,
79-
const struct min_heap_callbacks *func, void *args)
78+
void __min_heap_sift_down_inline(min_heap_char *heap, int pos, size_t elem_size,
79+
const struct min_heap_callbacks *func, void *args)
8080
{
8181
void *left, *right;
8282
void *data = heap->data;
@@ -108,13 +108,14 @@ void __min_heap_sift_down(min_heap_char *heap, int pos, size_t elem_size,
108108
}
109109
}
110110

111-
#define min_heap_sift_down(_heap, _pos, _func, _args) \
112-
__min_heap_sift_down((min_heap_char *)_heap, _pos, __minheap_obj_size(_heap), _func, _args)
111+
#define min_heap_sift_down_inline(_heap, _pos, _func, _args) \
112+
__min_heap_sift_down_inline((min_heap_char *)_heap, _pos, __minheap_obj_size(_heap), \
113+
_func, _args)
113114

114115
/* Sift up ith element from the heap, O(log2(nr)). */
115116
static __always_inline
116-
void __min_heap_sift_up(min_heap_char *heap, size_t elem_size, size_t idx,
117-
const struct min_heap_callbacks *func, void *args)
117+
void __min_heap_sift_up_inline(min_heap_char *heap, size_t elem_size, size_t idx,
118+
const struct min_heap_callbacks *func, void *args)
118119
{
119120
void *data = heap->data;
120121
size_t parent;
@@ -128,27 +129,28 @@ void __min_heap_sift_up(min_heap_char *heap, size_t elem_size, size_t idx,
128129
}
129130
}
130131

131-
#define min_heap_sift_up(_heap, _idx, _func, _args) \
132-
__min_heap_sift_up((min_heap_char *)_heap, __minheap_obj_size(_heap), _idx, _func, _args)
132+
#define min_heap_sift_up_inline(_heap, _idx, _func, _args) \
133+
__min_heap_sift_up_inline((min_heap_char *)_heap, __minheap_obj_size(_heap), _idx, \
134+
_func, _args)
133135

134136
/* Floyd's approach to heapification that is O(nr). */
135137
static __always_inline
136-
void __min_heapify_all(min_heap_char *heap, size_t elem_size,
137-
const struct min_heap_callbacks *func, void *args)
138+
void __min_heapify_all_inline(min_heap_char *heap, size_t elem_size,
139+
const struct min_heap_callbacks *func, void *args)
138140
{
139141
int i;
140142

141143
for (i = heap->nr / 2 - 1; i >= 0; i--)
142-
__min_heap_sift_down(heap, i, elem_size, func, args);
144+
__min_heap_sift_down_inline(heap, i, elem_size, func, args);
143145
}
144146

145-
#define min_heapify_all(_heap, _func, _args) \
146-
__min_heapify_all((min_heap_char *)_heap, __minheap_obj_size(_heap), _func, _args)
147+
#define min_heapify_all_inline(_heap, _func, _args) \
148+
__min_heapify_all_inline((min_heap_char *)_heap, __minheap_obj_size(_heap), _func, _args)
147149

148150
/* Remove minimum element from the heap, O(log2(nr)). */
149151
static __always_inline
150-
bool __min_heap_pop(min_heap_char *heap, size_t elem_size,
151-
const struct min_heap_callbacks *func, void *args)
152+
bool __min_heap_pop_inline(min_heap_char *heap, size_t elem_size,
153+
const struct min_heap_callbacks *func, void *args)
152154
{
153155
void *data = heap->data;
154156

@@ -158,36 +160,35 @@ bool __min_heap_pop(min_heap_char *heap, size_t elem_size,
158160
/* Place last element at the root (position 0) and then sift down. */
159161
heap->nr--;
160162
memcpy(data, data + (heap->nr * elem_size), elem_size);
161-
__min_heap_sift_down(heap, 0, elem_size, func, args);
163+
__min_heap_sift_down_inline(heap, 0, elem_size, func, args);
162164

163165
return true;
164166
}
165167

166-
#define min_heap_pop(_heap, _func, _args) \
167-
__min_heap_pop((min_heap_char *)_heap, __minheap_obj_size(_heap), _func, _args)
168+
#define min_heap_pop_inline(_heap, _func, _args) \
169+
__min_heap_pop_inline((min_heap_char *)_heap, __minheap_obj_size(_heap), _func, _args)
168170

169171
/*
170172
* Remove the minimum element and then push the given element. The
171173
* implementation performs 1 sift (O(log2(nr))) and is therefore more
172174
* efficient than a pop followed by a push that does 2.
173175
*/
174176
static __always_inline
175-
void __min_heap_pop_push(min_heap_char *heap,
176-
const void *element, size_t elem_size,
177-
const struct min_heap_callbacks *func,
178-
void *args)
177+
void __min_heap_pop_push_inline(min_heap_char *heap, const void *element, size_t elem_size,
178+
const struct min_heap_callbacks *func, void *args)
179179
{
180180
memcpy(heap->data, element, elem_size);
181-
__min_heap_sift_down(heap, 0, elem_size, func, args);
181+
__min_heap_sift_down_inline(heap, 0, elem_size, func, args);
182182
}
183183

184-
#define min_heap_pop_push(_heap, _element, _func, _args) \
185-
__min_heap_pop_push((min_heap_char *)_heap, _element, __minheap_obj_size(_heap), _func, _args)
184+
#define min_heap_pop_push_inline(_heap, _element, _func, _args) \
185+
__min_heap_pop_push_inline((min_heap_char *)_heap, _element, __minheap_obj_size(_heap), \
186+
_func, _args)
186187

187188
/* Push an element on to the heap, O(log2(nr)). */
188189
static __always_inline
189-
bool __min_heap_push(min_heap_char *heap, const void *element, size_t elem_size,
190-
const struct min_heap_callbacks *func, void *args)
190+
bool __min_heap_push_inline(min_heap_char *heap, const void *element, size_t elem_size,
191+
const struct min_heap_callbacks *func, void *args)
191192
{
192193
void *data = heap->data;
193194
int pos;
@@ -201,18 +202,19 @@ bool __min_heap_push(min_heap_char *heap, const void *element, size_t elem_size,
201202
heap->nr++;
202203

203204
/* Sift child at pos up. */
204-
__min_heap_sift_up(heap, elem_size, pos, func, args);
205+
__min_heap_sift_up_inline(heap, elem_size, pos, func, args);
205206

206207
return true;
207208
}
208209

209-
#define min_heap_push(_heap, _element, _func, _args) \
210-
__min_heap_push((min_heap_char *)_heap, _element, __minheap_obj_size(_heap), _func, _args)
210+
#define min_heap_push_inline(_heap, _element, _func, _args) \
211+
__min_heap_push_inline((min_heap_char *)_heap, _element, __minheap_obj_size(_heap), \
212+
_func, _args)
211213

212214
/* Remove ith element from the heap, O(log2(nr)). */
213215
static __always_inline
214-
bool __min_heap_del(min_heap_char *heap, size_t elem_size, size_t idx,
215-
const struct min_heap_callbacks *func, void *args)
216+
bool __min_heap_del_inline(min_heap_char *heap, size_t elem_size, size_t idx,
217+
const struct min_heap_callbacks *func, void *args)
216218
{
217219
void *data = heap->data;
218220

@@ -224,12 +226,53 @@ bool __min_heap_del(min_heap_char *heap, size_t elem_size, size_t idx,
224226
if (idx == heap->nr)
225227
return true;
226228
func->swp(data + (idx * elem_size), data + (heap->nr * elem_size), args);
227-
__min_heap_sift_up(heap, elem_size, idx, func, args);
228-
__min_heap_sift_down(heap, idx, elem_size, func, args);
229+
__min_heap_sift_up_inline(heap, elem_size, idx, func, args);
230+
__min_heap_sift_down_inline(heap, idx, elem_size, func, args);
229231

230232
return true;
231233
}
232234

235+
#define min_heap_del_inline(_heap, _idx, _func, _args) \
236+
__min_heap_del_inline((min_heap_char *)_heap, __minheap_obj_size(_heap), _idx, \
237+
_func, _args)
238+
239+
void __min_heap_init(min_heap_char *heap, void *data, int size);
240+
void *__min_heap_peek(struct min_heap_char *heap);
241+
bool __min_heap_full(min_heap_char *heap);
242+
void __min_heap_sift_down(min_heap_char *heap, int pos, size_t elem_size,
243+
const struct min_heap_callbacks *func, void *args);
244+
void __min_heap_sift_up(min_heap_char *heap, size_t elem_size, size_t idx,
245+
const struct min_heap_callbacks *func, void *args);
246+
void __min_heapify_all(min_heap_char *heap, size_t elem_size,
247+
const struct min_heap_callbacks *func, void *args);
248+
bool __min_heap_pop(min_heap_char *heap, size_t elem_size,
249+
const struct min_heap_callbacks *func, void *args);
250+
void __min_heap_pop_push(min_heap_char *heap, const void *element, size_t elem_size,
251+
const struct min_heap_callbacks *func, void *args);
252+
bool __min_heap_push(min_heap_char *heap, const void *element, size_t elem_size,
253+
const struct min_heap_callbacks *func, void *args);
254+
bool __min_heap_del(min_heap_char *heap, size_t elem_size, size_t idx,
255+
const struct min_heap_callbacks *func, void *args);
256+
257+
#define min_heap_init(_heap, _data, _size) \
258+
__min_heap_init((min_heap_char *)_heap, _data, _size)
259+
#define min_heap_peek(_heap) \
260+
(__minheap_cast(_heap) __min_heap_peek((min_heap_char *)_heap))
261+
#define min_heap_full(_heap) \
262+
__min_heap_full((min_heap_char *)_heap)
263+
#define min_heap_sift_down(_heap, _pos, _func, _args) \
264+
__min_heap_sift_down((min_heap_char *)_heap, _pos, __minheap_obj_size(_heap), _func, _args)
265+
#define min_heap_sift_up(_heap, _idx, _func, _args) \
266+
__min_heap_sift_up((min_heap_char *)_heap, __minheap_obj_size(_heap), _idx, _func, _args)
267+
#define min_heapify_all(_heap, _func, _args) \
268+
__min_heapify_all((min_heap_char *)_heap, __minheap_obj_size(_heap), _func, _args)
269+
#define min_heap_pop(_heap, _func, _args) \
270+
__min_heap_pop((min_heap_char *)_heap, __minheap_obj_size(_heap), _func, _args)
271+
#define min_heap_pop_push(_heap, _element, _func, _args) \
272+
__min_heap_pop_push((min_heap_char *)_heap, _element, __minheap_obj_size(_heap), \
273+
_func, _args)
274+
#define min_heap_push(_heap, _element, _func, _args) \
275+
__min_heap_push((min_heap_char *)_heap, _element, __minheap_obj_size(_heap), _func, _args)
233276
#define min_heap_del(_heap, _idx, _func, _args) \
234277
__min_heap_del((min_heap_char *)_heap, __minheap_obj_size(_heap), _idx, _func, _args)
235278

kernel/events/core.c

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3870,7 +3870,7 @@ static noinline int visit_groups_merge(struct perf_event_context *ctx,
38703870
perf_assert_pmu_disabled((*evt)->pmu_ctx->pmu);
38713871
}
38723872

3873-
min_heapify_all(&event_heap, &perf_min_heap, NULL);
3873+
min_heapify_all_inline(&event_heap, &perf_min_heap, NULL);
38743874

38753875
while (event_heap.nr) {
38763876
ret = func(*evt, data);
@@ -3879,9 +3879,9 @@ static noinline int visit_groups_merge(struct perf_event_context *ctx,
38793879

38803880
*evt = perf_event_groups_next(*evt, pmu);
38813881
if (*evt)
3882-
min_heap_sift_down(&event_heap, 0, &perf_min_heap, NULL);
3882+
min_heap_sift_down_inline(&event_heap, 0, &perf_min_heap, NULL);
38833883
else
3884-
min_heap_pop(&event_heap, &perf_min_heap, NULL);
3884+
min_heap_pop_inline(&event_heap, &perf_min_heap, NULL);
38853885
}
38863886

38873887
return 0;

lib/Kconfig

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -780,3 +780,6 @@ config FIRMWARE_TABLE
780780

781781
config UNION_FIND
782782
bool
783+
784+
config MIN_HEAP
785+
bool

lib/Kconfig.debug

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2279,6 +2279,7 @@ config TEST_LIST_SORT
22792279
config TEST_MIN_HEAP
22802280
tristate "Min heap test"
22812281
depends on DEBUG_KERNEL || m
2282+
select MIN_HEAP
22822283
help
22832284
Enable this to turn on min heap function tests. This test is
22842285
executed only once during system boot (so affects only boot time),

lib/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ lib-y := ctype.o string.o vsprintf.o cmdline.o \
4040
lib-$(CONFIG_UNION_FIND) += union_find.o
4141
lib-$(CONFIG_PRINTK) += dump_stack.o
4242
lib-$(CONFIG_SMP) += cpumask.o
43+
lib-$(CONFIG_MIN_HEAP) += min_heap.o
4344

4445
lib-y += kobject.o klist.o
4546
obj-y += lockref.o

lib/min_heap.c

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
#include <linux/export.h>
3+
#include <linux/min_heap.h>
4+
5+
void __min_heap_init(min_heap_char *heap, void *data, int size)
6+
{
7+
__min_heap_init_inline(heap, data, size);
8+
}
9+
EXPORT_SYMBOL(__min_heap_init);
10+
11+
void *__min_heap_peek(struct min_heap_char *heap)
12+
{
13+
return __min_heap_peek_inline(heap);
14+
}
15+
EXPORT_SYMBOL(__min_heap_peek);
16+
17+
bool __min_heap_full(min_heap_char *heap)
18+
{
19+
return __min_heap_full_inline(heap);
20+
}
21+
EXPORT_SYMBOL(__min_heap_full);
22+
23+
void __min_heap_sift_down(min_heap_char *heap, int pos, size_t elem_size,
24+
const struct min_heap_callbacks *func, void *args)
25+
{
26+
__min_heap_sift_down_inline(heap, pos, elem_size, func, args);
27+
}
28+
EXPORT_SYMBOL(__min_heap_sift_down);
29+
30+
void __min_heap_sift_up(min_heap_char *heap, size_t elem_size, size_t idx,
31+
const struct min_heap_callbacks *func, void *args)
32+
{
33+
__min_heap_sift_up_inline(heap, elem_size, idx, func, args);
34+
}
35+
EXPORT_SYMBOL(__min_heap_sift_up);
36+
37+
void __min_heapify_all(min_heap_char *heap, size_t elem_size,
38+
const struct min_heap_callbacks *func, void *args)
39+
{
40+
__min_heapify_all_inline(heap, elem_size, func, args);
41+
}
42+
EXPORT_SYMBOL(__min_heapify_all);
43+
44+
bool __min_heap_pop(min_heap_char *heap, size_t elem_size,
45+
const struct min_heap_callbacks *func, void *args)
46+
{
47+
return __min_heap_pop_inline(heap, elem_size, func, args);
48+
}
49+
EXPORT_SYMBOL(__min_heap_pop);
50+
51+
void __min_heap_pop_push(min_heap_char *heap, const void *element, size_t elem_size,
52+
const struct min_heap_callbacks *func, void *args)
53+
{
54+
__min_heap_pop_push_inline(heap, element, elem_size, func, args);
55+
}
56+
EXPORT_SYMBOL(__min_heap_pop_push);
57+
58+
bool __min_heap_push(min_heap_char *heap, const void *element, size_t elem_size,
59+
const struct min_heap_callbacks *func, void *args)
60+
{
61+
return __min_heap_push_inline(heap, element, elem_size, func, args);
62+
}
63+
EXPORT_SYMBOL(__min_heap_push);
64+
65+
bool __min_heap_del(min_heap_char *heap, size_t elem_size, size_t idx,
66+
const struct min_heap_callbacks *func, void *args)
67+
{
68+
return __min_heap_del_inline(heap, elem_size, idx, func, args);
69+
}
70+
EXPORT_SYMBOL(__min_heap_del);

0 commit comments

Comments
 (0)