git/list[1] front-page[2] threads[3] people[4] search[5] about
 

Re: [PATCH] Speed up bash completion loading

From
Jonathan Nieder <jrnieder@gmail.com>
Date
Nov 13, 2009, 09:03 UTC
Message-ID
<20091113090343.GA5355@progeny.tock>
In-Reply-To
<20091113085028.GA4804@progeny.tock>

On my slow laptop (P3 600MHz), system-wide bash completions take too much time to load (> 2s), and a significant fraction of this time is spent loading git-completion.bash:

$ time bash -c ". ./git-completion.bash"  # hot cache, before this patch

real 0m0.509s user 0m0.310s sys 0m0.180s

Kirill noticed that most of the time is spent warming up merge_strategy, all_command & porcelain_command caches.

Since git is not used in each and every interactive xterm, it seems best to load completion support with cold caches, and then load each needed thing lazily. This has most of the speed advantage of pre-generating everything at build time, without the complication of figuring out at build time what commands will be available at run time.

The result is that loading completion is significantly faster now:

$ time bash -c ". ./git-completion.bash"  # cold cache, after this patch

real 0m0.171s user 0m0.060s sys 0m0.040s

Suggested-by: Kirill Smelkov <kirr@mns.spb.ru>
Cc: Shawn O. Pearce <spearce@spearce.org>
Cc: Stephen Boyd <bebarino@gmail.com>
Cc: Sverre Rabbelier <srabbelier@gmail.com>
Cc: Junio C Hamano <junio@pobox.com>
Signed-off-by: Jonathan Nieder <jrnieder@gmail.com>
---
Jonathan Nieder wrote:
> Stephen Boyd wrote:
Show 6 quoted lines
>> Ah ok. I think this proves even more that pregenerating the
>> completion is a bad idea. With dynamic population we don't have
>> these problems and it only takes 250ms more to load on a P3
>> 700Mhz.
>
> Hmm, 250 ms is a lot.
Here’s the real patch.  Sorry about that.
Jonathan
 contrib/completion/git-completion.bash |   67 +++++++++++++++----------------
 1 files changed, 32 insertions(+), 35 deletions(-)
diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash
index e3ddecc..7088ec7 100755
--- a/contrib/completion/git-completion.bash
+++ b/contrib/completion/git-completion.bash
@@ -21,13 +21,7 @@
 #    2) Added the following line to your .bashrc:
 #        source ~/.git-completion.sh
 #
-#    3) You may want to make sure the git executable is available
-#       in your PATH before this script is sourced, as some caching
-#       is performed while the script loads.  If git isn't found
-#       at source time then all lookups will be done on demand,
-#       which may be slightly slower.
-#
-#    4) Consider changing your PS1 to also show the current branch:
+#    3) Consider changing your PS1 to also show the current branch:
 #        PS1='[\u@\h \W$(__git_ps1 " (%s)")]\$ '
 #
 #       The argument to __git_ps1 will be displayed only if you
@@ -324,12 +318,8 @@ __git_remotes ()
 	done
 }
 
-__git_merge_strategies ()
+__git_list_merge_strategies ()
 {
-	if [ -n "${__git_merge_strategylist-}" ]; then
-		echo "$__git_merge_strategylist"
-		return
-	fi
 	git merge -s help 2>&1 |
 	sed -n -e '/[Aa]vailable strategies are: /,/^$/{
 		s/\.$//
@@ -339,8 +329,11 @@ __git_merge_strategies ()
 		p
 	}'
 }
-__git_merge_strategylist=
-__git_merge_strategylist=$(__git_merge_strategies 2>/dev/null)
+
+__git_compute_merge_strategies ()
+{
+	: ${__git_merge_strategies=$(__git_list_merge_strategies)}
+}
 
 __git_complete_file ()
 {
@@ -474,27 +467,24 @@ __git_complete_remote_or_refspec ()
 
 __git_complete_strategy ()
 {
+	__git_compute_merge_strategies
 	case "${COMP_WORDS[COMP_CWORD-1]}" in
 	-s|--strategy)
-		__gitcomp "$(__git_merge_strategies)"
+		__gitcomp "$__git_merge_strategies"
 		return 0
 	esac
 	local cur="${COMP_WORDS[COMP_CWORD]}"
 	case "$cur" in
 	--strategy=*)
-		__gitcomp "$(__git_merge_strategies)" "" "${cur##--strategy=}"
+		__gitcomp "$__git_merge_strategies" "" "${cur##--strategy=}"
 		return 0
 		;;
 	esac
 	return 1
 }
 
-__git_all_commands ()
+__git_list_all_commands ()
 {
-	if [ -n "${__git_all_commandlist-}" ]; then
-		echo "$__git_all_commandlist"
-		return
-	fi
 	local i IFS=" "$'\n'
 	for i in $(git help -a|egrep '^  [a-zA-Z0-9]')
 	do
@@ -504,17 +494,17 @@ __git_all_commands ()
 		esac
 	done
 }
-__git_all_commandlist=
-__git_all_commandlist="$(__git_all_commands 2>/dev/null)"
 
