Merge pull request #800 from harfbuzz/bitops

Optimize set through optimized bit operations
This commit is contained in:
Behdad Esfahbod 2018-02-18 11:35:51 -08:00 committed by GitHub
commit 79d0d791b9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 204 additions and 97 deletions

View File

@ -20,6 +20,7 @@ LT_INIT([disable-static])
# Check for programs
AC_USE_SYSTEM_EXTENSIONS
AC_PROG_CC
AC_PROG_CC_C99
AM_PROG_CC_C_O
AC_PROG_CXX
dnl AX_CXX_COMPILE_STDCXX(11, noext, optional)
@ -75,7 +76,8 @@ GTK_DOC_CHECK([1.15],[--flavour no-tmpl])
AM_CONDITIONAL([ENABLE_GTK_DOC], false)
])
# Functions and headers
# Types, functions, and headers
AC_CHECK_TYPES(unsigned __int128)
AC_CHECK_FUNCS(atexit mprotect sysconf getpagesize mmap isatty newlocale strtod_l setlinebuf)
AC_CHECK_HEADERS(unistd.h sys/mman.h xlocale.h)

View File

@ -99,7 +99,7 @@ struct ValueFormat : HBUINT16
#endif
inline unsigned int get_len (void) const
{ return _hb_popcount32 ((unsigned int) *this); }
{ return _hb_popcount ((unsigned int) *this); }
inline unsigned int get_size (void) const
{ return get_len () * Value::static_size; }

View File

