pack-objects: use mru list when iterating over packs

In the original implementation of want_object_in_pack(), we
always looked for the object in every pack, so the order did
not matter for performance.

As of the last few patches, however, we can now often break
out of the loop early after finding the first instance, and
avoid looking in the other packs at all. In this case, pack
order can make a big difference, because we'd like to find
the objects by looking at as few packs as possible.

This patch switches us to the same packed_git_mru list that
is now used by normal object lookups.

Here are timings for p5303 on linux.git:

Test                      HEAD^                HEAD
------------------------------------------------------------------------
5303.3: rev-list (1)      31.31(31.07+0.23)    31.28(31.00+0.27) -0.1%
5303.4: repack (1)        40.35(38.84+2.60)    40.53(39.31+2.32) +0.4%
5303.6: rev-list (50)     31.37(31.15+0.21)    31.41(31.16+0.24) +0.1%
5303.7: repack (50)       58.25(68.54+2.03)    47.28(57.66+1.89) -18.8%
5303.9: rev-list (1000)   31.91(31.57+0.33)    31.93(31.64+0.28) +0.1%
5303.10: repack (1000)    304.80(376.00+3.92)  87.21(159.54+2.84) -71.4%

The rev-list numbers are unchanged, which makes sense (they
are not exercising this code at all). The 50- and 1000-pack
repack cases show considerable improvement.

The single-pack repack case doesn't, of course; there's
nothing to improve. In fact, it gives us a baseline for how
fast we could possibly go. You can see that though rev-list
can approach the single-pack case even with 1000 packs,
repack doesn't. The reason is simple: the loop we are
optimizing is only part of what the repack is doing. After
the "counting" phase, we do delta compression, which is much
more expensive when there are multiple packs, because we
have fewer deltas we can reuse (you can also see that these
numbers come from a multicore machine; the CPU times are
much higher than the wall-clock times due to the delta
phase).

So the good news is that in cases with many packs, we used
to be dominated by the "counting" phase, and now we are
dominated by the delta compression (which is faster, and
which we have already parallelized).

Here are similar numbers for git.git:

Test                      HEAD^               HEAD
---------------------------------------------------------------------
5303.3: rev-list (1)      1.55(1.51+0.02)     1.54(1.53+0.00) -0.6%
5303.4: repack (1)        1.82(1.80+0.08)     1.82(1.78+0.09) +0.0%
5303.6: rev-list (50)     1.58(1.57+0.00)     1.58(1.56+0.01) +0.0%
5303.7: repack (50)       2.50(3.12+0.07)     2.31(2.95+0.06) -7.6%
5303.9: rev-list (1000)   2.22(2.20+0.02)     2.23(2.19+0.03) +0.5%
5303.10: repack (1000)    10.47(16.78+0.22)   7.50(13.76+0.22) -28.4%

Not as impressive in terms of percentage, but still
measurable wins.  If you look at the wall-clock time
improvements in the 1000-pack case, you can see that linux
improved by roughly 10x as many seconds as git. That's
because it has roughly 10x as many objects, and we'd expect
this improvement to scale linearly with the number of
objects (since the number of packs is kept constant). It's
just that the "counting" phase is a smaller percentage of
the total time spent for a git.git repack, and hence the
percentage win is smaller.

The implementation itself is a straightforward use of the
MRU code. We only bother marking a pack as used when we know
that we are able to break early out of the loop, for two
reasons:

  1. If we can't break out early, it does no good; we have
     to visit each pack anyway, so we might as well avoid
     even the minor overhead of managing the cache order.

  2. The mru_mark() function reorders the list, which would
     screw up our traversal. So it is only safe to mark when
     we are about to break out of the loop. We could record
     the found pack and mark it after the loop finishes, of
     course, but that's more complicated and it doesn't buy
     us anything due to (1).

Note that this reordering does have a potential impact on
the final pack, as we store only a single "found" pack for
each object, even if it is present in multiple packs. In
principle, any copy is acceptable, as they all refer to the
same content. But in practice, they may differ in whether
they are stored as deltas, against which base, etc. This may
have an impact on delta reuse, and even the delta search
(since we skip pairs that were already in the same pack).

