@@ -574,17 +574,129 @@ static void bench_ast_load(const std::vector<std::string>& headers,
574574 const std::string& resource_dir) {
575575 std::println (" \n === AST LOAD LATENCY ({} headers, {} runs) ===" , count, runs);
576576
577- // Source file that uses symbols from multiple headers .
578- std::string source_text = make_preamble (headers, count) + R"cpp(
577+ // Light source: uses only a few types (lazy PCH load, best case) .
578+ std::string light_source = make_preamble (headers, count) + R"cpp(
579579int main() {
580- std::vector<std::string> v = {"hello", "world"};
581- std::map<int, std::string> m;
582- auto opt = std::make_optional(42);
580+ std::vector<int> v = {1, 2, 3};
581+ return v[0];
582+ }
583+ )cpp" ;
584+
585+ // Heavy source: references symbols from as many headers as possible
586+ // to force maximum PCH deserialization (worst case).
587+ std::string heavy_source = make_preamble (headers, count) + R"cpp(
588+ template <typename... Ts> void use(Ts&&...) {}
589+
590+ int main() {
591+ // <cstddef> <cstdint> <climits> <cfloat>
592+ std::size_t sz = 0; std::uint64_t u64 = 0;
593+
594+ // <type_traits> <concepts> <compare>
595+ static_assert(std::is_integral_v<int>);
596+ static_assert(std::integral<int>);
597+ std::strong_ordering cmp = 1 <=> 2;
598+
599+ // <initializer_list> <utility> <tuple> <optional> <variant> <any> <expected>
600+ auto il = {1, 2, 3};
601+ auto pr = std::make_pair(1, 2);
602+ auto tp = std::make_tuple(1, "hello", 3.14);
603+ std::optional<int> opt = 42;
604+ std::variant<int, double, std::string> var = "hello";
605+ std::any a = 42;
606+
607+ // <bitset> <bit> <string_view> <string> <charconv> <format>
608+ std::bitset<64> bs(0xFF);
609+ auto pc = std::popcount(42u);
610+ std::string_view sv = "hello";
611+ std::string s = "world";
612+ auto fmt = std::format("{} {}", s, 42);
613+
614+ // <array> <vector> <deque> <list> <forward_list>
615+ std::array<int, 3> arr = {1, 2, 3};
616+ std::vector<std::string> vec = {"a", "b"};
617+ std::deque<int> dq = {1, 2};
618+ std::list<int> lst = {1, 2};
619+ std::forward_list<int> fl = {1, 2};
620+
621+ // <set> <map> <unordered_set> <unordered_map>
622+ std::set<int> st = {1, 2, 3};
623+ std::map<std::string, int> mp = {{"a", 1}};
624+ std::unordered_set<int> us = {1, 2};
625+ std::unordered_map<std::string, int> um = {{"b", 2}};
626+
627+ // <stack> <queue> <span>
628+ std::stack<int> stk;
629+ std::queue<int> que;
630+ std::span<const int> spn(arr);
631+
632+ // <iterator> <ranges> <algorithm> <numeric>
633+ auto it = vec.begin();
634+ auto rng = vec | std::views::take(1);
635+ std::sort(vec.begin(), vec.end());
636+ auto sum = std::accumulate(arr.begin(), arr.end(), 0);
637+
638+ // <memory> <memory_resource> <scoped_allocator> <functional>
639+ auto up = std::make_unique<int>(42);
640+ auto sp = std::make_shared<std::string>("test");
641+ std::function<int(int)> fn = [](int x) { return x * 2; };
642+
643+ // <ratio> <chrono>
644+ using half = std::ratio<1, 2>;
645+ auto now = std::chrono::system_clock::now();
646+
647+ // <exception> <stdexcept> <system_error>
648+ try { throw std::runtime_error("test"); } catch(...) {}
649+ auto ec = std::make_error_code(std::errc::invalid_argument);
650+
651+ // <typeinfo> <typeindex> <source_location>
652+ auto& ti = typeid(int);
653+ std::type_index tidx(ti);
654+ auto loc = std::source_location::current();
655+
656+ // <new> <limits> <numbers> <valarray> <complex> <random>
657+ static_assert(std::numeric_limits<double>::is_iec559);
658+ constexpr auto pi = std::numbers::pi;
659+ std::valarray<double> va = {1.0, 2.0, 3.0};
660+ std::complex<double> cx(1.0, 2.0);
661+ std::mt19937 rng_eng(42);
662+
663+ // <iosfwd> <ios> <streambuf> <istream> <ostream> <iostream> <sstream> <fstream>
664+ std::stringstream ss;
665+ ss << "hello " << 42;
666+ std::cout << ss.str() << std::endl;
667+
668+ // <cmath> <cstdio> <cstdlib> <cstring> <ctime> <cassert> <cerrno>
669+ auto sq = std::sqrt(2.0);
670+ auto len = std::strlen("hello");
671+ auto t = std::time(nullptr);
672+ assert(sq > 1.0);
673+
674+ // <atomic> <mutex> <condition_variable> <thread> <future>
675+ std::atomic<int> ai{0};
676+ std::mutex mtx;
677+ std::condition_variable cv;
678+
679+ // <semaphore> <latch> <barrier> <stop_token> <shared_mutex>
680+ std::counting_semaphore<1> sem(1);
681+ std::latch lat(1);
682+
683+ // <regex> <filesystem>
684+ std::regex rx("hello.*");
685+ auto cwd = std::filesystem::current_path();
686+
687+ // <locale> <codecvt>
688+ auto& loc2 = std::locale::classic();
689+
690+ use(sz, u64, cmp, il, pr, tp, opt, var, a, bs, pc, sv, s, fmt,
691+ arr, vec, dq, lst, fl, st, mp, us, um, stk, que, spn,
692+ it, rng, sum, up, sp, fn, now, ec, ti, tidx, loc,
693+ pi, va, cx, rng_eng, ss, sq, len, t, ai, mtx, cv,
694+ sem, lat, rx, cwd, loc2);
583695 return 0;
584696}
585697)cpp" ;
586- std::string source_file = " /tmp/ast-load-test.cpp " ;
587- auto preamble_bound = static_cast <std::uint32_t >(make_preamble (headers, count) .size ());
698+ auto preamble = make_preamble (headers, count) ;
699+ auto preamble_bound = static_cast <std::uint32_t >(preamble .size ());
588700
589701 // --- Build monolithic PCH ---
590702 std::string mono_preamble = make_preamble (headers, count);
@@ -627,51 +739,61 @@ int main() {
627739
628740 std::string chain_pch = prev_pch;
629741
630- // --- Benchmark: compile source with monolithic PCH ---
631- std::vector<double > mono_times, chain_times;
742+ // --- Run both scenarios: light and heavy ---
743+ struct Scenario {
744+ const char * name;
745+ std::string source;
746+ };
632747
633- for (int r = 0 ; r < runs; ++r) {
634- double t =
635- compile_with_pch (source_text, source_file, mono_pch, preamble_bound, resource_dir);
636- if (t >= 0 )
637- mono_times.push_back (t);
638- }
748+ Scenario scenarios[] = {
749+ {" light (3 types)" , light_source},
750+ {" heavy (all headers)" , heavy_source},
751+ };
639752
640- // --- Benchmark: compile source with chained PCH ---
641- for (int r = 0 ; r < runs; ++r) {
642- // For chained PCH, bound=0 since the PCH doesn't cover the source file's bytes.
643- // But we need the source to NOT re-parse the preamble includes.
644- // With chained PCH loaded via ImplicitPCHInclude and bound=0,
645- // clang will parse the source from the beginning but the includes
646- // will hit the PCH's already-parsed headers.
647- double t =
648- compile_with_pch (source_text, source_file, chain_pch, preamble_bound, resource_dir);
649- if (t >= 0 )
650- chain_times.push_back (t);
651- }
753+ for (auto & [name, source]: scenarios) {
754+ std::println (" \n --- {} ---" , name);
755+ std::string source_file = " /tmp/ast-load-test.cpp" ;
652756
653- // --- Report ---
654- if (!mono_times.empty ()) {
655- std::sort (mono_times.begin (), mono_times.end ());
656- double med = mono_times[mono_times.size () / 2 ];
657- std::println (" Monolithic PCH → compile: median {:.1f}ms (min {:.1f}, max {:.1f})" ,
658- med,
659- mono_times.front (),
660- mono_times.back ());
661- }
662- if (!chain_times.empty ()) {
663- std::sort (chain_times.begin (), chain_times.end ());
664- double med = chain_times[chain_times.size () / 2 ];
665- std::println (" Chained PCH → compile: median {:.1f}ms (min {:.1f}, max {:.1f})" ,
666- med,
667- chain_times.front (),
668- chain_times.back ());
669- }
670- if (!mono_times.empty () && !chain_times.empty ()) {
671- double mono_med = mono_times[mono_times.size () / 2 ];
672- double chain_med = chain_times[chain_times.size () / 2 ];
673- double ratio = chain_med / mono_med;
674- std::println (" Ratio (chained/mono): {:.2f}x" , ratio);
757+ std::vector<double > mono_times, chain_times;
758+
759+ for (int r = 0 ; r < runs; ++r) {
760+ double t =
761+ compile_with_pch (source, source_file, mono_pch, preamble_bound, resource_dir);
762+ if (t >= 0 )
763+ mono_times.push_back (t);
764+ }
765+
766+ for (int r = 0 ; r < runs; ++r) {
767+ double t =
768+ compile_with_pch (source, source_file, chain_pch, preamble_bound, resource_dir);
769+ if (t >= 0 )
770+ chain_times.push_back (t);
771+ }
772+
773+ if (!mono_times.empty ()) {
774+ std::sort (mono_times.begin (), mono_times.end ());
775+ double med = mono_times[mono_times.size () / 2 ];
776+ std::println (" Monolithic PCH → compile: median {:.1f}ms (min {:.1f}, max {:.1f})" ,
777+ med,
778+ mono_times.front (),
779+ mono_times.back ());
780+ }
781+ if (!chain_times.empty ()) {
782+ std::sort (chain_times.begin (), chain_times.end ());
783+ double med = chain_times[chain_times.size () / 2 ];
784+ std::println (" Chained PCH → compile: median {:.1f}ms (min {:.1f}, max {:.1f})" ,
785+ med,
786+ chain_times.front (),
787+ chain_times.back ());
788+ }
789+ if (!mono_times.empty () && !chain_times.empty ()) {
790+ double mono_med = mono_times[mono_times.size () / 2 ];
791+ double chain_med = chain_times[chain_times.size () / 2 ];
792+ double ratio = chain_med / mono_med;
793+ std::println (" Ratio (chained/mono): {:.2f}x" , ratio);
794+ } else if (chain_times.empty ()) {
795+ std::println (" Chained PCH: compilation FAILED (heavy source may have errors)" );
796+ }
675797 }
676798
677799 // Cleanup.
0 commit comments