Skip to content

Commit 62e6d2b

Browse files
committed
Speeding up C implementation
Switch from Sarwate's algorithm to byte-order free Slicing-by-8. The specialized so files for the built-in CRCs by digest-crc contain only the initialization functions of the library and the table data. Each table data file is generated by `extconf.rb`. User-defined CRC classes are also accelerated since a generic `Digest::CRC#update` is provided. However, the `Digest::CRC::POLYNOMIAL` constants must be overridden. Also, at build time, the environment variable "RUBY_DIGEST_CRC_ENABLE_SLICING_BY_16=1" can be used to enable "Slicing-by-16" for a further slight speedup.
1 parent 91ee75f commit 62e6d2b

File tree

135 files changed

+1017
-2528
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

135 files changed

+1017
-2528
lines changed

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ pkg
66
/ext/digest/crc*/Makefile
77
/ext/digest/crc*/mkmf.log
88
/ext/digest/crc*/extconf.h
9+
/ext/digest/crc*/*_table.h
910
/ext/digest/crc*/*.o
1011
/ext/digest/crc*/*.so
1112
/spec/integration/docker/digest-crc.gem

README.md

+17
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,8 @@ module Digest
117117

118118
WIDTH = 32
119119

120+
POLYNOMIAL = 0x04c11db7
121+
120122
REFLECT_INPUT = true
121123

122124
INIT_CRC = 0xffffffff
@@ -130,6 +132,21 @@ module Digest
130132
end
131133
```
132134

135+
### Notes on using with Ractor
136+
137+
* The `Digest::CRC#update` method in Ruby requires `YourCRC::TABLE` to be
138+
in the "frozen" state. This is because if `YourCRC::TABLE` is mutable,
139+
it can only be referenced by the main Ractor.
140+
* The `Digest::CRC#update` method by C must first be called in the main
141+
Ractor. This is because the internal tables must be initialized from the
142+
main Ractor.
143+
144+
```ruby
145+
# To digest empty characters in the main Ractor
146+
# for initialization of the internal tables.
147+
YourCRC.digest("")
148+
```
149+
133150
## Benchmarks
134151

135152
### Ruby 2.7.4 (pure Ruby)

Rakefile

+1
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ end
2020
require 'rake/clean'
2121
CLEAN.include('ext/digest/crc*/extconf.h')
2222
CLEAN.include('ext/digest/crc*/Makefile')
23+
CLEAN.include('ext/digest/crc*/*_table.h')
2324
CLEAN.include('ext/digest/crc*/*.o')
2425
CLEAN.include('ext/digest/crc*/*.so')
2526

ext/digest/Rakefile

+2
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ CRCS = Dir['crc*']
1717
DLEXT = MakeMakefile::CONFIG['DLEXT']
1818

1919
CRCS.each do |crc|
20+
next unless File.file?(File.join(crc, "extconf.rb"))
21+
2022
crc_ext = "#{crc}_ext"
2123

2224
file "#{crc}/Makefile" => "#{crc}/extconf.rb" do

ext/digest/crc.h

+70
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
#ifndef RUBY_DIGEST_CRC_H
2+
#define RUBY_DIGEST_CRC_H 1
3+
4+
#include <ruby.h>
5+
#include <stddef.h>
6+
#include <stdint.h>
7+
8+
enum crc_input_direction {
9+
crc_normal_input = 0,
10+
crc_reflect_input = 1
11+
};
12+
13+
enum crc_allocation_type {
14+
crc_alloc_static = 0,
15+
crc_alloc_heap = 1
16+
};
17+
18+
struct crc_model
19+
{
20+
int bitwidth : 8;
21+
enum crc_input_direction reflect_input : 1;
22+
enum crc_allocation_type table_heap : 1;
23+
uint64_t polynomial;
24+
const void *table;
25+
};
26+
27+
/*
28+
* This structure is materialized in each shared object file that defines
29+
* a CRC. Therefore, instead of checking the structure address, the
30+
* `wrap_struct_name` field is simply checked. This is to avoid link errors
31+
* at build time.
32+
*/
33+
static const rb_data_type_t crc_model_const_type = {
34+
"digest-crc:model-const",
35+
{
36+
NULL,
37+
NULL,
38+
NULL,
39+
NULL
40+
}
41+
};
42+
43+
static inline void ruby_digest_crc_model_implant(VALUE crc_class, const struct crc_model *model)
44+
{
45+
ID id_model = rb_intern("digest-crc-model");
46+
VALUE obj = rb_data_typed_object_wrap(rb_cObject, (void *)(uintptr_t)model, &crc_model_const_type);
47+
rb_obj_freeze(obj);
48+
rb_ivar_set(crc_class, id_model, obj);
49+
}
50+
51+
#if !defined(HAVE_RB_EXT_RACTOR_SAFE)
52+
static inline void ruby_digest_crc_ensure_ractor_main(const char *mesg)
53+
{
54+
(void)mesg;
55+
}
56+
#else
57+
#include <ruby/ractor.h>
58+
59+
static inline void ruby_digest_crc_ensure_ractor_main(const char *mesg)
60+
{
61+
if (rb_funcallv(rb_cRactor, rb_intern("main"), 0, NULL) !=
62+
rb_funcallv(rb_cRactor, rb_intern("current"), 0, NULL)) {
63+
VALUE ractor = rb_const_get(rb_cObject, rb_intern("Ractor"));
64+
VALUE ractor_error = rb_const_get(ractor, rb_intern("Error"));
65+
rb_raise(ractor_error, "%s", mesg);
66+
}
67+
}
68+
#endif
69+
70+
#endif // RUBY_DIGEST_CRC_H

0 commit comments

Comments
 (0)