Skip to content

Conversation

@andersio
Copy link
Member

@andersio andersio commented Apr 22, 2017

As mentioned in #282, os_unfair_lock is generally faster all-around (slightly when uncontended, significantly when contended), since it does not need to maintain fairness among waiting threads.

This branch is a transitional implementation purely in Swift, until eventually Swift supports availability propagation of enum cases to switch case scopes.

Related: SR-4079

Lock is an abstract base class of two implementations: UnfairLock and PthreadLock. When constructing a lock through Lock.Type.make, it picks the implementation with #available runtime checks.

It turns out that virtual dispatch (subclassing) is still pretty fast, and is certainly faster than the implementation of the #282 trick in pure Swift, which requires per-instance thunks.

Benchmark Source.

5_000_000 iterations
@Lock: 40.4060875994435 ns
@NSLock: 67.0863552018613 ns
@PthreadLock: 47.6224342023663 ns
@UnfairLock: 40.1545092006554 ns

Existentials are rather slow, by the way.

Inspecting the generated object code with -Owmo reveals that Swift has inlined the vtable. So I guess even if SR-4079 got fixed someday, it would hardly be faster than Lock.

    0x1052c5566 <+38>:   cmpq   0x7bacb(%rip), %rax       ; (void *)0x0000000105350690: type metadata for ReactiveSwift.Lock
    0x1052c556d <+45>:   je     0x1052c58dc               ; <+924> [inlined] function signature specialization <Arg[0] = Dead> of ReactiveSwift.Lock.lock () -> () at Signal.swift:189
    0x1052c5573 <+51>:   cmpq   0x7bac6(%rip), %rax       ; (void *)0x0000000105350738: type metadata for ReactiveSwift.Lock.UnfairLock
    0x1052c557a <+58>:   je     0x1052c558a               ; <+74> [inlined] ReactiveSwift.Lock.UnfairLock.lock () -> () at Signal.swift:302
    0x1052c557c <+60>:   cmpq   0x7bac5(%rip), %rax       ; (void *)0x00000001053507f0: type metadata for ReactiveSwift.Lock.PthreadLock
    0x1052c5583 <+67>:   je     0x1052c559c               ; <+92> [inlined] ReactiveSwift.Lock.PthreadLock.lock () -> () at Signal.swift:302
    0x1052c5585 <+69>:   callq  *0x58(%rax)
    0x1052c5588 <+72>:   jmp    0x1052c55ac               ; <+108> at Signal.swift:303
    0x1052c558a <+74>:   incq   0x8e077(%rip)             ; __profc_/Users/anders/Repo/RAS/Sources/Scheduler.swift:_TFCC13ReactiveSwift13TestSchedulerP33_B7AFF4CC5431FDC431F4B042BAEABD8F15ScheduledActionCfT4dateV10Foundation4Date6actionFT_T__S1_ + 5256
->  0x1052c5591 <+81>:   addq   $0x10, %rdi
    0x1052c5595 <+85>:   callq  0x105329fa2               ; symbol stub for: os_unfair_lock_lock

@andersio andersio force-pushed the unfair-lock branch 8 times, most recently from 8a32498 to fe505e8 Compare April 24, 2017 17:42
@andersio andersio force-pushed the unfair-lock branch 3 times, most recently from 6988243 to 2d3f845 Compare April 24, 2017 18:33
@andersio andersio removed the additive label Apr 24, 2017
Copy link
Contributor

@liscio liscio left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This all looks good to me.

/// `Lock` exposes `os_unfair_lock` on supported platforms, with pthread mutex as the
// fallback.
internal class Lock {
#if os(macOS) || os(iOS) || os(tvOS) || os(watchOS)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's a bummer that this uses both #if os and @available :(

@mdiep mdiep merged commit 7bbb458 into master May 10, 2017
@mdiep mdiep deleted the unfair-lock branch May 10, 2017 19:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants