Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
510529d
design: the Windows performance plan for 0.0.14 -- warm start and com…
Sunrisepeak Oct 12, 2026
69a2804
0.0.14: completion lists come whole and a word's next request is answ…
Sunrisepeak Oct 12, 2026
d6cdc14
0.0.14: the last session's files are ready in clangd before the edito…
Sunrisepeak Oct 12, 2026
f017d8e
fix: a Json brace-initialized from one value is an array holding it -…
Sunrisepeak Oct 12, 2026
508285d
conformance: completion asked as an editor asks it while a word is ty…
Sunrisepeak Oct 12, 2026
61df6c3
docs: 0.0.14 -- version, changelog, S3-4-34 (about.cacheDevDrive), th…
Sunrisepeak Oct 12, 2026
6f9d3ee
0.0.14 G1: a file the person closed is not among the next session's t…
Sunrisepeak Oct 12, 2026
873004b
build: GlobalMemoryStatusEx and DeviceIoControl join the platform pac…
Sunrisepeak Oct 12, 2026
9a605f4
conformance: reask 'incomplete' waits for an answer on its way, as VS…
Sunrisepeak Oct 12, 2026
b8c80ee
0.0.14 G1: a file the editor already has is known by its path, not by…
Sunrisepeak Oct 12, 2026
553664c
ci: test with mcppls-clangd PR #7's build (run 38268673872), the 0.0.…
Sunrisepeak Oct 12, 2026
0d2b61c
0.0.14 G1: a file the editor opens while clangd is still building it …
Sunrisepeak Oct 12, 2026
982a516
ci: test with mcppls-clangd PR #7's build run 38281740153 (ece332f): …
Sunrisepeak Oct 12, 2026
df525fb
conformance: U17 bounds the completions asked per typed word; those a…
Sunrisepeak Oct 12, 2026
d200515
build: the payload pins the released mcppls-clangd v0.0.15 (23.1.0-mc…
Sunrisepeak Oct 12, 2026
d04abc2
docs: the plan says what is left for the next round: package trees in…
Sunrisepeak Oct 12, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
475 changes: 475 additions & 0 deletions .agents/docs/2026-10-12-windows-performance-plan.md

Large diffs are not rendered by default.

31 changes: 31 additions & 0 deletions .github/workflows/release-checks.yml
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,37 @@ jobs:
done
"stage/mcppls-devtools${{ matrix.exe }}" measure summary "$results" --max-cold 12 --max-warm 5 \
| tee "$results/summary.md" >> "$GITHUB_STEP_SUMMARY"
# 0.0.14 Windows plan, phase 0: a project the size where module checks cost (thirty module units with
# <windows.h>, built by MSVC), opened cold and then warm with the same cache. Each run's measures and the
# server's log are files of their own, and the step summary says what the two runs measured.
- name: The MSVC developer environment, for the large fixture's own build
if: runner.os == 'Windows'
shell: pwsh
run: |
$vs = & "${env:ProgramFiles(x86)}\Microsoft Visual Studio\Installer\vswhere.exe" -latest -products * -requires Microsoft.VisualStudio.Component.VC.Tools.x86.x64 -property installationPath
Set-Content -Path "$env:RUNNER_TEMP\capture-msvc.cmd" -Value "@call `"$vs\VC\Auxiliary\Build\vcvars64.bat`" >nul`r`n@set"
cmd /d /c "$env:RUNNER_TEMP\capture-msvc.cmd" | Out-File -Encoding utf8 "$env:RUNNER_TEMP\msvc.env"
- name: A large module project, cold and warm
if: runner.os == 'Windows'
run: |
set -euo pipefail
large="$RUNNER_TEMP/timing/large"
mkdir -p "$large"
for start in cold warm; do
warm=(); [ "$start" = warm ] && warm=(--expect-warm)
status=0
"stage/mcppls-conformance${{ matrix.exe }}" run --server "payload/bin/mcppls${{ matrix.exe }}" --payload payload \
--fixture conformance/fixtures/ux-modules-windows-large --msvc-env "$RUNNER_TEMP/msvc.env" \
--workspace-dir "$RUNNER_TEMP/large/workspace" --cache-dir "$RUNNER_TEMP/large/cache" \
--timeout 900 --measure "$large/$start.json" --verbose ${warm[@]+"${warm[@]}"} > "$large/$start.log" 2>&1 || status=$?
grep -E '^(PASS|FAIL) |failure\(s\)' "$large/$start.log" > "$large/$start-checks.txt" || true
grep -E "Prerequisites of |Completion in .*: [0-9]+ ms|Module memos: |Module inputs: |ahead of the editor|ready in clangd|startup \(" "$large/$start.log" \
| sed -E 's/^.*\[(info|warning)\] //' > "$large/$start-costs.txt" || true
{ echo "### Large module project, $start"; echo; echo '```'; cat "$large/$start-checks.txt"; echo '```'; } >> "$large/summary.md"
[ "$status" = 0 ] || echo "::warning::the large fixture's $start run failed: see prerelease-timing-${{ matrix.platform }}/large/$start-checks.txt"
done
cat "$large/summary.md" >> "$GITHUB_STEP_SUMMARY"
! grep -q '^FAIL ' "$large/cold-checks.txt" "$large/warm-checks.txt"
- uses: actions/upload-artifact@v7
if: always()
with:
Expand Down
46 changes: 46 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,52 @@ release's notes are that section.
Versions are three-part semantic versions, `MAJOR.MINOR.PATCH`, and every editor plugin carries the
product version unchanged.

## [0.0.14] — 2026-10-13

The bundled engine is [mcppls-clangd 0.0.15](https://github.com/Sunrisepeak/mcppls-clangd/releases/tag/v0.0.15)
(`23.1.0-mcppls.3`): 32 patches, three of them new. This release is about Windows: opening a project again and completion
as you type, where every check that a module is still good is a file operation that opens the file.

### Completion as you type

- **A module that includes `<windows.h>` no longer carries all of it.** A module's BMI kept every declaration of an
`extern "C"` block its global module fragment includes, used or not, and nearly all of `<windows.h>` -- and of a C
library's headers -- is in such blocks. The engine leaves the unused ones out, as it already did for a namespace's: a
module unit that includes `<windows.h>` builds a BMI of kilobytes instead of megabytes, and a completion in a file
that imports many of them reads only what they use (`::` in a file importing 25 of them: 2.2 s before, 0.3 s now).
- **A completion list comes whole.** clangd gives up to 1000 items instead of 100, so a list whose matches fit is
complete and the editor filters it as you type instead of asking again at every key. A client that asks at every key
anyway is answered from the word's complete list at once. In a file whose modules include Qt, the requests while a
word is typed went down by about two fifths.
- **Checking a module costs less where its headers come from a package or the system.** A header of an mcpp or xlings
package version -- the toolchain's own, such as llvm-mingw's `<windows.h>`, or a library's -- is checked by the
version's install markers instead of one by one; system headers, the Windows SDK's and Visual Studio's included, once
per session, as clang itself skips them. Your project's files are checked at every request, as before.
- **On Windows, preambles are kept in memory** with 16 GiB or more, instead of being written to the temporary
directory and scanned there. New setting `mcppls.engine.preambleStorage` (`auto`, `disk`, `memory`).

### Opening a project again

- **What the last session learned is kept.** Which BMIs were up to date and what each module unit imports are written to
the workspace cache and read back by the next session, each checked again before it is used, so a second opening
checks instead of hashing every input and preprocessing every module unit again.
- **The files you last worked in are ready first.** The five files last opened or edited are opened in clangd as soon as
it starts, before the editor asks; the editor takes them over as they are. With 16 GiB or more, six ASTs of files you
switch between are kept instead of three.

### Seeing what happens

- A slow completion (200 ms or more) in a module importer says in the log where its time went; the log says what the
last session left and which package stores are checked by their markers. On Windows, a cache that is not on a Dev
Drive is said once, in the log and the overview (`about.cacheDevDrive`); see "Windows: a cache on a Dev Drive" in the
troubleshooting guide. mcppls changes no security setting.

### Fixes

- A cache sweep asked for by category (`mcppls.sweepCache` with `categories`) ran none of them.
- A completion clangd answered late was not let go of when its word was given up.
- Two test programs never reported their failures; they do now.

## [0.0.13] — 2026-10-12

The bundled engine is [mcppls-clangd 0.0.14](https://github.com/Sunrisepeak/mcppls-clangd/releases/tag/v0.0.14)
Expand Down
29 changes: 29 additions & 0 deletions conformance/fixtures/ux-modules-qt/scenario.json
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,35 @@
"diagnosticsRefresh": 6.0
}
},
{
"id": "U17-words-as-typed",
"kind": "typing",
"file": "src/main.cpp",
"seconds": 45,
"hz": 10,
"completion-every-ms": 0,
"reask": "incomplete",
"description": "0.0.14 A1, A2: completion asked as VS Code asks it while a word is typed in a file whose module units include Qt headers: the word's first character asks; the keys typed while that answer is on its way wait for it, and once it is in the next key asks again only when it was marked incomplete. With clangd's lists complete (--limit-results), a word costs one or two requests, and each answers in time.",
"scripts": [
{
"line": 12,
"text": "QStringList names = inventory.itemNames(); QString joined = names.join(QStringLiteral(\",\")); window.setWindowTitle(joined);"
},
{
"line": 12,
"text": "QTimer timer; timer.setInterval(100); QObject::connect(&timer, &QTimer::timeout, &app, [&] { window.showCount(inventory.rowCount()); });"
}
],
"budget": {
"firstOpenSeconds": 120,
"p95": 1.0,
"max": 3.0,
"timeouts": 0,
"restarts": 0,
"filesSetAside": 0,
"askedAsTypedPerWord": 2.0
}
},
{
"id": "U16-module-autosave",
"kind": "typing",
Expand Down
13 changes: 13 additions & 0 deletions conformance/fixtures/ux-modules-windows-large/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
cmake_minimum_required(VERSION 3.28)
project(bigwin LANGUAGES CXX)

set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)

add_library(big)
target_sources(big PUBLIC FILE_SET CXX_MODULES FILES src/m00.cppm src/m01.cppm src/m02.cppm src/m03.cppm src/m04.cppm src/m05.cppm src/m06.cppm src/m07.cppm src/m08.cppm src/m09.cppm src/m10.cppm src/m11.cppm src/m12.cppm src/m13.cppm src/m14.cppm src/m15.cppm src/m16.cppm src/m17.cppm src/m18.cppm src/m19.cppm src/m20.cppm src/m21.cppm src/m22.cppm src/m23.cppm src/m24.cppm src/m25.cppm src/m26.cppm src/m27.cppm src/m28.cppm src/m29.cppm)
target_compile_definitions(big PUBLIC UNICODE _UNICODE NOMINMAX WIN32_LEAN_AND_MEAN)

add_executable(app src/main.cpp)
target_link_libraries(app PRIVATE big)
131 changes: 131 additions & 0 deletions conformance/fixtures/ux-modules-windows-large/scenario.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
{
"name": "ux-modules-windows-large",
"description": "0.0.14 Windows plan, phase 0: a Windows program the size where module checks cost -- thirty module units whose global module fragments include <windows.h>, in chains four deep, and a main.cpp that includes <windows.h> and imports twenty-five of them -- built by CMake with MSVC (cl.exe) and Ninja. Run cold and then warm with the same cache directory (the release checks' performance job): first navigation, idle latency, completion asked as a word is typed, and on the warm run the module files reused. Budgets are the first measurements' with room; they hold what the plan's work must not undo.",
"prepare-environment": "msvc",
"prepare": [
[
"cmake",
"-S",
".",
"-B",
"build",
"-G",
"Ninja",
"-DCMAKE_CXX_COMPILER=cl",
"-DCMAKE_EXPORT_COMPILE_COMMANDS=ON"
],
[
"cmake",
"--build",
"build"
]
],
"checks": [
{
"id": "S1",
"kind": "status",
"source": "cmake",
"profile-kind": "build-toolchain",
"state": "ready",
"timeout": 600
},
{
"id": "C1",
"kind": "diagnostics-empty",
"file": "src/main.cpp",
"timeout": 900
},
{
"id": "T1-definition-into-module",
"kind": "definition",
"file": "src/main.cpp",
"at": [
29,
24
],
"expect": "src/m29.cppm",
"timeout": 120
},
{
"id": "W1-modules-reused",
"kind": "module-cache-reused",
"module": "big.m29"
},
{
"id": "U3-idle",
"kind": "latency",
"phase": "idle",
"rounds": 20,
"requests": [
{
"kind": "completion",
"name": "module-member",
"file": "src/main.cpp",
"at": [
29,
23
]
},
{
"kind": "completion",
"name": "windows-api",
"file": "src/main.cpp",
"at": [
32,
6
]
},
{
"kind": "hover",
"file": "src/main.cpp",
"at": [
29,
24
]
},
{
"kind": "definition",
"file": "src/main.cpp",
"at": [
29,
24
],
"expect": "src/m29.cppm"
}
],
"budget": {
"completion.p95": 3.0,
"hover.p95": 1.5,
"definition.p95": 2.0,
"engineShare": 0.8
}
},
{
"id": "U17-words-as-typed",
"kind": "typing",
"file": "src/main.cpp",
"seconds": 45,
"hz": 10,
"completion-every-ms": 0,
"reask": "incomplete",
"scripts": [
{
"line": 33,
"text": "HANDLE handle = CreateFileW(nullptr, GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr);"
},
{
"line": 33,
"text": "total += m28::value() + m27::value(); m26::Window26 other; if (other.visible()) total += m25::ticks() % 3;"
}
],
"budget": {
"firstOpenSeconds": 300,
"p95": 2.0,
"max": 5.0,
"timeouts": 0,
"restarts": 0,
"filesSetAside": 0
}
}
]
}
9 changes: 9 additions & 0 deletions conformance/fixtures/ux-modules-windows-large/src/m00.cppm
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
module;
#include <windows.h>
export module big.m00;

export namespace m00 {
inline DWORD value() { return ::GetCurrentProcessId() % 7; }
inline ULONGLONG ticks() { return ::GetTickCount64(); }
struct Window00 { HWND handle = nullptr; bool visible() const { return handle != nullptr; } };
} // namespace m00
10 changes: 10 additions & 0 deletions conformance/fixtures/ux-modules-windows-large/src/m01.cppm
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
module;
#include <windows.h>
export module big.m01;
import big.m00;

export namespace m01 {
inline DWORD value() { return ::GetCurrentProcessId() % 7 + m00::value(); }
inline ULONGLONG ticks() { return ::GetTickCount64(); }
struct Window01 { HWND handle = nullptr; bool visible() const { return handle != nullptr; } };
} // namespace m01
10 changes: 10 additions & 0 deletions conformance/fixtures/ux-modules-windows-large/src/m02.cppm
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
module;
#include <windows.h>
export module big.m02;
import big.m01;

export namespace m02 {
inline DWORD value() { return ::GetCurrentProcessId() % 7 + m01::value(); }
inline ULONGLONG ticks() { return ::GetTickCount64(); }
struct Window02 { HWND handle = nullptr; bool visible() const { return handle != nullptr; } };
} // namespace m02
10 changes: 10 additions & 0 deletions conformance/fixtures/ux-modules-windows-large/src/m03.cppm
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
module;
#include <windows.h>
export module big.m03;
import big.m02;

export namespace m03 {
inline DWORD value() { return ::GetCurrentProcessId() % 7 + m02::value(); }
inline ULONGLONG ticks() { return ::GetTickCount64(); }
struct Window03 { HWND handle = nullptr; bool visible() const { return handle != nullptr; } };
} // namespace m03
10 changes: 10 additions & 0 deletions conformance/fixtures/ux-modules-windows-large/src/m04.cppm
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
module;
#include <windows.h>
export module big.m04;
import big.m03;

export namespace m04 {
inline DWORD value() { return ::GetCurrentProcessId() % 7 + m03::value(); }
inline ULONGLONG ticks() { return ::GetTickCount64(); }
struct Window04 { HWND handle = nullptr; bool visible() const { return handle != nullptr; } };
} // namespace m04
9 changes: 9 additions & 0 deletions conformance/fixtures/ux-modules-windows-large/src/m05.cppm
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
module;
#include <windows.h>
export module big.m05;

export namespace m05 {
inline DWORD value() { return ::GetCurrentProcessId() % 7; }
inline ULONGLONG ticks() { return ::GetTickCount64(); }
struct Window05 { HWND handle = nullptr; bool visible() const { return handle != nullptr; } };
} // namespace m05
10 changes: 10 additions & 0 deletions conformance/fixtures/ux-modules-windows-large/src/m06.cppm
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
module;
#include <windows.h>
export module big.m06;
import big.m05;

export namespace m06 {
inline DWORD value() { return ::GetCurrentProcessId() % 7 + m05::value(); }
inline ULONGLONG ticks() { return ::GetTickCount64(); }
struct Window06 { HWND handle = nullptr; bool visible() const { return handle != nullptr; } };
} // namespace m06
10 changes: 10 additions & 0 deletions conformance/fixtures/ux-modules-windows-large/src/m07.cppm
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
module;
#include <windows.h>
export module big.m07;
import big.m06;

export namespace m07 {
inline DWORD value() { return ::GetCurrentProcessId() % 7 + m06::value(); }
inline ULONGLONG ticks() { return ::GetTickCount64(); }
struct Window07 { HWND handle = nullptr; bool visible() const { return handle != nullptr; } };
} // namespace m07
10 changes: 10 additions & 0 deletions conformance/fixtures/ux-modules-windows-large/src/m08.cppm
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
module;
#include <windows.h>
export module big.m08;
import big.m07;

export namespace m08 {
inline DWORD value() { return ::GetCurrentProcessId() % 7 + m07::value(); }
inline ULONGLONG ticks() { return ::GetTickCount64(); }
struct Window08 { HWND handle = nullptr; bool visible() const { return handle != nullptr; } };
} // namespace m08
10 changes: 10 additions & 0 deletions conformance/fixtures/ux-modules-windows-large/src/m09.cppm
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
module;
#include <windows.h>
export module big.m09;
import big.m08;

export namespace m09 {
inline DWORD value() { return ::GetCurrentProcessId() % 7 + m08::value(); }
inline ULONGLONG ticks() { return ::GetTickCount64(); }
struct Window09 { HWND handle = nullptr; bool visible() const { return handle != nullptr; } };
} // namespace m09
9 changes: 9 additions & 0 deletions conformance/fixtures/ux-modules-windows-large/src/m10.cppm
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
module;
#include <windows.h>
export module big.m10;

export namespace m10 {
inline DWORD value() { return ::GetCurrentProcessId() % 7; }
inline ULONGLONG ticks() { return ::GetTickCount64(); }
struct Window10 { HWND handle = nullptr; bool visible() const { return handle != nullptr; } };
} // namespace m10
Loading
Loading