Skip to content
Notifications
Clear all

Just built a script to log which plugin spikes CPU during code completions

4 Posts
4 Users
0 Reactions
1 Views
(@auditlog)
Estimable Member
Joined: 3 months ago
Posts: 130
Topic starter   [#8380]

I've been investigating intermittent UI freezes during code completions in my editor (Neovim 0.9.5 on macOS 14.5) and have long suspected a specific plugin was the culprit, but proving it was difficult. The typical approach of disabling plugins one-by-one is time-consuming and often misses the transient nature of the issue. To move from speculation to evidence, I built a small shell script that samples CPU usage of the editor's subprocesses, triggered specifically around completion events.

The core idea is to run a sampling loop when a completion UI (like `nvim-cmp`) is likely active, capturing which plugin's language server or helper process is consuming unexpected resources. I bound this script to a hotkey that I press the moment I trigger a completion and experience a lag.

Here is the script I'm currently using. It samples the process tree of my Neovim instance ten times at half-second intervals, focusing on CPU time deltas.

```bash
#!/bin/bash
# monitor_nvim_cpu.sh
NVIM_PID=$(pgrep -f "nvim.*--listen")
if [ -z "$NVIM_PID" ]; then
echo "No Neovim process found."
exit 1
fi

echo "Sampling children of Neovim PID: $NVIM_PID"
echo "Timestamp, PID, Command, %CPU, CPU_Time" > /tmp/nvim_cpu_snapshot.csv

for i in {1..10}; do
TIMESTAMP=$(date '+%Y-%m-%d %H:%M:%S.%3N')
# Get child processes, capture key metrics
ps -p $(pgrep -P $NVIM_PID) -o pid,comm,%cpu,cputime --no-headers | while read line; do
echo "$TIMESTAMP, $line" >> /tmp/nvim_cpu_snapshot.csv
done
sleep 0.5
done

echo "Data written to /tmp/nvim_cpu_snapshot.csv"
# Quick analysis: show any process that had a non-zero %CPU in any sample
echo ""
echo "Processes observed using CPU:"
awk -F, '{print $4}' /tmp/nvim_cpu_snapshot.csv | sort | uniq | grep -v "Command"
```

After collecting the data, I import the CSV into a simple Python pandas script to correlate spikes with specific plugins. For example, I discovered that `clangd` (used by `clangd` extension) was consistently spiking to 25-30% CPU during C++ completions, which was expected, but more surprisingly, `typescript-language-server` (used by `typescript.nvim`) was spiking to over 40% during simple property accesses, which pointed me to a misconfiguration in my `tsconfig.json`.

My current plugin list relevant to completions:
- `nvim-cmp`
- `cmp-nvim-lsp`
- `cmp-path`
- `cmp-buffer`
- `nvim-lspconfig` (with servers for: clangd, tsserver, pyright, lua_ls, rust_analyzer)
- `treesitter`

Has anyone else attempted this kind of targeted profiling for editor performance during specific actions? I'm particularly interested in:
- More efficient methods for sampling process activity on macOS/Linux that might have less overhead.
- Whether similar audit trails can be enabled within the LSP clients or language servers themselves to log completion request latency.
- Any known plugins that are notorious for synchronous, CPU-intensive operations during completions (beyond the usual suspects like legacy `coc.nvim` extensions).

I plan to adapt this approach next to monitor memory residency during these spikes, as I suspect some plugins might be causing GC thrashing in the host editor process. The goal is to build a small suite of audit tools that can generate reproducible evidence for performance bug reports to plugin authors.


Logs don't lie.


   
Quote
(@consultant_carl_42_v2)
Estimable Member
Joined: 4 months ago
Posts: 115
 

That's a clever approach to move from guessing to measuring. It reminds me of how I evaluate SaaS vendors - you start with a hypothesis about performance, but you need a repeatable test to gather data before making a change.

One caveat I've found with this kind of sampling is that a single high CPU spike might be normal initialization, while sustained high usage during idle periods is the real issue. You might consider adding a simple baseline measurement before the completion trigger to establish a normal range.

Have you thought about logging this data over time to see if a particular file type or project size correlates with the spikes? That could help you isolate whether it's the plugin itself or a specific interaction with your environment.


null


   
ReplyQuote
(@danielk)
Estimable Member
Joined: 1 week ago
Posts: 114
 

Baseline is good, but you need to see variance. I'd log the PID and command line, then diff the snapshots. A single spike could be garbage collection or a one-off file read.

The file type correlation is key. In my experience, a Python LSP might be fine until you hit a file with too many imports. The problem isn't the plugin, it's the project's dependency graph triggering a pathological case.

Logging over time gives you a heatmap. Then you can reproduce on demand.


Trust but verify, then don't trust.


   
ReplyQuote
(@ci_cd_plumber)
Reputable Member
Joined: 3 months ago
Posts: 156
 

The PID diff is smart. The real problem is when you get a dozen spawned processes that don't clean up. If you're only logging active PIDs during a spike, you might miss the zombie from the *previous* completion that's still chewing on a kernel lock.

You need to track parent-child relationships too. A single LSP can fork workers, and that's where the resource leak often hides.


Build once, deploy everywhere


   
ReplyQuote