Skip to content

Commit

Permalink
Merge branch 'jk/pack-bitmap'
Browse files Browse the repository at this point in the history
Borrow the bitmap index into packfiles from JGit to speed up
enumeration of objects involved in a commit range without having to
fully traverse the history.

* jk/pack-bitmap: (26 commits)
  ewah: unconditionally ntohll ewah data
  ewah: support platforms that require aligned reads
  read-cache: use get_be32 instead of hand-rolled ntoh_l
  block-sha1: factor out get_be and put_be wrappers
  do not discard revindex when re-preparing packfiles
  pack-bitmap: implement optional name_hash cache
  t/perf: add tests for pack bitmaps
  t: add basic bitmap functionality tests
  count-objects: recognize .bitmap in garbage-checking
  repack: consider bitmaps when performing repacks
  repack: handle optional files created by pack-objects
  repack: turn exts array into array-of-struct
  repack: stop using magic number for ARRAY_SIZE(exts)
  pack-objects: implement bitmap writing
  rev-list: add bitmap mode to speed up object lists
  pack-objects: use bitmaps when packing objects
  pack-objects: split add_object_entry
  pack-bitmap: add support for bitmap indexes
  documentation: add documentation for the bitmap format
  ewah: compressed bitmap implementation
  ...
  • Loading branch information
gitster committed Feb 27, 2014
2 parents 6784fab + 6b5b3a2 commit 0f9e62e
Show file tree
Hide file tree
Showing 33 changed files with 4,736 additions and 276 deletions.
25 changes: 25 additions & 0 deletions Documentation/config.txt
Expand Up @@ -1870,6 +1870,31 @@ pack.packSizeLimit::
Common unit suffixes of 'k', 'm', or 'g' are
supported.

pack.useBitmaps::
When true, git will use pack bitmaps (if available) when packing
to stdout (e.g., during the server side of a fetch). Defaults to
true. You should not generally need to turn this off unless
you are debugging pack bitmaps.

pack.writebitmaps::
When true, git will write a bitmap index when packing all
objects to disk (e.g., when `git repack -a` is run). This
index can speed up the "counting objects" phase of subsequent
packs created for clones and fetches, at the cost of some disk
space and extra time spent on the initial repack. Defaults to
false.

pack.writeBitmapHashCache::
When true, git will include a "hash cache" section in the bitmap
index (if one is written). This cache can be used to feed git's
delta heuristics, potentially leading to better deltas between
bitmapped and non-bitmapped objects (e.g., when serving a fetch
between an older, bitmapped pack and objects that have been
pushed since the last gc). The downside is that it consumes 4
bytes per object of disk space, and that JGit's bitmap
implementation does not understand it, causing it to complain if
Git and JGit are used on the same repository. Defaults to false.

pager.<cmd>::
If the value is boolean, turns on or off pagination of the
output of a particular Git subcommand when writing to a tty.
Expand Down
9 changes: 8 additions & 1 deletion Documentation/git-repack.txt
Expand Up @@ -9,7 +9,7 @@ git-repack - Pack unpacked objects in a repository
SYNOPSIS
--------
[verse]
'git repack' [-a] [-A] [-d] [-f] [-F] [-l] [-n] [-q] [--window=<n>] [--depth=<n>]
'git repack' [-a] [-A] [-d] [-f] [-F] [-l] [-n] [-q] [-b] [--window=<n>] [--depth=<n>]

DESCRIPTION
-----------
Expand Down Expand Up @@ -110,6 +110,13 @@ other objects in that pack they already have locally.
The default is unlimited, unless the config variable
`pack.packSizeLimit` is set.

-b::
--write-bitmap-index::
Write a reachability bitmap index as part of the repack. This
only makes sense when used with `-a` or `-A`, as the bitmaps
must be able to refer to all reachable objects. This option
overrides the setting of `pack.writebitmaps`.


Configuration
-------------
Expand Down
1 change: 1 addition & 0 deletions Documentation/git-rev-list.txt
Expand Up @@ -55,6 +55,7 @@ SYNOPSIS
[ \--reverse ]
[ \--walk-reflogs ]
[ \--no-walk ] [ \--do-walk ]
[ \--use-bitmap-index ]
<commit>... [ \-- <paths>... ]

DESCRIPTION
Expand Down
8 changes: 8 additions & 0 deletions Documentation/rev-list-options.txt
Expand Up @@ -257,6 +257,14 @@ See also linkgit:git-reflog[1].
Output excluded boundary commits. Boundary commits are
prefixed with `-`.

ifdef::git-rev-list[]
--use-bitmap-index::

Try to speed up the traversal using the pack bitmap index (if
one is available). Note that when traversing with `--objects`,
trees and blobs will not have their associated path printed.
endif::git-rev-list[]

--

History Simplification
Expand Down
164 changes: 164 additions & 0 deletions Documentation/technical/bitmap-format.txt
@@ -0,0 +1,164 @@
GIT bitmap v1 format
====================

