Skip to content

Instantly share code, notes, and snippets.

@pfactum
Created May 15, 2014 10:07
Show Gist options
  • Save pfactum/ce03564dd073fb779953 to your computer and use it in GitHub Desktop.
Save pfactum/ce03564dd073fb779953 to your computer and use it in GitHub Desktop.
diff --git a/libpfrng.c b/libpfrng.c
index f41c0c2..2cfefe2 100644
--- a/libpfrng.c
+++ b/libpfrng.c
@@ -56,8 +56,40 @@ pthread_mutex_t fast_lock;
/// Handles per-thread previous timings
static struct timespec *time_prev;
-/// Handles pre-calculated weights for bits rolling
-static double rolling_weights_table[UINT64_SIZE];
+/// Array of masks for bit rolling weighting
+const uint64_t rolling_rounds[][2] =
+{
+ {0x1ULL << 0ULL, 0},
+ {0x3ULL << 1ULL, 1},
+ {0xFULL << 3ULL, 2},
+ {0xFFULL << 7ULL, 4},
+ {0xFFFFULL << 15ULL, 8},
+ {0xFFFFFFFFULL << 31ULL, 16}
+};
+
+/**
+ * Returns Hamming weight of given number
+ *
+ * This function returns the amount of bits set in
+ * given integer number. If GCC is used, __builtin_popcount()
+ * is invoked instead of own algorithm.
+ *
+ * @param _value given number to examine
+ *
+ * @return Hamming weight of given number
+ */
+static uint8_t pfrng_popcount(uint64_t _value)
+{
+#ifdef __GNUC__
+ return __builtin_popcount(_value);
+#else /* __GNUC__ */
+ uint8_t ret = 0;
+ for (uint8_t i = 0; i < UINT64_SIZE; i++)
+ if ((_value >> i) & 1ULL)
+ ret++;
+ return ret;
+#endif /* __GNUC__ */
+}
/**
* Collapses uint64_t to one bit
@@ -71,23 +103,20 @@ static double rolling_weights_table[UINT64_SIZE];
*/
static inline uint8_t pfrng_get_rolled_bit(uint64_t _x)
{
+ uint8_t sum = 0;
+
// As _x is time value in nanoseconds, it's obvious that rightmost bits
// change faster than leftmost. So it's necessary to sum them with
- // different weights.
-
- double sum = 0;
- for (uint8_t i = 0; i < UINT64_SIZE; i++)
- {
- // Double negative trick is stolen from stackoverflow:
- // https://stackoverflow.com/questions/47981/how-do-you-set-clear-and-toggle-a-single-bit-in-c-c
- uint8_t current_bit = !!(_x & (1 << i));
- if (current_bit)
- sum += rolling_weights_table[i];
- }
- uint8_t integer_sum = (uint8_t)round(sum);
+ // different weights. Weighting is implemented via scaling window.
+ // On each step the amount of bits set in current windows is examined.
+ // If calculated amount is larger than threshold (half of window size),
+ // the resulting sum is XOR'ed. That's why it's required less rightmost bits
+ // than leftmost to trigger XOR.
+ for (unsigned int i = 0; i < sizeof(rolling_rounds) / sizeof(rolling_rounds[0]); i++)
+ if (pfrng_popcount(_x & rolling_rounds[i][0]) > rolling_rounds[i][1])
+ sum ^= 1;
- // Heart of uniform distribution: odds and evens appear with p=0.5
- return (integer_sum % 2) ? 1 : 0;
+ return sum;
}
/**
@@ -434,10 +463,6 @@ void pfrng_init(void)
if (unlikely(mixer_output == NULL || time_prev == NULL))
abort();
- // Init weights table
- for (uint8_t i = 0; i < UINT64_SIZE; i++)
- rolling_weights_table[i] = 1.0 / (i + 1);
-
// Init statistic variables
stats.bits_misses = 0;
for (uint8_t i = 0; i < UINT64_SIZE; i++)
@@ -471,9 +496,6 @@ void pfrng_init(void)
void pfrng_done(void)
{
// Cleanup
- for (uint8_t i = 0; i < UINT64_SIZE; i++)
- rolling_weights_table[i] = 0;
-
stats.bits_misses = 0;
for (uint8_t i = 0; i < UINT64_SIZE; i++)
stats.freq[i] = 0;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment