zsh-users
 help / color / mirror / code / Atom feed
* Shell startup, aliases vs. functions vs. autoloadable functions, and zcompile benefits
@ 2021-11-27  8:10 Zach Riggle
  2021-11-27  8:33 ` Roman Perepelitsa
  0 siblings, 1 reply; 18+ messages in thread
From: Zach Riggle @ 2021-11-27  8:10 UTC (permalink / raw)
  To: Zsh Users

[-- Attachment #1: Type: text/plain, Size: 975 bytes --]

I've started playing with moving many of the functions I've declared in a
script with most of my aliases, and migrating them to their own file in a
directory contained by $fpath.

I'm not sure if this is overall a net performance gain to defer the various
aliases / function-wrappers-as-aliases to autoloadable modules, but it
seems fun.  Does anybody have information on which approach is faster?

   1. alias foo='foo --flag1 --flag2'
   2. foo() { command foo --flag1 --flag2 "$@" }
   3. autoloadable module containing (2)

Finally, I'm aware of the zcompile tool to create .zwc bytecode files, but
it appears that this causes issues with $functions / $functions_source and
sometimes things don't work.

Is there a true performance benefit in the general case for pre-compiling
ALL of my zsh scripts (i.e. everything inside ~/.zprezto that /usr/bin/file
says is a "zsh script text executable")?  What are the benefits /
trade-offs / caveats of doing this?

*Zach Riggle*

[-- Attachment #2: Type: text/html, Size: 1304 bytes --]

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: Shell startup, aliases vs. functions vs. autoloadable functions, and zcompile benefits
  2021-11-27  8:10 Shell startup, aliases vs. functions vs. autoloadable functions, and zcompile benefits Zach Riggle
@ 2021-11-27  8:33 ` Roman Perepelitsa
  2021-11-27 20:22   ` Bart Schaefer
  0 siblings, 1 reply; 18+ messages in thread
From: Roman Perepelitsa @ 2021-11-27  8:33 UTC (permalink / raw)
  To: Zach Riggle; +Cc: Zsh Users

On Sat, Nov 27, 2021 at 9:11 AM Zach Riggle <zachriggle@gmail.com> wrote:
>
> Does anybody have information on which approach is faster?
>
> alias foo='foo --flag1 --flag2'
> foo() { command foo --flag1 --flag2 "$@" }
> autoloadable module containing (2)

In theory the first and the last should have the same performance and
the middle should be slower because it has to parse the body. In
practice you won't find any difference: zsh parser is very fast.

There is a difference in usability between these options. When using
an alias, you can expand it (provided that you have a binding for
expanding aliases) to check what it does and then either undo the
expansion (provided you have a binding) or modify it. I do this all
the time. Aliases also allow you to bypass them with `\foo` rather
than `command foo`. Less typing.

> Finally, I'm aware of the zcompile tool to create .zwc bytecode files, but it appears that this causes issues with $functions / $functions_source and sometimes things don't work.

zcompiling has two main gotchas:

1. Aliases get expanded differently.
2. If you move a source file (e.g., to backup) and then move it back
(restore from backup), you might end up with a mismatched zwc with
greater mtime.

See https://github.com/romkatv/zsh-bench#cutting-corners for a more
detailed description.

> Is there a true performance benefit in the general case for pre-compiling ALL of my zsh scripts (i.e. everything inside ~/.zprezto that /usr/bin/file says is a "zsh script text executable")?

The only benefit of zcompiling is performance. For autoloadable
functions that don't get invoked on startup you won't be able to tell
the difference. This means that the only benefit of zcompiling is
faster zsh startup. You can simply try it: zcompile your stuff and see
if you can tell the difference in zsh startup speed using your own
senses. If you cannot, there is no point in zcompiling. If you want a
machine-mediated measurement, you can use
https://github.com/romkatv/zsh-bench and look at first_prompt_lag and
first_command_lag in the output.

Roman.


^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: Shell startup, aliases vs. functions vs. autoloadable functions, and zcompile benefits
  2021-11-27  8:33 ` Roman Perepelitsa
@ 2021-11-27 20:22   ` Bart Schaefer
  2021-11-30  2:30     ` Zach Riggle
  0 siblings, 1 reply; 18+ messages in thread
From: Bart Schaefer @ 2021-11-27 20:22 UTC (permalink / raw)
  To: Zach Riggle; +Cc: Zsh Users

On Sat, Nov 27, 2021 at 12:33 AM Roman Perepelitsa
<roman.perepelitsa@gmail.com> wrote:
>
> On Sat, Nov 27, 2021 at 9:11 AM Zach Riggle <zachriggle@gmail.com> wrote:
> >
> > alias foo='foo --flag1 --flag2'
> > foo() { command foo --flag1 --flag2 "$@" }
> > autoloadable module containing (2)
>
> In theory the first and the last should have the same performance and
> the middle should be slower because it has to parse the body. In
> practice you won't find any difference: zsh parser is very fast.

This is true for shell startup time; at runtime there's a small
penalty for the middle one (allocating the function context) and on
the very first execution there's an additional load-time cost for the
third one.  Again you're unlikely to notice unless your $fpath entries
are numerous and deep.

The tradeoff is that the third one occupies less memory than either of
the other two (though about the same as the alias) which is
significant if there are a lot of seldom-used functions.

