Skip to content

Commit 141fd7f

Browse files
committed
Add Zstd::Context for perforance
1 parent ed6c63e commit 141fd7f

File tree

10 files changed

+1235
-64
lines changed

10 files changed

+1235
-64
lines changed

README.md

Lines changed: 75 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -53,14 +53,40 @@ require 'zstd-ruby'
5353
#### Simple Compression
5454

5555
```ruby
56-
compressed_data = Zstd.compress(data)
57-
compressed_data = Zstd.compress(data, level: complession_level) # default compression_level is 3
56+
compressed_data = Zstd.compress(data) # default: 3
57+
compressed_data = Zstd.compress(data, level: 6)
5858
```
5959

60-
#### Compression with Dictionary
60+
### Context-based Compression
61+
62+
For better performance with multiple operations, use reusable contexts:
63+
6164
```ruby
62-
# dictionary is supposed to have been created using `zstd --train`
63-
compressed_using_dict = Zstd.compress("", dict: File.read('dictionary_file'))
65+
# Unified context (recommended)
66+
ctx = Zstd::Context.new(level: 6)
67+
compressed = ctx.compress(data)
68+
original = ctx.decompress(compressed)
69+
70+
# Specialized contexts for memory optimization
71+
cctx = Zstd::CContext.new(level: 6) # Compression-only
72+
dctx = Zstd::DContext.new # Decompression-only
73+
```
74+
75+
### Dictionary Compression
76+
77+
Dictionaries provide better compression for similar data:
78+
79+
```ruby
80+
dictionary = File.read('dictionary_file')
81+
82+
# Using module methods
83+
compressed = Zstd.compress(data, level: 3, dict: dictionary)
84+
original = Zstd.decompress(compressed, dict: dictionary)
85+
86+
# Using contexts for better performance
87+
ctx = Zstd::Context.new(level: 6, dict: dictionary)
88+
compressed = ctx.compress(data)
89+
original = ctx.decompress(compressed)
6490
```
6591

6692
#### Compression with CDict
@@ -128,16 +154,9 @@ res << stream.finish
128154

129155
### Decompression
130156

131-
#### Simple Decompression
132-
133157
```ruby
134158
data = Zstd.decompress(compressed_data)
135-
```
136-
137-
#### Decompression with Dictionary
138-
```ruby
139-
# dictionary is supposed to have been created using `zstd --train`
140-
Zstd.decompress(compressed_using_dict, dict: File.read('dictionary_file'))
159+
data = Zstd.decompress(compressed_data, dict: dictionary)
141160
```
142161

143162
#### Decompression with DDict
@@ -157,79 +176,73 @@ result = ''
157176
result << stream.decompress(cstr[0, 10])
158177
result << stream.decompress(cstr[10..-1])
159178
```
179+
## API Reference
180+
181+
### Context Classes
182+
183+
#### `Zstd::Context`
184+
Unified context for both compression and decompression.
160185

161-
#### Streaming Decompression with dictionary
162186
```ruby
163-
cstr = "" # Compressed data
164-
stream = Zstd::StreamingDecompress.new(dict: File.read('dictionary_file'))
165-
result = ''
166-
result << stream.decompress(cstr[0, 10])
167-
result << stream.decompress(cstr[10..-1])
187+
ctx = Zstd::Context.new # Default settings
188+
ctx = Zstd::Context.new(level: 6) # With compression level
189+
ctx = Zstd::Context.new(level: 6, dict: dictionary) # With dictionary
168190
```
169191

170-
DDict can also be specified to `dict:`.
192+
- `compress(data)` → String
193+
- `decompress(compressed_data)` → String
171194

172-
#### Streaming Decompression with Position Tracking
173-
174-
If you need to know how much of the input data was consumed during decompression, you can use the `decompress_with_pos` method:
195+
#### `Zstd::CContext`
196+
Compression-only context for memory optimization.
175197

176198
```ruby
177-
cstr = "" # Compressed data
178-
stream = Zstd::StreamingDecompress.new
179-
result, consumed_bytes = stream.decompress_with_pos(cstr[0, 10])
180-
# result contains the decompressed data
181-
# consumed_bytes contains the number of bytes from input that were processed
199+
cctx = Zstd::CContext.new(level: 6) # With compression level
200+
cctx = Zstd::CContext.new(level: 6, dict: dictionary) # With dictionary
182201
```
183202

184-
This is particularly useful when processing streaming data where you need to track the exact position in the input stream.
203+
- `compress(data)` → String
185204

186-
### Skippable frame
205+
#### `Zstd::DContext`
206+
Decompression-only context for memory optimization.
187207

188208
```ruby
189-
compressed_data_with_skippable_frame = Zstd.write_skippable_frame(compressed_data, "sample data")
190-
191-
Zstd.read_skippable_frame(compressed_data_with_skippable_frame)
192-
# => "sample data"
209+
dctx = Zstd::DContext.new # Default settings
210+
dctx = Zstd::DContext.new(dict: dictionary) # With dictionary
193211
```
194212

195-
### Stream Writer and Reader Wrapper
196-
**EXPERIMENTAL**
213+
- `decompress(compressed_data)` → String
197214

198-
* These features are experimental and may be subject to API changes in future releases.
199-
* There may be performance and compatibility issues, so extensive testing is required before production use.
200-
* If you have any questions, encounter bugs, or have suggestions, please report them via [GitHub issues](https://github.com/SpringMT/zstd-ruby/issues).
215+
### Module Methods
201216

202-
#### Zstd::StreamWriter
217+
#### Compression
218+
- `Zstd.compress(data)` → String (default level 3)
219+
- `Zstd.compress(data, dict: dictionary)` → String
203220

204-
```ruby
205-
require 'stringio'
206-
require 'zstd-ruby'
221+
#### Decompression
222+
- `Zstd.decompress(compressed_data)` → String
223+
- `Zstd.decompress(compressed_data, dict: dictionary)` → String
207224