@ -139,7 +139,7 @@ hb_ot_map_builder_t::compile (hb_ot_map_t &m,
{
static_assert ((!(HB_GLYPH_FLAG_DEFINED & (HB_GLYPH_FLAG_DEFINED + 1))), "");
unsigned int global_bit_mask = HB_GLYPH_FLAG_DEFINED + 1;
unsigned int global_bit_shift = _hb_popcount32 (HB_GLYPH_FLAG_DEFINED);
unsigned int global_bit_shift = _hb_popcount (HB_GLYPH_FLAG_DEFINED);
m.global_mask = global_bit_mask;

View File

@ -51,7 +51,7 @@
#include <stdio.h>
#include <stdarg.h>
#if defined(_MSC_VER)
#if defined(_MSC_VER) || defined(__MINGW32__)
#include <intrin.h>
#endif
@ -322,72 +322,189 @@ static_assert ((sizeof (hb_var_int_t) == 4), "");
typedef const struct _hb_void_t *hb_void_t;
#define HB_VOID ((const _hb_void_t *) nullptr)
/* Return the number of 1 bits in mask. */
/* Return the number of 1 bits in v. */
template <typename T>
static inline HB_CONST_FUNC unsigned int
_hb_popcount32 (uint32_t mask)
_hb_popcount (T v)
{
#if __GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4)
return __builtin_popcount (mask);
#else
/* "HACKMEM 169" */
uint32_t y;
y = (mask >> 1) &033333333333;
y = mask - y - ((y >>1) & 033333333333);
return (((y + (y >> 3)) & 030707070707) % 077);
#if (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4)) && defined(__OPTIMIZE__)
if (sizeof (T) <= sizeof (unsigned int))
return __builtin_popcount (v);
if (sizeof (T) <= sizeof (unsigned long))
return __builtin_popcountl (v);
if (sizeof (T) <= sizeof (unsigned long long))
return __builtin_popcountll (v);
#endif
if (sizeof (T) <= 4)
{
/* "HACKMEM 169" */
uint32_t y;
y = (v >> 1) &033333333333;
y = v - y - ((y >>1) & 033333333333);
return (((y + (y >> 3)) & 030707070707) % 077);
}
if (sizeof (T) == 8)
{
unsigned int shift = 32;
return _hb_popcount<uint32_t> ((uint32_t) v) + _hb_popcount ((uint32_t) (v >> shift));
}
if (sizeof (T) == 16)
{
unsigned int shift = 64;
return _hb_popcount<uint64_t> ((uint64_t) v) + _hb_popcount ((uint64_t) (v >> shift));
}
assert (0);
}
static inline HB_CONST_FUNC unsigned int
_hb_popcount64 (uint64_t mask)
{
#if __GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4)
if (sizeof (long) >= sizeof (mask))
return __builtin_popcountl (mask);
#endif
return _hb_popcount32 (mask & 0xFFFFFFFF) + _hb_popcount32 (mask >> 32);
}
template <typename T> static inline unsigned int _hb_popcount (T mask);
template <> inline unsigned int _hb_popcount<uint32_t> (uint32_t mask) { return _hb_popcount32 (mask); }
template <> inline unsigned int _hb_popcount<uint64_t> (uint64_t mask) { return _hb_popcount64 (mask); }
/* Returns the number of bits needed to store number */
template <typename T>
static inline HB_CONST_FUNC unsigned int
_hb_bit_storage (unsigned int number)
_hb_bit_storage (T v)
{
if (unlikely (!v)) return 0;
#if defined(__GNUC__) && (__GNUC__ >= 4) && defined(__OPTIMIZE__)
return likely (number) ? (sizeof (unsigned int) * 8 - __builtin_clz (number)) : 0;
#elif defined(_MSC_VER)
unsigned long where;
if (_BitScanReverse (&where, number)) return 1 + where;
return 0;
#else
unsigned int n_bits = 0;
while (number) {
n_bits++;
number >>= 1;
}
return n_bits;
if (sizeof (T) <= sizeof (unsigned int))
return sizeof (unsigned int) * 8 - __builtin_clz (v);
if (sizeof (T) <= sizeof (unsigned long))
return sizeof (unsigned long) * 8 - __builtin_clzl (v);
if (sizeof (T) <= sizeof (unsigned long long))
return sizeof (unsigned long long) * 8 - __builtin_clzll (v);
#endif
#if defined(_MSC_VER) || defined(__MINGW32__)
if (sizeof (T) <= sizeof (unsigned int))
{
unsigned long where;
_BitScanReverse (&where, v);
return 1 + where;
}
# if _WIN64
if (sizeof (T) <= 8)
{
unsigned long where;
_BitScanReverse64 (&where, v);
return 1 + where;
}
# endif
#endif
if (sizeof (T) <= 4)
{
/* "bithacks" */
const unsigned int b[] = {0x2, 0xC, 0xF0, 0xFF00, 0xFFFF0000};
const unsigned int S[] = {1, 2, 4, 8, 16};
unsigned int r = 0;
for (int i = 4; i >= 0; i--)
if (v & b[i])
{
v >>= S[i];
r |= S[i];
}
return r + 1;
}
if (sizeof (T) <= 8)
{
/* "bithacks" */
const uint64_t b[] = {0x2, 0xC, 0xF0, 0xFF00, 0xFFFF0000, 0xFFFFFFFF00000000};
const unsigned int S[] = {1, 2, 4, 8, 16, 32};
unsigned int r = 0;
for (int i = 5; i >= 0; i--)
if (v & b[i])
{
v >>= S[i];
r |= S[i];
}
return r + 1;
}
if (sizeof (T) == 16)
{
unsigned int shift = 64;
return (v >> shift) ? _hb_bit_storage<uint64_t> ((uint64_t) v >> shift) + shift :
_hb_bit_storage<uint64_t> ((uint64_t) v);
}
assert (0);
}
/* Returns the number of zero bits in the least significant side of number */
/* Returns the number of zero bits in the least significant side of v */
template <typename T>
static inline HB_CONST_FUNC unsigned int
_hb_ctz (unsigned int number)
_hb_ctz (T v)
{
if (unlikely (!v)) return 0;
#if defined(__GNUC__) && (__GNUC__ >= 4) && defined(__OPTIMIZE__)
return likely (number) ? __builtin_ctz (number) : 0;
#elif defined(_MSC_VER)
unsigned long where;
if (_BitScanForward (&where, number)) return where;
return 0;
#else
unsigned int n_bits = 0;
if (unlikely (!number)) return 0;
while (!(number & 1)) {
n_bits++;
number >>= 1;
}
return n_bits;
if (sizeof (T) <= sizeof (unsigned int))
return __builtin_ctz (v);
if (sizeof (T) <= sizeof (unsigned long))
return __builtin_ctzl (v);
if (sizeof (T) <= sizeof (unsigned long long))
return __builtin_ctzll (v);
#endif
#if defined(_MSC_VER) || defined(__MINGW32__)
if (sizeof (T) <= sizeof (unsigned int))
{
unsigned long where;
_BitScanForward (&where, v);
return where;
}
# if _WIN64
if (sizeof (T) <= 8)
{
unsigned long where;
_BitScanForward64 (&where, v);
return where;
}
# endif
#endif
if (sizeof (T) <= 4)
{
/* "bithacks" */
unsigned int c = 32;
v &= - (int32_t) v;
if (v) c--;
if (v & 0x0000FFFF) c -= 16;
if (v & 0x00FF00FF) c -= 8;
if (v & 0x0F0F0F0F) c -= 4;
if (v & 0x33333333) c -= 2;
if (v & 0x55555555) c -= 1;
return c;
}
if (sizeof (T) <= 8)
{
/* "bithacks" */
unsigned int c = 64;
v &= - (int64_t) (v);
if (v) c--;
if (v & 0x00000000FFFFFFFF) c -= 32;
if (v & 0x0000FFFF0000FFFF) c -= 16;
if (v & 0x00FF00FF00FF00FF) c -= 8;
if (v & 0x0F0F0F0F0F0F0F0F) c -= 4;
if (v & 0x3333333333333333) c -= 2;
if (v & 0x5555555555555555) c -= 1;
return c;
}
if (sizeof (T) == 16)
{
unsigned int shift = 64;
return (uint64_t) v ? _hb_bit_storage<uint64_t> ((uint64_t) v) :
_hb_bit_storage<uint64_t> ((uint64_t) v >> shift) + shift;
}
assert (0);
}
static inline bool

View File

@ -109,21 +109,16 @@ struct hb_set_t
unsigned int i = m / ELT_BITS;
unsigned int j = m & ELT_MASK;
for (; j < ELT_BITS; j++)
if (v[i] & (elt_t (1) << j))
goto found;
for (i++; i < len (); i++)
if (v[i])
for (j = 0; j < ELT_BITS; j++)
if (v[i] & (elt_t (1) << j))
goto found;
const elt_t vv = v[i] & ~((elt_t (1) << j) - 1);
for (const elt_t *p = &vv; i < len (); p = &v[++i])
if (*p)
{
*codepoint = i * ELT_BITS + elt_get_min (*p);
return true;
}
*codepoint = INVALID;
return false;
found:
*codepoint = i * ELT_BITS + j;
return true;
}
inline bool previous (hb_codepoint_t *codepoint) const
{
@ -136,52 +131,39 @@ struct hb_set_t
unsigned int i = m / ELT_BITS;
unsigned int j = m & ELT_MASK;
for (; (int) j >= 0; j--)
if (v[i] & (elt_t (1) << j))
goto found;
for (i--; (int) i >= 0; i--)
if (v[i])
for (j = ELT_BITS - 1; (int) j >= 0; j--)
if (v[i] & (elt_t (1) << j))
goto found;
const elt_t vv = v[i] & ((elt_t (1) << (j + 1)) - 1);
for (const elt_t *p = &vv; (int) i >= 0; p = &v[--i])
if (*p)
{
*codepoint = i * ELT_BITS + elt_get_max (*p);
return true;
}
*codepoint = INVALID;
return false;
found:
*codepoint = i * ELT_BITS + j;
return true;
}
inline hb_codepoint_t get_min (void) const
{
for (unsigned int i = 0; i < len (); i++)
if (v[i])
{
elt_t e = v[i];
for (unsigned int j = 0; j < ELT_BITS; j++)
if (e & (elt_t (1) << j))
return i * ELT_BITS + j;
}
return i * ELT_BITS + elt_get_min (v[i]);
return INVALID;
}
inline hb_codepoint_t get_max (void) const
{
for (int i = len () - 1; i >= 0; i--)
if (v[i])
{
elt_t e = v[i];
for (int j = ELT_BITS - 1; j >= 0; j--)
if (e & (elt_t (1) << j))
return i * ELT_BITS + j;
}
return i * ELT_BITS + elt_get_max (v[i]);
return 0;
}
typedef uint32_t elt_t;
static const unsigned int ELT_BITS = sizeof (elt_t) * 8;
static const unsigned int PAGE_BITS = ELT_BITS * ELT_BITS; /* 1024. Use to tune. */
typedef unsigned long long elt_t;
static const unsigned int PAGE_BITS = 1024;
static_assert ((PAGE_BITS & ((PAGE_BITS) - 1)) == 0, "");
static inline unsigned int elt_get_min (const elt_t &elt) { return _hb_ctz (elt); }
static inline unsigned int elt_get_max (const elt_t &elt) { return _hb_bit_storage (elt) - 1; }
#if 0 && HAVE_VECTOR_SIZE
/* The vectorized version does not work with clang as non-const
* elt() errs "non-const reference cannot bind to vector element". */
@ -192,6 +174,7 @@ struct hb_set_t
vector_t v;
static const unsigned int ELT_BITS = sizeof (elt_t) * 8;
static const unsigned int ELT_MASK = ELT_BITS - 1;
static const unsigned int BITS = sizeof (vector_t) * 8;
static const unsigned int MASK = BITS - 1;

View File

@ -86,7 +86,7 @@ hb_subset_test_open_font (const char *font_path)
return face;
}
g_assert (false);
return NULL;
return NULL; /* Shut up, compiler! */
}
static inline hb_face_t *
@ -115,7 +115,7 @@ hb_subset_test_check (hb_face_t *expected,
{
hb_blob_t *expected_blob = hb_face_reference_table (expected, table);
hb_blob_t *actual_blob = hb_face_reference_table (actual, table);
hb_test_assert_blob_eq(expected_blob, actual_blob);
hb_test_assert_blobs_equal (expected_blob, actual_blob);
hb_blob_destroy (expected_blob);
hb_blob_destroy (actual_blob);
}

View File

@ -160,8 +160,14 @@ typedef void (*hb_test_fixture_func_t) (void);
#if !GLIB_CHECK_VERSION(2,30,0)
#define g_test_fail() g_error("Test failed")
#endif
#ifndef g_assert_true
#define g_assert_true g_assert
#endif
#ifndef g_assert_cmpmem
#define g_assert_cmpmem(m1, l1, m2, l2) g_assert_true (l1 == l2 && memcmp (m1, m2, l1) == 0)
#endif
static inline void hb_test_assert_blob_eq(hb_blob_t *expected_blob, hb_blob_t *actual_blob)
static inline void hb_test_assert_blobs_equal (hb_blob_t *expected_blob, hb_blob_t *actual_blob)
{
unsigned int expected_length, actual_length;
const char *raw_expected = hb_blob_get_data (expected_blob, &expected_length);
@ -170,7 +176,6 @@ static inline void hb_test_assert_blob_eq(hb_blob_t *expected_blob, hb_blob_t *a
g_assert_cmpint(0, ==, memcmp(raw_expected, raw_actual, expected_length));
}
static inline void
hb_test_add_func (const char *test_path,
hb_test_func_t test_func)