Otherwise, everything Roman said.


^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: Shell startup, aliases vs. functions vs. autoloadable functions, and zcompile benefits
  2021-11-27 20:22   ` Bart Schaefer
@ 2021-11-30  2:30     ` Zach Riggle
  2021-11-30  4:11       ` Bart Schaefer
  2021-11-30  7:51       ` Roman Perepelitsa
  0 siblings, 2 replies; 18+ messages in thread
From: Zach Riggle @ 2021-11-30  2:30 UTC (permalink / raw)
  To: Bart Schaefer; +Cc: Zsh Users

[-- Attachment #1: Type: text/plain, Size: 2763 bytes --]

Good points all around!  I did play around with hyperfine compiling all zsh
scripts in ~/.zprezto and didn't see a tangible difference.

The benchmark I used

$ hyperfine 'zsh -i -l "exit 0"'


Obviously this is not the BEST benchmark, but it is useful for checking
shell startup time -- I might give zsh-bench a look.  I did spend a lot of
time with zprof to benchmark my init scripts, but the most time-consuming
is simply the "source" statement loading other things, but there's no
context as to WHICH files being sourced take the most time.

> you might end up with a mismatched zwc with greater mtime.

This is something I expected to cause hard-to-diagnose problems.  Luckily,
the most common way I'd change files inadvertently (git checkout
branchname) does not use the time from the commit.

I would expect that the md5sum of a file is reasonably fast, and could be
stored in the .zwc for sanity checking, instead of just the "newer than"
check.

> Again you're unlikely to notice unless your $fpath entries are numerous
and deep

> The tradeoff is that the third one occupies less memory than either of
the other two (though about the same as the alias) which is significant if
there are a lot of seldom-used functions.

I expect that I have more $fpath entries than usual, but the total number
of autoloadable functions is much more.

$ echo $#fpath
22

