bit_counter 0.1.0-java
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +16 -0
- data/.rspec +2 -0
- data/.travis.yml +10 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +21 -0
- data/README.md +100 -0
- data/Rakefile +18 -0
- data/benchmark/bignum.rb +65 -0
- data/benchmark/fixnum.rb +55 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/bit_counter.gemspec +30 -0
- data/ext/bit_counter/big_pack.h +14 -0
- data/ext/bit_counter/bignum_in_ulong.h +18 -0
- data/ext/bit_counter/bit_counter.c +172 -0
- data/ext/bit_counter/bit_counter.h +24 -0
- data/ext/bit_counter/extconf.rb +48 -0
- data/ext/bit_counter/popcountl.h +48 -0
- data/lib/bit_counter/core_ext.rb +21 -0
- data/lib/bit_counter/cruby.rb +8 -0
- data/lib/bit_counter/jruby.rb +28 -0
- data/lib/bit_counter/version.rb +3 -0
- data/lib/bit_counter.rb +34 -0
- metadata +123 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: f8eb8b47b481f6d69bf9450b7658e15d072917d5
|
4
|
+
data.tar.gz: 68cb49ca77de3442dec21a645909007ef1f2190b
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 1f7be43c9d726d95f5c4e8000174da52199849401721b042195585bdfe4f10ecec36cc695a21b10a12a00fd9ce9942cf1eae3f62e6c3f1c4a1412c8c8cad7a76
|
7
|
+
data.tar.gz: 9d422adedd20638cf5ec25730118ea66e14cdf7b13f25ae2f1a84aae643ef43903ab5b15f56f00e348393d6a7607bec1db1bb84af36330316781357782b9bedb
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2016 Jkr2255
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,100 @@
|
|
1
|
+
# BitCounter
|
2
|
+
|
3
|
+
Counting bits (popcount, Hamming weight) in integer, made faster.
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
Add this line to your application's Gemfile:
|
8
|
+
|
9
|
+
```ruby
|
10
|
+
gem 'bit_counter'
|
11
|
+
```
|
12
|
+
|
13
|
+
And then execute:
|
14
|
+
|
15
|
+
$ bundle
|
16
|
+
|
17
|
+
Or install it yourself as:
|
18
|
+
|
19
|
+
$ gem install bit_counter
|
20
|
+
|
21
|
+
## Usage
|
22
|
+
|
23
|
+
Module `BitCounter` appears after `require 'bit_counter'`.
|
24
|
+
|
25
|
+
`BitCounter.count(num)` returns bit count of `num`. (`num` must be `Integer` (`Fixnum` or `Bignum`); otherwise raises `TypeError`)
|
26
|
+
|
27
|
+
`BitCounter.count_fixnum(num)` and `BitCounter.count_bignum(num)` are provided, but directly using these are not recommended.
|
28
|
+
|
29
|
+
To use `Integer#bit_count`, `require 'bit_counter/core_ext'`. (This feature is not default behavior)
|
30
|
+
|
31
|
+
### for negative numbers
|
32
|
+
Theoretically, Ruby treats negative integers as if *infinite* 1-bits are leaded (`printf '%x', -1` gives `..f` ),
|
33
|
+
so counting 1 bits for negative numbers is meaningless.
|
34
|
+
|
35
|
+
In this implementation, `bit_count` for negative number counts *0* bits and negates the result. (distinguishing with positive version) Same in Ruby:
|
36
|
+
```rb
|
37
|
+
return -bit_count(~num) if num < 0
|
38
|
+
```
|
39
|
+
|
40
|
+
Note that both `bit_count(0)` and `bit_count(-1)` still return 0.
|
41
|
+
|
42
|
+
## Development
|
43
|
+
|
44
|
+
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
45
|
+
|
46
|
+
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
47
|
+
|
48
|
+
## Implementations
|
49
|
+
|
50
|
+
### JRuby
|
51
|
+
Java offers `Long.bitCount`, so simply calling this for `Fixnum`.
|
52
|
+
|
53
|
+
JRuby uses [`java.lang.BigInteger`](https://docs.oracle.com/javase/7/docs/api/java/math/BigInteger.html) for Ruby's `Bignum`, which also has `bitCount()` method.
|
54
|
+
|
55
|
+
### CRuby & Rubinius
|
56
|
+
In C extensions, `Bignum` is converted to array of `long`, and bit is counted using loops.
|
57
|
+
Bit counting of `long` is done by using `POPCNT` instruction, if available.
|
58
|
+
|
59
|
+
### Limitations
|
60
|
+
This gem is mainly developed on x64 GCC environment, so in other environments it may not work or be slow.
|
61
|
+
|
62
|
+
Pull requests for other environments are welcome.
|
63
|
+
|
64
|
+
## Benchmark
|
65
|
+
Compared to `num.to_s(2).count('1')` (popular method for popcount in CRuby), this gem is 5x - 20x faster.
|
66
|
+
|
67
|
+
```
|
68
|
+
# for fixnum
|
69
|
+
user system total real
|
70
|
+
String 7.770000 0.000000 7.770000 ( 7.771210)
|
71
|
+
Arithmetic 4.950000 0.000000 4.950000 ( 4.953082)
|
72
|
+
Loop 9.860000 0.000000 9.860000 ( 9.863400)
|
73
|
+
Gem 0.610000 0.000000 0.610000 ( 0.612627)
|
74
|
+
|
75
|
+
# for bignum
|
76
|
+
for 280736 bits, 1000 times
|
77
|
+
user system total real
|
78
|
+
String 1.470000 0.030000 1.500000 ( 1.491823)
|
79
|
+
Pack 8.810000 0.000000 8.810000 ( 8.823835)
|
80
|
+
Gem 0.040000 0.000000 0.040000 ( 0.032927)
|
81
|
+
|
82
|
+
for 80 bits, 1000000 times
|
83
|
+
user system total real
|
84
|
+
String 0.820000 0.000000 0.820000 ( 0.827302)
|
85
|
+
Pack 3.860000 0.000000 3.860000 ( 3.869715)
|
86
|
+
Gem 0.160000 0.000000 0.160000 ( 0.150728)
|
87
|
+
|
88
|
+
```
|
89
|
+
|
90
|
+
(Done in Core i3 (with POPCNT instruction), Linux x64, Ruby 2.3.0)
|
91
|
+
|
92
|
+
## Contributing
|
93
|
+
|
94
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/jkr2255/bit_counter.
|
95
|
+
|
96
|
+
|
97
|
+
## License
|
98
|
+
|
99
|
+
The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
|
100
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
2
|
+
require "rspec/core/rake_task"
|
3
|
+
|
4
|
+
RSpec::Core::RakeTask.new(:spec)
|
5
|
+
|
6
|
+
if RUBY_PLATFORM =~ /java/
|
7
|
+
task :default => [:spec]
|
8
|
+
else
|
9
|
+
require "rake/extensiontask"
|
10
|
+
|
11
|
+
task :build => :compile
|
12
|
+
|
13
|
+
Rake::ExtensionTask.new("bit_counter") do |ext|
|
14
|
+
ext.lib_dir = "lib/bit_counter"
|
15
|
+
end
|
16
|
+
|
17
|
+
task :default => [:clobber, :compile, :spec]
|
18
|
+
end
|
data/benchmark/bignum.rb
ADDED
@@ -0,0 +1,65 @@
|
|
1
|
+
require 'bit_counter'
|
2
|
+
require 'benchmark'
|
3
|
+
|
4
|
+
def count_string(num)
|
5
|
+
return -count_string(~num) if num < 0
|
6
|
+
num.to_s(2).count '1'
|
7
|
+
end
|
8
|
+
|
9
|
+
$table = 128.times.map { |i| BitCounter.count(i) }
|
10
|
+
|
11
|
+
def count_from_pack(num)
|
12
|
+
return -count_string(~num) if num < 0
|
13
|
+
[num].pack('w').chars.reduce(0) { |cnt, c| cnt + $table[c.ord & 0x7f] }
|
14
|
+
end
|
15
|
+
|
16
|
+
|
17
|
+
value = 7**100000
|
18
|
+
count = 1_000
|
19
|
+
|
20
|
+
if value.respond_to?(:bit_length)
|
21
|
+
puts "for #{value.bit_length} bits, #{count} times"
|
22
|
+
end
|
23
|
+
|
24
|
+
Benchmark.bm 10 do |r|
|
25
|
+
r.report "String" do
|
26
|
+
count.times do
|
27
|
+
count_string(value)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
r.report "Pack" do
|
31
|
+
count.times do
|
32
|
+
count_from_pack(value)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
r.report "C" do
|
36
|
+
count.times do
|
37
|
+
BitCounter.count_bignum(value)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
value = 2**80 - 6
|
43
|
+
count = 1_000_000
|
44
|
+
|
45
|
+
if value.respond_to?(:bit_length)
|
46
|
+
puts "for #{value.bit_length} bits, #{count} times"
|
47
|
+
end
|
48
|
+
|
49
|
+
Benchmark.bm 10 do |r|
|
50
|
+
r.report "String" do
|
51
|
+
count.times do
|
52
|
+
count_string(value)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
r.report "Pack" do
|
56
|
+
count.times do
|
57
|
+
count_from_pack(value)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
r.report "C" do
|
61
|
+
count.times do
|
62
|
+
BitCounter.count_bignum(value)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
data/benchmark/fixnum.rb
ADDED
@@ -0,0 +1,55 @@
|
|
1
|
+
require 'bit_counter'
|
2
|
+
require 'benchmark'
|
3
|
+
|
4
|
+
def count_string(num)
|
5
|
+
return -count_string(~num) if num < 0
|
6
|
+
num.to_s(2).count '1'
|
7
|
+
end
|
8
|
+
|
9
|
+
def count_arithmetic_32(x)
|
10
|
+
return -count_string(~x) if x < 0
|
11
|
+
m1 = 0x55555555
|
12
|
+
m2 = 0x33333333
|
13
|
+
m4 = 0x0f0f0f0f
|
14
|
+
x -= (x >> 1) & m1
|
15
|
+
x = (x & m2) + ((x >> 2) & m2)
|
16
|
+
x = (x + (x >> 4)) & m4
|
17
|
+
x += x >> 8
|
18
|
+
(x + (x >> 16)) & 0x3f
|
19
|
+
end
|
20
|
+
|
21
|
+
def count_bit_loop(x)
|
22
|
+
return -count_string(~x) if x < 0
|
23
|
+
ret = 0
|
24
|
+
while x > 0
|
25
|
+
ret += 1
|
26
|
+
x &= x - 1
|
27
|
+
end
|
28
|
+
ret
|
29
|
+
end
|
30
|
+
|
31
|
+
value = 0x3EDC_BA98
|
32
|
+
count = 10_000_000
|
33
|
+
|
34
|
+
Benchmark.bm 10 do |r|
|
35
|
+
r.report "String" do
|
36
|
+
count.times do
|
37
|
+
count_string(value)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
r.report "Arithmetic" do
|
41
|
+
count.times do
|
42
|
+
count_arithmetic_32(value)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
r.report "Loop" do
|
46
|
+
count.times do
|
47
|
+
count_bit_loop(value)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
r.report "Native" do
|
51
|
+
count.times do
|
52
|
+
BitCounter.count_fixnum(value)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
data/bin/console
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "bundler/setup"
|
4
|
+
require "bit_counter"
|
5
|
+
|
6
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
7
|
+
# with your gem easier. You can also use a different console, if you like.
|
8
|
+
|
9
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
10
|
+
# require "pry"
|
11
|
+
# Pry.start
|
12
|
+
|
13
|
+
require "irb"
|
14
|
+
IRB.start
|
data/bin/setup
ADDED
data/bit_counter.gemspec
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'bit_counter/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "bit_counter"
|
8
|
+
spec.version = BitCounter::VERSION
|
9
|
+
spec.authors = ["Jkr2255"]
|
10
|
+
spec.email = ["magnesium.oxide.play@gmail.com"]
|
11
|
+
|
12
|
+
spec.summary = %q{Calculate bit count (popcount, Hamming weight) faster in many environments.}
|
13
|
+
spec.homepage = "https://github.com/jkr2255/bit_counter"
|
14
|
+
spec.license = "MIT"
|
15
|
+
|
16
|
+
spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
17
|
+
spec.bindir = "exe"
|
18
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
19
|
+
spec.require_paths = ["lib"]
|
20
|
+
if RUBY_PLATFORM =~ /java/
|
21
|
+
spec.platform = 'java'
|
22
|
+
else
|
23
|
+
spec.extensions = ["ext/bit_counter/extconf.rb"]
|
24
|
+
end
|
25
|
+
|
26
|
+
spec.add_development_dependency "bundler", "~> 1.11"
|
27
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
28
|
+
spec.add_development_dependency "rake-compiler"
|
29
|
+
spec.add_development_dependency "rspec", "~> 3.0"
|
30
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
#ifndef BIG_PACK_H
|
2
|
+
#define BIG_PACK_H 1
|
3
|
+
|
4
|
+
#ifdef HAVE_RB_BIG_PACK
|
5
|
+
#define BIG_PACK(val, ptr, cnt) rb_big_pack(val, ptr, cnt)
|
6
|
+
#elif defined(HAVE_RB_INTEGER_PACK)
|
7
|
+
#define BIG_PACK(val, ptr, cnt) rb_integer_pack(val, ptr, cnt, sizeof(long), 0, \
|
8
|
+
INTEGER_PACK_LSWORD_FIRST|INTEGER_PACK_NATIVE_BYTE_ORDER| \
|
9
|
+
INTEGER_PACK_2COMP)
|
10
|
+
#else
|
11
|
+
#error This Ruby is not supported.
|
12
|
+
#endif
|
13
|
+
|
14
|
+
#endif
|
@@ -0,0 +1,18 @@
|
|
1
|
+
#ifndef BIGNUM_IN_ULONG_H
|
2
|
+
#define BIGNUM_IN_ULONG_H 1
|
3
|
+
|
4
|
+
#ifdef HAVE_RB_ABSINT_NUMWORDS
|
5
|
+
#define BIGNUM_IN_ULONG(v) rb_absint_numwords(v, sizeof(unsigned long), NULL)
|
6
|
+
#else
|
7
|
+
static size_t BIGNUM_IN_ULONG(VALUE v){
|
8
|
+
static ID size_id = 0;
|
9
|
+
VALUE ret_val;
|
10
|
+
if(!size_id){
|
11
|
+
size_id = rb_intern("size");
|
12
|
+
}
|
13
|
+
ret_val = rb_funcall(v, size_id, 0);
|
14
|
+
return (NUM2SIZET(ret_val) + sizeof(unsigned long) - 1) / sizeof(unsigned long);
|
15
|
+
}
|
16
|
+
#endif
|
17
|
+
|
18
|
+
#endif
|
@@ -0,0 +1,172 @@
|
|
1
|
+
#include "bit_counter.h"
|
2
|
+
|
3
|
+
VALUE rb_mBitCounter;
|
4
|
+
|
5
|
+
static VALUE bitcounter_cimpl_count_fixnum(VALUE self, VALUE num){
|
6
|
+
long l_num = NUM2LONG(num);
|
7
|
+
int val = POPCOUNTL(l_num);
|
8
|
+
if(l_num < 0) val -= sizeof(long) * CHAR_BIT;
|
9
|
+
return INT2FIX(val);
|
10
|
+
}
|
11
|
+
|
12
|
+
#ifdef HAVE___GET_CPUID
|
13
|
+
static VALUE bitcounter_cimpl_cpu_popcnt_p(VALUE self){
|
14
|
+
unsigned int eax, ebx, ecx = 0, edx;
|
15
|
+
__get_cpuid(1, &eax, &ebx, &ecx, &edx);
|
16
|
+
return (ecx & bit_POPCNT) ? Qtrue : Qfalse;
|
17
|
+
}
|
18
|
+
#else
|
19
|
+
static VALUE bitcounter_cimpl_cpu_popcnt_p(VALUE self){
|
20
|
+
return Qfalse;
|
21
|
+
}
|
22
|
+
#endif
|
23
|
+
|
24
|
+
#ifdef HAVE_POPCNT_GCC_ASM
|
25
|
+
static VALUE bitcounter_cimpl_count_fixnum_asm(VALUE self, VALUE num){
|
26
|
+
long l_num = NUM2LONG(num);
|
27
|
+
long val;
|
28
|
+
__asm__ volatile ("POPCNT %1, %0;": "=r"(val): "r"(l_num) : );
|
29
|
+
if(l_num < 0) val -= sizeof(long) * CHAR_BIT;
|
30
|
+
return INT2FIX(val);
|
31
|
+
}
|
32
|
+
#define ASM_POPCOUNT 1
|
33
|
+
#else
|
34
|
+
static VALUE bitcounter_cimpl_count_fixnum_asm(VALUE self, VALUE num){
|
35
|
+
/* dummy function for C compiler, never called from Ruby */
|
36
|
+
return Qnil;
|
37
|
+
}
|
38
|
+
#define ASM_POPCOUNT 0
|
39
|
+
#endif
|
40
|
+
|
41
|
+
/* for bignum */
|
42
|
+
static VALUE bitcounter_cimpl_count_bignum(VALUE self, VALUE num){
|
43
|
+
int negated = 0;
|
44
|
+
unsigned long * packed;
|
45
|
+
VALUE abs_num;
|
46
|
+
size_t words, i;
|
47
|
+
LONG_LONG ret = 0;
|
48
|
+
if(FIXNUM_P(num)){
|
49
|
+
return bitcounter_cimpl_count_fixnum(self, num);
|
50
|
+
}
|
51
|
+
Check_Type(num, T_BIGNUM);
|
52
|
+
if(RBIGNUM_NEGATIVE_P(num)){
|
53
|
+
negated = 1;
|
54
|
+
abs_num = BIG_NEG(num);
|
55
|
+
}else{
|
56
|
+
abs_num = num;
|
57
|
+
}
|
58
|
+
words = BIGNUM_IN_ULONG(abs_num);
|
59
|
+
if(words < ALLOCA_THRESHOLD){
|
60
|
+
packed = ALLOCA_N(unsigned long, words);
|
61
|
+
}else{
|
62
|
+
packed = ALLOC_N(unsigned long, words);
|
63
|
+
}
|
64
|
+
BIG_PACK(abs_num, packed, words);
|
65
|
+
for(i = 0; i < words; ++i){
|
66
|
+
ret += POPCOUNTL(packed[i]);
|
67
|
+
}
|
68
|
+
if(negated) ret = -ret;
|
69
|
+
if(words >= ALLOCA_THRESHOLD) xfree(packed);
|
70
|
+
return LL2NUM(ret);
|
71
|
+
}
|
72
|
+
|
73
|
+
#if defined(HAVE_POPCNT_LL_GCC_ASM) && (SIZEOF_LONG_LONG == SIZEOF_LONG * 2)
|
74
|
+
/* for Windows */
|
75
|
+
static VALUE bitcounter_cimpl_count_bignum_asm(VALUE self, VALUE num){
|
76
|
+
int negated = 0;
|
77
|
+
unsigned long * packed;
|
78
|
+
unsigned long long * ull_packed;
|
79
|
+
unsigned long long ull_i, ull_o = 0;
|
80
|
+
VALUE abs_num;
|
81
|
+
size_t words, i, ull_words;
|
82
|
+
LONG_LONG ret = 0;
|
83
|
+
if(FIXNUM_P(num)){
|
84
|
+
return bitcounter_cimpl_count_fixnum(self, num);
|
85
|
+
}
|
86
|
+
Check_Type(num, T_BIGNUM);
|
87
|
+
if(RBIGNUM_NEGATIVE_P(num)){
|
88
|
+
negated = 1;
|
89
|
+
abs_num = BIG_NEG(num);
|
90
|
+
}else{
|
91
|
+
abs_num = num;
|
92
|
+
}
|
93
|
+
words = BIGNUM_IN_ULONG(abs_num);
|
94
|
+
if(words < ALLOCA_THRESHOLD){
|
95
|
+
packed = ALLOCA_N(unsigned long, words);
|
96
|
+
}else{
|
97
|
+
packed = ALLOC_N(unsigned long, words);
|
98
|
+
}
|
99
|
+
BIG_PACK(abs_num, packed, words);
|
100
|
+
ull_words = words / 2;
|
101
|
+
ull_packed = (unsigned long long *) packed;
|
102
|
+
for(i = 0; i < ull_words; ++i){
|
103
|
+
ull_i = ull_packed[i];
|
104
|
+
__asm__ volatile ("POPCNT %1, %0;": "=r"(ull_o): "r"(ull_i) : );
|
105
|
+
ret += ull_o;
|
106
|
+
}
|
107
|
+
if(words & 1) ret += POPCOUNTL(packed[words-1]);
|
108
|
+
if(negated) ret = -ret;
|
109
|
+
if(words >= ALLOCA_THRESHOLD) xfree(packed);
|
110
|
+
return LL2NUM(ret);
|
111
|
+
}
|
112
|
+
|
113
|
+
#elif defined(HAVE_POPCNT_GCC_ASM)
|
114
|
+
static VALUE bitcounter_cimpl_count_bignum_asm(VALUE self, VALUE num){
|
115
|
+
int negated = 0;
|
116
|
+
unsigned long * packed;
|
117
|
+
unsigned long ul_i, ul_o = 0;
|
118
|
+
VALUE abs_num;
|
119
|
+
size_t words, i;
|
120
|
+
LONG_LONG ret = 0;
|
121
|
+
if(FIXNUM_P(num)){
|
122
|
+
return bitcounter_cimpl_count_fixnum(self, num);
|
123
|
+
}
|
124
|
+
Check_Type(num, T_BIGNUM);
|
125
|
+
if(RBIGNUM_NEGATIVE_P(num)){
|
126
|
+
negated = 1;
|
127
|
+
abs_num = BIG_NEG(num);
|
128
|
+
}else{
|
129
|
+
abs_num = num;
|
130
|
+
}
|
131
|
+
words = BIGNUM_IN_ULONG(abs_num);
|
132
|
+
if(words < ALLOCA_THRESHOLD){
|
133
|
+
packed = ALLOCA_N(unsigned long, words);
|
134
|
+
}else{
|
135
|
+
packed = ALLOC_N(unsigned long, words);
|
136
|
+
}
|
137
|
+
BIG_PACK(abs_num, packed, words);
|
138
|
+
for(i = 0; i < words; ++i){
|
139
|
+
ul_i = packed[i];
|
140
|
+
__asm__ volatile ("POPCNT %1, %0;": "=r"(ul_o): "r"(ul_i) : );
|
141
|
+
ret += ul_o;
|
142
|
+
}
|
143
|
+
if(negated) ret = -ret;
|
144
|
+
if(words >= ALLOCA_THRESHOLD) xfree(packed);
|
145
|
+
return LL2NUM(ret);
|
146
|
+
}
|
147
|
+
#else
|
148
|
+
static VALUE bitcounter_cimpl_count_bignum_asm(VALUE self, VALUE num){
|
149
|
+
/* dummy function for C compiler, never called from Ruby */
|
150
|
+
return Qnil;
|
151
|
+
}
|
152
|
+
|
153
|
+
#endif
|
154
|
+
|
155
|
+
|
156
|
+
void
|
157
|
+
Init_bit_counter(void)
|
158
|
+
{
|
159
|
+
VALUE rb_mCImpl;
|
160
|
+
VALUE have_cpu_popcnt;
|
161
|
+
rb_mBitCounter = rb_define_module("BitCounter");
|
162
|
+
rb_mCImpl = rb_define_module_under(rb_mBitCounter, "CImpl");
|
163
|
+
have_cpu_popcnt = bitcounter_cimpl_cpu_popcnt_p(rb_mCImpl);
|
164
|
+
rb_define_module_function(rb_mCImpl, "cpu_popcnt?", bitcounter_cimpl_cpu_popcnt_p, 0);
|
165
|
+
if(ASM_POPCOUNT && have_cpu_popcnt){
|
166
|
+
rb_define_method(rb_mCImpl, "count_fixnum", bitcounter_cimpl_count_fixnum_asm, 1);
|
167
|
+
rb_define_method(rb_mCImpl, "count_bignum", bitcounter_cimpl_count_bignum_asm, 1);
|
168
|
+
}else{
|
169
|
+
rb_define_method(rb_mCImpl, "count_fixnum", bitcounter_cimpl_count_fixnum, 1);
|
170
|
+
rb_define_method(rb_mCImpl, "count_bignum", bitcounter_cimpl_count_bignum, 1);
|
171
|
+
}
|
172
|
+
}
|
@@ -0,0 +1,24 @@
|
|
1
|
+
#ifndef BIT_COUNTER_H
|
2
|
+
#define BIT_COUNTER_H 1
|
3
|
+
|
4
|
+
#include "ruby.h"
|
5
|
+
|
6
|
+
#include <limits.h>
|
7
|
+
|
8
|
+
#ifdef HAVE_CPUID_H
|
9
|
+
#include <cpuid.h>
|
10
|
+
#endif
|
11
|
+
|
12
|
+
#include "popcountl.h"
|
13
|
+
#include "bignum_in_ulong.h"
|
14
|
+
#include "big_pack.h"
|
15
|
+
|
16
|
+
#ifdef HAVE_RB_BIG_XOR
|
17
|
+
#define BIG_NEG(val) rb_big_xor(val, INT2FIX(-1))
|
18
|
+
#else
|
19
|
+
#define BIG_NEG(val) rb_funcall(val, rb_intern("~"), 0)
|
20
|
+
#endif
|
21
|
+
|
22
|
+
#define ALLOCA_THRESHOLD (8192 / sizeof(unsigned long))
|
23
|
+
|
24
|
+
#endif /* BIT_COUNTER_H */
|
@@ -0,0 +1,48 @@
|
|
1
|
+
require "mkmf"
|
2
|
+
|
3
|
+
have_func('__builtin_popcountl(1)')
|
4
|
+
have_func('rb_absint_numwords')
|
5
|
+
have_func('rb_big_pack')
|
6
|
+
have_func('rb_integer_pack')
|
7
|
+
have_func('rb_big_xor')
|
8
|
+
have_header('cpuid.h') && have_func('__get_cpuid', 'cpuid.h')
|
9
|
+
check_sizeof('long')
|
10
|
+
check_sizeof('long long')
|
11
|
+
message 'checking for usability of POPCNT in GCC-style inline assembler... '
|
12
|
+
gcc_popcount = try_compile <<SRC
|
13
|
+
int main(){
|
14
|
+
long a=1, b=2;
|
15
|
+
__asm__ volatile ("POPCNT %1, %0;"
|
16
|
+
:"=r"(b)
|
17
|
+
:"r"(a)
|
18
|
+
:
|
19
|
+
);
|
20
|
+
return 0;
|
21
|
+
}
|
22
|
+
SRC
|
23
|
+
if gcc_popcount
|
24
|
+
message "yes\n"
|
25
|
+
$defs << '-DHAVE_POPCNT_GCC_ASM'
|
26
|
+
message 'checking for usability of POPCNT for long long in GCC-style inline assembler... '
|
27
|
+
gcc_ll_popcount = try_compile <<SRC
|
28
|
+
int main(){
|
29
|
+
long long a=1, b=2;
|
30
|
+
__asm__ volatile ("POPCNT %1, %0;"
|
31
|
+
:"=r"(b)
|
32
|
+
:"r"(a)
|
33
|
+
:
|
34
|
+
);
|
35
|
+
return 0;
|
36
|
+
}
|
37
|
+
SRC
|
38
|
+
if gcc_ll_popcount
|
39
|
+
message "yes\n"
|
40
|
+
$defs << '-DHAVE_POPCNT_LL_GCC_ASM'
|
41
|
+
else
|
42
|
+
message "no\n"
|
43
|
+
end
|
44
|
+
else
|
45
|
+
message "no\n"
|
46
|
+
end
|
47
|
+
|
48
|
+
create_makefile("bit_counter/bit_counter")
|
@@ -0,0 +1,48 @@
|
|
1
|
+
/* header for long popcount (without CPU-native) */
|
2
|
+
|
3
|
+
#ifndef POPCOUNTL_H
|
4
|
+
#define POPCOUNTL_H 1
|
5
|
+
|
6
|
+
#ifdef HAVE___BUILTIN_POPCOUNTL
|
7
|
+
#define POPCOUNTL(x) __builtin_popcountl(x)
|
8
|
+
#elif SIZEOF_LONG == 8
|
9
|
+
static inline int POPCOUNTL(unsigned long x){
|
10
|
+
x = ((x & 0xaaaaaaaaaaaaaaaaUL) >> 1)
|
11
|
+
+ (x & 0x5555555555555555UL);
|
12
|
+
x = ((x & 0xccccccccccccccccUL) >> 2)
|
13
|
+
+ (x & 0x3333333333333333UL);
|
14
|
+
x = ((x & 0xf0f0f0f0f0f0f0f0UL) >> 4)
|
15
|
+
+ (x & 0x0f0f0f0f0f0f0f0fUL);
|
16
|
+
x = ((x & 0xff00ff00ff00ff00UL) >> 8)
|
17
|
+
+ (x & 0x00ff00ff00ff00ffUL);
|
18
|
+
x = ((x & 0xffff0000ffff0000UL) >> 16)
|
19
|
+
+ (x & 0x0000ffff0000ffffUL);
|
20
|
+
x = ((x & 0xffffffff00000000UL) >> 32)
|
21
|
+
+ (x & 0x00000000ffffffffUL);
|
22
|
+
return (int) x;
|
23
|
+
}
|
24
|
+
#elif SIZEOF_LONG == 4
|
25
|
+
static inline int POPCOUNTL(unsigned long x){
|
26
|
+
x = ((x & 0xaaaaaaaaUL) >> 1)
|
27
|
+
+ (x & 0x55555555UL);
|
28
|
+
x = ((x & 0xccccccccUL) >> 2)
|
29
|
+
+ (x & 0x33333333UL);
|
30
|
+
x = ((x & 0xf0f0f0f0UL) >> 4)
|
31
|
+
+ (x & 0x0f0f0f0fUL);
|
32
|
+
x = ((x & 0xff00ff00UL) >> 8)
|
33
|
+
+ (x & 0x00ff00ffUL);
|
34
|
+
x = ((x & 0xffff0000UL) >> 16)
|
35
|
+
+ (x & 0x0000ffffUL);
|
36
|
+
return (int) x;
|
37
|
+
}
|
38
|
+
#else
|
39
|
+
#error Unsupported architecture
|
40
|
+
#endif
|
41
|
+
|
42
|
+
#if SIZEOF_LONG == 8
|
43
|
+
#define BER_MASK 0x7f7f7f7f7f7f7f7fUL
|
44
|
+
#elif SIZEOF_LONG == 4
|
45
|
+
#define BER_MASK 0x7f7f7f7fUL
|
46
|
+
#endif
|
47
|
+
|
48
|
+
#endif
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'bit_counter'
|
2
|
+
|
3
|
+
# :nodoc:
|
4
|
+
class Fixnum
|
5
|
+
#
|
6
|
+
# calling BitCounter.#count with self
|
7
|
+
#
|
8
|
+
def bit_count
|
9
|
+
BitCounter.count_fixnum(self)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
# :nodoc:
|
14
|
+
class Bignum
|
15
|
+
#
|
16
|
+
# calling BitCounter.#count with self
|
17
|
+
#
|
18
|
+
def bit_count
|
19
|
+
BitCounter.count_bignum(self)
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'java'
|
2
|
+
|
3
|
+
#
|
4
|
+
# module for bit counting
|
5
|
+
#
|
6
|
+
module BitCounter
|
7
|
+
|
8
|
+
#
|
9
|
+
# module for Java-specific codes of BitCounter.
|
10
|
+
# @note not intended for direct use.
|
11
|
+
#
|
12
|
+
module JavaImpl
|
13
|
+
def count_fixnum(num)
|
14
|
+
raise TypeError unless num.is_a?(::Fixnum)
|
15
|
+
count = Java::JavaLang::Long.bitCount(num)
|
16
|
+
num >= 0 ? count : count - 64
|
17
|
+
end
|
18
|
+
|
19
|
+
def count_bignum(num)
|
20
|
+
raise TypeError unless num.is_a?(::Bignum)
|
21
|
+
count = num.to_java.bitCount
|
22
|
+
num >= 0 ? count : -count
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
extend JavaImpl
|
27
|
+
|
28
|
+
end
|
data/lib/bit_counter.rb
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
require "bit_counter/version"
|
2
|
+
|
3
|
+
if RUBY_PLATFORM =~ /java/
|
4
|
+
require 'bit_counter/jruby'
|
5
|
+
else
|
6
|
+
require "bit_counter/bit_counter"
|
7
|
+
require 'bit_counter/cruby'
|
8
|
+
end
|
9
|
+
|
10
|
+
#
|
11
|
+
# module for bit counting
|
12
|
+
#
|
13
|
+
module BitCounter
|
14
|
+
|
15
|
+
module_function
|
16
|
+
|
17
|
+
#
|
18
|
+
# counts bits in Integer.
|
19
|
+
# @return [Integer] num the number of bits.
|
20
|
+
# if positive number is given, count 1 bits.
|
21
|
+
# if negative number is given, count 0 bits and -(count) is returned.
|
22
|
+
# @raise [TypeError] when non-Integer was given.
|
23
|
+
#
|
24
|
+
def count(num)
|
25
|
+
case num
|
26
|
+
when Fixnum
|
27
|
+
count_fixnum(num)
|
28
|
+
when Bignum
|
29
|
+
count_bignum(num)
|
30
|
+
else
|
31
|
+
raise TypeError
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
metadata
ADDED
@@ -0,0 +1,123 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: bit_counter
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: java
|
6
|
+
authors:
|
7
|
+
- Jkr2255
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2016-04-22 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
15
|
+
requirements:
|
16
|
+
- - "~>"
|
17
|
+
- !ruby/object:Gem::Version
|
18
|
+
version: '1.11'
|
19
|
+
name: bundler
|
20
|
+
prerelease: false
|
21
|
+
type: :development
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.11'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
requirement: !ruby/object:Gem::Requirement
|
29
|
+
requirements:
|
30
|
+
- - "~>"
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: '10.0'
|
33
|
+
name: rake
|
34
|
+
prerelease: false
|
35
|
+
type: :development
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '10.0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
requirement: !ruby/object:Gem::Requirement
|
43
|
+
requirements:
|
44
|
+
- - ">="
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
version: '0'
|
47
|
+
name: rake-compiler
|
48
|
+
prerelease: false
|
49
|
+
type: :development
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
requirement: !ruby/object:Gem::Requirement
|
57
|
+
requirements:
|
58
|
+
- - "~>"
|
59
|
+
- !ruby/object:Gem::Version
|
60
|
+
version: '3.0'
|
61
|
+
name: rspec
|
62
|
+
prerelease: false
|
63
|
+
type: :development
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '3.0'
|
69
|
+
description:
|
70
|
+
email:
|
71
|
+
- magnesium.oxide.play@gmail.com
|
72
|
+
executables: []
|
73
|
+
extensions: []
|
74
|
+
extra_rdoc_files: []
|
75
|
+
files:
|
76
|
+
- ".gitignore"
|
77
|
+
- ".rspec"
|
78
|
+
- ".travis.yml"
|
79
|
+
- Gemfile
|
80
|
+
- LICENSE.txt
|
81
|
+
- README.md
|
82
|
+
- Rakefile
|
83
|
+
- benchmark/bignum.rb
|
84
|
+
- benchmark/fixnum.rb
|
85
|
+
- bin/console
|
86
|
+
- bin/setup
|
87
|
+
- bit_counter.gemspec
|
88
|
+
- ext/bit_counter/big_pack.h
|
89
|
+
- ext/bit_counter/bignum_in_ulong.h
|
90
|
+
- ext/bit_counter/bit_counter.c
|
91
|
+
- ext/bit_counter/bit_counter.h
|
92
|
+
- ext/bit_counter/extconf.rb
|
93
|
+
- ext/bit_counter/popcountl.h
|
94
|
+
- lib/bit_counter.rb
|
95
|
+
- lib/bit_counter/core_ext.rb
|
96
|
+
- lib/bit_counter/cruby.rb
|
97
|
+
- lib/bit_counter/jruby.rb
|
98
|
+
- lib/bit_counter/version.rb
|
99
|
+
homepage: https://github.com/jkr2255/bit_counter
|
100
|
+
licenses:
|
101
|
+
- MIT
|
102
|
+
metadata: {}
|
103
|
+
post_install_message:
|
104
|
+
rdoc_options: []
|
105
|
+
require_paths:
|
106
|
+
- lib
|
107
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
108
|
+
requirements:
|
109
|
+
- - ">="
|
110
|
+
- !ruby/object:Gem::Version
|
111
|
+
version: '0'
|
112
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
113
|
+
requirements:
|
114
|
+
- - ">="
|
115
|
+
- !ruby/object:Gem::Version
|
116
|
+
version: '0'
|
117
|
+
requirements: []
|
118
|
+
rubyforge_project:
|
119
|
+
rubygems_version: 2.4.8
|
120
|
+
signing_key:
|
121
|
+
specification_version: 4
|
122
|
+
summary: Calculate bit count (popcount, Hamming weight) faster in many environments.
|
123
|
+
test_files: []
|