Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
162 changes: 162 additions & 0 deletions pgo.ps1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
# PGO (Profile-Guided Optimization) training script for Node.js (Clang / LLVM)
#
# Runs PGO training workloads against an instrumented Node.js binary
# (Release\node.exe) and merges the resulting .profraw files into
# node.profdata for use with -fprofile-use.
#
# Usage (from a VS Developer Command Prompt):
# .\pgo.ps1 # Run workloads (15s each) and merge
# .\pgo.ps1 -Duration 30 # Run workloads (30s each) and merge
#
# Prerequisites:
# - Release\node.exe must be an instrumented build (built with pgo-generate)
# - llvm-profdata must be available (shipped with VS LLVM toolset)
#
# Output:
# - node.profdata in the repo root (ready for vcbuild.bat pgo-use)

param(
[int]$Duration = 15
)

Set-StrictMode -Version Latest
$ErrorActionPreference = 'Stop'

# ---------------------------------------------------------------------------
# Locate llvm-profdata shipped with Visual Studio's LLVM toolset
# ---------------------------------------------------------------------------

function Find-LlvmProfdata {
# vcbuild.bat uses %VCINSTALLDIR%\Tools\Llvm\x64\bin for clang.exe - same spot for profdata
$vcInstallDir = $env:VCINSTALLDIR

if ($vcInstallDir) {
$candidate = Join-Path $vcInstallDir "Tools\Llvm\x64\bin\llvm-profdata.exe"
if (Test-Path $candidate) {
return $candidate
}
}

# Fallback: try VS 2022 / 2026 default install locations
$vsPaths = @(
"${env:ProgramFiles}\Microsoft Visual Studio\2026\Enterprise\VC\Tools\Llvm\x64\bin",
"${env:ProgramFiles}\Microsoft Visual Studio\2026\Community\VC\Tools\Llvm\x64\bin",
"${env:ProgramFiles}\Microsoft Visual Studio\2022\Enterprise\VC\Tools\Llvm\x64\bin",
"${env:ProgramFiles}\Microsoft Visual Studio\2022\Community\VC\Tools\Llvm\x64\bin"
)
foreach ($dir in $vsPaths) {
$candidate = Join-Path $dir "llvm-profdata.exe"
if (Test-Path $candidate) {
return $candidate
}
}

# Last resort: PATH
$fromPath = Get-Command llvm-profdata -ErrorAction SilentlyContinue
if ($fromPath) {
return $fromPath.Source
}

return $null
}

# ---------------------------------------------------------------------------
# Validate prerequisites
# ---------------------------------------------------------------------------

$instrumentedNode = Join-Path $PSScriptRoot "Release\node.exe"
if (-not (Test-Path $instrumentedNode)) {
Write-Error "Instrumented binary not found: $instrumentedNode`nBuild with: vcbuild.bat pgo-generate"
exit 1
}

$pgoRunAll = Join-Path $PSScriptRoot "tools\pgo\pgo-run-all.js"
if (-not (Test-Path $pgoRunAll)) {
Write-Error "PGO training script not found: $pgoRunAll"
exit 1
}

$llvmProfdata = Find-LlvmProfdata
if (-not $llvmProfdata) {
Write-Error "llvm-profdata not found. Install the LLVM toolset via Visual Studio Installer."
exit 1
}

# ---------------------------------------------------------------------------
# STEP 1 – Run workloads with the instrumented binary to collect profiles
# ---------------------------------------------------------------------------

Write-Host "`n=== STEP 1: Collect PGO profiles ===" -ForegroundColor Cyan

# Directory that will receive .profraw files from the instrumented binary.
# %p (PID) and %m (module hash) keep concurrent/fork'd processes from colliding.
$profileDir = Join-Path $PSScriptRoot "pgo-profiles"

if (Test-Path $profileDir) {
Remove-Item -Recurse -Force $profileDir
}
New-Item -ItemType Directory -Path $profileDir | Out-Null

$env:LLVM_PROFILE_FILE = Join-Path $profileDir "node-%p-%m.profraw"

Write-Host "Instrumented node : $instrumentedNode"
Write-Host "Profile output : $($env:LLVM_PROFILE_FILE)"
Write-Host "Duration per script: ${Duration}s"
Write-Host ""