$ for d in $fpath; do n=$(ls $d/* | wc -l); echo "$n $d"; done | sort -nr |
head -3
    1162 /usr/share/zsh/5.8/functions
     559 /usr/share/zsh/site-functions
     141 /Users/zachriggle/.zprezto/modules/completion/external/src


*Zach Riggle*


On Sat, Nov 27, 2021 at 2:22 PM Bart Schaefer <schaefer@brasslantern.com>
wrote:

> On Sat, Nov 27, 2021 at 12:33 AM Roman Perepelitsa
> <roman.perepelitsa@gmail.com> wrote:
> >
> > On Sat, Nov 27, 2021 at 9:11 AM Zach Riggle <zachriggle@gmail.com>
> wrote:
> > >
> > > alias foo='foo --flag1 --flag2'
> > > foo() { command foo --flag1 --flag2 "$@" }
> > > autoloadable module containing (2)
> >
> > In theory the first and the last should have the same performance and
> > the middle should be slower because it has to parse the body. In
> > practice you won't find any difference: zsh parser is very fast.
>
> This is true for shell startup time; at runtime there's a small
> penalty for the middle one (allocating the function context) and on
> the very first execution there's an additional load-time cost for the
> third one.  Again you're unlikely to notice unless your $fpath entries
> are numerous and deep.
>
> The tradeoff is that the third one occupies less memory than either of
> the other two (though about the same as the alias) which is
> significant if there are a lot of seldom-used functions.
>
> Otherwise, everything Roman said.
>

[-- Attachment #2: Type: text/html, Size: 4893 bytes --]

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: Shell startup, aliases vs. functions vs. autoloadable functions, and zcompile benefits
  2021-11-30  2:30     ` Zach Riggle
@ 2021-11-30  4:11       ` Bart Schaefer
  2021-11-30  8:18         ` Zach Riggle
  2021-11-30  7:51       ` Roman Perepelitsa
  1 sibling, 1 reply; 18+ messages in thread
From: Bart Schaefer @ 2021-11-30  4:11 UTC (permalink / raw)
  To: Zach Riggle; +Cc: Zsh Users

On Mon, Nov 29, 2021 at 6:30 PM Zach Riggle <zachriggle@gmail.com> wrote:
>
> I would expect that the md5sum of a file is reasonably fast, and could be stored in the .zwc for sanity checking, instead of just the "newer than" check.

To what are you comparing that checksum?  It could tell you if the
.zwc file were corrupted, but not whether the file differs from all
the component files that were compiled into it.  Even if you could
somehow tell they were different, that doesn't answer the question of
whether the .zwc contains newer versions of any of those functions.
The .zwc does contain a check that it matches the parser version of
the shell that's trying to read it.

> I expect that I have more $fpath entries than usual, but the total number of autoloadable functions is much more.

That's exactly the point:  You're unlikely to ever execute most of
those functions, so storing an autoload entry for them is much more
space-efficient (and startup-time faster) than actually parsing and
storing the function definitions themselves.

> $ for d in $fpath; do n=$(ls $d/* | wc -l); echo "$n $d"; done | sort -nr | head -3

Good heavens, so many processes and pipes.

n=($^fpath(e^'n=($REPLY/*(N.)); reply=("$#n $REPLY")'^))
print -l ${${(On)n}[1,3]}


^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: Shell startup, aliases vs. functions vs. autoloadable functions, and zcompile benefits
  2021-11-30  2:30     ` Zach Riggle
  2021-11-30  4:11       ` Bart Schaefer
@ 2021-11-30  7:51       ` Roman Perepelitsa
  2021-11-30  8:10         ` Mikael Magnusson
  2021-11-30  8:25         ` Zach Riggle
  1 sibling, 2 replies; 18+ messages in thread
From: Roman Perepelitsa @ 2021-11-30  7:51 UTC (permalink / raw)
  To: Zach Riggle; +Cc: Bart Schaefer, Zsh Users

On Tue, Nov 30, 2021 at 3:30 AM Zach Riggle <zachriggle@gmail.com> wrote:
>
> The benchmark I used
>
> $ hyperfine 'zsh -i -l "exit 0"'
>
>
> Obviously this is not the BEST benchmark [for checking shell startup time]

Obviously. It's the worst or at least a strong contender for the title.

Since you care about interactive zsh performance, at least skim
through the homepage of zsh-bench. It'll save you time.

Roman.


^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: Shell startup, aliases vs. functions vs. autoloadable functions, and zcompile benefits
  2021-11-30  7:51       ` Roman Perepelitsa
@ 2021-11-30  8:10         ` Mikael Magnusson
  2021-11-30  8:27           ` Mikael Magnusson
  2021-11-30  8:29           ` Roman Perepelitsa
  2021-11-30  8:25         ` Zach Riggle
  1 sibling, 2 replies; 18+ messages in thread
From: Mikael Magnusson @ 2021-11-30  8:10 UTC (permalink / raw)
  To: Roman Perepelitsa; +Cc: Zach Riggle, Bart Schaefer, Zsh Users

On 11/30/21, Roman Perepelitsa <roman.perepelitsa@gmail.com> wrote:
> On Tue, Nov 30, 2021 at 3:30 AM Zach Riggle <zachriggle@gmail.com> wrote:
>>
>> The benchmark I used
>>
>> $ hyperfine 'zsh -i -l "exit 0"'
>>
>>
>> Obviously this is not the BEST benchmark [for checking shell startup
>> time]
>
> Obviously. It's the worst or at least a strong contender for the title.

I think it's fine to do this (assuming he actually meant -i -l -c
"exit 0" (missing -c in the quoted command)), if you don't do any
weird stuff in your startup files (like you mention in your "how not
to benchmark" section). Assuming your startup files are synchronous,
the above will tell you how long it takes to execute your init file
plus some other overhead, so if you do something and the number is
lower, the thing you did will indeed make startup faster. If your
startup files are not synchronous, then obviously nothing I said
applies and everything you say is true. BTW, the above command can
easily be improved for worseness:
% time (repeat 1000; zsh -ic exit)

> Since you care about interactive zsh performance, at least skim
> through the homepage of zsh-bench. It'll save you time.

% ./zsh-bench
==> benchmarking login shell of mikaelh ...
zsh-bench: cannot find prompt; make sure it contains hostname or the
last part of the current directory

Sadly no time was saved :(.

-- 
Mikael Magnusson


^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: Shell startup, aliases vs. functions vs. autoloadable functions, and zcompile benefits
  2021-11-30  4:11       ` Bart Schaefer
@ 2021-11-30  8:18         ` Zach Riggle
  0 siblings, 0 replies; 18+ messages in thread
From: Zach Riggle @ 2021-11-30  8:18 UTC (permalink / raw)
  To: Bart Schaefer; +Cc: Zsh Users

[-- Attachment #1: Type: text/plain, Size: 4071 bytes --]

> n=($^fpath(e^'n=($REPLY/*(N.)); reply=("$#n $REPLY")'^))
> print -l ${${(On)n}[1,3]}

And this continues to demonstrate that Zsh is the only language that the
more I learn, the less readable my code becomes.  I really do appreciate
you demonstrating the most-Zsh way to achieve the desired result.

There really ought to be an explainshell.com equivalent for Zsh expressions
/ expansion / modifiers / etc.  The information is already nicely codified
in Zsh autocompletion (e.g. ${(<TAB> ), it would be nice to feed in
expressions like the above and get a sane explanation.

I actually intend to use this goal for a babys-first-Rust project, so we'll
see how far along I get.  The MVP of the project is to take a glob / path
expansion expression (e.g. foo/**/bar(^/.) ) and convert it into a BSD find
expression.

