Skip to content

Commit 252d079

Browse files
pcloudsgitster
authored andcommitted
read-cache.c: optimize reading index format v4
Index format v4 requires some more computation to assemble a path based on a previous one. The current code is not very efficient because - it doubles memory copy, we assemble the final path in a temporary first before putting it back to a cache_entry - strbuf_remove() in expand_name_field() is not exactly a good fit for stripping a part at the end, _setlen() would do the same job and is much cheaper. - the open-coded loop to find the end of the string in expand_name_field() can't beat an optimized strlen() This patch avoids the temporary buffer and writes directly to the new cache_entry, which addresses the first two points. The last point could also be avoided if the total string length fits in the first 12 bits of ce_flags, if not we fall back to strlen(). Running "test-tool read-cache 100" on webkit.git (275k files), reading v2 only takes 4.226 seconds, while v4 takes 5.711 seconds, 35% more time. The patch reduces read time on v4 to 4.319 seconds. Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
1 parent fe8321e commit 252d079

File tree

1 file changed

+60
-68
lines changed

1 file changed

+60
-68
lines changed

read-cache.c

Lines changed: 60 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -1713,63 +1713,24 @@ int read_index(struct index_state *istate)
17131713
return read_index_from(istate, get_index_file(), get_git_dir());
17141714
}
17151715

