Ticket #13352: trac_13352_bitset_len_v3.patch

File trac_13352_bitset_len_v3.patch, 4.9 KB (added by dcoudert, 7 years ago)
  • sage/misc/bitset.pxi

    # HG changeset patch
    # User dcoudert <david.coudert@inria.fr>
    # Date 1376770316 -7200
    # Node ID c540f38e213a0d09f0f3b556e4fb50183e6c07bf
    # Parent  7b31b3a05a5ff78ea1b4c59880efdc1fb6638f99
    trac #13352 -- Running time improvement of the bitset_len method
    
    diff --git a/sage/misc/bitset.pxi b/sage/misc/bitset.pxi
    a b  
    438438    Calculate the number of items in the set (i.e., the number of nonzero bits).
    439439    """
    440440    cdef long len = 0
    441     cdef long i = bitset_first(bits)
    442     while i >= 0:
    443         len += 1
    444         i = bitset_next(bits, i + 1)
     441    cdef long i
     442    for i from 0 <= i < bits.limbs:
     443        if bits.bits[i]:
     444            len += popcountl(bits.bits[i])
    445445    return len
    446446
    447447cdef inline long bitset_hash(bitset_t bits):
  • sage/misc/bitset_pxd.pxi

    diff --git a/sage/misc/bitset_pxd.pxi b/sage/misc/bitset_pxd.pxi
    a b  
    3131    # corresponding limb number, while (n & offset_mask) gives the bit
    3232    # number inside of the limb.
    3333
     34cdef extern from "sage/misc/popcount.h":
     35    unsigned int popcountl(unsigned long)
     36
    3437cdef struct bitset_s:
    3538    # The size of a bitset B counts the maximum number of bits that B can
    3639    # hold. This size is independent of how many elements of B are toggled to
  • new file sage/misc/popcount.h

    diff --git a/sage/misc/popcount.h b/sage/misc/popcount.h
    new file mode 100644
    - +  
     1/*
     2  Fast methods for counting bits.
     3
     4  Implements some well known methods for counting the number of 1-bits in a 32
     5  and 64-bits integer. When available, it uses the __builtin_popcount or
     6  __builtin_popcountl methods.  Otherwise, it uses the MIT Hakmem 169 method.
     7  See e.g.  http://en.wikipedia.org/wiki/HAKMEM
     8
     9  Usage:
     10  1) Add this into a .pxi of .pyx file
     11
     12  cdef extern from "sage/misc/popcount.h":
     13      unsigned int popcount(unsigned int)
     14      unsigned int popcountl(unsigned long)
     15
     16  2) Then use popcount(i) for counting '1' bits in an unsigned int and
     17  popcountl(l) for counting '1' bits in an unsigned long
     18
     19*/
     20
     21//******************************************************************************
     22//    Copyright (C) 2013 David Coudert <david.coudert@inria.fr>
     23//
     24//   Distributed under the terms of the GNU General Public License (GPL)
     25//
     26//    This code is distributed in the hope that it will be useful,
     27//    but WITHOUT ANY WARRANTY; without even the implied warranty of
     28//    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     29//    General Public License for more details.
     30//
     31//  The full text of the GPL is available at:
     32//
     33//                  http://www.gnu.org/licenses/
     34//******************************************************************************
     35
     36#include <stdio.h>
     37
     38// MIT HAKMEM 169
     39
     40inline unsigned int popcount32(unsigned int i){
     41  // Returns the number of '1' bits in a 32-bits integer.
     42  i = i - ((i >> 1) & 0x55555555);
     43  i = (i & 0x33333333) + ((i >> 2) & 0x33333333);
     44  return( ((i + (i >> 4) & 0x0F0F0F0F) * 0x01010101) >> 24 );
     45}
     46
     47inline unsigned int popcount64(unsigned long i){
     48  // Returns the number of '1' bits in a 64-bits integer.
     49  i = i - ((i >> 1) & 0x5555555555555555ULL);
     50  i = (i & 0x3333333333333333ULL) + ((i >> 2) & 0x3333333333333333ULL);
     51  return( ( ((i + (i >> 4)) & 0x0f0f0f0f0f0f0f0fULL) * 0x0101010101010101ULL ) >> 56 );
     52}
     53
     54
     55
     56/*
     57 * Tricks to decide at first loading time if __builtin_popcount is available.
     58 * Otherwise, we use the MIT HAKMEM methods.
     59 *
     60 * This is based on a discussion initiated on a patch proposed by Cristian
     61 * Rodriguez <crrodriguez@opensuse.org> for lib/ext2fs/bitops.c. The discussion
     62 * can be found at http://patchwork.ozlabs.org/patch/209776/
     63 * Further explanation can be found on the online doc of gcc
     64 * http://gcc.gnu.org/onlinedocs/gcc/X86-Built_002din-Functions.html
     65 */
     66
     67// popcount for unsigned int
     68inline unsigned int popcount(unsigned int w) __attribute__ ((ifunc ("resolve_popcount")));
     69
     70__attribute__ ((__target__ ("popcnt")))
     71static unsigned int hakmem_popcount(unsigned int w){
     72  return (sizeof(unsigned int)==4? popcount32(w) : popcount64(w));
     73}
     74
     75static void (*resolve_popcount (void)) (void){
     76#if (__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8)
     77  __builtin_cpu_init();
     78  if (__builtin_cpu_supports("popcnt"))
     79    return __builtin_popcount;
     80#endif
     81  // Default behavior
     82  return hakmem_popcount;
     83}
     84
     85
     86// popcountl for unsigned long
     87inline unsigned int popcountl(unsigned long w) __attribute__ ((ifunc ("resolve_popcountl")));
     88
     89__attribute__ ((__target__ ("popcnt")))
     90static unsigned int hakmem_popcountl(unsigned long w){
     91  return (sizeof(unsigned long)==8? popcount64(w) : popcount32(w));
     92}
     93
     94static void (*resolve_popcountl (void)) (void){
     95#if (__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8)
     96  __builtin_cpu_init();
     97  if (__builtin_cpu_supports("popcnt"))
     98    return __builtin_popcountl;
     99#endif
     100  // Default behavior
     101  return hakmem_popcountl;
     102}