-__git_porcelain_commands ()
+__git_compute_all_commands ()
+{
+	: ${__git_all_commands=$(__git_list_all_commands)}
+}
+
+__git_list_porcelain_commands ()
 {
-	if [ -n "${__git_porcelain_commandlist-}" ]; then
-		echo "$__git_porcelain_commandlist"
-		return
-	fi
 	local i IFS=" "$'\n'
-	for i in "help" $(__git_all_commands)
+	__git_compute_all_commands
+	for i in "help" $__git_all_commands
 	do
 		case $i in
 		*--*)             : helper pattern;;
@@ -595,8 +585,11 @@ __git_porcelain_commands ()
 		esac
 	done
 }
-__git_porcelain_commandlist=
-__git_porcelain_commandlist="$(__git_porcelain_commands 2>/dev/null)"
+
+__git_compute_porcelain_commands ()
+{
+	: ${__git_porcelain_commands=$(__git_list_porcelain_commands)}
+}
 
 __git_aliases ()
 {
@@ -1088,7 +1081,8 @@ _git_help ()
 		return
 		;;
 	esac
-	__gitcomp "$(__git_all_commands)
+	__git_compute_all_commands
+	__gitcomp "__git_all_commands
 		attributes cli core-tutorial cvs-migration
 		diffcore gitk glossary hooks ignore modules
 		repository-layout tutorial tutorial-2
@@ -1444,7 +1438,8 @@ _git_config ()
 		return
 		;;
 	pull.twohead|pull.octopus)
-		__gitcomp "$(__git_merge_strategies)"
+		__git_compute_merge_strategies
+		__gitcomp "$__git_merge_strategies"
 		return
 		;;
 	color.branch|color.diff|color.interactive|\
@@ -1545,7 +1540,8 @@ _git_config ()
 	pager.*)
 		local pfx="${cur%.*}."
 		cur="${cur#*.}"
-		__gitcomp "$(__git_all_commands)" "$pfx" "$cur"
+		__git_compute_all_commands
+		__gitcomp "__git_all_commands" "$pfx" "$cur"
 		return
 		;;
 	remote.*.*)
@@ -2142,7 +2138,8 @@ _git ()
 			--help
 			"
 			;;
-		*)     __gitcomp "$(__git_porcelain_commands) $(__git_aliases)" ;;
+		*)     __git_compute_porcelain_commands
+		       __gitcomp "$__git_porcelain_commands $(__git_aliases)" ;;
 		esac
 		return
 	fi
-- 
1.6.5.2
Previous: Jonathan NiederNext: Jonathan Nieder
Message 13 of 36 in “excerpts from tomorrow's "What's cooking" draft”
  1. Junio C HamanoNov 11, 2009
  2. Ben WaltonNov 11, 2009
  3. Johan HerlandNov 11, 2009
  4. Sverre RabbelierNov 11, 2009
  5. Daniel BarkalowNov 11, 2009
  6. Sverre RabbelierNov 15, 2009
  7. Daniel BarkalowNov 15, 2009
  8. ks/precompute-completionJonathan Nieder, Nov 11, 2009
  9. Stephen BoydNov 13, 2009
  10. Jonathan NiederNov 13, 2009
  11. Stephen BoydNov 13, 2009
  12. Speed up bash completion loadingJonathan Nieder, Nov 13, 2009
  13. Jonathan NiederNov 13, 2009
  14. Jonathan NiederNov 13, 2009
  15. Stephen BoydNov 13, 2009
  16. Jonathan NiederNov 14, 2009
  17. Jonathan NiederNov 14, 2009
  18. SZEDER GáborNov 14, 2009
  19. Jonathan NiederNov 14, 2009
  20. Stephen BoydNov 14, 2009
  21. Jonathan NiederNov 15, 2009
  22. Junio C HamanoNov 15, 2009
  23. Speed up bash completion loadingJonathan Nieder, Nov 15, 2009
  24. Shawn O. PearceNov 16, 2009
  25. Stephen BoydNov 16, 2009
  26. Speed up bash completion loadingJonathan Nieder, Nov 18, 2009
  27. Shawn O. PearceNov 18, 2009
  28. Nicolas SebrechtNov 11, 2009
  29. Nicolas PitreNov 11, 2009
  30. Petr BaudisNov 11, 2009
  31. Nicolas PitreNov 11, 2009
  32. Nicolas SebrechtNov 11, 2009
  33. Petr BaudisNov 11, 2009
  34. Nicolas PitreNov 11, 2009
  35. give priority to progress messagesNicolas Pitre, Nov 11, 2009
  36. Jakub NarebskiNov 11, 2009

Read the whole thread, see it on lore, or plain text.

$ cat FOOTERMessages come from the public archive at lore.kernel.org/git, fetched every hour. The front page is chosen and written each morning by an AI editor and can be wrong; the threads themselves are the record. About and API. For agents: an MCP server at https://gitlist.dev/mcp, and any thread, story or person page as Markdown by adding .md to its URL (or sending Accept: text/markdown). Details in /llms.txt.