1716-
static struct cache_entry *cache_entry_from_ondisk(struct mem_pool *mem_pool,
1717-
struct ondisk_cache_entry *ondisk,
1718-
unsigned int flags,
1719-
const char *name,
1720-
size_t len)
1721-
{
1722-
struct cache_entry *ce = mem_pool__ce_alloc(mem_pool, len);
1723-
1724-
ce->ce_stat_data.sd_ctime.sec = get_be32(&ondisk->ctime.sec);
1725-
ce->ce_stat_data.sd_mtime.sec = get_be32(&ondisk->mtime.sec);
1726-
ce->ce_stat_data.sd_ctime.nsec = get_be32(&ondisk->ctime.nsec);
1727-
ce->ce_stat_data.sd_mtime.nsec = get_be32(&ondisk->mtime.nsec);
1728-
ce->ce_stat_data.sd_dev = get_be32(&ondisk->dev);
1729-
ce->ce_stat_data.sd_ino = get_be32(&ondisk->ino);
1730-
ce->ce_mode = get_be32(&ondisk->mode);
1731-
ce->ce_stat_data.sd_uid = get_be32(&ondisk->uid);
1732-
ce->ce_stat_data.sd_gid = get_be32(&ondisk->gid);
1733-
ce->ce_stat_data.sd_size = get_be32(&ondisk->size);
1734-
ce->ce_flags = flags & ~CE_NAMEMASK;
1735-
ce->ce_namelen = len;
1736-
ce->index = 0;
1737-
hashcpy(ce->oid.hash, ondisk->sha1);
1738-
memcpy(ce->name, name, len);
1739-
ce->name[len] = '\0';
1740-
return ce;
1741-
}
1742-
1743-
/*
1744-
* Adjacent cache entries tend to share the leading paths, so it makes
1745-
* sense to only store the differences in later entries. In the v4
1746-
* on-disk format of the index, each on-disk cache entry stores the
1747-
* number of bytes to be stripped from the end of the previous name,
1748-
* and the bytes to append to the result, to come up with its name.
1749-
*/
1750-
static unsigned long expand_name_field(struct strbuf *name, const char *cp_)
1751-
{
1752-
const unsigned char *ep, *cp = (const unsigned char *)cp_;
1753-
size_t len = decode_varint(&cp);
1754-
1755-
if (name->len < len)
1756-
die("malformed name field in the index");
1757-
strbuf_remove(name, name->len - len, len);
1758-
for (ep = cp; *ep; ep++)
1759-
; /* find the end */
1760-
strbuf_add(name, cp, ep - cp);
1761-
return (const char *)ep + 1 - cp_;
1762-
}
1763-
1764-
static struct cache_entry *create_from_disk(struct mem_pool *mem_pool,
1716+
static struct cache_entry *create_from_disk(struct index_state *istate,
17651717
struct ondisk_cache_entry *ondisk,
17661718
unsigned long *ent_size,
1767-
struct strbuf *previous_name)
1719+
const struct cache_entry *previous_ce)
17681720
{
17691721
struct cache_entry *ce;
17701722
size_t len;
17711723
const char *name;
17721724
unsigned int flags;
1725+
size_t copy_len;
1726+
/*
1727+
* Adjacent cache entries tend to share the leading paths, so it makes
1728+
* sense to only store the differences in later entries. In the v4
1729+
* on-disk format of the index, each on-disk cache entry stores the
1730+
* number of bytes to be stripped from the end of the previous name,
1731+
* and the bytes to append to the result, to come up with its name.
1732+
*/
1733+
int expand_name_field = istate->version == 4;
17731734

17741735
/* On-disk flags are just 16 bits */
17751736
flags = get_be16(&ondisk->flags);
@@ -1789,21 +1750,54 @@ static struct cache_entry *create_from_disk(struct mem_pool *mem_pool,
17891750
else
17901751
name = ondisk->name;
17911752

1792-
if (!previous_name) {
1793-
/* v3 and earlier */
1794-
if (len == CE_NAMEMASK)
1795-
len = strlen(name);
1796-
ce = cache_entry_from_ondisk(mem_pool, ondisk, flags, name, len);
1753+
if (expand_name_field) {
1754+
const unsigned char *cp = (const unsigned char *)name;
1755+
size_t strip_len, previous_len;
17971756

1798-
*ent_size = ondisk_ce_size(ce);
1799-
} else {
1800-
unsigned long consumed;
1801-
consumed = expand_name_field(previous_name, name);
1802-
ce = cache_entry_from_ondisk(mem_pool, ondisk, flags,
1803-
previous_name->buf,
1804-
previous_name->len);
1757+
previous_len = previous_ce ? previous_ce->ce_namelen : 0;
1758+
strip_len = decode_varint(&cp);
1759+
if (previous_len < strip_len) {
1760+
if (previous_ce)
1761+
die(_("malformed name field in the index, near path '%s'"),
1762+
previous_ce->name);
1763+
else
1764+
die(_("malformed name field in the index in the first path"));
1765+
}
1766+
copy_len = previous_len - strip_len;
1767+
name = (const char *)cp;
1768+
}
1769+
1770+
if (len == CE_NAMEMASK) {
1771+
len = strlen(name);
1772+
if (expand_name_field)
1773+
len += copy_len;
1774+
}
1775+
1776+
ce = mem_pool__ce_alloc(istate->ce_mem_pool, len);
1777+
1778+
ce->ce_stat_data.sd_ctime.sec = get_be32(&ondisk->ctime.sec);
1779+
ce->ce_stat_data.sd_mtime.sec = get_be32(&ondisk->mtime.sec);
1780+
ce->ce_stat_data.sd_ctime.nsec = get_be32(&ondisk->ctime.nsec);
1781+
ce->ce_stat_data.sd_mtime.nsec = get_be32(&ondisk->mtime.nsec);
1782+
ce->ce_stat_data.sd_dev = get_be32(&ondisk->dev);
1783+
ce->ce_stat_data.sd_ino = get_be32(&ondisk->ino);
1784+
ce->ce_mode = get_be32(&ondisk->mode);
1785+
ce->ce_stat_data.sd_uid = get_be32(&ondisk->uid);
1786+
ce->ce_stat_data.sd_gid = get_be32(&ondisk->gid);
1787+
ce->ce_stat_data.sd_size = get_be32(&ondisk->size);
1788+
ce->ce_flags = flags & ~CE_NAMEMASK;
1789+
ce->ce_namelen = len;
1790+
ce->index = 0;
1791+
hashcpy(ce->oid.hash, ondisk->sha1);
18051792

1806-
*ent_size = (name - ((char *)ondisk)) + consumed;
1793+
if (expand_name_field) {
1794+
if (copy_len)
1795+
memcpy(ce->name, previous_ce->name, copy_len);
1796+
memcpy(ce->name + copy_len, name, len + 1 - copy_len);
1797+
*ent_size = (name - ((char *)ondisk)) + len + 1 - copy_len;
1798+
} else {
1799+
memcpy(ce->name, name, len + 1);
1800+
*ent_size = ondisk_ce_size(ce);
18071801
}
18081802
return ce;
18091803
}
@@ -1898,7 +1892,7 @@ int do_read_index(struct index_state *istate, const char *path, int must_exist)
18981892
struct cache_header *hdr;
18991893
void *mmap;
19001894
size_t mmap_size;
1901-
struct strbuf previous_name_buf = STRBUF_INIT, *previous_name;
1895+
const struct cache_entry *previous_ce = NULL;
19021896

19031897
if (istate->initialized)
19041898
return istate->cache_nr;
@@ -1936,11 +1930,9 @@ int do_read_index(struct index_state *istate, const char *path, int must_exist)
19361930
istate->initialized = 1;
19371931

19381932
if (istate->version == 4) {
1939-
previous_name = &previous_name_buf;
19401933
mem_pool_init(&istate->ce_mem_pool,
19411934
estimate_cache_size_from_compressed(istate->cache_nr));
19421935
} else {
1943-
previous_name = NULL;
19441936
mem_pool_init(&istate->ce_mem_pool,
19451937
estimate_cache_size(mmap_size, istate->cache_nr));
19461938
}
@@ -1952,12 +1944,12 @@ int do_read_index(struct index_state *istate, const char *path, int must_exist)
19521944
unsigned long consumed;
19531945

19541946
disk_ce = (struct ondisk_cache_entry *)((char *)mmap + src_offset);
1955-
ce = create_from_disk(istate->ce_mem_pool, disk_ce, &consumed, previous_name);
1947+
ce = create_from_disk(istate, disk_ce, &consumed, previous_ce);
19561948
set_index_entry(istate, i, ce);
19571949

19581950
src_offset += consumed;
1951+
previous_ce = ce;
19591952
}
1960-
strbuf_release(&previous_name_buf);
19611953
istate->timestamp.sec = st.st_mtime;
19621954
istate->timestamp.nsec = ST_MTIME_NSEC(st);
19631955

0 commit comments

Comments
 (0)