>> for d in $fpath; do n=$(ls $d/* | wc -l); echo "$n $d"; done | sort -nr
| head -3
> Good heavens, so many processes and pipes.

Pipes are nicely composable, and maintainable by others not intimately
familiar with Zsh section 14.  The Unix philosophy still applies -- do one
thing and do it well.  Shells are good at connecting inputs and outputs and
modifying them.

Sure, $#fpath + 4 processes is heavy vs. an array construct and a builtin
-- but I don't think anybody is writing shell scripts for performance.

This is more apparent when using e.g. the Rust package 'fd' via `fd --type
f .` instead of extglob with **/*(.).  8.2 seconds vs 328 seconds (sample
size is 1,641,649 files).  The only trade-off is that "fd" does not
guarantee any ordering.  Skipping fd's internal sorting flags, and piping
directly to sort(1) gives a runtime of 30 seconds -- still ten times faster
than extglob (which is close to the number of CPU cores I have).

$ hyperfine --runs 2 "zsh -il -c 'echo **/*(.)'" "zsh -il -c 'fd --type f
.'"
Benchmark 1: zsh -il -c 'echo **/*(.)'
  Time (mean ± σ):     328.973 s ±  2.153 s    [User: 198.746 s, System:
86.629 s]
  Range (min … max):   327.451 s … 330.496 s    2 runs

Benchmark 2: zsh -il -c 'fd --type f .'
  Time (mean ± σ):      8.281 s ±  0.703 s    [User: 17.441 s, System:
47.829 s]
  Range (min … max):    7.784 s …  8.778 s    2 runs


Shells ultimately exist to spawn processes and create pipes.  I'd wager
that (A) below is more maintainable than (B).

A. | sort | head -3

B. {$(${(On)n}[1,3]


If there's a sufficient performance benefit to in-shell-process
computation, I would love to see some standard library expansions of zsh to
reimplement common GNU/BSD utilities as functions

*Zach Riggle*


On Mon, Nov 29, 2021 at 10:12 PM Bart Schaefer <schaefer@brasslantern.com>
wrote:

> On Mon, Nov 29, 2021 at 6:30 PM Zach Riggle <zachriggle@gmail.com> wrote:
> >
> > I would expect that the md5sum of a file is reasonably fast, and could
> be stored in the .zwc for sanity checking, instead of just the "newer than"
> check.
>
> To what are you comparing that checksum?  It could tell you if the
> .zwc file were corrupted, but not whether the file differs from all
> the component files that were compiled into it.  Even if you could
> somehow tell they were different, that doesn't answer the question of
> whether the .zwc contains newer versions of any of those functions.
> The .zwc does contain a check that it matches the parser version of
> the shell that's trying to read it.
>
> > I expect that I have more $fpath entries than usual, but the total
> number of autoloadable functions is much more.
>
> That's exactly the point:  You're unlikely to ever execute most of
> those functions, so storing an autoload entry for them is much more
> space-efficient (and startup-time faster) than actually parsing and
> storing the function definitions themselves.
>
> > $ for d in $fpath; do n=$(ls $d/* | wc -l); echo "$n $d"; done | sort
> -nr | head -3
>
> Good heavens, so many processes and pipes.
>
> n=($^fpath(e^'n=($REPLY/*(N.)); reply=("$#n $REPLY")'^))
> print -l ${${(On)n}[1,3]}
>

[-- Attachment #2: Type: text/html, Size: 5646 bytes --]

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: Shell startup, aliases vs. functions vs. autoloadable functions, and zcompile benefits
  2021-11-30  7:51       ` Roman Perepelitsa
  2021-11-30  8:10         ` Mikael Magnusson
@ 2021-11-30  8:25         ` Zach Riggle
  1 sibling, 0 replies; 18+ messages in thread
From: Zach Riggle @ 2021-11-30  8:25 UTC (permalink / raw)
  To: Roman Perepelitsa; +Cc: Bart Schaefer, Zsh Users

[-- Attachment #1: Type: text/plain, Size: 1541 bytes --]

The only performance metric I care about is the number of milliseconds
between pressing ⌘T to open a new tab, and having a shell available to
accept input.  Sure, input gets buffered before the prompt shows -- but
it's still what I was trying to improve.

For this metric, I think "zsh -il -c exit" is a sufficient benchmark -- I
have looked at zsh/zprof, and it provides some insight, but when 90% of the
startup time is the "source" builtin and there's no additional granularity
(source a → source b → source veryslow → source d) so it's not quite useful.

I looked at zsh-bench when it hit HackerNews (I think?) and its feature set
does not appeal to me.  I don't care whether oh-my-zsh or prezto are
faster, or which prompt can be the faciest 18-segment display and still
have no input lag.  As its benchmarks show, prezto is usually high on the
"first prompt lag" and very low on "input lag".  The former is what I was
testing for.

*Zach Riggle*


On Tue, Nov 30, 2021 at 1:51 AM Roman Perepelitsa <
roman.perepelitsa@gmail.com> wrote:

> On Tue, Nov 30, 2021 at 3:30 AM Zach Riggle <zachriggle@gmail.com> wrote:
> >
> > The benchmark I used
> >
> > $ hyperfine 'zsh -i -l "exit 0"'
> >
> >
> > Obviously this is not the BEST benchmark [for checking shell startup
> time]
>
> Obviously. It's the worst or at least a strong contender for the title.
>
> Since you care about interactive zsh performance, at least skim
> through the homepage of zsh-bench. It'll save you time.
>
> Roman.
>

[-- Attachment #2: Type: text/html, Size: 2238 bytes --]

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: Shell startup, aliases vs. functions vs. autoloadable functions, and zcompile benefits
  2021-11-30  8:10         ` Mikael Magnusson
@ 2021-11-30  8:27           ` Mikael Magnusson
  2021-11-30  8:30             ` Zach Riggle
  2021-11-30  9:11             ` Roman Perepelitsa
  2021-11-30  8:29           ` Roman Perepelitsa
  1 sibling, 2 replies; 18+ messages in thread
From: Mikael Magnusson @ 2021-11-30  8:27 UTC (permalink / raw)
  To: Roman Perepelitsa; +Cc: Zsh Users

On 11/30/21, Mikael Magnusson <mikachu@gmail.com> wrote:
> On 11/30/21, Roman Perepelitsa <roman.perepelitsa@gmail.com> wrote:
>> On Tue, Nov 30, 2021 at 3:30 AM Zach Riggle <zachriggle@gmail.com> wrote:
>>>
>>> The benchmark I used
>>>
>>> $ hyperfine 'zsh -i -l "exit 0"'
>>>
>>>
>>> Obviously this is not the BEST benchmark [for checking shell startup
>>> time]
>>
>> Obviously. It's the worst or at least a strong contender for the title.
>
> I think it's fine to do this (assuming he actually meant -i -l -c
> "exit 0" (missing -c in the quoted command)), if you don't do any
> weird stuff in your startup files (like you mention in your "how not
> to benchmark" section). Assuming your startup files are synchronous,
> the above will tell you how long it takes to execute your init file
> plus some other overhead, so if you do something and the number is
> lower, the thing you did will indeed make startup faster. If your
> startup files are not synchronous, then obviously nothing I said
> applies and everything you say is true. BTW, the above command can
> easily be improved for worseness:
> % time (repeat 1000; zsh -ic exit)
>
>> Since you care about interactive zsh performance, at least skim
>> through the homepage of zsh-bench. It'll save you time.
>
> % ./zsh-bench
> ==> benchmarking login shell of mikaelh ...
> zsh-bench: cannot find prompt; make sure it contains hostname or the
> last part of the current directory
>
> Sadly no time was saved :(.

Got it running by changing prompt_pat to %,
first_command_lag_ms=48.233
first_prompt_lag_ms=33.816
exit_time_ms=32.177

I'd say these values are fairly comparable :).

It would be very useful to have an option to benchmark normal
interactive shells instead of login shells btw, my profile runs stuff
like fortune which is obviously pretty slow in comparison to startup
code, and not at all interesting to benchmark. The  quoted times in
this mail is with all the -l removed.

Also, you recommend against zcompiling .zshrc in your document, but
first_command_lag_ms=56.709
first_prompt_lag_ms=42.367
exit_time_ms=40.885

Increasing startup time by 33% seems like a bad tradeoff to me, then
again, I know exactly how things work and am not likely to make the
mistake you mention.

-- 
Mikael Magnusson


^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: Shell startup, aliases vs. functions vs. autoloadable functions, and zcompile benefits
  2021-11-30  8:10         ` Mikael Magnusson
  2021-11-30  8:27           ` Mikael Magnusson
@ 2021-11-30  8:29           ` Roman Perepelitsa
  1 sibling, 0 replies; 18+ messages in thread
From: Roman Perepelitsa @ 2021-11-30  8:29 UTC (permalink / raw)
  To: Mikael Magnusson; +Cc: Zach Riggle, Bart Schaefer, Zsh Users

On Tue, Nov 30, 2021 at 9:10 AM Mikael Magnusson <mikachu@gmail.com> wrote:
>
> On 11/30/21, Roman Perepelitsa <roman.perepelitsa@gmail.com> wrote:
> > On Tue, Nov 30, 2021 at 3:30 AM Zach Riggle <zachriggle@gmail.com> wrote:
> >>
> >> The benchmark I used
> >>
> >> $ hyperfine 'zsh -i -l "exit 0"'
> >>
> >>
> >> Obviously this is not the BEST benchmark [for checking shell startup
> >> time]
> >
> > Obviously. It's the worst or at least a strong contender for the title.
>
> I think it's fine to do this (assuming he actually meant -i -l -c
> "exit 0" (missing -c in the quoted command)), if you don't do any
> weird stuff in your startup files

Indeed, if all of the following conditions are met, the timing of `zsh
-lic exit` is close to zsh startup time:

- No prompt_subst.
- No precmd hooks.
- No zle hooks.
- No exit hooks.
- No HISTFILE.
- No .zlogout.
- No conditioning on ZSH_EXECUTION_STRING.

I don't know if these things are weird but rc files using at least one
of them are common.

> > Since you care about interactive zsh performance, at least skim
> > through the homepage of zsh-bench. It'll save you time.
>
> % ./zsh-bench
> ==> benchmarking login shell of mikaelh ...
> zsh-bench: cannot find prompt; make sure it contains hostname or the
> last part of the current directory
>
> Sadly no time was saved :(.

That's an unorthodox reading of "skim through the homepage".

Roman.


^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: Shell startup, aliases vs. functions vs. autoloadable functions, and zcompile benefits
  2021-11-30  8:27           ` Mikael Magnusson
@ 2021-11-30  8:30             ` Zach Riggle
  2021-11-30  9:11             ` Roman Perepelitsa
  1 sibling, 0 replies; 18+ messages in thread
From: Zach Riggle @ 2021-11-30  8:30 UTC (permalink / raw)
  To: Mikael Magnusson; +Cc: Roman Perepelitsa, Zsh Users

[-- Attachment #1: Type: text/plain, Size: 2650 bytes --]

And here I am all the way up at ~600ms startup lag ($EMPLOYER has a busted
Python3 distro that adds a lot of ms each interpreter start-up), but very
low prompt lag.

*Zach Riggle*


On Tue, Nov 30, 2021 at 2:28 AM Mikael Magnusson <mikachu@gmail.com> wrote:

> On 11/30/21, Mikael Magnusson <mikachu@gmail.com> wrote:
> > On 11/30/21, Roman Perepelitsa <roman.perepelitsa@gmail.com> wrote:
> >> On Tue, Nov 30, 2021 at 3:30 AM Zach Riggle <zachriggle@gmail.com>
> wrote:
> >>>
> >>> The benchmark I used
> >>>
> >>> $ hyperfine 'zsh -i -l "exit 0"'
> >>>
> >>>
> >>> Obviously this is not the BEST benchmark [for checking shell startup
> >>> time]
> >>
> >> Obviously. It's the worst or at least a strong contender for the title.
> >
> > I think it's fine to do this (assuming he actually meant -i -l -c
> > "exit 0" (missing -c in the quoted command)), if you don't do any
> > weird stuff in your startup files (like you mention in your "how not
> > to benchmark" section). Assuming your startup files are synchronous,
> > the above will tell you how long it takes to execute your init file
> > plus some other overhead, so if you do something and the number is
> > lower, the thing you did will indeed make startup faster. If your
> > startup files are not synchronous, then obviously nothing I said
> > applies and everything you say is true. BTW, the above command can
> > easily be improved for worseness:
> > % time (repeat 1000; zsh -ic exit)
> >
> >> Since you care about interactive zsh performance, at least skim
> >> through the homepage of zsh-bench. It'll save you time.
> >
> > % ./zsh-bench
> > ==> benchmarking login shell of mikaelh ...
> > zsh-bench: cannot find prompt; make sure it contains hostname or the
> > last part of the current directory
> >
> > Sadly no time was saved :(.
>
> Got it running by changing prompt_pat to %,
> first_command_lag_ms=48.233
> first_prompt_lag_ms=33.816
> exit_time_ms=32.177
>
> I'd say these values are fairly comparable :).
>
> It would be very useful to have an option to benchmark normal
> interactive shells instead of login shells btw, my profile runs stuff
> like fortune which is obviously pretty slow in comparison to startup
> code, and not at all interesting to benchmark. The  quoted times in
> this mail is with all the -l removed.
>
> Also, you recommend against zcompiling .zshrc in your document, but
> first_command_lag_ms=56.709
> first_prompt_lag_ms=42.367
> exit_time_ms=40.885
>
> Increasing startup time by 33% seems like a bad tradeoff to me, then
> again, I know exactly how things work and am not likely to make the
> mistake you mention.
>
> --
> Mikael Magnusson
>
>

[-- Attachment #2: Type: text/html, Size: 3726 bytes --]

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: Shell startup, aliases vs. functions vs. autoloadable functions, and zcompile benefits
  2021-11-30  8:27           ` Mikael Magnusson
  2021-11-30  8:30             ` Zach Riggle
@ 2021-11-30  9:11             ` Roman Perepelitsa
  2021-11-30 11:49               ` Zach Riggle
  1 sibling, 1 reply; 18+ messages in thread
From: Roman Perepelitsa @ 2021-11-30  9:11 UTC (permalink / raw)
  To: Mikael Magnusson; +Cc: Zsh Users

On Tue, Nov 30, 2021 at 9:27 AM Mikael Magnusson <mikachu@gmail.com> wrote:
>
> It would be very useful to have an option to benchmark normal
> interactive shells instead of login shells

Sounds useful indeed. I added `--login no` (the default is yes).

> Also, you recommend against zcompiling .zshrc in your document, but
> first_command_lag_ms=56.709
> first_prompt_lag_ms=42.367
> exit_time_ms=40.885
>
> Increasing startup time by 33% seems like a bad tradeoff to me, then
> again, I know exactly how things work and am not likely to make the
> mistake you mention.

In the document I recommend that publishers of zsh configuration
frameworks (think ohmyzsh, etc.) not zcompile user rc files by
default.

FWIW, I zcompile my own rc files, although I do it in a way that
avoids issues caused by mtime and missing source files (I don't
mention the latter problem in the doc; basically, if you zcompile an
rc file and remove the source, the rc file will still be in effect).
The only downside I get from zcompiling is that aliases get expanded
differently but that's fine with me.


^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: Shell startup, aliases vs. functions vs. autoloadable functions, and zcompile benefits
  2021-11-30  9:11             ` Roman Perepelitsa
@ 2021-11-30 11:49               ` Zach Riggle
  2021-11-30 12:30                 ` Roman Perepelitsa
  0 siblings, 1 reply; 18+ messages in thread
From: Zach Riggle @ 2021-11-30 11:49 UTC (permalink / raw)
  To: Roman Perepelitsa; +Cc: Mikael Magnusson, Zsh Users

[-- Attachment #1: Type: text/plain, Size: 2037 bytes --]

> FWIW, I zcompile my own rc files

What is the optimal way to compile a huge set of them?  I've read Section
17 on zcompile, and it mentions "digest" files -- multiple files,
containing named functions, where the .zwc is added to $fpath instead of
the directory containing it.

It's trivial to set up an inotify watcher (Linux) or similar for other OSes
("brew install fswatch" seems to do fine on macOS) and recompile an entire
tree on-modify.  What I'm not aware of is any way to map dependencies
between various scripts (e.g. "source A" → "source B" → "source C") such
that when B changes, all of A/B/C get recompiled.  In fact, due to the
"source" keyword and Zsh internals, I'm not actually sure if it's even
necessary or beneficial.

*Zach Riggle*


On Tue, Nov 30, 2021 at 3:12 AM Roman Perepelitsa <
roman.perepelitsa@gmail.com> wrote:

> On Tue, Nov 30, 2021 at 9:27 AM Mikael Magnusson <mikachu@gmail.com>
> wrote:
> >
> > It would be very useful to have an option to benchmark normal
> > interactive shells instead of login shells
>
> Sounds useful indeed. I added `--login no` (the default is yes).
>
> > Also, you recommend against zcompiling .zshrc in your document, but
> > first_command_lag_ms=56.709
> > first_prompt_lag_ms=42.367
> > exit_time_ms=40.885
> >
> > Increasing startup time by 33% seems like a bad tradeoff to me, then
> > again, I know exactly how things work and am not likely to make the
> > mistake you mention.
>
> In the document I recommend that publishers of zsh configuration
> frameworks (think ohmyzsh, etc.) not zcompile user rc files by
> default.
>
> FWIW, I zcompile my own rc files, although I do it in a way that
> avoids issues caused by mtime and missing source files (I don't
> mention the latter problem in the doc; basically, if you zcompile an
> rc file and remove the source, the rc file will still be in effect).
> The only downside I get from zcompiling is that aliases get expanded
> differently but that's fine with me.
>
>

[-- Attachment #2: Type: text/html, Size: 2791 bytes --]

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: Shell startup, aliases vs. functions vs. autoloadable functions, and zcompile benefits
  2021-11-30 11:49               ` Zach Riggle
@ 2021-11-30 12:30                 ` Roman Perepelitsa
  2021-11-30 15:10                   ` Zach Riggle
  0 siblings, 1 reply; 18+ messages in thread
From: Roman Perepelitsa @ 2021-11-30 12:30 UTC (permalink / raw)
  To: Zach Riggle; +Cc: Mikael Magnusson, Zsh Users

On Tue, Nov 30, 2021 at 12:49 PM Zach Riggle <zachriggle@gmail.com> wrote:
>
> > FWIW, I zcompile my own rc files
>
> What is the optimal way to compile a huge set of them?

Most of the zsh files you are using are *installed* rather than
modified by yourself. You can zcompile them when you install/update
them. E.g., you have some code that installs/updates prezto. Invoke
zcompile in there.

There are also zsh startup files that you modify manually every now
and then (.zshrc, etc.). Compiling these files is harder and is more
likely to cause issues. On the other hand, the benefits of compiling
them are tiny, simply because those files are tiny. Once again, you
can manually zcompile different things and check whether you notice
any difference. If you don't, there is no need to invest in
zcompiling.

If first_prompt_lag of your shell is 600ms, zcompiling won't have
noticeable effects. You'll need to optimize other things first for
zcompiling to matter.

Roman.


^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: Shell startup, aliases vs. functions vs. autoloadable functions, and zcompile benefits
  2021-11-30 12:30                 ` Roman Perepelitsa
@ 2021-11-30 15:10                   ` Zach Riggle
  2021-11-30 16:37                     ` Bart Schaefer
  0 siblings, 1 reply; 18+ messages in thread
From: Zach Riggle @ 2021-11-30 15:10 UTC (permalink / raw)
  To: Roman Perepelitsa; +Cc: Mikael Magnusson, Zsh Users

[-- Attachment #1: Type: text/plain, Size: 2785 bytes --]

> If first_prompt_lag of your shell is 600ms, zcompiling won't have
noticeable effects

Definitely agreed.  Fortunately, you can redefine "source" as a function
and trap all calls and measure how long it takes to load each file.

function source () {
    local __before="$EPOCHREALTIME"
    builtin source "$@"
    local __after="$EPOCHREALTIME"
    local __ms="$(( (__after - __before) * 1000 ))"
    echo "$__ms $@" >>| /tmp/zprezto-bench.$$.txt
}


In my case this narrowed it down pretty easily to some of the larger
functions that were getting parsed on shell-startup (deferring to
autoloaded functions being key).  Oddly, there is one source'd script that
seems to consistently have an issue with measurement of $EPOCHREALTIME.

I've managed to get first-prompt time down to ~300ms from ~650ms, and the
rest is a toss-up.  0.3s is fast enough for a login prompt for me!

$ rm -f /tmp/zprez* ; zsh -il -c exit; cat /tmp/zprez* | sort -rn | head
1638284631932.147 /Users/zachriggle/.zprezto/modules/zsh-z/init.zsh
119.97294425964355 /Users/zachriggle/.zprezto/modules/redacted1/init.zsh
85.247993469238281 /Users/zachriggle/.zprezto/modules/zach-riggle/init.zsh
47.258853912353516 /Users/zachriggle/.zprezto/modules/completion/init.zsh
30.105113983154297 /Users/zachriggle/.zprezto/modules/prompt/init.zsh
23.977041244506836
/Users/zachriggle/.zprezto/modules/redacted2/scripts/init.zsh
20.226001739501953 /Users/zachriggle/.zprezto/modules/zach-riggle/path.zsh
19.911050796508789
/Users/zachriggle/.zprezto/modules/syntax-highlighting/init.zsh
19.558191299438477
/Users/zachriggle/.zprezto/modules/syntax-highlighting/external/zsh-syntax-highlighting.zsh


*Zach Riggle*


On Tue, Nov 30, 2021 at 6:30 AM Roman Perepelitsa <
roman.perepelitsa@gmail.com> wrote:

> On Tue, Nov 30, 2021 at 12:49 PM Zach Riggle <zachriggle@gmail.com> wrote:
> >
> > > FWIW, I zcompile my own rc files
> >
> > What is the optimal way to compile a huge set of them?
>
> Most of the zsh files you are using are *installed* rather than
> modified by yourself. You can zcompile them when you install/update
> them. E.g., you have some code that installs/updates prezto. Invoke
> zcompile in there.
>
> There are also zsh startup files that you modify manually every now
> and then (.zshrc, etc.). Compiling these files is harder and is more
> likely to cause issues. On the other hand, the benefits of compiling
> them are tiny, simply because those files are tiny. Once again, you
> can manually zcompile different things and check whether you notice
> any difference. If you don't, there is no need to invest in
> zcompiling.
>
> If first_prompt_lag of your shell is 600ms, zcompiling won't have
> noticeable effects. You'll need to optimize other things first for
> zcompiling to matter.
>
> Roman.
>

[-- Attachment #2: Type: text/html, Size: 3986 bytes --]

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: Shell startup, aliases vs. functions vs. autoloadable functions, and zcompile benefits
  2021-11-30 15:10                   ` Zach Riggle
@ 2021-11-30 16:37                     ` Bart Schaefer
  2021-11-30 16:41                       ` Roman Perepelitsa
  0 siblings, 1 reply; 18+ messages in thread
From: Bart Schaefer @ 2021-11-30 16:37 UTC (permalink / raw)
  To: Zach Riggle; +Cc: Roman Perepelitsa, Mikael Magnusson, Zsh Users

On Tue, Nov 30, 2021 at 7:10 AM Zach Riggle <zachriggle@gmail.com> wrote:
>
> Fortunately, you can redefine "source" as a function and trap all calls and measure how long it takes to load each file.

If you redefine "source" as a function, then any "typeset" commands in
the sourced files take on the semantics of "local" and the parameters
are created only in the context of the function wrapper.  That may not
have any significant effect on benchmarking, but for example settings
from syntax-highlighting/init.zsh might not be visible to
syntax-highlighting/external/zsh-syntax-highlighting.zsh (if that
matters).

In any case you definitely don't want this in place outside of benchmarking.


^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: Shell startup, aliases vs. functions vs. autoloadable functions, and zcompile benefits
  2021-11-30 16:37                     ` Bart Schaefer
@ 2021-11-30 16:41                       ` Roman Perepelitsa
  0 siblings, 0 replies; 18+ messages in thread
From: Roman Perepelitsa @ 2021-11-30 16:41 UTC (permalink / raw)
  To: Bart Schaefer; +Cc: Zach Riggle, Mikael Magnusson, Zsh Users

On Tue, Nov 30, 2021 at 5:38 PM Bart Schaefer <schaefer@brasslantern.com> wrote:
>
> On Tue, Nov 30, 2021 at 7:10 AM Zach Riggle <zachriggle@gmail.com> wrote:
> >
> > Fortunately, you can redefine "source" as a function and trap all calls and measure how long it takes to load each file.
>
> If you redefine "source" as a function, then any "typeset" commands in
> the sourced files take on the semantics of "local" and the parameters
> are created only in the context of the function wrapper.  That may not
> have any significant effect on benchmarking, but for example settings
> from syntax-highlighting/init.zsh might not be visible to
> syntax-highlighting/external/zsh-syntax-highlighting.zsh (if that
> matters).

All decent plugins (including zsh-syntax-highlighting) can be sourced
from functions because that's how many plugins managers (including
prezto) source them.

Roman.


^ permalink raw reply	[flat|nested] 18+ messages in thread

end of thread, other threads:[~2021-11-30 16:46 UTC | newest]

Thread overview: 18+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-11-27  8:10 Shell startup, aliases vs. functions vs. autoloadable functions, and zcompile benefits Zach Riggle
2021-11-27  8:33 ` Roman Perepelitsa
2021-11-27 20:22   ` Bart Schaefer
2021-11-30  2:30     ` Zach Riggle
2021-11-30  4:11       ` Bart Schaefer
2021-11-30  8:18         ` Zach Riggle
2021-11-30  7:51       ` Roman Perepelitsa
2021-11-30  8:10         ` Mikael Magnusson
2021-11-30  8:27           ` Mikael Magnusson
2021-11-30  8:30             ` Zach Riggle
2021-11-30  9:11             ` Roman Perepelitsa
2021-11-30 11:49               ` Zach Riggle
2021-11-30 12:30                 ` Roman Perepelitsa
2021-11-30 15:10                   ` Zach Riggle
2021-11-30 16:37                     ` Bart Schaefer
2021-11-30 16:41                       ` Roman Perepelitsa
2021-11-30  8:29           ` Roman Perepelitsa
2021-11-30  8:25         ` Zach Riggle

Code repositories for project(s) associated with this inbox:

	https://git.vuxu.org/mirror/zsh/

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).