It's not clear whether this change of order would hurt or
even help average cases, though. The most likely reason to
have duplicate objects is from the completion of thin packs
(e.g., you have some objects in a "base" pack, then receive
several pushes; the packs you receive may be thin on the
wire, with deltas that refer to bases outside the pack, but
we complete them with duplicate base objects when indexing
them).

In such a case the current code would always find the thin
duplicates (because we currently walk the packs in reverse
chronological order). Whereas with this patch, some of those
duplicates would be found in the base pack instead.

In my tests repacking a real-world case of linux.git with
3600 thin-pack pushes (on top of a large "base" pack), the
resulting pack was about 0.04% larger with this patch. On
the other hand, because we were more likely to hit the base
pack, there were more opportunities for delta reuse, and we
had 50,000 fewer objects to examine in the delta search.

Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
1 file changed
tree: 4412f1ceec8a177910386b29fc152b804a93b1b3
  1. block-sha1/
  2. builtin/
  3. ci/
  4. compat/
  5. contrib/
  6. Documentation/
  7. ewah/
  8. git-gui/
  9. gitk-git/
  10. gitweb/
  11. mergetools/
  12. perl/
  13. po/
  14. ppc/
  15. refs/
  16. t/
  17. templates/
  18. vcs-svn/
  19. xdiff/
  20. .gitattributes
  21. .gitignore
  22. .mailmap
  23. .travis.yml
  24. abspath.c
  25. aclocal.m4
  26. advice.c
  27. advice.h
  28. alias.c
  29. alloc.c
  30. archive-tar.c
  31. archive-zip.c
  32. archive.c
  33. archive.h
  34. argv-array.c
  35. argv-array.h
  36. attr.c
  37. attr.h
  38. base85.c
  39. bisect.c
  40. bisect.h
  41. blob.c
  42. blob.h
  43. branch.c
  44. branch.h
  45. builtin.h
  46. bulk-checkin.c
  47. bulk-checkin.h
  48. bundle.c
  49. bundle.h
  50. cache-tree.c
  51. cache-tree.h
  52. cache.h
  53. check-builtins.sh
  54. check-racy.c
  55. check_bindir
  56. color.c
  57. color.h
  58. column.c
  59. column.h
  60. combine-diff.c
  61. command-list.txt
  62. commit-slab.h
  63. commit.c
  64. commit.h
  65. common-main.c
  66. config.c
  67. config.mak.in
  68. config.mak.uname
  69. configure.ac
  70. connect.c
  71. connect.h
  72. connected.c
  73. connected.h
  74. convert.c
  75. convert.h
  76. copy.c
  77. COPYING
  78. credential-cache--daemon.c
  79. credential-cache.c
  80. credential-store.c
  81. credential.c
  82. credential.h
  83. csum-file.c
  84. csum-file.h
  85. ctype.c
  86. daemon.c
  87. date.c
  88. decorate.c
  89. decorate.h
  90. delta.h
  91. diff-delta.c
  92. diff-lib.c
  93. diff-no-index.c
  94. diff.c
  95. diff.h
  96. diffcore-break.c
  97. diffcore-delta.c
  98. diffcore-order.c
  99. diffcore-pickaxe.c
  100. diffcore-rename.c
  101. diffcore.h
  102. dir.c
  103. dir.h
  104. editor.c
  105. entry.c
  106. environment.c
  107. exec_cmd.c
  108. exec_cmd.h
  109. fast-import.c
  110. fetch-pack.c
  111. fetch-pack.h
  112. fmt-merge-msg.h
  113. fsck.c
  114. fsck.h
  115. generate-cmdlist.sh
  116. gettext.c
  117. gettext.h
  118. git-add--interactive.perl
  119. git-archimport.perl
  120. git-bisect.sh
  121. git-compat-util.h
  122. git-cvsexportcommit.perl
  123. git-cvsimport.perl
  124. git-cvsserver.perl
  125. git-difftool--helper.sh
  126. git-difftool.perl
  127. git-filter-branch.sh
  128. git-instaweb.sh
  129. git-merge-octopus.sh
  130. git-merge-one-file.sh
  131. git-merge-resolve.sh
  132. git-mergetool--lib.sh
  133. git-mergetool.sh
  134. git-p4.py
  135. git-parse-remote.sh
  136. git-quiltimport.sh
  137. git-rebase--am.sh
  138. git-rebase--interactive.sh
  139. git-rebase--merge.sh
  140. git-rebase.sh
  141. git-relink.perl
  142. git-remote-testgit.sh
  143. git-request-pull.sh
  144. git-send-email.perl
  145. git-sh-i18n.sh
  146. git-sh-setup.sh
  147. git-stash.sh
  148. git-submodule.sh
  149. git-svn.perl
  150. GIT-VERSION-GEN
  151. git-web--browse.sh
  152. git.c
  153. git.rc
  154. gpg-interface.c
  155. gpg-interface.h
  156. graph.c
  157. graph.h
  158. grep.c
  159. grep.h
  160. hashmap.c
  161. hashmap.h
  162. help.c
  163. help.h
  164. hex.c
  165. http-backend.c
  166. http-fetch.c
  167. http-push.c
  168. http-walker.c
  169. http.c
  170. http.h
  171. ident.c
  172. imap-send.c
  173. INSTALL
  174. khash.h
  175. kwset.c
  176. kwset.h
  177. levenshtein.c
  178. levenshtein.h
  179. LGPL-2.1
  180. line-log.c
  181. line-log.h
  182. line-range.c
  183. line-range.h
  184. list-objects.c
  185. list-objects.h
  186. ll-merge.c
  187. ll-merge.h
  188. lockfile.c
  189. lockfile.h
  190. log-tree.c
  191. log-tree.h
  192. mailinfo.c
  193. mailinfo.h
  194. mailmap.c
  195. mailmap.h
  196. Makefile
  197. match-trees.c
  198. merge-blobs.c
  199. merge-blobs.h
  200. merge-recursive.c
  201. merge-recursive.h
  202. merge.c
  203. mergesort.c
  204. mergesort.h
  205. mru.c
  206. mru.h
  207. name-hash.c
  208. notes-cache.c
  209. notes-cache.h
  210. notes-merge.c
  211. notes-merge.h
  212. notes-utils.c
  213. notes-utils.h
  214. notes.c
  215. notes.h
  216. object.c
  217. object.h
  218. pack-bitmap-write.c
  219. pack-bitmap.c
  220. pack-bitmap.h
  221. pack-check.c
  222. pack-objects.c
  223. pack-objects.h
  224. pack-revindex.c
  225. pack-revindex.h
  226. pack-write.c
  227. pack.h
  228. pager.c
  229. parse-options-cb.c
  230. parse-options.c
  231. parse-options.h
  232. patch-delta.c
  233. patch-ids.c
  234. patch-ids.h
  235. path.c
  236. pathspec.c
  237. pathspec.h
  238. pkt-line.c
  239. pkt-line.h
  240. preload-index.c
  241. pretty.c
  242. prio-queue.c
  243. prio-queue.h
  244. progress.c
  245. progress.h
  246. prompt.c
  247. prompt.h
  248. quote.c
  249. quote.h
  250. reachable.c
  251. reachable.h
  252. read-cache.c
  253. README.md
  254. ref-filter.c
  255. ref-filter.h
  256. reflog-walk.c
  257. reflog-walk.h
  258. refs.c
  259. refs.h
  260. remote-curl.c
  261. remote-testsvn.c
  262. remote.c
  263. remote.h
  264. replace_object.c
  265. rerere.c
  266. rerere.h
  267. resolve-undo.c
  268. resolve-undo.h
  269. revision.c
  270. revision.h
  271. run-command.c
  272. run-command.h
  273. send-pack.c
  274. send-pack.h
  275. sequencer.c
  276. sequencer.h
  277. server-info.c
  278. setup.c
  279. sh-i18n--envsubst.c
  280. sha1-array.c
  281. sha1-array.h
  282. sha1-lookup.c
  283. sha1-lookup.h
  284. sha1_file.c
  285. sha1_name.c
  286. shallow.c
  287. shell.c
  288. shortlog.h
  289. show-index.c
  290. sideband.c
  291. sideband.h
  292. sigchain.c
  293. sigchain.h
  294. split-index.c
  295. split-index.h
  296. strbuf.c
  297. strbuf.h
  298. streaming.c
  299. streaming.h
  300. string-list.c
  301. string-list.h
  302. submodule-config.c
  303. submodule-config.h
  304. submodule.c
  305. submodule.h
  306. symlinks.c
  307. tag.c
  308. tag.h
  309. tar.h
  310. tempfile.c
  311. tempfile.h
  312. thread-utils.c
  313. thread-utils.h
  314. trace.c
  315. trace.h
  316. trailer.c
  317. trailer.h
  318. transport-helper.c
  319. transport.c
  320. transport.h
  321. tree-diff.c
  322. tree-walk.c
  323. tree-walk.h
  324. tree.c
  325. tree.h
  326. unicode_width.h
  327. unimplemented.sh
  328. unix-socket.c
  329. unix-socket.h
  330. unpack-trees.c
  331. unpack-trees.h
  332. update_unicode.sh
  333. upload-pack.c
  334. url.c
  335. url.h
  336. urlmatch.c
  337. urlmatch.h
  338. usage.c
  339. userdiff.c
  340. userdiff.h
  341. utf8.c
  342. utf8.h
  343. varint.c
  344. varint.h
  345. version.c
  346. version.h
  347. versioncmp.c
  348. walker.c
  349. walker.h
  350. wildmatch.c
  351. wildmatch.h
  352. worktree.c
  353. worktree.h
  354. wrap-for-bin.sh
  355. wrapper.c
  356. write_or_die.c
  357. ws.c
  358. wt-status.c
  359. wt-status.h
  360. xdiff-interface.c
  361. xdiff-interface.h
  362. zlib.c
