solid-resp-ractor 0.1.3 → 0.1.4

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 35d18ef0e429d762b4695758fa7981ee08737006462eabb35b65a8d66259c1ba
4
- data.tar.gz: 4add467ffb81cf4d6bdef18aaaf5e8d810f3fe9995f41d03ae1f8315b57d4775
3
+ metadata.gz: e95465d9cdecff50ce715e8150335b5f90da49121a1a047edb433210e308015c
4
+ data.tar.gz: 1dd82d1a8d80ea412d1ce2257f51b081dc5231718f9aaed71386d739a6bc8ac3
5
5
  SHA512:
6
- metadata.gz: 244ae341c2eb140844318618cc9ef305019c680b3462155cc2c55728a3ec6662466e9eab9c1e7fbed70a43ab4497bf0c8769b8b348fbf789fbfe231c6aa66730
7
- data.tar.gz: 1947979260a1e0fc8003097262910876bb420a520b5b712277497c57ebad90f7d1995a716f938f8f2153a3bbb3b1425edac79720e383a170dd2e90dc8efa8ad8
6
+ metadata.gz: bd9661ccd7e481088936f7b3425da4bacf8a58754dab3122397bbd003d03c7704fe1e6b813be11575dab60b790548c776ca1e452998c80891b8a967c4ccfa7d2
7
+ data.tar.gz: 901b266a995302514ebe6f0fb4cdfd0e9641e415b908fb109b249b8ed14e0b0f2fe2572c2e902158d6af9918f50cc1ed725f1a48f1c70c825484578dbbc99a4b
data/CHANGELOG.md CHANGED
@@ -2,6 +2,17 @@
2
2
 
3
3
  All notable changes to this project are documented in this file.
4
4
 
5
+ ## [0.1.4] - 2026-10-01
6
+
7
+ ### Changed
8
+
9
+ - Reuse a destination String for non-blocking IO reads instead of allocating a
10
+ chunk-sized String for every socket response.
11
+ - Reduce loopback GET allocation from 16,609.8 to 120.8 bytes per operation
12
+ and from 6.0 to 4.0 objects per operation in the Ruby 4.0.1 benchmark.
13
+ - Improve loopback GET throughput by 8.18% at eight Ractors while preserving
14
+ Reader parser-buffer invariants.
15
+
5
16
  ## [0.1.3] - 2026-09-30
6
17
 
7
18
  ### Changed
@@ -50,6 +61,7 @@ All notable changes to this project are documented in this file.
50
61
  input, and parallel Ractors.
51
62
  - Ractor-shareable default encoder with no global mutable state.
52
63
 
64
+ [0.1.4]: https://github.com/nicolasva/solid-resp-ractor/compare/v0.1.3...v0.1.4
53
65
  [0.1.3]: https://github.com/nicolasva/solid-resp-ractor/compare/v0.1.2...v0.1.3
54
66
  [0.1.2]: https://github.com/nicolasva/solid-resp-ractor/compare/v0.1.1...v0.1.2
55
67
  [0.1.1]: https://github.com/nicolasva/solid-resp-ractor/compare/v0.1.0...v0.1.1
data/README.md CHANGED
@@ -272,6 +272,89 @@ after at least 16 KiB have been consumed and that prefix occupies at least half
272
272
  of the buffer. This avoids copying a large unread suffix after small fragmented
273
273
  reads while still releasing consumed data during long-lived streams.
274
274
 
