Skip to content

Commit e35e19a

Browse files
committed
Improve the performance of string xor operation
Use `each_byte` instead of `bytes` to speed up string xor operation and reduce object allocations. Inspired by commit 02c3867. ``` ruby require 'benchmark/ips' require 'allocation_tracer' a = 32.times.map { rand(256) }.pack('C*') b = 32.times.map { rand(256) }.pack('C*') def xor_byte_strings1(s1, s2) s1.bytes.zip(s2.bytes).map { |(c1,c2)| c1 ^ c2 }.pack('c*') end def xor_byte_strings2(s1, s2) s2_bytes = s2.bytes s1.bytes.map.with_index { |c1, i| c1 ^ s2_bytes[i] }.pack('c*') end def xor_byte_strings3(s1, s2) s2_bytes = s2.bytes s1.each_byte.with_index { |c1, i| s2_bytes[i] ^= c1 } s2_bytes.pack('C*') end fail if xor_byte_strings1(a, b) != xor_byte_strings2(a, b) fail if xor_byte_strings1(a, b) != xor_byte_strings3(a, b) Benchmark.ips do |x| x.report('xor_byte_strings1') { xor_byte_strings1(a, b) } x.report('xor_byte_strings2') { xor_byte_strings2(a, b) } x.report('xor_byte_strings3') { xor_byte_strings3(a, b) } x.compare! end Tracer = ObjectSpace::AllocationTracer Tracer.setup(%i{type}) p xor_byte_strings1: Tracer.trace { xor_byte_strings1(a, b) } p xor_byte_strings2: Tracer.trace { xor_byte_strings2(a, b) } p xor_byte_strings3: Tracer.trace { xor_byte_strings3(a, b) } ``` ``` Warming up -------------------------------------- xor_byte_strings1 10.668k i/100ms xor_byte_strings2 11.814k i/100ms xor_byte_strings3 13.139k i/100ms Calculating ------------------------------------- xor_byte_strings1 116.667k (± 3.1%) i/s - 586.740k xor_byte_strings2 129.932k (± 4.3%) i/s - 649.770k xor_byte_strings3 142.506k (± 4.2%) i/s - 722.645k Comparison: xor_byte_strings3: 142506.3 i/s xor_byte_strings2: 129932.4 i/s - 1.10x slower xor_byte_strings1: 116666.8 i/s - 1.22x slower {:xor_byte_strings1=>{[:T_ARRAY]=>[38, 0, 0, 0, 0, 0], [:T_STRING]=>[2, 0, 0, 0, 0, 0]}} {:xor_byte_strings2=>{[:T_ARRAY]=>[3, 0, 0, 0, 0, 0], [:T_DATA]=>[1, 0, 0, 0, 0, 0], [:T_IMEMO]=>[2, 0, 0, 0, 0, 0], [:T_STRING]=>[2, 0, 0, 0, 0, 0]}} {:xor_byte_strings3=>{[:T_ARRAY]=>[1, 0, 0, 0, 0, 0], [:T_DATA]=>[1, 0, 0, 0, 0, 0], [:T_IMEMO]=>[2, 0, 0, 0, 0, 0], [:T_STRING]=>[2, 0, 0, 0, 0, 0]}} ```
1 parent f432e12 commit e35e19a

File tree

1 file changed

+2
-1
lines changed

1 file changed

+2
-1
lines changed

actionpack/lib/action_controller/metal/request_forgery_protection.rb

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -379,7 +379,8 @@ def per_form_csrf_token(session, action_path, method)
379379

380380
def xor_byte_strings(s1, s2)
381381
s2_bytes = s2.bytes
382-
s1.bytes.map.with_index { |c1, i| c1 ^ s2_bytes[i] }.pack('c*')
382+
s1.each_byte.with_index { |c1, i| s2_bytes[i] ^= c1 }
383+
s2_bytes.pack('C*')
383384
end
384385

385386
# The form's authenticity parameter. Override to provide your own.

0 commit comments

Comments
 (0)