CONTRIBUTING guideI am a user of oh-my-posh 2 with a pretty custom profile that primarily relies on shell commands (kubectl, az, and so on). I've found through experimentation that it's far more responsive to run all these commands in parallel rather than a serial foreach loop - rather than the prompt sort of "popping in" on a delay, it's almost instant.
I'm in the process of trying to update to v3 and noticed the segments get executed as a byproduct of calling enabled() which happens in a foreach-over-foreach (foreach block/foreach segment in the block).
While I've not yet finished converting or testing, I'm guessing you'd still see a pretty decent perf improvement for prompts that use a lot of commands if things were done in parallel and rendered after all the info is gathered.
Admittedly I'm more of a junior level Go programmer so I may be missing something here and could be totally wrong. And, also, since I've not yet finished converting my theme I can't actually cite stats. It just seems logical that if the shell is executing the oh-my-posh executable, then that executable is starting a separate process in serial for additional shell commands that some multithreading could speed that up.
Could be, I'll give that a "go" ^^
Preliminary test from yesterday evening.

Not faster with parallelism ๐
Were those all running shell commands in the background? It seems like the stuff that would benefit from parallel execution would be shell commands. Stuff like the path that you can get from the environment would, as you're showing, likely suffer from the thread context switch overhead.
Were those all running shell commands in the background? It seems like the stuff that would benefit from parallel execution would be shell commands. Stuff like the path that you can get from the environment would, as you're showing, likely suffer from the thread context switch overhead.
Yes they were all running in parallel on a go routine. The git one is the most impactful one, but I optimized it a few days back. Tbh, I think that for example caching PWD has been more beneficial than adding a parallel execution. 0.07 seconds is plenty fast, wouldn't you say?
I'd absolutely agree. I was just trying to make sure I understood what the comparison was - if it was "fork a bunch of parallel processes to read from the environment" it'd be slower, but if it was "I have five copies of the expensive git block" running in parallel the optimization might have been more apparent. And it well could be that Go is far more optimized for shell execution than PowerShell and its associated underpinnings - when I was running kubectl, az, and dotnet CLI to get relative status for those into my theme I was getting more like .25s without parallel and .1s with parallel, so it made a huge difference.

