sstables: remove quadratic (and possibly exponential) compile time in parse()

parse() taking a list of elements is quadratic (during compile time) in
that it generates recursive calls to itself, each time with one fewer
parameter. The total size of the parameter lists in all these generated
functions is quadratic in the initial parameter list size.

It's also exponential if we ignore inlining limits, since each .then()
call expands to two branches - a ready future branch and a non-ready
future branch. If the compiler did not give up, we'd have 2^list_len
branches. For sure the compiler does not do so indefinitely, but the effort
getting there is wasted.

Simplify by using a fold expression over the comma operator. Instead
of passing the remaining parameter list in each step, we pass only
the parameter we are processing now, making processing linear, and not
generating unnecessary functions.

It would be better expressed using pack expansion statements, but these
are part of C++26.

The largest offender is probably stats_metadata, with 21 elements.

dev-mode sstables.o:

   text	   data	    bss	    dec	    hex	filename
1760059	   1312	   7673	1769044	 1afe54	sstables.o.before
1745533	   1312	   7673	1754518	 1ac596	sstables.o.after

We save about 15k of text with presumably a corresponding (small)
decrease in compile time.

Closes scylladb/scylladb#26735
This commit is contained in:
Avi Kivity
2025-10-28 11:35:58 +02:00
committed by Tomasz Grabiec
parent cb30eb2e21
commit 9b6ce030d0

View File

@@ -275,9 +275,11 @@ future<> parse(const schema&, sstable_version_types, random_access_reader& in, T
// All composite parsers must come after this
template<typename First, typename... Rest>
future<> parse(const schema& s, sstable_version_types v, random_access_reader& in, First& first, Rest&&... rest) {
return parse(s, v, in, first).then([v, &s, &in, &rest...] {
return parse(s, v, in, std::forward<Rest>(rest)...);
});
auto fut = parse(s, v, in, first);
(..., (void)(fut = fut.then([&s, v, &in, &rest] () mutable {
return parse(s, v, in, std::forward<Rest>(rest));
})));
return fut;
}
// Intended to be used for a type that describes itself through describe_type().