Skip to content
This repository was archived by the owner on Nov 9, 2017. It is now read-only.

Commit 4d1f0ef

Browse files
committed
Merge branch 'tr/xdiff-fast-hash'
Use word-at-a-time comparison to find end of line or NUL (end of buffer), borrowed from the linux-kernel discussion. By Thomas Rast * tr/xdiff-fast-hash: xdiff: choose XDL_FAST_HASH code on sizeof(long) instead of __WORDSIZE xdiff: load full words in the inner loop of xdl_hash_record
2 parents e834fa0 + 6f1af02 commit 4d1f0ef

File tree

2 files changed

+118
-0
lines changed

2 files changed

+118
-0
lines changed

Makefile

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -288,6 +288,11 @@ all::
288288
# dependency rules.
289289
#
290290
# Define NATIVE_CRLF if your platform uses CRLF for line endings.
291+
#
292+
# Define XDL_FAST_HASH to use an alternative line-hashing method in
293+
# the diff algorithm. It gives a nice speedup if your processor has
294+
# fast unaligned word loads. Does NOT work on big-endian systems!
295+
# Enabled by default on x86_64.
291296

292297
GIT-VERSION-FILE: FORCE
293298
@$(SHELL_PATH) ./GIT-VERSION-GEN
@@ -902,6 +907,9 @@ EXTLIBS =
902907
# because maintaining the nesting to match is a pain. If
903908
# we had "elif" things would have been much nicer...
904909

910+
ifeq ($(uname_M),x86_64)
911+
XDL_FAST_HASH = YesPlease
912+
endif
905913
ifeq ($(uname_S),OSF1)
906914
# Need this for u_short definitions et al
907915
BASIC_CFLAGS += -D_OSF_SOURCE
@@ -1775,6 +1783,10 @@ ifndef NO_MSGFMT_EXTENDED_OPTIONS
17751783
MSGFMT += --check --statistics
17761784
endif
17771785

1786+
ifneq (,$(XDL_FAST_HASH))
1787+
BASIC_CFLAGS += -DXDL_FAST_HASH
1788+
endif
1789+
17781790
ifeq ($(TCLTK_PATH),)
17791791
NO_TCLTK=NoThanks
17801792
endif

xdiff/xutils.c

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@
2020
*
2121
*/
2222

23+
#include <limits.h>
24+
#include <assert.h>
2325
#include "xinclude.h"
2426

2527

@@ -276,6 +278,109 @@ static unsigned long xdl_hash_record_with_whitespace(char const **data,
276278
return ha;
277279
}
278280

281+
#ifdef XDL_FAST_HASH
282+
283+
#define ONEBYTES 0x0101010101010101ul
284+
#define NEWLINEBYTES 0x0a0a0a0a0a0a0a0aul
285+
#define HIGHBITS 0x8080808080808080ul
286+
287+
/* Return the high bit set in the first byte that is a zero */
288+
static inline unsigned long has_zero(unsigned long a)
289+
{
290+
return ((a - ONEBYTES) & ~a) & HIGHBITS;
291+
}
292+
293+
static inline long count_masked_bytes(unsigned long mask)
294+
{
295+
if (sizeof(long) == 8) {
296+
/*
297+
* Jan Achrenius on G+: microoptimized version of
298+
* the simpler "(mask & ONEBYTES) * ONEBYTES >> 56"
299+
* that works for the bytemasks without having to
300+
* mask them first.
301+
*/
302+
return mask * 0x0001020304050608 >> 56;
303+
} else {
304+
/*
305+
* Modified Carl Chatfield G+ version for 32-bit *
306+
*
307+
* (a) gives us
308+
* -1 (0, ff), 0 (ffff) or 1 (ffffff)
309+
* (b) gives us
310+
* 0 for 0, 1 for (ff ffff ffffff)
311+
* (a+b+1) gives us
312+
* correct 0-3 bytemask count result
313+
*/
314+
long a = (mask - 256) >> 23;
315+
long b = mask & 1;
316+
return a + b + 1;
317+
}
318+
}
319+
320+
unsigned long xdl_hash_record(char const **data, char const *top, long flags)
321+
{
322+
unsigned long hash = 5381;
323+
unsigned long a = 0, mask = 0;
324+
char const *ptr = *data;
325+
char const *end = top - sizeof(unsigned long) + 1;
326+
327+
if (flags & XDF_WHITESPACE_FLAGS)
328+
return xdl_hash_record_with_whitespace(data, top, flags);
329+
330+
ptr -= sizeof(unsigned long);
331+
do {
332+
hash += hash << 5;
333+
hash ^= a;
334+
ptr += sizeof(unsigned long);
335+
if (ptr >= end)
336+
break;
337+
a = *(unsigned long *)ptr;
338+
/* Do we have any '\n' bytes in this word? */
339+
mask = has_zero(a ^ NEWLINEBYTES);
340+
} while (!mask);
341+
342+
if (ptr >= end) {
343+
/*
344+
* There is only a partial word left at the end of the
345+
* buffer. Because we may work with a memory mapping,
346+
* we have to grab the rest byte by byte instead of
347+
* blindly reading it.
348+
*
349+
* To avoid problems with masking in a signed value,
350+
* we use an unsigned char here.
351+
*/
352+
const char *p;
353+
for (p = top - 1; p >= ptr; p--)
354+
a = (a << 8) + *((const unsigned char *)p);
355+
mask = has_zero(a ^ NEWLINEBYTES);
356+
if (!mask)
357+
/*
358+
* No '\n' found in the partial word. Make a
359+
* mask that matches what we read.
360+
*/
361+
mask = 1UL << (8 * (top - ptr) + 7);
362+
}
363+
364+
/* The mask *below* the first high bit set */
365+
mask = (mask - 1) & ~mask;
366+
mask >>= 7;
367+
hash += hash << 5;
368+
hash ^= a & mask;
369+
370+
/* Advance past the last (possibly partial) word */
371+
ptr += count_masked_bytes(mask);
372+
373+
if (ptr < top) {
374+
assert(*ptr == '\n');
375+
ptr++;
376+
}
377+
378+
*data = ptr;
379+
380+
return hash;
381+
}
382+
383+
#else /* XDL_FAST_HASH */
279384

280385
unsigned long xdl_hash_record(char const **data, char const *top, long flags) {
281386
unsigned long ha = 5381;
@@ -293,6 +398,7 @@ unsigned long xdl_hash_record(char const **data, char const *top, long flags) {
293398
return ha;
294399
}
295400

401+
#endif /* XDL_FAST_HASH */
296402

297403
unsigned int xdl_hashbits(unsigned int size) {
298404
unsigned int val = 1, bits = 0;

0 commit comments

Comments
 (0)