Running kubectl, dotnet, and az in serial ends up being 0.61 seconds +/- 0.02s
With the PowerShell parallel execution thing I'm running, it's more like .26s.
I'll see what I can do to use that Set-PoshContext thing I noticed in the PS module.
Here's the theme JSON if you're interested...
{
"blocks": [
{
"alignment": "left",
"segments": [
{
"foreground": "#ff8080",
"invert_powerline": false,
"leading_diamond": "",
"powerline_symbol": "๎ฐ",
"properties": {
"always_enabled": false,
"display_exit_code": true,
"error_color": "#e91e63",
"postfix": "]",
"prefix": "[โง EXIT"
},
"style": "powerline",
"trailing_diamond": "",
"type": "exit"
},
{
"background": "#ffe9aa",
"foreground": "#100e23",
"invert_powerline": false,
"leading_diamond": "",
"powerline_symbol": "๎ฐ",
"properties": {
"root_icon": "โ "
},
"style": "powerline",
"trailing_diamond": "",
"type": "root"
},
{
"background": "#000000",
"foreground": "#ffffff",
"invert_powerline": false,
"leading_diamond": "",
"powerline_symbol": "๎ฐ",
"properties": {
"postfix": " ",
"user_info_separator": "@"
},
"style": "powerline",
"trailing_diamond": "",
"type": "session"
},
{
"background": "#0000c0",
"foreground": "#ffffff",
"invert_powerline": false,
"leading_diamond": "",
"powerline_symbol": "๎ฐ",
"properties": {
"prefix": " ๏ ",
"style": "short"
},
"style": "powerline",
"trailing_diamond": "",
"type": "path"
},
{
"background": "#00ffff",
"foreground": "#000000",
"invert_powerline": false,
"leading_diamond": "",
"powerline_symbol": "๎ฐ",
"properties": {
"command": "o=$(dotnet --version 2>/dev/null); X=$?; if [ $X -eq 0 ]; then { echo $o; exit 0; }; elif [ $X -eq 145 ]; then { echo \"[unsupported global.json]\"; exit 0; }; else exit 1; fi",
"prefix": " ๎ฟ ",
"shell": "bash"
},
"style": "powerline",
"type": "command"
},
{
"background": "#ebcc34",
"foreground": "#000000",
"invert_powerline": false,
"leading_diamond": "",
"powerline_symbol": "๎ฐ",
"properties": {
"command": "kubectl config current-context",
"prefix": " โ ",
"shell": "bash"
},
"style": "powerline",
"type": "command"
},
{
"background": "#3493eb",
"foreground": "#000000",
"invert_powerline": false,
"leading_diamond": "",
"powerline_symbol": "๎ฐ",
"properties": {
"command": "az account show --query name -o tsv",
"prefix": " ๏ด ",
"shell": "bash"
},
"style": "powerline",
"type": "command"
},
{
"background": "#00cc00",
"foreground": "#000000",
"powerline_symbol": "๎ฐ",
"properties": {
"bitbucket_icon": "๎",
"branch_ahead_icon": "โ",
"branch_behind_icon": "โ",
"branch_icon": "๎ ",
"branch_identical_icon": "โก",
"cherry_pick_icon": "๏ ",
"commit_icon": "๏ น ",
"display_stash_count": true,
"display_upstream_icon": true,
"git_icon": "๎",
"github_icon": "๎",
"gitlab_icon": "๎",
"local_staged_icon": "๏",
"local_working_icon": "๏",
"merge_icon": "๎ฆ ",
"rebase_icon": "๏ ",
"stash_count_icon": "๏ ",
"tag_icon": "๏งธ "
},
"style": "powerline",
"type": "git"
}
],
"type": "prompt",
"vertical_offset": 0
},
{
"alignment": "right",
"segments": [
{
"foreground": "#ffffff",
"properties": {
"postfix": "]",
"prefix": "[",
"time_format": "15:04"
},
"style": "plain",
"type": "time"
}
],
"type": "prompt",
"vertical_offset": 0
}
],
"console_title": false,
"final_space": true
}
Ok. I see. Command is the worst of them all. Pretty sure all of that can run a lot faster with dedicated segments. Or, as you stated, using posh context to create a few environment variables from PS can even facilitate stuff. I still have the commit with parallel execution, I could share the binary to see if that makes a huge difference for these things.
Yeah, I'd be happy to run the timings to see if there's an improvement.
Which platform are you using?
MacOS 10.15.7

Using the config with several commands, it appears that the serial version is ~0.58s and the parallel version is ~0.42s pretty consistently. There were a couple of weird outliers in the parallel version where it hit 0.9s but I'm unclear where that came from; it could be a difference in the actual commands being executed or something and not reflective of anything in oh-my-posh.
Admittedly this may not be literally 100% apples-to-apples with my native PowerShell theme because in PowerShell I do some fast work with sed directly on some files to get the Azure subscription status, but it's hard to do complex logic in one line in a theme JSON file so I'm using the az CLI for that here.
Let me see about creating a bash script to call for the command for az CLI to make sure the comparison is even more true.
Regardless of that, though, it does appear that if you have a lot of commands in your theme, parallel is faster by quite a bit.
Tried switching to use sed and jq instead of calling the az CLI and the script by itself is pretty consistently about 0.55s while az CLI is 0.39s. Apparently switching to a script to bypass az won't save me anything.
#!/usr/local/bin/bash
CurrentSub=$(sed -nr "/^\[AzureCloud\]/ { :l /^subscription[ ]*=/ { s/.*=[ ]*//; p; q;}; n; b l;}" ~/.azure/clouds.config)
[ -z $CurrentSub ] && exit 1
jq -r --arg CurrentSub "$CurrentSub" '.subscriptions[] | select(.id==$CurrentSub) | .name' ~/.azure/azureProfile.json
I'm in favor of keeping the logic (_the parallel logic_) in that case, AND I would really like to get rid of command and create actual segments for those use-cases too.
That's fair. I could probably pony up a PR or three to help out with that.
Most helpful comment
That's fair. I could probably pony up a PR or three to help out with that.