Skip to content

Commit a42108a

Browse files
committed
⚡️ Slightly faster SequenceSet#xor
The results are highly dependant on data, YJIT, etc... but in my tests this implementation seems to _consistently_ run faster. ``` Comparison: builtin: L ^ R local: 967.0 i/s before #567 (559b86a): 917.5 i/s - 1.05x slower before #562 (a228ee1): 901.2 i/s - 1.07x slower v0.5.12: 840.0 i/s - 1.15x slower v0.5.0: 830.7 i/s - 1.16x slower v0.4.21: 797.0 i/s - 1.21x slower v0.5.9: 790.4 i/s - 1.22x slower builtin: L.xor! R local: 960.9 i/s before #562 (a228ee1): 954.3 i/s - 1.01x slower before #567 (559b86a): 940.0 i/s - 1.02x slower v0.5.12: 796.4 i/s - 1.21x slower v0.5.0: 633.9 i/s - 1.52x slower v0.4.21: 620.1 i/s - 1.55x slower v0.5.9: 619.0 i/s - 1.55x slower ``` When I first wrote this code, the `master` branch was consistently 1.3x-1.5x slower. The speedup is much less now, so other implementation details are probably more important. When the internal set data implementation is changed (see #484), this and other operations should be re-evaluated. I suspect an iterative "merge" may be the fastest overall approach, but that code is more complex than simply relying on the core add/subtract/complement methods with simple boolean algebra.
1 parent a228ee1 commit a42108a

File tree

2 files changed

+12
-6
lines changed

2 files changed

+12
-6
lines changed

benchmarks/sequence_set-xor.yml

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,12 @@ prelude: |
2323
def xor2(other) remain_frozen dup.xor2! other end
2424
def xor3(other) remain_frozen dup.xor3! other end
2525
26+
unless instance_methods.include?(:xor!)
27+
def xor!(other)
28+
replace(self ^ other)
29+
end
30+
end
31+
2632
# (L | R) - (L & R)
2733
def xor1!(other)
2834
modifying!
@@ -69,8 +75,8 @@ prelude: |
6975
end
7076
7177
benchmark:
72-
" L ^ R": l, r = sets; l ^ r
73-
" L.xor! R": l, r = sets; l.xor! r
78+
"builtin: L ^ R": l, r = sets; l ^ r
79+
"builtin: L.xor! R": l, r = sets; l.xor! r
7480
" (L | R) - (R & L)": l, r = sets; (l | r) - (r & l)
7581
"0.5.8 (L | R) - (R & L)": l, r = sets; l.xor0 r
7682
"dup1 (L | R) - (R & L)": l, r = sets; l.xor1 r

lib/net/imap/sequence_set.rb

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1778,10 +1778,10 @@ def intersect!(other)
17781778
#
17791779
# Related: #xor, #merge, #subtract
17801780
def xor!(other)
1781-
modifying!
1782-
other = IMAP::SequenceSet(other)
1783-
both = self & other
1784-
merge(other).subtract(both)
1781+
modifying! # short-circuit before processing input
1782+
other = SequenceSet.new(other)
1783+
copy = dup
1784+
merge(other).subtract(other.subtract(copy.complement!))
17851785
end
17861786

17871787
# Returns whether #string is fully normalized: entries have been sorted,

0 commit comments

Comments
 (0)