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
15 changes: 9 additions & 6 deletions profiles/mrsauravsahu/.zshrc
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,10 @@ XARGS_OPTIONS=$(if [ "${currentOs}" = "linux" ]; then echo '--no-run-if-empty';
PATH_PREFIX="/opt/homebrew/bin"
PATH_PREFIX="${PATH_PREFIX}:/home/linuxbrew/.linuxbrew/bin/"

export PATH="${PATH}:${PATH_PREFIX}"
# Keep PATH/path free of duplicates. PATH_PREFIX is prepended again at the end of
# this file, so exporting it here too is what put homebrew (and everything else)
# on PATH twice.
typeset -U path PATH

# add friday scripts to the path
if [ -d "${HOME}/GenAI/code/friday/scripts" ]; then
Expand Down Expand Up @@ -61,11 +64,9 @@ alias ll='ls -l'
alias l='ls'
alias h=helm
alias k=kubectl
alias colima_start='colima start --mount-type virtiofs --cpu 12 --memory 20 --disk 256 --vm-type vz --vz-rosetta'

PATH_PREFIX="${PATH_PREFIX}:/Users/Saurav_Sahu/.dotnet/tools"
PATH_PREFIX="${PATH_PREFIX}:/opt/homebrew/opt/ruby@3.2/bin"
PATH_PREFIX="${PATH_PREFIX}:/opt/homebrew/lib/ruby/gems/3.2.0/bin"
# macOS-only entries (colima, homebrew ruby@3.2, ~/.dotnet/tools) live in
# ~/.mrsauravsahu/dotfiles/darwin.zshrc - this file is the cross-platform base.
PATH_PREFIX="${PATH_PREFIX}:${CLI_CONFIG_ROOT}/current/path"
PATH_PREFIX="${CLI_CONFIG_ROOT}/current/asdf/shims:${PATH_PREFIX}"

Expand All @@ -74,7 +75,9 @@ PATH_PREFIX="${CLI_CONFIG_ROOT}/current/asdf/shims:${PATH_PREFIX}"
# fi

if [ -d "${CLI_CONFIG_ROOT}/current/asdf" ]; then
PATH_PREFIX="${HOME}/${CLI_CONFIG_ROOT}/current/asdf/shims/bin:${PATH_PREFIX}"
# (dropped a "${HOME}/${CLI_CONFIG_ROOT}/..." entry here - CLI_CONFIG_ROOT is
# already absolute, so it expanded to /Users/<user>//Users/<user>/... and never
# existed. The correct shims dir is already prepended above.)
. ${ASDF_DATA_DIR:-$HOME/.asdf}/plugins/golang/set-env.zsh
. ${ASDF_DATA_DIR:-$HOME/.asdf}/plugins/dotnet/set-dotnet-env.zsh
fi
Expand Down
110 changes: 110 additions & 0 deletions specs/003-startup-perf-audit/results.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
# Shell Startup Perf Audit — Round 3

**Status:** Completed
**Baseline:** ~700ms wall (post-002)
**Result:** ~465ms wall — **231ms saved (33%)**

Measured with 12 interleaved baseline/patched runs of `zsh -i -c exit` (interleaving
cancels machine drift; sequential A/B runs were too noisy to read).

---

## Where the time actually went

Coarse phase timing (`$EPOCHREALTIME` markers around each section) was the only
reliable instrument here. `zprof` misses subprocess cost, and `setopt xtrace`
emitted 66k lines and distorted the very thing being measured.

| Phase | Cost |
|---|---|
| oh-my-posh `init zsh` | 103ms |
| compinit (full) | 95ms |
| thefuck `--alias` | 68ms |
| zimfw `init.zsh` | 54ms |
| everything else | ~3ms |
| **config subtotal** | **323ms** |
| global `/etc/zshrc` + `/etc/zprofile` | 90ms |

---

## The "sometimes it's very slow" bug

The 24h compinit cache guard from spec 002 was **permanently stuck in the slow path**:

```zsh
if [[ -n ${ZDOTDIR:-$HOME}/.zcompdump(#qN.mh+24) ]]; then compinit; else compinit -C; fi
```

`compinit` only rewrites the dump file when completions actually changed. When they
haven't, the dump's mtime is never refreshed — so once it crossed 24 hours old it
matched `mh+24` forever, and **every** shell paid full `compinit` instead of `-C`.
The local `.zcompdump` was 18 days stale and still being re-checked on every start.

Worse, when the dump is missing entirely, full `compinit` + `compdump` costs
**3.35s** (measured via zprof: 2992ms self time in `compinit`, 806 `compdef` calls,
129ms in `compdump`). That is the occasional multi-second hang.

**Fix:** `touch` the dump after a full `compinit` so the guard resets and the next
start takes the fast path.

---

## Fixes applied

| Fix | Saving | File |
|---|---|---|
| `touch` zcompdump after full compinit — un-stick the 24h guard | ~80ms/start, avoids 3.3s cold stalls | `src/scripts/setup.programs-conf.zsh` |
| Lazy-load `thefuck` behind a self-replacing `fuck()` stub | ~68ms | `src/scripts/setup.programs-conf.zsh` |
| Cache `oh-my-posh init zsh` output, regenerate when binary/theme is newer | ~103ms | `src/installers/ohmyposh.configure.zsh` |
| `typeset -U path PATH` + drop the redundant early `export PATH` | PATH 85 → 41 entries, 0 dups | `profiles/mrsauravsahu/.zshrc` |
| Remove broken `${HOME}/${CLI_CONFIG_ROOT}/...` shims entry | — (dead path) | `profiles/mrsauravsahu/.zshrc` |
| Move macOS-only PATH/alias lines out of the cross-platform `.zshrc` | — (organization) | `profiles/mrsauravsahu/.zshrc` |

### On caching oh-my-posh

`oh-my-posh init zsh` output is deterministic **except** `POSH_SESSION_ID`, a fresh
UUID per call that keys oh-my-posh's own segment cache. Freezing it into the cached
file would make every shell share one session ID and bleed cached segments between
shells. The generated conf therefore strips that line from the cache and exports a
fresh per-shell value using fork-free zsh builtins (`$$` + `$RANDOM`).

---

## Verified after patching

- oh-my-posh prompt renders; `_omp_precmd` hook installed
- `POSH_SESSION_ID` unique per shell
- `fuck` stub correctly replaces itself with the real thefuck function on first call
- 1733 completions loaded
- PATH: 85 → 41 entries, 0 duplicates

---

## Not addressed

- **`zimfw init.zsh` (54ms)** — would need module pruning; left alone.
- **Global `/etc/zshrc` + `/etc/zprofile` (90ms)** — system files, mostly
`path_helper`. Not ours to edit.
- **`~/.mrsauravsahu/dotfiles/darwin.zshrc`** lives in a separate repo
(`mrsauravsahu/dotfiles`). It still duplicates 11 things from `.zshrc`
(aliases `cat`/`ll`/`l`/`h`/`k`, the `nvim` function, ruby/dotnet PATH entries,
both asdf `set-env` sources) and adds a second, non-existent `~/.asdf/shims`.
Deduping it was deliberately deferred.

---

## ⚠️ Requires a paired dotfiles PR

`darwin.zshrc` is sourced from the **middle** of `.zshrc`, so for anything defined
in both files, whichever defines it *later* silently wins. Both files defined
`colima_start` with **different flags**, and `.zshrc` defined it after the source —
so the `.zshrc` version won and the effective alias never had `--with-kubernetes`.

Removing that duplicate here would have silently flipped Kubernetes **on**. To keep
behavior identical, a paired change drops the flag in `darwin.zshrc`:

- branch: `colima-no-kubernetes` in `mrsauravsahu/dotfiles` (commit `68d77dc`)
- PR: https://github.com/mrsauravsahu/dotfiles/pull/new/colima-no-kubernetes

**Merge the dotfiles PR together with this one.** Verified: with both applied, the
effective `colima_start` is byte-identical to today's.
14 changes: 13 additions & 1 deletion src/installers/ohmyposh.configure.zsh
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,19 @@ TOOL=ohmyposh
CONF=$CLI_CONFIG_CONF_LOCATION/$TOOL.conf.sh

echo -n >$CONF
printf 'eval "$($CLI_CONFIG_ROOT/current/ohmyposh/oh-my-posh init zsh --config $CLI_CONFIG_ROOT/current/ohmyposh/themes/$CLI_CONFIG_THEME.omp.json)"\n' >>$CONF
# `oh-my-posh init zsh` costs ~170ms per shell start. Its output only changes when
# the binary or the theme changes, so cache it and re-generate when either is newer.
printf '_omp_bin=$CLI_CONFIG_ROOT/current/ohmyposh/oh-my-posh\n' >>$CONF
printf '_omp_theme=$CLI_CONFIG_ROOT/current/ohmyposh/themes/$CLI_CONFIG_THEME.omp.json\n' >>$CONF
printf '_omp_cache=$CLI_CONFIG_ROOT/current/ohmyposh/init.$CLI_CONFIG_THEME.zsh\n' >>$CONF
printf 'if [[ ! -s $_omp_cache || $_omp_bin -nt $_omp_cache || $_omp_theme -nt $_omp_cache ]]; then\n' >>$CONF
# POSH_SESSION_ID is a fresh UUID per `init` call and keys oh-my-posh's segment
# cache, so it must stay per-shell rather than being frozen into the cached file.
printf ' $_omp_bin init zsh --config $_omp_theme | grep -v "^export POSH_SESSION_ID=" >| $_omp_cache\n' >>$CONF
printf 'fi\n' >>$CONF
printf 'source $_omp_cache\n' >>$CONF
printf 'export POSH_SESSION_ID="cli-config-$$-${RANDOM}${RANDOM}"\n' >>$CONF
printf 'unset _omp_bin _omp_theme _omp_cache\n' >>$CONF
printf 'if [[ -n "$NVIM" ]]; then\n' >>$CONF
printf ' _omp_strip_zwsp() {\n' >>$CONF
printf '%s\n' " local zwsp=\$'\\xe2\\x80\\x8b'" >>$CONF
Expand Down
19 changes: 17 additions & 2 deletions src/scripts/setup.programs-conf.zsh
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,17 @@ echo '# init all cli-config tools' >> $CLI_CONFIG_PROGRAMS_CONF
{
echo 'autoload -Uz compinit'
echo 'setopt extendedglob'
echo 'if [[ -n ${ZDOTDIR:-$HOME}/.zcompdump(#qN.mh+24) ]]; then'
echo '_cli_config_zcompdump=${ZDOTDIR:-$HOME}/.zcompdump'
echo 'if [[ -n ${_cli_config_zcompdump}(#qN.mh+24) ]]; then'
echo ' compinit'
echo ' # compinit only rewrites the dump when completions actually changed, so its'
echo ' # mtime can stay stale forever - touch it so the 24h check resets and the'
echo ' # next start takes the fast -C path.'
echo ' touch ${_cli_config_zcompdump}'
echo 'else'
echo ' compinit -C'
echo 'fi'
echo 'unset _cli_config_zcompdump'
echo 'unsetopt extendedglob'
} >> $CLI_CONFIG_PROGRAMS_CONF

Expand All @@ -24,4 +30,13 @@ for tool in $(find $CLI_CONFIG_CONF_LOCATION -type f \( -name '*.conf.sh' -o -na
echo ". $tool" >>$CLI_CONFIG_PROGRAMS_CONF
done

echo eval "\$(thefuck --alias)" >>$CLI_CONFIG_PROGRAMS_CONF
# `thefuck --alias` costs ~370ms per shell start (python interpreter startup), which
# was roughly half of total startup time. Define a stub that pays that cost only on
# the first `fuck` invocation, then replaces itself with the real alias function.
{
echo 'fuck() {'
echo ' unset -f fuck'
echo ' eval "$(thefuck --alias)"'
echo ' fuck "$@"'
echo '}'
} >>$CLI_CONFIG_PROGRAMS_CONF
Loading