208-
io = StringIO.new
209-
stream = Zstd::StreamWriter.new(io)
210-
stream.write("abc")
211-
stream.finish
225+
#### Utilities
226+
- `Zstd.zstd_version` → Integer
212227

213-
io.rewind
214-
# Retrieve the compressed data
215-
compressed_data = io.read
216-
```
228+
### Performance Guidelines
217229

218-
#### Zstd::StreamReader
230+
| Use Case | Recommended API | Benefits |
231+
|----------|----------------|----------|
232+
| Single operations | `Zstd.compress/decompress` | Simple, no setup |
233+
| Multiple operations | `Zstd::Context` | 2-3x faster, convenient |
234+
| Specialized needs | `Zstd::CContext/DContext` | Direct API access |
219235

220-
```ruby
221-
require 'stringio'
222-
require 'zstd-ruby' # Add the appropriate require statement if necessary
236+
**Compression Levels:** 1-3 (fast, 3 is default), 9-19 (better compression)
223237

224-
io = StringIO.new(compressed_data)
225-
reader = Zstd::StreamReader.new(io)
238+
## Benchmarks
226239

227-
# Read and output the decompressed data
228-
puts reader.read(10) # 'abc'
229-
puts reader.read(10) # 'def'
230-
puts reader.read(10) # '' (end of data)
231-
```
240+
To test performance on your system:
232241

242+
```bash
243+
cd benchmarks
244+
ruby quick_benchmark.rb # Fast overview of all APIs (recommended)
245+
```
233246

234247
## JRuby
235248
This gem does not support JRuby.
@@ -266,7 +279,6 @@ To install this gem onto your local machine, run `bundle exec rake install`. To
266279

267280
Bug reports and pull requests are welcome on GitHub at https://github.com/SpringMT/zstd-ruby. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
268281

269-
270282
## License
271283

272284
The gem is available as open source under the terms of the [BSD-3-Clause License](https://opensource.org/licenses/BSD-3-Clause).

benchmarks/context_reuse.rb

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
require 'benchmark/ips'
2+
3+
$LOAD_PATH.unshift '../lib'
4+
require 'zstd-ruby'
5+
6+
# Sample data - size typical of real-world scenarios
7+
data = "Hello, World! " * 1000 # ~13KB string
8+
9+
# Pre-compress data for decompression tests
10+
compressed_data = Zstd.compress(data)
11+
12+
puts "Benchmarking context reuse vs module methods"
13+
puts "Data size: #{data.bytesize} bytes, Compressed: #{compressed_data.bytesize} bytes"
14+
puts
15+
16+
Benchmark.ips do |x|
17+
x.config(time: 3, warmup: 1)
18+
19+
# Compression benchmarks
20+
x.report("Module compress (new context each time)") do
21+
Zstd.compress(data)
22+
end
23+
24+
ctx = Zstd::Context.new
25+
x.report("Context compress (reused)") do
26+
ctx.compress(data)
27+
end
28+
29+
cctx = Zstd::CContext.new
30+
x.report("CContext compress (reused)") do
31+
cctx.compress(data)
32+
end
33+
34+
# Decompression benchmarks
35+
x.report("Module decompress (new context each time)") do
36+
Zstd.decompress(compressed_data)
37+
end
38+
39+
x.report("Context decompress (reused)") do
40+
ctx.decompress(compressed_data)
41+
end
42+
43+
dctx = Zstd::DContext.new
44+
x.report("DContext decompress (reused)") do
45+
dctx.decompress(compressed_data)
46+
end
47+
48+
x.compare!
49+
end

0 commit comments

Comments
 (0)