275
+ ## Reader benchmark
276
+
277
+ Encoding, Reader-only, TCP, allocation, and Ractor-scaling benchmarks live in
278
+ the separate `benchmark_solid_resp_ractor` sibling bundle so benchmark tooling
279
+ and generated reports remain outside the gem.
280
+
281
+ Run the complete matrix from the sibling checkout:
282
+
283
+ ```sh
284
+ cd ../benchmark_solid_resp_ractor
285
+ RBENV_VERSION=4.0.1 \
286
+ BENCHMARK_OUTPUT=results/ruby-4.0.1-reader.md \
287
+ bundle exec rake
288
+ ```
289
+
290
+ ### Current baseline
291
+
292
+ **Environment:** Ruby 4.0.1 (arm64-darwin25); solid-resp-ractor 0.1.3;
293
+ Redis 8.10.0. Values are medians of three runs with one second of warmup and
294
+ three seconds of measurement per row. Pipeline metrics are amortized per
295
+ command. Reader-only rows consume an in-memory repeating 16 KiB source.
296
+ Reader + TCP rows use an isolated loopback Redis server. Allocation metrics
297
+ are measured separately in one Ractor with GC disabled, then repeated across
298
+ the scaling rows; Redis allocations are excluded.
299
+
300
+ | Ractors | Layer | Operation | ops/s | Scaling efficiency | alloc/op | bytes/op |
301
+ |---:|---|---|---:|---:|---:|---:|
302
+ | 1 | Encode | GET | 1,261,840 | 100.0% | 2.0 | 104.8 |
303
+ | 2 | Encode | GET | 2,298,471 | 91.1% | 2.0 | 104.8 |
304
+ | 4 | Encode | GET | 3,898,679 | 77.2% | 2.0 | 104.8 |
305
+ | 8 | Encode | GET | 5,234,281 | 51.9% | 2.0 | 104.8 |
306
+ | 1 | Encode | SET | 974,781 | 100.0% | 2.0 | 104.8 |
307
+ | 2 | Encode | SET | 1,797,823 | 92.2% | 2.0 | 104.8 |
308
+ | 4 | Encode | SET | 3,249,650 | 83.3% | 2.0 | 104.8 |
309
+ | 8 | Encode | SET | 4,470,244 | 57.3% | 2.0 | 104.8 |
310
+ | 1 | Encode | pipeline 50 | 1,345,762 | 100.0% | 2.0 | 145.8 |
311
+ | 2 | Encode | pipeline 50 | 2,482,408 | 92.2% | 2.0 | 145.8 |
312
+ | 4 | Encode | pipeline 50 | 4,250,421 | 79.0% | 2.0 | 145.8 |
313
+ | 8 | Encode | pipeline 50 | 5,779,732 | 53.7% | 2.0 | 145.8 |
314
+ | 1 | Reader | +OK | 1,690,247 | 100.0% | 2.0 | 40.8 |
315
+ | 2 | Reader | +OK | 3,197,883 | 94.6% | 2.0 | 40.8 |
316
+ | 4 | Reader | +OK | 5,788,420 | 85.6% | 2.0 | 40.8 |
317
+ | 8 | Reader | +OK | 8,029,110 | 59.4% | 2.0 | 40.8 |
318
+ | 1 | Reader | integer | 1,562,655 | 100.0% | 2.0 | 40.8 |
319
+ | 2 | Reader | integer | 2,968,526 | 95.0% | 2.0 | 40.8 |
320
+ | 4 | Reader | integer | 5,459,562 | 87.3% | 2.0 | 40.8 |
321
+ | 8 | Reader | integer | 7,423,102 | 59.4% | 2.0 | 40.8 |
322
+ | 1 | Reader | bulk 16 B | 787,426 | 100.0% | 3.0 | 120.8 |
323
+ | 2 | Reader | bulk 16 B | 1,533,310 | 97.4% | 3.0 | 120.8 |
324
+ | 4 | Reader | bulk 16 B | 2,796,102 | 88.8% | 3.0 | 120.8 |
325
+ | 8 | Reader | bulk 16 B | 4,090,043 | 64.9% | 3.0 | 120.8 |
326
+ | 1 | Reader | bulk 1 KiB | 709,062 | 100.0% | 3.0 | 1,105.8 |
327
+ | 2 | Reader | bulk 1 KiB | 1,225,199 | 86.4% | 3.0 | 1,105.8 |
328
+ | 4 | Reader | bulk 1 KiB | 2,055,006 | 72.5% | 3.0 | 1,105.8 |
329
+ | 8 | Reader | bulk 1 KiB | 2,597,586 | 45.8% | 3.0 | 1,105.8 |
330
+ | 1 | Reader | array 50 | 32,877 | 100.0% | 103.0 | 2,680.8 |
331
+ | 2 | Reader | array 50 | 60,955 | 92.7% | 103.0 | 2,680.8 |
332
+ | 4 | Reader | array 50 | 115,104 | 87.5% | 103.0 | 2,680.8 |
333
+ | 8 | Reader | array 50 | 185,791 | 70.6% | 103.0 | 2,680.8 |
334
+ | 1 | Reader | nested RESP3 | 87,279 | 100.0% | 34.0 | 1,080.9 |
335
+ | 2 | Reader | nested RESP3 | 166,456 | 95.4% | 34.0 | 1,080.9 |
336
+ | 4 | Reader | nested RESP3 | 302,211 | 86.6% | 34.0 | 1,080.9 |
337
+ | 8 | Reader | nested RESP3 | 433,100 | 62.0% | 34.0 | 1,080.9 |
338
+ | 1 | Reader + TCP | GET | 40,173 | 100.0% | 4.0 | 120.8 |
339
+ | 2 | Reader + TCP | GET | 66,235 | 82.4% | 4.0 | 120.8 |
340
+ | 4 | Reader + TCP | GET | 89,146 | 55.5% | 4.0 | 120.8 |
341
+ | 8 | Reader + TCP | GET | 102,060 | 31.8% | 4.0 | 120.8 |
342
+ | 1 | Reader + TCP | pipeline 50 | 473,123 | 100.0% | 3.0 | 120.0 |
343
+ | 2 | Reader + TCP | pipeline 50 | 878,582 | 92.8% | 3.0 | 120.0 |
344
+ | 4 | Reader + TCP | pipeline 50 | 1,478,781 | 78.1% | 3.0 | 120.0 |
345
+ | 8 | Reader + TCP | pipeline 50 | 2,044,122 | 54.0% | 3.0 | 120.0 |
346
+
347
+ Pure bulk decoding represents roughly 5.1% of the service time of a
348
+ non-pipelined loopback GET, but about 60% of an amortized pipeline command.
349
+ These are directional ratios rather than profiler attribution.
350
+
351
+ Reusing the destination String passed to `read_nonblock` reduced GET
352
+ allocation from 16,609.8 to 120.8 bytes/op (-99.27%) and from 6.0 to 4.0
353
+ objects/op. GET throughput changed by -0.68% at 1R and improved by 8.18% at
354
+ 8R. Pipeline allocation fell from 472.3 to 120.0 bytes/op (-74.59%), while
355
+ throughput improved by 2.23% at 8R. The allocation improvement is structural
356
+ and does not require changing Reader parser invariants.
357
+
275
358
  ## Development
276
359
 
277
360
  ```sh
@@ -47,6 +47,7 @@ module SolidRespRactor
47
47
 
48
48
  @selector = selector
49
49
  @clock = clock
50
+ @read_buffer = +""
50
51
  end
51
52
 
52
53
  def read(timeout:)
@@ -54,7 +55,11 @@ module SolidRespRactor
54
55
 
55
56
  deadline = nil
56
57
  loop do
57
- chunk = @io.read_nonblock(@chunk_size, exception: false)
58
+ chunk = @io.read_nonblock(
59
+ @chunk_size,
60
+ @read_buffer,
61
+ exception: false,
62
+ )
58
63
  return chunk unless chunk == :wait_readable || chunk == :wait_writable
59
64
 
60
65
  deadline ||= @clock.now + timeout if timeout
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module SolidRespRactor
4
- VERSION = "0.1.3"
4
+ VERSION = "0.1.4"
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: solid-resp-ractor
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.3
4
+ version: 0.1.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Nicolas Vandenbogaerde