There are several other ways; but for a decimal 32-bit integer, NumberOfSetBits is by far the fastest.
I recently stumbled upon Brian Kernighan's algorithm , which O(log(n)) has O(n) instead of most others. I do not know why it is not so fast here. but it still has a measurable advantage over all other non-specialized functions.
Of course, nothing can beat NumberOfSetBits with O(1) .
my tests are:
function getBitCount($value) { $count = 0; while($value) { $count += ($value & 1); $value = $value >> 1; } return $count; } function getBitCount2($value) { $count = 0; while($value) { if ($value & 1)$count++; $value >>= 1; } return $count; }
banchmark results (sorted)
> php count-bits.php 2.286831 s decbin 1.364934 s NumberOfSetBits 3.241821 s Kernighan 3.498779 s bitsBySubstr* 3.582412 s getBitCount2a 3.614841 s getBitCount2 3.751102 s getBitCount 3.769621 s bitsBySubstrInt* 5.806785 s bitsByPregMatchAll* 5.748319 s bitsByCountChars1* 6.350801 s bitsByNegPregReplace* 6.615289 s bitsByPregReplace* 13.863838 s getBitCount3 16.39626 s getBitCount3a 19.304038 s bitsByCountChars*
These are numbers from one of my launches (since PHP 7.0.22); others showed different orders in the group in 3.5 seconds. I can say that on my machine four of these five are pretty equal, and bitsBySubstrInt always a bit slower due to types.
For most other methods, decbin is required (which basically takes longer than the actual calculation, I noted them * in the test results); only BitsBySubstr would approach a winner without this gem leg.
It seems to me that you can make count_chars 3 times faster by restricting it to existing characters only. It seems like indexing an array is quite time consuming.
edit:
- another version of
preg_replace - fixed version of
preg_match_all - Kernigan algorithm added (fastest algorithm for integers of arbitrary size)
- garbage collection added to benchmarking function
- reran tests
source share