- A header appears at the beginning:

4-byte signature: {'B', 'I', 'T', 'M'}

2-byte version number (network byte order)
The current implementation only supports version 1
of the bitmap index (the same one as JGit).

2-byte flags (network byte order)

The following flags are supported:

- BITMAP_OPT_FULL_DAG (0x1) REQUIRED
This flag must always be present. It implies that the bitmap
index has been generated for a packfile with full closure
(i.e. where every single object in the packfile can find
its parent links inside the same packfile). This is a
requirement for the bitmap index format, also present in JGit,
that greatly reduces the complexity of the implementation.

- BITMAP_OPT_HASH_CACHE (0x4)
If present, the end of the bitmap file contains
`N` 32-bit name-hash values, one per object in the
pack. The format and meaning of the name-hash is
described below.

4-byte entry count (network byte order)

The total count of entries (bitmapped commits) in this bitmap index.

20-byte checksum

The SHA1 checksum of the pack this bitmap index belongs to.

- 4 EWAH bitmaps that act as type indexes

Type indexes are serialized after the hash cache in the shape
of four EWAH bitmaps stored consecutively (see Appendix A for
the serialization format of an EWAH bitmap).

There is a bitmap for each Git object type, stored in the following
order:

- Commits
- Trees
- Blobs
- Tags

In each bitmap, the `n`th bit is set to true if the `n`th object
in the packfile is of that type.

The obvious consequence is that the OR of all 4 bitmaps will result
in a full set (all bits set), and the AND of all 4 bitmaps will
result in an empty bitmap (no bits set).

- N entries with compressed bitmaps, one for each indexed commit

Where `N` is the total amount of entries in this bitmap index.
Each entry contains the following:

- 4-byte object position (network byte order)
The position **in the index for the packfile** where the
bitmap for this commit is found.

- 1-byte XOR-offset
The xor offset used to compress this bitmap. For an entry
in position `x`, a XOR offset of `y` means that the actual
bitmap representing this commit is composed by XORing the
bitmap for this entry with the bitmap in entry `x-y` (i.e.
the bitmap `y` entries before this one).

Note that this compression can be recursive. In order to
XOR this entry with a previous one, the previous entry needs
to be decompressed first, and so on.

The hard-limit for this offset is 160 (an entry can only be
xor'ed against one of the 160 entries preceding it). This
number is always positive, and hence entries are always xor'ed
with **previous** bitmaps, not bitmaps that will come afterwards
in the index.

- 1-byte flags for this bitmap
At the moment the only available flag is `0x1`, which hints
that this bitmap can be re-used when rebuilding bitmap indexes
for the repository.

- The compressed bitmap itself, see Appendix A.

== Appendix A: Serialization format for an EWAH bitmap

Ewah bitmaps are serialized in the same protocol as the JAVAEWAH
library, making them backwards compatible with the JGit
implementation:

- 4-byte number of bits of the resulting UNCOMPRESSED bitmap

- 4-byte number of words of the COMPRESSED bitmap, when stored

- N x 8-byte words, as specified by the previous field

This is the actual content of the compressed bitmap.

- 4-byte position of the current RLW for the compressed
bitmap

All words are stored in network byte order for their corresponding
sizes.

The compressed bitmap is stored in a form of run-length encoding, as
follows. It consists of a concatenation of an arbitrary number of
chunks. Each chunk consists of one or more 64-bit words

H L_1 L_2 L_3 .... L_M

H is called RLW (run length word). It consists of (from lower to higher
order bits):

- 1 bit: the repeated bit B

- 32 bits: repetition count K (unsigned)

- 31 bits: literal word count M (unsigned)

The bitstream represented by the above chunk is then:

- K repetitions of B

- The bits stored in `L_1` through `L_M`. Within a word, bits at
lower order come earlier in the stream than those at higher
order.

The next word after `L_M` (if any) must again be a RLW, for the next
chunk. For efficient appending to the bitstream, the EWAH stores a
pointer to the last RLW in the stream.


== Appendix B: Optional Bitmap Sections

These sections may or may not be present in the `.bitmap` file; their
presence is indicated by the header flags section described above.

Name-hash cache
---------------

If the BITMAP_OPT_HASH_CACHE flag is set, the end of the bitmap contains
a cache of 32-bit values, one per object in the pack. The value at
position `i` is the hash of the pathname at which the `i`th object
(counting in index order) in the pack can be found. This can be fed
into the delta heuristics to compare objects with similar pathnames.

The hash algorithm used is:

hash = 0;
while ((c = *name++))
if (!isspace(c))
hash = (hash >> 2) + (c << 24);

Note that this hashing scheme is tied to the BITMAP_OPT_HASH_CACHE flag.
If implementations want to choose a different hashing scheme, they are
free to do so, but MUST allocate a new header flag (because comparing
hashes made under two different schemes would be pointless).
16 changes: 14 additions & 2 deletions Makefile
Expand Up @@ -664,6 +664,8 @@ LIB_H += diff.h
LIB_H += diffcore.h
LIB_H += dir.h
LIB_H += exec_cmd.h
LIB_H += ewah/ewok.h
LIB_H += ewah/ewok_rlw.h
LIB_H += fetch-pack.h
LIB_H += fmt-merge-msg.h
LIB_H += fsck.h
Expand Down Expand Up @@ -691,8 +693,10 @@ LIB_H += notes-merge.h
LIB_H += notes-utils.h
LIB_H += notes.h
LIB_H += object.h
LIB_H += pack-objects.h
LIB_H += pack-revindex.h
LIB_H += pack.h
LIB_H += pack-bitmap.h

This comment has been minimized.

Copy link
@Lxhenn

Lxhenn Jan 11, 2015

file:///home/ty/VirtualBox%20VMs/vaG2_default_1420676113804_81063/vaG2_default_1420676113804_81063.vbox-prev

LIB_H += parse-options.h
LIB_H += patch-ids.h
LIB_H += pathspec.h
Expand Down Expand Up @@ -796,6 +800,10 @@ LIB_OBJS += dir.o
LIB_OBJS += editor.o
LIB_OBJS += entry.o
LIB_OBJS += environment.o
LIB_OBJS += ewah/bitmap.o
LIB_OBJS += ewah/ewah_bitmap.o
LIB_OBJS += ewah/ewah_io.o
LIB_OBJS += ewah/ewah_rlw.o
LIB_OBJS += exec_cmd.o
LIB_OBJS += fetch-pack.o
LIB_OBJS += fsck.o
Expand Down Expand Up @@ -827,7 +835,10 @@ LIB_OBJS += notes-cache.o
LIB_OBJS += notes-merge.o
LIB_OBJS += notes-utils.o
LIB_OBJS += object.o
LIB_OBJS += pack-bitmap.o
LIB_OBJS += pack-bitmap-write.o
LIB_OBJS += pack-check.o
LIB_OBJS += pack-objects.o
LIB_OBJS += pack-revindex.o
LIB_OBJS += pack-write.o
LIB_OBJS += pager.o
Expand Down Expand Up @@ -2480,8 +2491,9 @@ profile-clean:
$(RM) $(addsuffix *.gcno,$(addprefix $(PROFILE_DIR)/, $(object_dirs)))

clean: profile-clean coverage-clean
$(RM) *.o *.res block-sha1/*.o ppc/*.o compat/*.o compat/*/*.o xdiff/*.o vcs-svn/*.o \
builtin/*.o $(LIB_FILE) $(XDIFF_LIB) $(VCSSVN_LIB)
$(RM) *.o *.res block-sha1/*.o ppc/*.o compat/*.o compat/*/*.o
$(RM) xdiff/*.o vcs-svn/*.o ewah/*.o builtin/*.o
$(RM) $(LIB_FILE) $(XDIFF_LIB) $(VCSSVN_LIB)
$(RM) $(ALL_PROGRAMS) $(SCRIPT_LIB) $(BUILT_INS) git$X
$(RM) $(TEST_PROGRAMS) $(NO_INSTALL)
$(RM) -r bin-wrappers $(dep_dirs)
Expand Down
32 changes: 0 additions & 32 deletions block-sha1/sha1.c
Expand Up @@ -62,38 +62,6 @@
#define setW(x, val) (W(x) = (val))
#endif

/*
* Performance might be improved if the CPU architecture is OK with
* unaligned 32-bit loads and a fast ntohl() is available.
* Otherwise fall back to byte loads and shifts which is portable,
* and is faster on architectures with memory alignment issues.
*/

#if defined(__i386__) || defined(__x86_64__) || \
defined(_M_IX86) || defined(_M_X64) || \
defined(__ppc__) || defined(__ppc64__) || \
defined(__powerpc__) || defined(__powerpc64__) || \
defined(__s390__) || defined(__s390x__)

#define get_be32(p) ntohl(*(unsigned int *)(p))
#define put_be32(p, v) do { *(unsigned int *)(p) = htonl(v); } while (0)

#else

#define get_be32(p) ( \
(*((unsigned char *)(p) + 0) << 24) | \
(*((unsigned char *)(p) + 1) << 16) | \
(*((unsigned char *)(p) + 2) << 8) | \
(*((unsigned char *)(p) + 3) << 0) )
#define put_be32(p, v) do { \
unsigned int __v = (v); \
*((unsigned char *)(p) + 0) = __v >> 24; \
*((unsigned char *)(p) + 1) = __v >> 16; \
*((unsigned char *)(p) + 2) = __v >> 8; \
*((unsigned char *)(p) + 3) = __v >> 0; } while (0)

#endif

/* This "rolls" over the 512-bit array */
#define W(x) (array[(x)&15])

Expand Down

0 comments on commit 0f9e62e

Please sign in to comment.