README.md

Git - fast, scalable, distributed revision control system

Git is a fast, scalable, distributed revision control system with an unusually rich command set that provides both high-level operations and full access to internals.

Git is an Open Source project covered by the GNU General Public License version 2 (some parts of it are under different licenses, compatible with the GPLv2). It was originally written by Linus Torvalds with help of a group of hackers around the net.

Please read the file INSTALL for installation instructions.

Many Git online resources are accessible from http://git-scm.com/ including full documentation and Git related tools.

See Documentation/gittutorial.txt to get started, then see Documentation/giteveryday.txt for a useful minimum set of commands, and Documentation/git-.txt for documentation of each command. If git has been correctly installed, then the tutorial can also be read with man gittutorial or git help tutorial, and the documentation of each command with man git-<commandname> or git help <commandname>.

CVS users may also want to read Documentation/gitcvs-migration.txt (man gitcvs-migration or git help cvs-migration if git is installed).

The user discussion and development of Git take place on the Git mailing list -- everyone is welcome to post bug reports, feature requests, comments and patches to git@vger.kernel.org (read Documentation/SubmittingPatches for instructions on patch submission). To subscribe to the list, send an email with just “subscribe git” in the body to majordomo@vger.kernel.org. The mailing list archives are available at http://news.gmane.org/gmane.comp.version-control.git/, http://marc.info/?l=git and other archival sites.

The maintainer frequently sends the “What's cooking” reports that list the current status of various development topics to the mailing list. The discussion following them give a good reference for project status, development direction and remaining tasks.

The name “git” was given by Linus Torvalds when he wrote the very first version. He described the tool as “the stupid content tracker” and the name as (depending on your mood):

  • random three-letter combination that is pronounceable, and not actually used by any common UNIX command. The fact that it is a mispronunciation of “get” may or may not be relevant.
  • stupid. contemptible and despicable. simple. Take your pick from the dictionary of slang.
  • “global information tracker”: you're in a good mood, and it actually works for you. Angels sing, and a light suddenly fills the room.
  • “goddamn idiotic truckload of sh*t”: when it breaks