$sw = [System.Diagnostics.Stopwatch]::StartNew()
$proc = Start-Process `
-FilePath $instrumentedNode `
-ArgumentList "`"$pgoRunAll`" --verbose --duration=$Duration" `
-Wait -PassThru -NoNewWindow
$sw.Stop()
Write-Host ("PGO training completed in {0}m {1}s (exit code: {2})" -f `
$sw.Elapsed.Minutes, $sw.Elapsed.Seconds, $proc.ExitCode)
if ($proc.ExitCode -ne 0) {
Write-Warning "PGO training exited with code $($proc.ExitCode) - continuing with merge"
}

# Remove the env var so subsequent builds are not affected
Remove-Item Env:\LLVM_PROFILE_FILE -ErrorAction SilentlyContinue

# ---------------------------------------------------------------------------
# STEP 2 – Merge .profraw files -> node.profdata
# ---------------------------------------------------------------------------

Write-Host "`n=== STEP 2: Merge profile data ===" -ForegroundColor Cyan

Write-Host "Using llvm-profdata: $llvmProfdata"

$profrawFiles = Get-ChildItem -Path $profileDir -Filter "*.profraw" -ErrorAction SilentlyContinue
if ($profrawFiles.Count -eq 0) {
Write-Error "No .profraw files found in '$profileDir'. The instrumented binary may not have generated profile data."
exit 1
}

$totalSize = ($profrawFiles | Measure-Object -Property Length -Sum).Sum
$totalSizeMB = [math]::Round($totalSize / 1MB, 1)
Write-Host "Found $($profrawFiles.Count) .profraw file(s), ${totalSizeMB} MB total"

$profdata = Join-Path $PSScriptRoot "node.profdata"
$mergeArgs = @("merge", "--output=$profdata") + ($profrawFiles | Select-Object -ExpandProperty FullName)

$mergeStopwatch = [System.Diagnostics.Stopwatch]::StartNew()
& $llvmProfdata @mergeArgs
$mergeExitCode = $LASTEXITCODE
$mergeStopwatch.Stop()

if ($mergeExitCode -ne 0) {
Write-Error "llvm-profdata merge failed (exit code $mergeExitCode)"
exit $mergeExitCode
}

$profdataSize = [math]::Round((Get-Item $profdata).Length / 1MB, 1)
Write-Host "Merge completed in $([math]::Round($mergeStopwatch.Elapsed.TotalSeconds, 1))s"

# Clean up .profraw files now that they've been merged
Remove-Item -Recurse -Force $profileDir
Write-Host "Removed $($profrawFiles.Count) .profraw file(s) (${totalSizeMB} MB reclaimed)"

Write-Host "`n=== PGO training complete ===" -ForegroundColor Green
Write-Host " Profile data: $profdata (${profdataSize} MB)"
Write-Host " Next step: vcbuild.bat pgo-use"
98 changes: 98 additions & 0 deletions tools/pgo/README.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
# Node.js PGO Training Scripts

Training workloads for Profile-Guided Optimization (PGO) builds using
Clang/LLVM (including Clang-CL on Windows).

## What is PGO?

PGO uses runtime profile data to guide compiler optimizations (inlining,
branch prediction, code layout), typically improving throughput by 5-20%.

The process has three phases:

1. **Instrument** — Build with `-fprofile-generate` (produces `.profraw` files)
2. **Train** — Run representative workloads to collect profile data
3. **Optimize** — Merge `.profraw` → `node.profdata` via `llvm-profdata`,
then rebuild with `-fprofile-use`

## Quick Start

From a VS Developer Command Prompt:

```powershell
# Step 1: Build the instrumented binary
vcbuild.bat pgo-generate

# Step 2: Run workloads and merge profile data
.\pgo.ps1

# Step 3: Build the optimized binary
vcbuild.bat pgo-use
```

`pgo.ps1` expects the instrumented binary at `Release\node.exe` (produced by
step 1) and writes `node.profdata` to the repo root (consumed by step 3).

```powershell
# Optionally set a longer training duration (default: 15s per script)
.\pgo.ps1 -Duration 30
```

## Training Scripts

All scripts use only Node.js built-in modules (no npm dependencies).
Each script is run as a separate process via `fork()`, producing its own
`.profraw` file.

| Script | What it exercises |
| ------------------------ | ------------------------------------------------------------- |
| `pgo-http-server.js` | llhttp parser, TCP stack, header serialization, JSON, routing |
| `pgo-json.js` | V8 JSON parser/serializer, string allocation, GC pressure |
| `pgo-crypto.js` | OpenSSL (hashing, HMAC, AES, RSA, ECDSA, random, KDF) |
| `pgo-streams-buffers.js` | Buffer C++ impl, stream state machine, back-pressure |
| `pgo-fs.js` | libuv fs operations, thread pool, path module |
| `pgo-async-patterns.js` | V8 Promises, microtask queue, EventEmitter, timers |
| `pgo-url-string.js` | Ada URL parser, V8 string internals, regex JIT |
| `pgo-compression.js` | zlib, brotli C libraries, streaming compression |
| `pgo-net.js` | libuv TCP/pipe handles, c-ares DNS resolver |
| `pgo-module-loading.js` | Module resolver, V8 script compilation, vm module |
| `pgo-child-workers.js` | Worker thread messaging, SharedArrayBuffer, inline eval |

### Running the Orchestrator Directly

The orchestrator can also be invoked directly (e.g. for testing individual
workloads). When used with `pgo.ps1`, this is handled automatically.

```bash
# Run all scripts
node tools/pgo/pgo-run-all.js --duration=15 --verbose

# Run specific scripts
node tools/pgo/pgo-run-all.js --scripts=http-server,json,crypto --duration=30

# Show help
node tools/pgo/pgo-run-all.js --help
```

Each script reads the `PGO_TRAINING_DURATION` environment variable (in
milliseconds) to determine how long to run. The orchestrator sets this
automatically from the `--duration` flag (in seconds).

## Files

```
tools/pgo/
├── pgo-run-all.js # Training orchestrator
├── pgo-http-server.js # HTTP server + client workload
├── pgo-json.js # JSON parse/stringify workload
├── pgo-crypto.js # Crypto operations workload
├── pgo-streams-buffers.js # Streams and Buffer workload
├── pgo-fs.js # File system operations workload
├── pgo-async-patterns.js # Promise/async, EventEmitter, timers workload
├── pgo-url-string.js # URL parsing, string ops, regex workload
├── pgo-compression.js # Gzip/brotli/deflate compression workload
├── pgo-net.js # TCP networking and DNS workload
├── pgo-module-loading.js # Module require/import, VM compilation workload
├── pgo-child-workers.js # Worker threads workload
└── README.md # This file
```
Loading
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
162 changes: 162 additions & 0 deletions pgo.ps1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
# PGO (Profile-Guided Optimization) training script for Node.js (Clang / LLVM)
#
# Runs PGO training workloads against an instrumented Node.js binary
# (Release\node.exe) and merges the resulting .profraw files into
# node.profdata for use with -fprofile-use.
#
# Usage (from a VS Developer Command Prompt):
# .\pgo.ps1 # Run workloads (15s each) and merge
# .\pgo.ps1 -Duration 30 # Run workloads (30s each) and merge
#
# Prerequisites:
# - Release\node.exe must be an instrumented build (built with pgo-generate)
# - llvm-profdata must be available (shipped with VS LLVM toolset)
#
# Output:
# - node.profdata in the repo root (ready for vcbuild.bat pgo-use)

param(
[int]$Duration = 15
)

Set-StrictMode -Version Latest
$ErrorActionPreference = 'Stop'

# ---------------------------------------------------------------------------
# Locate llvm-profdata shipped with Visual Studio's LLVM toolset
# ---------------------------------------------------------------------------

function Find-LlvmProfdata {
# vcbuild.bat uses %VCINSTALLDIR%\Tools\Llvm\x64\bin for clang.exe - same spot for profdata
$vcInstallDir = $env:VCINSTALLDIR

if ($vcInstallDir) {
$candidate = Join-Path $vcInstallDir "Tools\Llvm\x64\bin\llvm-profdata.exe"
if (Test-Path $candidate) {
return $candidate
}
}

# Fallback: try VS 2022 / 2026 default install locations
$vsPaths = @(
"${env:ProgramFiles}\Microsoft Visual Studio\2026\Enterprise\VC\Tools\Llvm\x64\bin",
"${env:ProgramFiles}\Microsoft Visual Studio\2026\Community\VC\Tools\Llvm\x64\bin",
"${env:ProgramFiles}\Microsoft Visual Studio\2022\Enterprise\VC\Tools\Llvm\x64\bin",
"${env:ProgramFiles}\Microsoft Visual Studio\2022\Community\VC\Tools\Llvm\x64\bin"
)
foreach ($dir in $vsPaths) {
$candidate = Join-Path $dir "llvm-profdata.exe"
if (Test-Path $candidate) {
return $candidate
}
}

# Last resort: PATH
$fromPath = Get-Command llvm-profdata -ErrorAction SilentlyContinue
if ($fromPath) {
return $fromPath.Source
}

return $null
}

# ---------------------------------------------------------------------------
# Validate prerequisites
# ---------------------------------------------------------------------------

$instrumentedNode = Join-Path $PSScriptRoot "Release\node.exe"
if (-not (Test-Path $instrumentedNode)) {
Write-Error "Instrumented binary not found: $instrumentedNode`nBuild with: vcbuild.bat pgo-generate"
exit 1
}

$pgoRunAll = Join-Path $PSScriptRoot "tools\pgo\pgo-run-all.js"
if (-not (Test-Path $pgoRunAll)) {
Write-Error "PGO training script not found: $pgoRunAll"
exit 1
}

$llvmProfdata = Find-LlvmProfdata
if (-not $llvmProfdata) {
Write-Error "llvm-profdata not found. Install the LLVM toolset via Visual Studio Installer."
exit 1
}

# ---------------------------------------------------------------------------
# STEP 1 – Run workloads with the instrumented binary to collect profiles
# ---------------------------------------------------------------------------

Write-Host "`n=== STEP 1: Collect PGO profiles ===" -ForegroundColor Cyan

# Directory that will receive .profraw files from the instrumented binary.
# %p (PID) and %m (module hash) keep concurrent/fork'd processes from colliding.
$profileDir = Join-Path $PSScriptRoot "pgo-profiles"

if (Test-Path $profileDir) {
Remove-Item -Recurse -Force $profileDir
}
New-Item -ItemType Directory -Path $profileDir | Out-Null

$env:LLVM_PROFILE_FILE = Join-Path $profileDir "node-%p-%m.profraw"

Write-Host "Instrumented node : $instrumentedNode"
Write-Host "Profile output : $($env:LLVM_PROFILE_FILE)"
Write-Host "Duration per script: ${Duration}s"
Write-Host ""

$sw = [System.Diagnostics.Stopwatch]::StartNew()
$proc = Start-Process `
-FilePath $instrumentedNode `
-ArgumentList "`"$pgoRunAll`" --verbose --duration=$Duration" `
-Wait -PassThru -NoNewWindow
$sw.Stop()
Write-Host ("PGO training completed in {0}m {1}s (exit code: {2})" -f `
$sw.Elapsed.Minutes, $sw.Elapsed.Seconds, $proc.ExitCode)
if ($proc.ExitCode -ne 0) {
Write-Warning "PGO training exited with code $($proc.ExitCode) - continuing with merge"
}

# Remove the env var so subsequent builds are not affected
Remove-Item Env:\LLVM_PROFILE_FILE -ErrorAction SilentlyContinue

# ---------------------------------------------------------------------------
# STEP 2 – Merge .profraw files -> node.profdata
# ---------------------------------------------------------------------------

Write-Host "`n=== STEP 2: Merge profile data ===" -ForegroundColor Cyan

Write-Host "Using llvm-profdata: $llvmProfdata"

$profrawFiles = Get-ChildItem -Path $profileDir -Filter "*.profraw" -ErrorAction SilentlyContinue
if ($profrawFiles.Count -eq 0) {
Write-Error "No .profraw files found in '$profileDir'. The instrumented binary may not have generated profile data."
exit 1
}

$totalSize = ($profrawFiles | Measure-Object -Property Length -Sum).Sum
$totalSizeMB = [math]::Round($totalSize / 1MB, 1)
Write-Host "Found $($profrawFiles.Count) .profraw file(s), ${totalSizeMB} MB total"

$profdata = Join-Path $PSScriptRoot "node.profdata"
$mergeArgs = @("merge", "--output=$profdata") + ($profrawFiles | Select-Object -ExpandProperty FullName)

$mergeStopwatch = [System.Diagnostics.Stopwatch]::StartNew()
& $llvmProfdata @mergeArgs
$mergeExitCode = $LASTEXITCODE
$mergeStopwatch.Stop()

if ($mergeExitCode -ne 0) {
Write-Error "llvm-profdata merge failed (exit code $mergeExitCode)"
exit $mergeExitCode
}

$profdataSize = [math]::Round((Get-Item $profdata).Length / 1MB, 1)
Write-Host "Merge completed in $([math]::Round($mergeStopwatch.Elapsed.TotalSeconds, 1))s"

# Clean up .profraw files now that they've been merged
Remove-Item -Recurse -Force $profileDir
Write-Host "Removed $($profrawFiles.Count) .profraw file(s) (${totalSizeMB} MB reclaimed)"

Write-Host "`n=== PGO training complete ===" -ForegroundColor Green
Write-Host " Profile data: $profdata (${profdataSize} MB)"
Write-Host " Next step: vcbuild.bat pgo-use"
98 changes: 98 additions & 0 deletions tools/pgo/README.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
# Node.js PGO Training Scripts

Training workloads for Profile-Guided Optimization (PGO) builds using
Clang/LLVM (including Clang-CL on Windows).

## What is PGO?

PGO uses runtime profile data to guide compiler optimizations (inlining,
branch prediction, code layout), typically improving throughput by 5-20%.

The process has three phases:

1. **Instrument** — Build with `-fprofile-generate` (produces `.profraw` files)
2. **Train** — Run representative workloads to collect profile data
3. **Optimize** — Merge `.profraw` → `node.profdata` via `llvm-profdata`,
then rebuild with `-fprofile-use`

## Quick Start

From a VS Developer Command Prompt:

```powershell
# Step 1: Build the instrumented binary
vcbuild.bat pgo-generate

# Step 2: Run workloads and merge profile data
.\pgo.ps1

# Step 3: Build the optimized binary
vcbuild.bat pgo-use
```

`pgo.ps1` expects the instrumented binary at `Release\node.exe` (produced by
step 1) and writes `node.profdata` to the repo root (consumed by step 3).

```powershell
# Optionally set a longer training duration (default: 15s per script)
.\pgo.ps1 -Duration 30
```

## Training Scripts

All scripts use only Node.js built-in modules (no npm dependencies).
Each script is run as a separate process via `fork()`, producing its own
`.profraw` file.

| Script | What it exercises |
| ------------------------ | ------------------------------------------------------------- |
| `pgo-http-server.js` | llhttp parser, TCP stack, header serialization, JSON, routing |
| `pgo-json.js` | V8 JSON parser/serializer, string allocation, GC pressure |
| `pgo-crypto.js` | OpenSSL (hashing, HMAC, AES, RSA, ECDSA, random, KDF) |
| `pgo-streams-buffers.js` | Buffer C++ impl, stream state machine, back-pressure |
| `pgo-fs.js` | libuv fs operations, thread pool, path module |
| `pgo-async-patterns.js` | V8 Promises, microtask queue, EventEmitter, timers |
| `pgo-url-string.js` | Ada URL parser, V8 string internals, regex JIT |
| `pgo-compression.js` | zlib, brotli C libraries, streaming compression |
| `pgo-net.js` | libuv TCP/pipe handles, c-ares DNS resolver |
| `pgo-module-loading.js` | Module resolver, V8 script compilation, vm module |
| `pgo-child-workers.js` | Worker thread messaging, SharedArrayBuffer, inline eval |

### Running the Orchestrator Directly

The orchestrator can also be invoked directly (e.g. for testing individual
workloads). When used with `pgo.ps1`, this is handled automatically.

```bash
# Run all scripts
node tools/pgo/pgo-run-all.js --duration=15 --verbose

# Run specific scripts
node tools/pgo/pgo-run-all.js --scripts=http-server,json,crypto --duration=30

# Show help
node tools/pgo/pgo-run-all.js --help
```

Each script reads the `PGO_TRAINING_DURATION` environment variable (in
milliseconds) to determine how long to run. The orchestrator sets this
automatically from the `--duration` flag (in seconds).

## Files

```
tools/pgo/
├── pgo-run-all.js # Training orchestrator
├── pgo-http-server.js # HTTP server + client workload
├── pgo-json.js # JSON parse/stringify workload
├── pgo-crypto.js # Crypto operations workload
├── pgo-streams-buffers.js # Streams and Buffer workload
├── pgo-fs.js # File system operations workload
├── pgo-async-patterns.js # Promise/async, EventEmitter, timers workload
├── pgo-url-string.js # URL parsing, string ops, regex workload
├── pgo-compression.js # Gzip/brotli/deflate compression workload
├── pgo-net.js # TCP networking and DNS workload
├── pgo-module-loading.js # Module require/import, VM compilation workload
├── pgo-child-workers.js # Worker threads workload
└── README.md # This file
```
Loading
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
162 changes: 162 additions & 0 deletions pgo.ps1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
# PGO (Profile-Guided Optimization) training script for Node.js (Clang / LLVM)
#
# Runs PGO training workloads against an instrumented Node.js binary
# (Release\node.exe) and merges the resulting .profraw files into
# node.profdata for use with -fprofile-use.
#
# Usage (from a VS Developer Command Prompt):
# .\pgo.ps1 # Run workloads (15s each) and merge
# .\pgo.ps1 -Duration 30 # Run workloads (30s each) and merge
#
# Prerequisites:
# - Release\node.exe must be an instrumented build (built with pgo-generate)
# - llvm-profdata must be available (shipped with VS LLVM toolset)
#
# Output:
# - node.profdata in the repo root (ready for vcbuild.bat pgo-use)

param(
[int]$Duration = 15
)

Set-StrictMode -Version Latest
$ErrorActionPreference = 'Stop'

# ---------------------------------------------------------------------------
# Locate llvm-profdata shipped with Visual Studio's LLVM toolset
# ---------------------------------------------------------------------------

function Find-LlvmProfdata {
# vcbuild.bat uses %VCINSTALLDIR%\Tools\Llvm\x64\bin for clang.exe - same spot for profdata
$vcInstallDir = $env:VCINSTALLDIR

if ($vcInstallDir) {
$candidate = Join-Path $vcInstallDir "Tools\Llvm\x64\bin\llvm-profdata.exe"
if (Test-Path $candidate) {
return $candidate
}
}

# Fallback: try VS 2022 / 2026 default install locations
$vsPaths = @(
"${env:ProgramFiles}\Microsoft Visual Studio\2026\Enterprise\VC\Tools\Llvm\x64\bin",
"${env:ProgramFiles}\Microsoft Visual Studio\2026\Community\VC\Tools\Llvm\x64\bin",
"${env:ProgramFiles}\Microsoft Visual Studio\2022\Enterprise\VC\Tools\Llvm\x64\bin",
"${env:ProgramFiles}\Microsoft Visual Studio\2022\Community\VC\Tools\Llvm\x64\bin"
)
foreach ($dir in $vsPaths) {
$candidate = Join-Path $dir "llvm-profdata.exe"
if (Test-Path $candidate) {
return $candidate
}
}

# Last resort: PATH
$fromPath = Get-Command llvm-profdata -ErrorAction SilentlyContinue
if ($fromPath) {
return $fromPath.Source
}

return $null
}

# ---------------------------------------------------------------------------
# Validate prerequisites
# ---------------------------------------------------------------------------

$instrumentedNode = Join-Path $PSScriptRoot "Release\node.exe"
if (-not (Test-Path $instrumentedNode)) {
Write-Error "Instrumented binary not found: $instrumentedNode`nBuild with: vcbuild.bat pgo-generate"
exit 1
}

$pgoRunAll = Join-Path $PSScriptRoot "tools\pgo\pgo-run-all.js"
if (-not (Test-Path $pgoRunAll)) {
Write-Error "PGO training script not found: $pgoRunAll"
exit 1
}

$llvmProfdata = Find-LlvmProfdata
if (-not $llvmProfdata) {
Write-Error "llvm-profdata not found. Install the LLVM toolset via Visual Studio Installer."
exit 1
}

# ---------------------------------------------------------------------------
# STEP 1 – Run workloads with the instrumented binary to collect profiles
# ---------------------------------------------------------------------------

Write-Host "`n=== STEP 1: Collect PGO profiles ===" -ForegroundColor Cyan

# Directory that will receive .profraw files from the instrumented binary.
# %p (PID) and %m (module hash) keep concurrent/fork'd processes from colliding.
$profileDir = Join-Path $PSScriptRoot "pgo-profiles"

if (Test-Path $profileDir) {
Remove-Item -Recurse -Force $profileDir
}
New-Item -ItemType Directory -Path $profileDir | Out-Null

$env:LLVM_PROFILE_FILE = Join-Path $profileDir "node-%p-%m.profraw"

Write-Host "Instrumented node : $instrumentedNode"
Write-Host "Profile output : $($env:LLVM_PROFILE_FILE)"
Write-Host "Duration per script: ${Duration}s"
Write-Host ""

$sw = [System.Diagnostics.Stopwatch]::StartNew()
$proc = Start-Process `
-FilePath $instrumentedNode `
-ArgumentList "`"$pgoRunAll`" --verbose --duration=$Duration" `
-Wait -PassThru -NoNewWindow
$sw.Stop()
Write-Host ("PGO training completed in {0}m {1}s (exit code: {2})" -f `
$sw.Elapsed.Minutes, $sw.Elapsed.Seconds, $proc.ExitCode)
if ($proc.ExitCode -ne 0) {
Write-Warning "PGO training exited with code $($proc.ExitCode) - continuing with merge"
}

# Remove the env var so subsequent builds are not affected
Remove-Item Env:\LLVM_PROFILE_FILE -ErrorAction SilentlyContinue

# ---------------------------------------------------------------------------
# STEP 2 – Merge .profraw files -> node.profdata
# ---------------------------------------------------------------------------

Write-Host "`n=== STEP 2: Merge profile data ===" -ForegroundColor Cyan

Write-Host "Using llvm-profdata: $llvmProfdata"

$profrawFiles = Get-ChildItem -Path $profileDir -Filter "*.profraw" -ErrorAction SilentlyContinue
if ($profrawFiles.Count -eq 0) {
Write-Error "No .profraw files found in '$profileDir'. The instrumented binary may not have generated profile data."
exit 1
}

$totalSize = ($profrawFiles | Measure-Object -Property Length -Sum).Sum
$totalSizeMB = [math]::Round($totalSize / 1MB, 1)
Write-Host "Found $($profrawFiles.Count) .profraw file(s), ${totalSizeMB} MB total"

$profdata = Join-Path $PSScriptRoot "node.profdata"
$mergeArgs = @("merge", "--output=$profdata") + ($profrawFiles | Select-Object -ExpandProperty FullName)

$mergeStopwatch = [System.Diagnostics.Stopwatch]::StartNew()
& $llvmProfdata @mergeArgs
$mergeExitCode = $LASTEXITCODE
$mergeStopwatch.Stop()

if ($mergeExitCode -ne 0) {
Write-Error "llvm-profdata merge failed (exit code $mergeExitCode)"
exit $mergeExitCode
}

$profdataSize = [math]::Round((Get-Item $profdata).Length / 1MB, 1)
Write-Host "Merge completed in $([math]::Round($mergeStopwatch.Elapsed.TotalSeconds, 1))s"

# Clean up .profraw files now that they've been merged
Remove-Item -Recurse -Force $profileDir
Write-Host "Removed $($profrawFiles.Count) .profraw file(s) (${totalSizeMB} MB reclaimed)"

Write-Host "`n=== PGO training complete ===" -ForegroundColor Green
Write-Host " Profile data: $profdata (${profdataSize} MB)"
Write-Host " Next step: vcbuild.bat pgo-use"
98 changes: 98 additions & 0 deletions tools/pgo/README.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
# Node.js PGO Training Scripts

Training workloads for Profile-Guided Optimization (PGO) builds using
Clang/LLVM (including Clang-CL on Windows).

## What is PGO?

PGO uses runtime profile data to guide compiler optimizations (inlining,
branch prediction, code layout), typically improving throughput by 5-20%.

The process has three phases:

1. **Instrument** — Build with `-fprofile-generate` (produces `.profraw` files)
2. **Train** — Run representative workloads to collect profile data
3. **Optimize** — Merge `.profraw` → `node.profdata` via `llvm-profdata`,
then rebuild with `-fprofile-use`

## Quick Start

From a VS Developer Command Prompt:

```powershell
# Step 1: Build the instrumented binary
vcbuild.bat pgo-generate

# Step 2: Run workloads and merge profile data
.\pgo.ps1

# Step 3: Build the optimized binary
vcbuild.bat pgo-use
```

`pgo.ps1` expects the instrumented binary at `Release\node.exe` (produced by
step 1) and writes `node.profdata` to the repo root (consumed by step 3).

```powershell
# Optionally set a longer training duration (default: 15s per script)
.\pgo.ps1 -Duration 30
```

## Training Scripts

All scripts use only Node.js built-in modules (no npm dependencies).
Each script is run as a separate process via `fork()`, producing its own
`.profraw` file.

| Script | What it exercises |
| ------------------------ | ------------------------------------------------------------- |
| `pgo-http-server.js` | llhttp parser, TCP stack, header serialization, JSON, routing |
| `pgo-json.js` | V8 JSON parser/serializer, string allocation, GC pressure |
| `pgo-crypto.js` | OpenSSL (hashing, HMAC, AES, RSA, ECDSA, random, KDF) |
| `pgo-streams-buffers.js` | Buffer C++ impl, stream state machine, back-pressure |
| `pgo-fs.js` | libuv fs operations, thread pool, path module |
| `pgo-async-patterns.js` | V8 Promises, microtask queue, EventEmitter, timers |
| `pgo-url-string.js` | Ada URL parser, V8 string internals, regex JIT |
| `pgo-compression.js` | zlib, brotli C libraries, streaming compression |
| `pgo-net.js` | libuv TCP/pipe handles, c-ares DNS resolver |
| `pgo-module-loading.js` | Module resolver, V8 script compilation, vm module |
| `pgo-child-workers.js` | Worker thread messaging, SharedArrayBuffer, inline eval |

### Running the Orchestrator Directly

The orchestrator can also be invoked directly (e.g. for testing individual
workloads). When used with `pgo.ps1`, this is handled automatically.

```bash
# Run all scripts
node tools/pgo/pgo-run-all.js --duration=15 --verbose

# Run specific scripts
node tools/pgo/pgo-run-all.js --scripts=http-server,json,crypto --duration=30

# Show help
node tools/pgo/pgo-run-all.js --help
```

Each script reads the `PGO_TRAINING_DURATION` environment variable (in
milliseconds) to determine how long to run. The orchestrator sets this
automatically from the `--duration` flag (in seconds).

## Files

```
tools/pgo/
├── pgo-run-all.js # Training orchestrator
├── pgo-http-server.js # HTTP server + client workload
├── pgo-json.js # JSON parse/stringify workload
├── pgo-crypto.js # Crypto operations workload
├── pgo-streams-buffers.js # Streams and Buffer workload
├── pgo-fs.js # File system operations workload
├── pgo-async-patterns.js # Promise/async, EventEmitter, timers workload
├── pgo-url-string.js # URL parsing, string ops, regex workload
├── pgo-compression.js # Gzip/brotli/deflate compression workload
├── pgo-net.js # TCP networking and DNS workload
├── pgo-module-loading.js # Module require/import, VM compilation workload
├── pgo-child-workers.js # Worker threads workload
└── README.md # This file
```
Loading
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
162 changes: 162 additions & 0 deletions pgo.ps1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
# PGO (Profile-Guided Optimization) training script for Node.js (Clang / LLVM)
#
# Runs PGO training workloads against an instrumented Node.js binary
# (Release\node.exe) and merges the resulting .profraw files into
# node.profdata for use with -fprofile-use.
#
# Usage (from a VS Developer Command Prompt):
# .\pgo.ps1 # Run workloads (15s each) and merge
# .\pgo.ps1 -Duration 30 # Run workloads (30s each) and merge
#
# Prerequisites:
# - Release\node.exe must be an instrumented build (built with pgo-generate)
# - llvm-profdata must be available (shipped with VS LLVM toolset)
#
# Output:
# - node.profdata in the repo root (ready for vcbuild.bat pgo-use)

param(
[int]$Duration = 15
)

Set-StrictMode -Version Latest
$ErrorActionPreference = 'Stop'

# ---------------------------------------------------------------------------
# Locate llvm-profdata shipped with Visual Studio's LLVM toolset
# ---------------------------------------------------------------------------

function Find-LlvmProfdata {
# vcbuild.bat uses %VCINSTALLDIR%\Tools\Llvm\x64\bin for clang.exe - same spot for profdata
$vcInstallDir = $env:VCINSTALLDIR

if ($vcInstallDir) {
$candidate = Join-Path $vcInstallDir "Tools\Llvm\x64\bin\llvm-profdata.exe"
if (Test-Path $candidate) {
return $candidate
}
}

# Fallback: try VS 2022 / 2026 default install locations
$vsPaths = @(
"${env:ProgramFiles}\Microsoft Visual Studio\2026\Enterprise\VC\Tools\Llvm\x64\bin",
"${env:ProgramFiles}\Microsoft Visual Studio\2026\Community\VC\Tools\Llvm\x64\bin",
"${env:ProgramFiles}\Microsoft Visual Studio\2022\Enterprise\VC\Tools\Llvm\x64\bin",
"${env:ProgramFiles}\Microsoft Visual Studio\2022\Community\VC\Tools\Llvm\x64\bin"
)
foreach ($dir in $vsPaths) {
$candidate = Join-Path $dir "llvm-profdata.exe"
if (Test-Path $candidate) {
return $candidate
}
}

# Last resort: PATH
$fromPath = Get-Command llvm-profdata -ErrorAction SilentlyContinue
if ($fromPath) {
return $fromPath.Source
}

return $null
}

# ---------------------------------------------------------------------------
# Validate prerequisites
# ---------------------------------------------------------------------------

$instrumentedNode = Join-Path $PSScriptRoot "Release\node.exe"
if (-not (Test-Path $instrumentedNode)) {
Write-Error "Instrumented binary not found: $instrumentedNode`nBuild with: vcbuild.bat pgo-generate"
exit 1
}

$pgoRunAll = Join-Path $PSScriptRoot "tools\pgo\pgo-run-all.js"
if (-not (Test-Path $pgoRunAll)) {
Write-Error "PGO training script not found: $pgoRunAll"
exit 1
}

$llvmProfdata = Find-LlvmProfdata
if (-not $llvmProfdata) {
Write-Error "llvm-profdata not found. Install the LLVM toolset via Visual Studio Installer."
exit 1
}

# ---------------------------------------------------------------------------
# STEP 1 – Run workloads with the instrumented binary to collect profiles
# ---------------------------------------------------------------------------

Write-Host "`n=== STEP 1: Collect PGO profiles ===" -ForegroundColor Cyan

# Directory that will receive .profraw files from the instrumented binary.
# %p (PID) and %m (module hash) keep concurrent/fork'd processes from colliding.
$profileDir = Join-Path $PSScriptRoot "pgo-profiles"

if (Test-Path $profileDir) {
Remove-Item -Recurse -Force $profileDir
}
New-Item -ItemType Directory -Path $profileDir | Out-Null

$env:LLVM_PROFILE_FILE = Join-Path $profileDir "node-%p-%m.profraw"

Write-Host "Instrumented node : $instrumentedNode"
Write-Host "Profile output : $($env:LLVM_PROFILE_FILE)"
Write-Host "Duration per script: ${Duration}s"
Write-Host ""

$sw = [System.Diagnostics.Stopwatch]::StartNew()
$proc = Start-Process `
-FilePath $instrumentedNode `
-ArgumentList "`"$pgoRunAll`" --verbose --duration=$Duration" `
-Wait -PassThru -NoNewWindow
$sw.Stop()
Write-Host ("PGO training completed in {0}m {1}s (exit code: {2})" -f `
$sw.Elapsed.Minutes, $sw.Elapsed.Seconds, $proc.ExitCode)
if ($proc.ExitCode -ne 0) {
Write-Warning "PGO training exited with code $($proc.ExitCode) - continuing with merge"
}

# Remove the env var so subsequent builds are not affected
Remove-Item Env:\LLVM_PROFILE_FILE -ErrorAction SilentlyContinue

# ---------------------------------------------------------------------------
# STEP 2 – Merge .profraw files -> node.profdata
# ---------------------------------------------------------------------------

Write-Host "`n=== STEP 2: Merge profile data ===" -ForegroundColor Cyan

Write-Host "Using llvm-profdata: $llvmProfdata"

$profrawFiles = Get-ChildItem -Path $profileDir -Filter "*.profraw" -ErrorAction SilentlyContinue
if ($profrawFiles.Count -eq 0) {
Write-Error "No .profraw files found in '$profileDir'. The instrumented binary may not have generated profile data."
exit 1
}

$totalSize = ($profrawFiles | Measure-Object -Property Length -Sum).Sum
$totalSizeMB = [math]::Round($totalSize / 1MB, 1)
Write-Host "Found $($profrawFiles.Count) .profraw file(s), ${totalSizeMB} MB total"

$profdata = Join-Path $PSScriptRoot "node.profdata"
$mergeArgs = @("merge", "--output=$profdata") + ($profrawFiles | Select-Object -ExpandProperty FullName)

$mergeStopwatch = [System.Diagnostics.Stopwatch]::StartNew()
& $llvmProfdata @mergeArgs
$mergeExitCode = $LASTEXITCODE
$mergeStopwatch.Stop()

if ($mergeExitCode -ne 0) {
Write-Error "llvm-profdata merge failed (exit code $mergeExitCode)"
exit $mergeExitCode
}

$profdataSize = [math]::Round((Get-Item $profdata).Length / 1MB, 1)
Write-Host "Merge completed in $([math]::Round($mergeStopwatch.Elapsed.TotalSeconds, 1))s"

# Clean up .profraw files now that they've been merged
Remove-Item -Recurse -Force $profileDir
Write-Host "Removed $($profrawFiles.Count) .profraw file(s) (${totalSizeMB} MB reclaimed)"

Write-Host "`n=== PGO training complete ===" -ForegroundColor Green
Write-Host " Profile data: $profdata (${profdataSize} MB)"
Write-Host " Next step: vcbuild.bat pgo-use"
98 changes: 98 additions & 0 deletions tools/pgo/README.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
# Node.js PGO Training Scripts

Training workloads for Profile-Guided Optimization (PGO) builds using
Clang/LLVM (including Clang-CL on Windows).

## What is PGO?

PGO uses runtime profile data to guide compiler optimizations (inlining,
branch prediction, code layout), typically improving throughput by 5-20%.

The process has three phases:

1. **Instrument** — Build with `-fprofile-generate` (produces `.profraw` files)
2. **Train** — Run representative workloads to collect profile data
3. **Optimize** — Merge `.profraw` → `node.profdata` via `llvm-profdata`,
then rebuild with `-fprofile-use`

## Quick Start

From a VS Developer Command Prompt:

```powershell
# Step 1: Build the instrumented binary
vcbuild.bat pgo-generate

# Step 2: Run workloads and merge profile data
.\pgo.ps1

# Step 3: Build the optimized binary
vcbuild.bat pgo-use
```

`pgo.ps1` expects the instrumented binary at `Release\node.exe` (produced by
step 1) and writes `node.profdata` to the repo root (consumed by step 3).

```powershell
# Optionally set a longer training duration (default: 15s per script)
.\pgo.ps1 -Duration 30
```

## Training Scripts

All scripts use only Node.js built-in modules (no npm dependencies).
Each script is run as a separate process via `fork()`, producing its own
`.profraw` file.

| Script | What it exercises |
| ------------------------ | ------------------------------------------------------------- |
| `pgo-http-server.js` | llhttp parser, TCP stack, header serialization, JSON, routing |
| `pgo-json.js` | V8 JSON parser/serializer, string allocation, GC pressure |
| `pgo-crypto.js` | OpenSSL (hashing, HMAC, AES, RSA, ECDSA, random, KDF) |
| `pgo-streams-buffers.js` | Buffer C++ impl, stream state machine, back-pressure |
| `pgo-fs.js` | libuv fs operations, thread pool, path module |
| `pgo-async-patterns.js` | V8 Promises, microtask queue, EventEmitter, timers |
| `pgo-url-string.js` | Ada URL parser, V8 string internals, regex JIT |
| `pgo-compression.js` | zlib, brotli C libraries, streaming compression |
| `pgo-net.js` | libuv TCP/pipe handles, c-ares DNS resolver |
| `pgo-module-loading.js` | Module resolver, V8 script compilation, vm module |
| `pgo-child-workers.js` | Worker thread messaging, SharedArrayBuffer, inline eval |

### Running the Orchestrator Directly

The orchestrator can also be invoked directly (e.g. for testing individual
workloads). When used with `pgo.ps1`, this is handled automatically.

```bash
# Run all scripts
node tools/pgo/pgo-run-all.js --duration=15 --verbose

# Run specific scripts
node tools/pgo/pgo-run-all.js --scripts=http-server,json,crypto --duration=30

# Show help
node tools/pgo/pgo-run-all.js --help
```

Each script reads the `PGO_TRAINING_DURATION` environment variable (in
milliseconds) to determine how long to run. The orchestrator sets this
automatically from the `--duration` flag (in seconds).

## Files

```
tools/pgo/
├── pgo-run-all.js # Training orchestrator
├── pgo-http-server.js # HTTP server + client workload
├── pgo-json.js # JSON parse/stringify workload
├── pgo-crypto.js # Crypto operations workload
├── pgo-streams-buffers.js # Streams and Buffer workload
├── pgo-fs.js # File system operations workload
├── pgo-async-patterns.js # Promise/async, EventEmitter, timers workload
├── pgo-url-string.js # URL parsing, string ops, regex workload
├── pgo-compression.js # Gzip/brotli/deflate compression workload
├── pgo-net.js # TCP networking and DNS workload
├── pgo-module-loading.js # Module require/import, VM compilation workload
├── pgo-child-workers.js # Worker threads workload
└── README.md # This file
```
Loading
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
162 changes: 162 additions & 0 deletions pgo.ps1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
# PGO (Profile-Guided Optimization) training script for Node.js (Clang / LLVM)
#
# Runs PGO training workloads against an instrumented Node.js binary
# (Release\node.exe) and merges the resulting .profraw files into
# node.profdata for use with -fprofile-use.
#
# Usage (from a VS Developer Command Prompt):
# .\pgo.ps1 # Run workloads (15s each) and merge
# .\pgo.ps1 -Duration 30 # Run workloads (30s each) and merge
#
# Prerequisites:
# - Release\node.exe must be an instrumented build (built with pgo-generate)
# - llvm-profdata must be available (shipped with VS LLVM toolset)
#
# Output:
# - node.profdata in the repo root (ready for vcbuild.bat pgo-use)

param(
[int]$Duration = 15
)

Set-StrictMode -Version Latest
$ErrorActionPreference = 'Stop'

# ---------------------------------------------------------------------------
# Locate llvm-profdata shipped with Visual Studio's LLVM toolset
# ---------------------------------------------------------------------------

function Find-LlvmProfdata {
# vcbuild.bat uses %VCINSTALLDIR%\Tools\Llvm\x64\bin for clang.exe - same spot for profdata
$vcInstallDir = $env:VCINSTALLDIR

if ($vcInstallDir) {
$candidate = Join-Path $vcInstallDir "Tools\Llvm\x64\bin\llvm-profdata.exe"
if (Test-Path $candidate) {
return $candidate
}
}

# Fallback: try VS 2022 / 2026 default install locations
$vsPaths = @(
"${env:ProgramFiles}\Microsoft Visual Studio\2026\Enterprise\VC\Tools\Llvm\x64\bin",
"${env:ProgramFiles}\Microsoft Visual Studio\2026\Community\VC\Tools\Llvm\x64\bin",
"${env:ProgramFiles}\Microsoft Visual Studio\2022\Enterprise\VC\Tools\Llvm\x64\bin",
"${env:ProgramFiles}\Microsoft Visual Studio\2022\Community\VC\Tools\Llvm\x64\bin"
)
foreach ($dir in $vsPaths) {
$candidate = Join-Path $dir "llvm-profdata.exe"
if (Test-Path $candidate) {
return $candidate
}
}

# Last resort: PATH
$fromPath = Get-Command llvm-profdata -ErrorAction SilentlyContinue
if ($fromPath) {
return $fromPath.Source
}

return $null
}

# ---------------------------------------------------------------------------
# Validate prerequisites
# ---------------------------------------------------------------------------

$instrumentedNode = Join-Path $PSScriptRoot "Release\node.exe"
if (-not (Test-Path $instrumentedNode)) {
Write-Error "Instrumented binary not found: $instrumentedNode`nBuild with: vcbuild.bat pgo-generate"
exit 1
}

$pgoRunAll = Join-Path $PSScriptRoot "tools\pgo\pgo-run-all.js"
if (-not (Test-Path $pgoRunAll)) {
Write-Error "PGO training script not found: $pgoRunAll"
exit 1
}

$llvmProfdata = Find-LlvmProfdata
if (-not $llvmProfdata) {
Write-Error "llvm-profdata not found. Install the LLVM toolset via Visual Studio Installer."
exit 1
}

# ---------------------------------------------------------------------------
# STEP 1 – Run workloads with the instrumented binary to collect profiles
# ---------------------------------------------------------------------------

Write-Host "`n=== STEP 1: Collect PGO profiles ===" -ForegroundColor Cyan

# Directory that will receive .profraw files from the instrumented binary.
# %p (PID) and %m (module hash) keep concurrent/fork'd processes from colliding.
$profileDir = Join-Path $PSScriptRoot "pgo-profiles"

if (Test-Path $profileDir) {
Remove-Item -Recurse -Force $profileDir
}
New-Item -ItemType Directory -Path $profileDir | Out-Null

$env:LLVM_PROFILE_FILE = Join-Path $profileDir "node-%p-%m.profraw"

Write-Host "Instrumented node : $instrumentedNode"
Write-Host "Profile output : $($env:LLVM_PROFILE_FILE)"
Write-Host "Duration per script: ${Duration}s"
Write-Host ""

$sw = [System.Diagnostics.Stopwatch]::StartNew()
$proc = Start-Process `
-FilePath $instrumentedNode `
-ArgumentList "`"$pgoRunAll`" --verbose --duration=$Duration" `
-Wait -PassThru -NoNewWindow
$sw.Stop()
Write-Host ("PGO training completed in {0}m {1}s (exit code: {2})" -f `
$sw.Elapsed.Minutes, $sw.Elapsed.Seconds, $proc.ExitCode)
if ($proc.ExitCode -ne 0) {
Write-Warning "PGO training exited with code $($proc.ExitCode) - continuing with merge"
}

# Remove the env var so subsequent builds are not affected
Remove-Item Env:\LLVM_PROFILE_FILE -ErrorAction SilentlyContinue

# ---------------------------------------------------------------------------
# STEP 2 – Merge .profraw files -> node.profdata
# ---------------------------------------------------------------------------

Write-Host "`n=== STEP 2: Merge profile data ===" -ForegroundColor Cyan

Write-Host "Using llvm-profdata: $llvmProfdata"

$profrawFiles = Get-ChildItem -Path $profileDir -Filter "*.profraw" -ErrorAction SilentlyContinue
if ($profrawFiles.Count -eq 0) {
Write-Error "No .profraw files found in '$profileDir'. The instrumented binary may not have generated profile data."
exit 1
}

$totalSize = ($profrawFiles | Measure-Object -Property Length -Sum).Sum
$totalSizeMB = [math]::Round($totalSize / 1MB, 1)
Write-Host "Found $($profrawFiles.Count) .profraw file(s), ${totalSizeMB} MB total"

$profdata = Join-Path $PSScriptRoot "node.profdata"
$mergeArgs = @("merge", "--output=$profdata") + ($profrawFiles | Select-Object -ExpandProperty FullName)

$mergeStopwatch = [System.Diagnostics.Stopwatch]::StartNew()
& $llvmProfdata @mergeArgs
$mergeExitCode = $LASTEXITCODE
$mergeStopwatch.Stop()

if ($mergeExitCode -ne 0) {
Write-Error "llvm-profdata merge failed (exit code $mergeExitCode)"
exit $mergeExitCode
}

$profdataSize = [math]::Round((Get-Item $profdata).Length / 1MB, 1)
Write-Host "Merge completed in $([math]::Round($mergeStopwatch.Elapsed.TotalSeconds, 1))s"

# Clean up .profraw files now that they've been merged
Remove-Item -Recurse -Force $profileDir
Write-Host "Removed $($profrawFiles.Count) .profraw file(s) (${totalSizeMB} MB reclaimed)"

Write-Host "`n=== PGO training complete ===" -ForegroundColor Green
Write-Host " Profile data: $profdata (${profdataSize} MB)"
Write-Host " Next step: vcbuild.bat pgo-use"
98 changes: 98 additions & 0 deletions tools/pgo/README.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
# Node.js PGO Training Scripts

Training workloads for Profile-Guided Optimization (PGO) builds using
Clang/LLVM (including Clang-CL on Windows).

## What is PGO?

PGO uses runtime profile data to guide compiler optimizations (inlining,
branch prediction, code layout), typically improving throughput by 5-20%.

The process has three phases:

1. **Instrument** — Build with `-fprofile-generate` (produces `.profraw` files)
2. **Train** — Run representative workloads to collect profile data
3. **Optimize** — Merge `.profraw` → `node.profdata` via `llvm-profdata`,
then rebuild with `-fprofile-use`

## Quick Start

From a VS Developer Command Prompt:

```powershell
# Step 1: Build the instrumented binary
vcbuild.bat pgo-generate

# Step 2: Run workloads and merge profile data
.\pgo.ps1

# Step 3: Build the optimized binary
vcbuild.bat pgo-use
```

`pgo.ps1` expects the instrumented binary at `Release\node.exe` (produced by
step 1) and writes `node.profdata` to the repo root (consumed by step 3).

```powershell
# Optionally set a longer training duration (default: 15s per script)
.\pgo.ps1 -Duration 30
```

## Training Scripts

All scripts use only Node.js built-in modules (no npm dependencies).
Each script is run as a separate process via `fork()`, producing its own
`.profraw` file.

| Script | What it exercises |
| ------------------------ | ------------------------------------------------------------- |
| `pgo-http-server.js` | llhttp parser, TCP stack, header serialization, JSON, routing |
| `pgo-json.js` | V8 JSON parser/serializer, string allocation, GC pressure |
| `pgo-crypto.js` | OpenSSL (hashing, HMAC, AES, RSA, ECDSA, random, KDF) |
| `pgo-streams-buffers.js` | Buffer C++ impl, stream state machine, back-pressure |
| `pgo-fs.js` | libuv fs operations, thread pool, path module |
| `pgo-async-patterns.js` | V8 Promises, microtask queue, EventEmitter, timers |
| `pgo-url-string.js` | Ada URL parser, V8 string internals, regex JIT |
| `pgo-compression.js` | zlib, brotli C libraries, streaming compression |
| `pgo-net.js` | libuv TCP/pipe handles, c-ares DNS resolver |
| `pgo-module-loading.js` | Module resolver, V8 script compilation, vm module |
| `pgo-child-workers.js` | Worker thread messaging, SharedArrayBuffer, inline eval |

### Running the Orchestrator Directly

The orchestrator can also be invoked directly (e.g. for testing individual
workloads). When used with `pgo.ps1`, this is handled automatically.

```bash
# Run all scripts
node tools/pgo/pgo-run-all.js --duration=15 --verbose

# Run specific scripts
node tools/pgo/pgo-run-all.js --scripts=http-server,json,crypto --duration=30

# Show help
node tools/pgo/pgo-run-all.js --help
```

Each script reads the `PGO_TRAINING_DURATION` environment variable (in
milliseconds) to determine how long to run. The orchestrator sets this
automatically from the `--duration` flag (in seconds).

## Files

```
tools/pgo/
├── pgo-run-all.js # Training orchestrator
├── pgo-http-server.js # HTTP server + client workload
├── pgo-json.js # JSON parse/stringify workload
├── pgo-crypto.js # Crypto operations workload
├── pgo-streams-buffers.js # Streams and Buffer workload
├── pgo-fs.js # File system operations workload
├── pgo-async-patterns.js # Promise/async, EventEmitter, timers workload
├── pgo-url-string.js # URL parsing, string ops, regex workload
├── pgo-compression.js # Gzip/brotli/deflate compression workload
├── pgo-net.js # TCP networking and DNS workload
├── pgo-module-loading.js # Module require/import, VM compilation workload
├── pgo-child-workers.js # Worker threads workload
└── README.md # This file
```
Loading
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
162 changes: 162 additions & 0 deletions pgo.ps1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
# PGO (Profile-Guided Optimization) training script for Node.js (Clang / LLVM)
#
# Runs PGO training workloads against an instrumented Node.js binary
# (Release\node.exe) and merges the resulting .profraw files into
# node.profdata for use with -fprofile-use.
#
# Usage (from a VS Developer Command Prompt):
# .\pgo.ps1 # Run workloads (15s each) and merge
# .\pgo.ps1 -Duration 30 # Run workloads (30s each) and merge
#
# Prerequisites:
# - Release\node.exe must be an instrumented build (built with pgo-generate)
# - llvm-profdata must be available (shipped with VS LLVM toolset)
#
# Output:
# - node.profdata in the repo root (ready for vcbuild.bat pgo-use)

param(
[int]$Duration = 15
)

Set-StrictMode -Version Latest
$ErrorActionPreference = 'Stop'

# ---------------------------------------------------------------------------
# Locate llvm-profdata shipped with Visual Studio's LLVM toolset
# ---------------------------------------------------------------------------

function Find-LlvmProfdata {
# vcbuild.bat uses %VCINSTALLDIR%\Tools\Llvm\x64\bin for clang.exe - same spot for profdata
$vcInstallDir = $env:VCINSTALLDIR

if ($vcInstallDir) {
$candidate = Join-Path $vcInstallDir "Tools\Llvm\x64\bin\llvm-profdata.exe"
if (Test-Path $candidate) {
return $candidate
}
}

# Fallback: try VS 2022 / 2026 default install locations
$vsPaths = @(
"${env:ProgramFiles}\Microsoft Visual Studio\2026\Enterprise\VC\Tools\Llvm\x64\bin",
"${env:ProgramFiles}\Microsoft Visual Studio\2026\Community\VC\Tools\Llvm\x64\bin",
"${env:ProgramFiles}\Microsoft Visual Studio\2022\Enterprise\VC\Tools\Llvm\x64\bin",
"${env:ProgramFiles}\Microsoft Visual Studio\2022\Community\VC\Tools\Llvm\x64\bin"
)
foreach ($dir in $vsPaths) {
$candidate = Join-Path $dir "llvm-profdata.exe"
if (Test-Path $candidate) {
return $candidate
}
}

# Last resort: PATH
$fromPath = Get-Command llvm-profdata -ErrorAction SilentlyContinue
if ($fromPath) {
return $fromPath.Source
}

return $null
}

# ---------------------------------------------------------------------------
# Validate prerequisites
# ---------------------------------------------------------------------------

$instrumentedNode = Join-Path $PSScriptRoot "Release\node.exe"
if (-not (Test-Path $instrumentedNode)) {
Write-Error "Instrumented binary not found: $instrumentedNode`nBuild with: vcbuild.bat pgo-generate"
exit 1
}

$pgoRunAll = Join-Path $PSScriptRoot "tools\pgo\pgo-run-all.js"
if (-not (Test-Path $pgoRunAll)) {
Write-Error "PGO training script not found: $pgoRunAll"
exit 1
}

$llvmProfdata = Find-LlvmProfdata
if (-not $llvmProfdata) {
Write-Error "llvm-profdata not found. Install the LLVM toolset via Visual Studio Installer."
exit 1
}

# ---------------------------------------------------------------------------
# STEP 1 – Run workloads with the instrumented binary to collect profiles
# ---------------------------------------------------------------------------

Write-Host "`n=== STEP 1: Collect PGO profiles ===" -ForegroundColor Cyan

# Directory that will receive .profraw files from the instrumented binary.
# %p (PID) and %m (module hash) keep concurrent/fork'd processes from colliding.
$profileDir = Join-Path $PSScriptRoot "pgo-profiles"

if (Test-Path $profileDir) {
Remove-Item -Recurse -Force $profileDir
}
New-Item -ItemType Directory -Path $profileDir | Out-Null

$env:LLVM_PROFILE_FILE = Join-Path $profileDir "node-%p-%m.profraw"

Write-Host "Instrumented node : $instrumentedNode"
Write-Host "Profile output : $($env:LLVM_PROFILE_FILE)"
Write-Host "Duration per script: ${Duration}s"
Write-Host ""

$sw = [System.Diagnostics.Stopwatch]::StartNew()
$proc = Start-Process `
-FilePath $instrumentedNode `
-ArgumentList "`"$pgoRunAll`" --verbose --duration=$Duration" `
-Wait -PassThru -NoNewWindow
$sw.Stop()
Write-Host ("PGO training completed in {0}m {1}s (exit code: {2})" -f `
$sw.Elapsed.Minutes, $sw.Elapsed.Seconds, $proc.ExitCode)
if ($proc.ExitCode -ne 0) {
Write-Warning "PGO training exited with code $($proc.ExitCode) - continuing with merge"
}

# Remove the env var so subsequent builds are not affected
Remove-Item Env:\LLVM_PROFILE_FILE -ErrorAction SilentlyContinue

# ---------------------------------------------------------------------------
# STEP 2 – Merge .profraw files -> node.profdata
# ---------------------------------------------------------------------------

Write-Host "`n=== STEP 2: Merge profile data ===" -ForegroundColor Cyan

Write-Host "Using llvm-profdata: $llvmProfdata"

$profrawFiles = Get-ChildItem -Path $profileDir -Filter "*.profraw" -ErrorAction SilentlyContinue
if ($profrawFiles.Count -eq 0) {
Write-Error "No .profraw files found in '$profileDir'. The instrumented binary may not have generated profile data."
exit 1
}

$totalSize = ($profrawFiles | Measure-Object -Property Length -Sum).Sum
$totalSizeMB = [math]::Round($totalSize / 1MB, 1)
Write-Host "Found $($profrawFiles.Count) .profraw file(s), ${totalSizeMB} MB total"

$profdata = Join-Path $PSScriptRoot "node.profdata"
$mergeArgs = @("merge", "--output=$profdata") + ($profrawFiles | Select-Object -ExpandProperty FullName)

$mergeStopwatch = [System.Diagnostics.Stopwatch]::StartNew()
& $llvmProfdata @mergeArgs
$mergeExitCode = $LASTEXITCODE
$mergeStopwatch.Stop()

if ($mergeExitCode -ne 0) {
Write-Error "llvm-profdata merge failed (exit code $mergeExitCode)"
exit $mergeExitCode
}

$profdataSize = [math]::Round((Get-Item $profdata).Length / 1MB, 1)
Write-Host "Merge completed in $([math]::Round($mergeStopwatch.Elapsed.TotalSeconds, 1))s"

# Clean up .profraw files now that they've been merged
Remove-Item -Recurse -Force $profileDir
Write-Host "Removed $($profrawFiles.Count) .profraw file(s) (${totalSizeMB} MB reclaimed)"

Write-Host "`n=== PGO training complete ===" -ForegroundColor Green
Write-Host " Profile data: $profdata (${profdataSize} MB)"
Write-Host " Next step: vcbuild.bat pgo-use"
98 changes: 98 additions & 0 deletions tools/pgo/README.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
# Node.js PGO Training Scripts

Training workloads for Profile-Guided Optimization (PGO) builds using
Clang/LLVM (including Clang-CL on Windows).

## What is PGO?

PGO uses runtime profile data to guide compiler optimizations (inlining,
branch prediction, code layout), typically improving throughput by 5-20%.

The process has three phases:

1. **Instrument** — Build with `-fprofile-generate` (produces `.profraw` files)
2. **Train** — Run representative workloads to collect profile data
3. **Optimize** — Merge `.profraw` → `node.profdata` via `llvm-profdata`,
then rebuild with `-fprofile-use`

## Quick Start

From a VS Developer Command Prompt:

```powershell
# Step 1: Build the instrumented binary
vcbuild.bat pgo-generate

# Step 2: Run workloads and merge profile data
.\pgo.ps1

# Step 3: Build the optimized binary
vcbuild.bat pgo-use
```

`pgo.ps1` expects the instrumented binary at `Release\node.exe` (produced by
step 1) and writes `node.profdata` to the repo root (consumed by step 3).

```powershell
# Optionally set a longer training duration (default: 15s per script)
.\pgo.ps1 -Duration 30
```

## Training Scripts

All scripts use only Node.js built-in modules (no npm dependencies).
Each script is run as a separate process via `fork()`, producing its own
`.profraw` file.

| Script | What it exercises |
| ------------------------ | ------------------------------------------------------------- |
| `pgo-http-server.js` | llhttp parser, TCP stack, header serialization, JSON, routing |
| `pgo-json.js` | V8 JSON parser/serializer, string allocation, GC pressure |
| `pgo-crypto.js` | OpenSSL (hashing, HMAC, AES, RSA, ECDSA, random, KDF) |
| `pgo-streams-buffers.js` | Buffer C++ impl, stream state machine, back-pressure |
| `pgo-fs.js` | libuv fs operations, thread pool, path module |
| `pgo-async-patterns.js` | V8 Promises, microtask queue, EventEmitter, timers |
| `pgo-url-string.js` | Ada URL parser, V8 string internals, regex JIT |
| `pgo-compression.js` | zlib, brotli C libraries, streaming compression |
| `pgo-net.js` | libuv TCP/pipe handles, c-ares DNS resolver |
| `pgo-module-loading.js` | Module resolver, V8 script compilation, vm module |
| `pgo-child-workers.js` | Worker thread messaging, SharedArrayBuffer, inline eval |

### Running the Orchestrator Directly

The orchestrator can also be invoked directly (e.g. for testing individual
workloads). When used with `pgo.ps1`, this is handled automatically.

```bash
# Run all scripts
node tools/pgo/pgo-run-all.js --duration=15 --verbose

# Run specific scripts
node tools/pgo/pgo-run-all.js --scripts=http-server,json,crypto --duration=30

# Show help
node tools/pgo/pgo-run-all.js --help
```

Each script reads the `PGO_TRAINING_DURATION` environment variable (in
milliseconds) to determine how long to run. The orchestrator sets this
automatically from the `--duration` flag (in seconds).

## Files

```
tools/pgo/
├── pgo-run-all.js # Training orchestrator
├── pgo-http-server.js # HTTP server + client workload
├── pgo-json.js # JSON parse/stringify workload
├── pgo-crypto.js # Crypto operations workload
├── pgo-streams-buffers.js # Streams and Buffer workload
├── pgo-fs.js # File system operations workload
├── pgo-async-patterns.js # Promise/async, EventEmitter, timers workload
├── pgo-url-string.js # URL parsing, string ops, regex workload
├── pgo-compression.js # Gzip/brotli/deflate compression workload
├── pgo-net.js # TCP networking and DNS workload
├── pgo-module-loading.js # Module require/import, VM compilation workload
├── pgo-child-workers.js # Worker threads workload
└── README.md # This file
```
Loading
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
162 changes: 162 additions & 0 deletions pgo.ps1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
# PGO (Profile-Guided Optimization) training script for Node.js (Clang / LLVM)
#
# Runs PGO training workloads against an instrumented Node.js binary
# (Release\node.exe) and merges the resulting .profraw files into
# node.profdata for use with -fprofile-use.
#
# Usage (from a VS Developer Command Prompt):
# .\pgo.ps1 # Run workloads (15s each) and merge
# .\pgo.ps1 -Duration 30 # Run workloads (30s each) and merge
#
# Prerequisites:
# - Release\node.exe must be an instrumented build (built with pgo-generate)
# - llvm-profdata must be available (shipped with VS LLVM toolset)
#
# Output:
# - node.profdata in the repo root (ready for vcbuild.bat pgo-use)

param(
[int]$Duration = 15
)

Set-StrictMode -Version Latest
$ErrorActionPreference = 'Stop'

# ---------------------------------------------------------------------------
# Locate llvm-profdata shipped with Visual Studio's LLVM toolset
# ---------------------------------------------------------------------------

function Find-LlvmProfdata {
# vcbuild.bat uses %VCINSTALLDIR%\Tools\Llvm\x64\bin for clang.exe - same spot for profdata
$vcInstallDir = $env:VCINSTALLDIR

if ($vcInstallDir) {
$candidate = Join-Path $vcInstallDir "Tools\Llvm\x64\bin\llvm-profdata.exe"
if (Test-Path $candidate) {
return $candidate
}
}

# Fallback: try VS 2022 / 2026 default install locations
$vsPaths = @(
"${env:ProgramFiles}\Microsoft Visual Studio\2026\Enterprise\VC\Tools\Llvm\x64\bin",
"${env:ProgramFiles}\Microsoft Visual Studio\2026\Community\VC\Tools\Llvm\x64\bin",
"${env:ProgramFiles}\Microsoft Visual Studio\2022\Enterprise\VC\Tools\Llvm\x64\bin",
"${env:ProgramFiles}\Microsoft Visual Studio\2022\Community\VC\Tools\Llvm\x64\bin"
)
foreach ($dir in $vsPaths) {
$candidate = Join-Path $dir "llvm-profdata.exe"
if (Test-Path $candidate) {
return $candidate
}
}

# Last resort: PATH
$fromPath = Get-Command llvm-profdata -ErrorAction SilentlyContinue
if ($fromPath) {
return $fromPath.Source
}

return $null
}

# ---------------------------------------------------------------------------
# Validate prerequisites
# ---------------------------------------------------------------------------

$instrumentedNode = Join-Path $PSScriptRoot "Release\node.exe"
if (-not (Test-Path $instrumentedNode)) {
Write-Error "Instrumented binary not found: $instrumentedNode`nBuild with: vcbuild.bat pgo-generate"
exit 1
}

$pgoRunAll = Join-Path $PSScriptRoot "tools\pgo\pgo-run-all.js"
if (-not (Test-Path $pgoRunAll)) {
Write-Error "PGO training script not found: $pgoRunAll"
exit 1
}

$llvmProfdata = Find-LlvmProfdata
if (-not $llvmProfdata) {
Write-Error "llvm-profdata not found. Install the LLVM toolset via Visual Studio Installer."
exit 1
}

# ---------------------------------------------------------------------------
# STEP 1 – Run workloads with the instrumented binary to collect profiles
# ---------------------------------------------------------------------------

Write-Host "`n=== STEP 1: Collect PGO profiles ===" -ForegroundColor Cyan

# Directory that will receive .profraw files from the instrumented binary.
# %p (PID) and %m (module hash) keep concurrent/fork'd processes from colliding.
$profileDir = Join-Path $PSScriptRoot "pgo-profiles"

if (Test-Path $profileDir) {
Remove-Item -Recurse -Force $profileDir
}
New-Item -ItemType Directory -Path $profileDir | Out-Null

$env:LLVM_PROFILE_FILE = Join-Path $profileDir "node-%p-%m.profraw"

Write-Host "Instrumented node : $instrumentedNode"
Write-Host "Profile output : $($env:LLVM_PROFILE_FILE)"
Write-Host "Duration per script: ${Duration}s"
Write-Host ""

$sw = [System.Diagnostics.Stopwatch]::StartNew()
$proc = Start-Process `
-FilePath $instrumentedNode `
-ArgumentList "`"$pgoRunAll`" --verbose --duration=$Duration" `
-Wait -PassThru -NoNewWindow
$sw.Stop()
Write-Host ("PGO training completed in {0}m {1}s (exit code: {2})" -f `
$sw.Elapsed.Minutes, $sw.Elapsed.Seconds, $proc.ExitCode)
if ($proc.ExitCode -ne 0) {
Write-Warning "PGO training exited with code $($proc.ExitCode) - continuing with merge"
}

# Remove the env var so subsequent builds are not affected
Remove-Item Env:\LLVM_PROFILE_FILE -ErrorAction SilentlyContinue

# ---------------------------------------------------------------------------
# STEP 2 – Merge .profraw files -> node.profdata
# ---------------------------------------------------------------------------

Write-Host "`n=== STEP 2: Merge profile data ===" -ForegroundColor Cyan

Write-Host "Using llvm-profdata: $llvmProfdata"

$profrawFiles = Get-ChildItem -Path $profileDir -Filter "*.profraw" -ErrorAction SilentlyContinue
if ($profrawFiles.Count -eq 0) {
Write-Error "No .profraw files found in '$profileDir'. The instrumented binary may not have generated profile data."
exit 1
}

$totalSize = ($profrawFiles | Measure-Object -Property Length -Sum).Sum
$totalSizeMB = [math]::Round($totalSize / 1MB, 1)
Write-Host "Found $($profrawFiles.Count) .profraw file(s), ${totalSizeMB} MB total"

$profdata = Join-Path $PSScriptRoot "node.profdata"
$mergeArgs = @("merge", "--output=$profdata") + ($profrawFiles | Select-Object -ExpandProperty FullName)

$mergeStopwatch = [System.Diagnostics.Stopwatch]::StartNew()
& $llvmProfdata @mergeArgs
$mergeExitCode = $LASTEXITCODE
$mergeStopwatch.Stop()

if ($mergeExitCode -ne 0) {
Write-Error "llvm-profdata merge failed (exit code $mergeExitCode)"
exit $mergeExitCode
}

$profdataSize = [math]::Round((Get-Item $profdata).Length / 1MB, 1)
Write-Host "Merge completed in $([math]::Round($mergeStopwatch.Elapsed.TotalSeconds, 1))s"

# Clean up .profraw files now that they've been merged
Remove-Item -Recurse -Force $profileDir
Write-Host "Removed $($profrawFiles.Count) .profraw file(s) (${totalSizeMB} MB reclaimed)"

Write-Host "`n=== PGO training complete ===" -ForegroundColor Green
Write-Host " Profile data: $profdata (${profdataSize} MB)"
Write-Host " Next step: vcbuild.bat pgo-use"
98 changes: 98 additions & 0 deletions tools/pgo/README.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
# Node.js PGO Training Scripts

Training workloads for Profile-Guided Optimization (PGO) builds using
Clang/LLVM (including Clang-CL on Windows).

## What is PGO?

PGO uses runtime profile data to guide compiler optimizations (inlining,
branch prediction, code layout), typically improving throughput by 5-20%.

The process has three phases:

1. **Instrument** — Build with `-fprofile-generate` (produces `.profraw` files)
2. **Train** — Run representative workloads to collect profile data
3. **Optimize** — Merge `.profraw` → `node.profdata` via `llvm-profdata`,
then rebuild with `-fprofile-use`

## Quick Start

From a VS Developer Command Prompt:

```powershell
# Step 1: Build the instrumented binary
vcbuild.bat pgo-generate

# Step 2: Run workloads and merge profile data
.\pgo.ps1

# Step 3: Build the optimized binary
vcbuild.bat pgo-use
```

`pgo.ps1` expects the instrumented binary at `Release\node.exe` (produced by
step 1) and writes `node.profdata` to the repo root (consumed by step 3).

```powershell
# Optionally set a longer training duration (default: 15s per script)
.\pgo.ps1 -Duration 30
```

## Training Scripts

All scripts use only Node.js built-in modules (no npm dependencies).
Each script is run as a separate process via `fork()`, producing its own
`.profraw` file.

| Script | What it exercises |
| ------------------------ | ------------------------------------------------------------- |
| `pgo-http-server.js` | llhttp parser, TCP stack, header serialization, JSON, routing |
| `pgo-json.js` | V8 JSON parser/serializer, string allocation, GC pressure |
| `pgo-crypto.js` | OpenSSL (hashing, HMAC, AES, RSA, ECDSA, random, KDF) |
| `pgo-streams-buffers.js` | Buffer C++ impl, stream state machine, back-pressure |
| `pgo-fs.js` | libuv fs operations, thread pool, path module |
| `pgo-async-patterns.js` | V8 Promises, microtask queue, EventEmitter, timers |
| `pgo-url-string.js` | Ada URL parser, V8 string internals, regex JIT |
| `pgo-compression.js` | zlib, brotli C libraries, streaming compression |
| `pgo-net.js` | libuv TCP/pipe handles, c-ares DNS resolver |
| `pgo-module-loading.js` | Module resolver, V8 script compilation, vm module |
| `pgo-child-workers.js` | Worker thread messaging, SharedArrayBuffer, inline eval |

### Running the Orchestrator Directly

The orchestrator can also be invoked directly (e.g. for testing individual
workloads). When used with `pgo.ps1`, this is handled automatically.

```bash
# Run all scripts
node tools/pgo/pgo-run-all.js --duration=15 --verbose

# Run specific scripts
node tools/pgo/pgo-run-all.js --scripts=http-server,json,crypto --duration=30

# Show help
node tools/pgo/pgo-run-all.js --help
```

Each script reads the `PGO_TRAINING_DURATION` environment variable (in
milliseconds) to determine how long to run. The orchestrator sets this
automatically from the `--duration` flag (in seconds).

## Files

```
tools/pgo/
├── pgo-run-all.js # Training orchestrator
├── pgo-http-server.js # HTTP server + client workload
├── pgo-json.js # JSON parse/stringify workload
├── pgo-crypto.js # Crypto operations workload
├── pgo-streams-buffers.js # Streams and Buffer workload
├── pgo-fs.js # File system operations workload
├── pgo-async-patterns.js # Promise/async, EventEmitter, timers workload
├── pgo-url-string.js # URL parsing, string ops, regex workload
├── pgo-compression.js # Gzip/brotli/deflate compression workload
├── pgo-net.js # TCP networking and DNS workload
├── pgo-module-loading.js # Module require/import, VM compilation workload
├── pgo-child-workers.js # Worker threads workload
└── README.md # This file
```
Loading
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
162 changes: 162 additions & 0 deletions pgo.ps1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
# PGO (Profile-Guided Optimization) training script for Node.js (Clang / LLVM)
#
# Runs PGO training workloads against an instrumented Node.js binary
# (Release\node.exe) and merges the resulting .profraw files into
# node.profdata for use with -fprofile-use.
#
# Usage (from a VS Developer Command Prompt):
# .\pgo.ps1 # Run workloads (15s each) and merge
# .\pgo.ps1 -Duration 30 # Run workloads (30s each) and merge
#
# Prerequisites:
# - Release\node.exe must be an instrumented build (built with pgo-generate)
# - llvm-profdata must be available (shipped with VS LLVM toolset)
#
# Output:
# - node.profdata in the repo root (ready for vcbuild.bat pgo-use)

param(
[int]$Duration = 15
)

Set-StrictMode -Version Latest
$ErrorActionPreference = 'Stop'

# ---------------------------------------------------------------------------
# Locate llvm-profdata shipped with Visual Studio's LLVM toolset
# ---------------------------------------------------------------------------

function Find-LlvmProfdata {
# vcbuild.bat uses %VCINSTALLDIR%\Tools\Llvm\x64\bin for clang.exe - same spot for profdata
$vcInstallDir = $env:VCINSTALLDIR

if ($vcInstallDir) {
$candidate = Join-Path $vcInstallDir "Tools\Llvm\x64\bin\llvm-profdata.exe"
if (Test-Path $candidate) {
return $candidate
}
}

# Fallback: try VS 2022 / 2026 default install locations
$vsPaths = @(
"${env:ProgramFiles}\Microsoft Visual Studio\2026\Enterprise\VC\Tools\Llvm\x64\bin",
"${env:ProgramFiles}\Microsoft Visual Studio\2026\Community\VC\Tools\Llvm\x64\bin",
"${env:ProgramFiles}\Microsoft Visual Studio\2022\Enterprise\VC\Tools\Llvm\x64\bin",
"${env:ProgramFiles}\Microsoft Visual Studio\2022\Community\VC\Tools\Llvm\x64\bin"
)
foreach ($dir in $vsPaths) {
$candidate = Join-Path $dir "llvm-profdata.exe"
if (Test-Path $candidate) {
return $candidate
}
}

# Last resort: PATH
$fromPath = Get-Command llvm-profdata -ErrorAction SilentlyContinue
if ($fromPath) {
return $fromPath.Source
}

return $null
}

# ---------------------------------------------------------------------------
# Validate prerequisites
# ---------------------------------------------------------------------------

$instrumentedNode = Join-Path $PSScriptRoot "Release\node.exe"
if (-not (Test-Path $instrumentedNode)) {
Write-Error "Instrumented binary not found: $instrumentedNode`nBuild with: vcbuild.bat pgo-generate"
exit 1
}

$pgoRunAll = Join-Path $PSScriptRoot "tools\pgo\pgo-run-all.js"
if (-not (Test-Path $pgoRunAll)) {
Write-Error "PGO training script not found: $pgoRunAll"
exit 1
}

$llvmProfdata = Find-LlvmProfdata
if (-not $llvmProfdata) {
Write-Error "llvm-profdata not found. Install the LLVM toolset via Visual Studio Installer."
exit 1
}

# ---------------------------------------------------------------------------
# STEP 1 – Run workloads with the instrumented binary to collect profiles
# ---------------------------------------------------------------------------

Write-Host "`n=== STEP 1: Collect PGO profiles ===" -ForegroundColor Cyan

# Directory that will receive .profraw files from the instrumented binary.
# %p (PID) and %m (module hash) keep concurrent/fork'd processes from colliding.
$profileDir = Join-Path $PSScriptRoot "pgo-profiles"

if (Test-Path $profileDir) {
Remove-Item -Recurse -Force $profileDir
}
New-Item -ItemType Directory -Path $profileDir | Out-Null

$env:LLVM_PROFILE_FILE = Join-Path $profileDir "node-%p-%m.profraw"

Write-Host "Instrumented node : $instrumentedNode"
Write-Host "Profile output : $($env:LLVM_PROFILE_FILE)"
Write-Host "Duration per script: ${Duration}s"
Write-Host ""

$sw = [System.Diagnostics.Stopwatch]::StartNew()
$proc = Start-Process `
-FilePath $instrumentedNode `
-ArgumentList "`"$pgoRunAll`" --verbose --duration=$Duration" `
-Wait -PassThru -NoNewWindow
$sw.Stop()
Write-Host ("PGO training completed in {0}m {1}s (exit code: {2})" -f `
$sw.Elapsed.Minutes, $sw.Elapsed.Seconds, $proc.ExitCode)
if ($proc.ExitCode -ne 0) {
Write-Warning "PGO training exited with code $($proc.ExitCode) - continuing with merge"
}

# Remove the env var so subsequent builds are not affected
Remove-Item Env:\LLVM_PROFILE_FILE -ErrorAction SilentlyContinue

# ---------------------------------------------------------------------------
# STEP 2 – Merge .profraw files -> node.profdata
# ---------------------------------------------------------------------------

Write-Host "`n=== STEP 2: Merge profile data ===" -ForegroundColor Cyan

Write-Host "Using llvm-profdata: $llvmProfdata"

$profrawFiles = Get-ChildItem -Path $profileDir -Filter "*.profraw" -ErrorAction SilentlyContinue
if ($profrawFiles.Count -eq 0) {
Write-Error "No .profraw files found in '$profileDir'. The instrumented binary may not have generated profile data."
exit 1
}

$totalSize = ($profrawFiles | Measure-Object -Property Length -Sum).Sum
$totalSizeMB = [math]::Round($totalSize / 1MB, 1)
Write-Host "Found $($profrawFiles.Count) .profraw file(s), ${totalSizeMB} MB total"

$profdata = Join-Path $PSScriptRoot "node.profdata"
$mergeArgs = @("merge", "--output=$profdata") + ($profrawFiles | Select-Object -ExpandProperty FullName)

$mergeStopwatch = [System.Diagnostics.Stopwatch]::StartNew()
& $llvmProfdata @mergeArgs
$mergeExitCode = $LASTEXITCODE
$mergeStopwatch.Stop()

if ($mergeExitCode -ne 0) {
Write-Error "llvm-profdata merge failed (exit code $mergeExitCode)"
exit $mergeExitCode
}

$profdataSize = [math]::Round((Get-Item $profdata).Length / 1MB, 1)
Write-Host "Merge completed in $([math]::Round($mergeStopwatch.Elapsed.TotalSeconds, 1))s"

# Clean up .profraw files now that they've been merged
Remove-Item -Recurse -Force $profileDir
Write-Host "Removed $($profrawFiles.Count) .profraw file(s) (${totalSizeMB} MB reclaimed)"

Write-Host "`n=== PGO training complete ===" -ForegroundColor Green
Write-Host " Profile data: $profdata (${profdataSize} MB)"
Write-Host " Next step: vcbuild.bat pgo-use"
98 changes: 98 additions & 0 deletions tools/pgo/README.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
# Node.js PGO Training Scripts

Training workloads for Profile-Guided Optimization (PGO) builds using
Clang/LLVM (including Clang-CL on Windows).

## What is PGO?

PGO uses runtime profile data to guide compiler optimizations (inlining,
branch prediction, code layout), typically improving throughput by 5-20%.

The process has three phases:

1. **Instrument** — Build with `-fprofile-generate` (produces `.profraw` files)
2. **Train** — Run representative workloads to collect profile data
3. **Optimize** — Merge `.profraw` → `node.profdata` via `llvm-profdata`,
then rebuild with `-fprofile-use`

## Quick Start

From a VS Developer Command Prompt:

```powershell
# Step 1: Build the instrumented binary
vcbuild.bat pgo-generate

# Step 2: Run workloads and merge profile data
.\pgo.ps1

# Step 3: Build the optimized binary
vcbuild.bat pgo-use
```

`pgo.ps1` expects the instrumented binary at `Release\node.exe` (produced by
step 1) and writes `node.profdata` to the repo root (consumed by step 3).

```powershell
# Optionally set a longer training duration (default: 15s per script)
.\pgo.ps1 -Duration 30
```

## Training Scripts

All scripts use only Node.js built-in modules (no npm dependencies).
Each script is run as a separate process via `fork()`, producing its own
`.profraw` file.

| Script | What it exercises |
| ------------------------ | ------------------------------------------------------------- |
| `pgo-http-server.js` | llhttp parser, TCP stack, header serialization, JSON, routing |
| `pgo-json.js` | V8 JSON parser/serializer, string allocation, GC pressure |
| `pgo-crypto.js` | OpenSSL (hashing, HMAC, AES, RSA, ECDSA, random, KDF) |
| `pgo-streams-buffers.js` | Buffer C++ impl, stream state machine, back-pressure |
| `pgo-fs.js` | libuv fs operations, thread pool, path module |
| `pgo-async-patterns.js` | V8 Promises, microtask queue, EventEmitter, timers |
| `pgo-url-string.js` | Ada URL parser, V8 string internals, regex JIT |
| `pgo-compression.js` | zlib, brotli C libraries, streaming compression |
| `pgo-net.js` | libuv TCP/pipe handles, c-ares DNS resolver |
| `pgo-module-loading.js` | Module resolver, V8 script compilation, vm module |
| `pgo-child-workers.js` | Worker thread messaging, SharedArrayBuffer, inline eval |

### Running the Orchestrator Directly

The orchestrator can also be invoked directly (e.g. for testing individual
workloads). When used with `pgo.ps1`, this is handled automatically.

```bash
# Run all scripts
node tools/pgo/pgo-run-all.js --duration=15 --verbose

# Run specific scripts
node tools/pgo/pgo-run-all.js --scripts=http-server,json,crypto --duration=30

# Show help
node tools/pgo/pgo-run-all.js --help
```

Each script reads the `PGO_TRAINING_DURATION` environment variable (in
milliseconds) to determine how long to run. The orchestrator sets this
automatically from the `--duration` flag (in seconds).

## Files

```
tools/pgo/
├── pgo-run-all.js # Training orchestrator
├── pgo-http-server.js # HTTP server + client workload
├── pgo-json.js # JSON parse/stringify workload
├── pgo-crypto.js # Crypto operations workload
├── pgo-streams-buffers.js # Streams and Buffer workload
├── pgo-fs.js # File system operations workload
├── pgo-async-patterns.js # Promise/async, EventEmitter, timers workload
├── pgo-url-string.js # URL parsing, string ops, regex workload
├── pgo-compression.js # Gzip/brotli/deflate compression workload
├── pgo-net.js # TCP networking and DNS workload
├── pgo-module-loading.js # Module require/import, VM compilation workload
├── pgo-child-workers.js # Worker threads workload
└── README.md # This file
```
Loading
Loading