Lock in $30 Savings on PRO—Offer Ends Soon! ⏳

Synchronizationを支える技術

 Synchronizationを支える技術

Shigure Shimotori

October 22, 2024
Tweet

More Decks by Shigure Shimotori

Other Decks in Programming

Transcript

  1. Synchronization framework • ༁ɿ௿ਫ४ͷϓϦϛςΟϒͳૢ࡞Λ༻͍ͯಉظߏ଄Λߏங͢Δɻ • iOS 18.0+ɺmacOS 15.0+ • AtomicͱMutex͕͋Δ

    • SE-0410: Low-Level Atomic Operations (Swift 6.0) • swift-atomicsΛେ͍ʹࢀߟʹͯ͠࡞ͬͨΈ͍ͨ • SE-0433: Synchronous Mutual Exclusion Lock (Swift 6.0) 8 https://developer.apple.com/documentation/synchronization
  2. 10 • Α͘ͳ͍ͷͰɺconcurrency checkΛ༗ޮʹ͢ΔͱΤϥʔʹͳΔ Α͘ͳ͍ιʔείʔυͷྫ var counter = 0 DispatchQueue.concurrentPerform(iterations:

    10) { _ in for _ in 0 ..< 1_000_000 { counter += 1 } } print(counter) Mutation of captured var ‘counter’ in concurrently-executing code 👈Swift 5Ͱ࣮ߦ͢Δͱ10_000_000ʹͳΒͳ͍
  3. 11 • Atomicʹੜ͍͑ͯΔϝιουΛ࢖ͬͯ஋ΛಡΈॻ͖͢Δ SynchronizationͷAtomic<Value>ͷ࢖༻ྫ let counter = Atomic<Int>(0) DispatchQueue.concurrentPerform(iterations: 10)

    { _ in for _ in 0 ..< 1_000_000 { counter.wrappingAdd(1, ordering: .relaxed) } } print(counter.load(ordering: .relaxed)) 👈10_000_000ʹͳΔʂ https://github.com/swiftlang/swift-evolution/blob/.../proposals/0410-atomics.md#proposed-solution
  4. 12 • Atomicʹੜ͍͑ͯΔϝιουΛ࢖ͬͯ஋ΛಡΈॻ͖͢Δ SynchronizationͷAtomic<Value>ͷ࢖༻ྫ let counter = Atomic<Int>(0) DispatchQueue.concurrentPerform(iterations: 10)

    { _ in for _ in 0 ..< 1_000_000 { counter.wrappingAdd(1, ordering: .relaxed) } } print(counter.load(ordering: .relaxed)) https://github.com/swiftlang/swift-evolution/blob/.../proposals/0410-atomics.md#proposed-solution
  5. Atomicʹੜ͑ͯΔϝιουͨͪ • addɺsubtractɿΦʔόʔϑϩʔͪ͠Ό͏ • wrapping(Add|Subtract)ɿϥοϓΞϥ΢ϯυͯ͘͠ΕΔ • bitwise(And|Or|Xor)ɿIntಉ࢜ • logical(And|Or|Xor)ɿBoolಉ࢜ •

    maxɺminɿͦͷ·Μ· • compareExchangeɿڧ͍൛ • weakCompareExchangeɿऑ͍൛ʢspurious failureΛڐ༰͢Δʣ • exchange(_ desired: consuming Value, ordering: AtomicUpdateOrdering) -> Valueɿcompare͠ͳ͍ • load(ordering: AtomicLoadOrdering) -> ValueɿಡΉ • store(_ desired: consuming Value, ordering: AtomicStoreOrdering)ɿॻ͘ 13 ※த਎ʹΑͬͯ࢖͑Δϝιου͸ҟͳΓ·͢
  6. Mutex<Value> • DarwinͰ͸ϩοΫʹos_unfair_lockΛ࢖༻ • Linux΍Windows΍WebAssembly༻ͷ࣮૷΋͋Δʢࡉ͔͍ڍಈ͸ҟͳΔʣ • os.OSAllocatedUnfairLock<State>ͱͷҧ͍ • Mutexʹ͸~Copyableͳ΋ͷ͔͠౉ͤͳ͍ 15

    https://github.com/swiftlang/swift-evolution/blob/.../proposals/0433-mutex.md
 https://github.com/swiftlang/swift/blob/.../stdlib/public/Synchronization/Mutex/Mutex.swift
 https://developer.apple.com/documentation/os/osallocatedunfairlock
  7. struct Atomic<Value: AtomicRepresentable> • AtomicRepresentable • static func decodeAtomicRepresentation( consuming

    Self.AtomicRepresentation ) -> Self • static func encodeAtomicRepresentation( consuming Self ) -> Self.AtomicRepresentation 19 https://developer.apple.com/documentation/synchronization/atomicrepresentable
  8. ඪ४ͰAtomicRepresentationͳօ͞Μ • UIntͷօ͞Μ • Intͷօ͞Μ • struct WordPair • double

    wide atomicsରԠͷ؀ڥԼͰ2ͭͷUIntΛ·ͱΊͯѻ͏ 21 https://developer.apple.com/documentation/synchronization/wordpair
 https://github.com/swiftlang/swift/blob/.../lib/Basic/LangOptions.cpp#L313-L409 Ҏ্ʂ
  9. AtomicIntegers.swift.gyb Ұ෦ൈਮ͓ͯ͠ૹΓ͓ͯ͠Γ·͢ • LLVMͷΞτϛοΫ໋ྩΛ͏·͘ϥοϓͯ͠Δ͚ͩ 22 public func ${lowerFirst(name)}( _ operand:

    ${intType}, ordering: AtomicUpdateOrdering ) -> (oldValue: ${intType}, newValue: ${intType}) { Builtin.atomicrmw_${atomicOperationName(intType, builtinName)}_${llvmOrder}_Int64( _rawAddress, operand._value ) } https://github.com/swiftlang/swift/blob/.../stdlib/public/Synchronization/Atomics/AtomicIntegers.swift.gyb#L95-L157
 https://github.com/swiftlang/swift/blob/.../utils/SwiftAtomics.py#L71-L82
 https://llvm.org/docs/LangRef.html#atomicrmw-instruction
  10. public func ${lowerFirst(name)}( _ operand: ${intType}, ordering: AtomicUpdateOrdering ) ->

    (oldValue: ${intType}, newValue: ${intType}) { Builtin.atomicrmw_${atomicOperationName(intType, builtinName)}_${llvmOrder}_Int64( _rawAddress, operand._value ) } AtomicIntegers.swift.gyb Ұ෦ൈਮ͓ͯ͠ૹΓ͓ͯ͠Γ·͢ • LLVMͷΞτϛοΫ໋ྩΛ͏·͘ϥοϓͯ͠Δ͚ͩ 23 integerOperations = [ # Swift name, llvm name, operator, doc name ("WrappingAdd", "add", "&+", "wrapping add"), ("WrappingSubtract", "sub", "&-", "wrapping subtract"), … https://github.com/swiftlang/swift/blob/.../stdlib/public/Synchronization/Atomics/AtomicIntegers.swift.gyb#L95-L157
 https://github.com/swiftlang/swift/blob/.../utils/SwiftAtomics.py#L71-L82
 https://llvm.org/docs/LangRef.html#atomicrmw-instruction
  11. IntҎ֎΋Atomic<Value>ʹ౉͍ͨ͠ʂ • σʔλͷ௕͕͞64bitҎ಺ʹऩ·ΔͳΒ࣮࣭Int64ͳͷͰηʔϑ • UnsafePointer΋IntͬͯݴͬͯΔ • WordPairͳΒUIntΛಉ࣌ʹ2ͭ΋ѻ͓͑ͯಘ • ྫɿIntܕϓϩύςΟ͕2ͭ͋Δߏ଄ମΛAtomicRepresentableʹ͢Δ 24

    https://github.com/swiftlang/swift/blob/.../stdlib/public/Synchronization/Atomics/AtomicPointers.swift#L40-L42 Int.encodeAtomicRepresentation( Int(bitPattern: value) )
  12. @_staticExclusiveOnly • -enable-experimental-feature͕ඞཁ • ~Copyableͳܕʹ͔͚ͭ͠ΒΕͳ͍ • Atomic͕~Copyableͳͷ͸͜ΕͷͨΊ…ͳͷ͔ͳ͋…ʁ • NGߦҝɿίϐʔɺvarɺinout 29

    https://github.com/swiftlang/swift/blob/.../stdlib/public/Synchronization/CMakeLists.txt#L75
 https://github.com/swiftlang/swift/blob/.../test/attr/attr_static_exclusive_only.swift#L3
  13. @_staticExclusiveOnlyʹΑΔ֬ೝ • varΛ࢖ͬͯͳ͍͔ͷ֬ೝ • void visitBoundVariable(VarDecl *VD)ͷ్த • inoutͰ౉͞Εͯͳ͍͔ͷ֬ೝ •

    TypeResolver::resolveASTFunctionTypeParams(...)ͷ్த 30 https://github.com/swiftlang/swift/blob/.../lib/Sema/TypeCheckType.cpp#L3853-L3860
 https://github.com/swiftlang/swift/blob/.../lib/Sema/TypeCheckDeclPrimary.cpp#L2672-L2695 SD->getAttrs().hasAttribute<StaticExclusiveOnlyAttr>()
  14. @_rawLayout(like: Value.AtomicRepresentation) • ݴޠʹΑΔ؅ཧΛʢ΄΅ʣ͠ͳ͍raw storageΛ࣋ͭ • ΞτϛοΫͳͲͷಛघͳΞΫηεΛߦ͏σʔλߏ଄ͷετϨʔδ • ~Copyableͳܕʹ͔͚ͭ͠ΒΕͣɺstored propertyΛ࣋ͭ͜ͱ͕Ͱ͖ͳ͘ͳΔ

    • ϥΠϑλΠϜશମʹΘͨΓ҆ఆͨ͠ΞυϨεΛ࣋ͭ • ܕͷαΠζͱΞϥΠϯϝϯτ͸like: Tͱಉ͡ 31 https://github.com/swiftlang/swift/blob/.../docs/ReferenceGuides/UnderscoredAttributes.md#_rawlayout
 https://github.com/swiftlang/swift/blob/.../test/Sema/raw_layout.swift
  15. the Law of Exclusivity • ม਺ͷมߋͱม਺ΞΫηε͸ഉଞతͰͳ͚Ε͹ͳΒͳ͍ • ੩త਍அ΍ಈత਍அΛͯ͠ҧ൓͕ͳ͍͔؂ࢹ͍ͯ͠Δ • letଋറͳͲͷෆมͷϝϞϦ͸ॻ͖ࠐΈ͕ൃੜͤͣಈతͳνΣοΫ͕ෆཁ

    • letͳΒ࣮ߦ࣌ʹΦʔόʔϔου͕ൃੜ͠ͳ͍ʂ 33 https://www.swift.org/blog/swift-5-exclusivity/
 https://github.com/swiftlang/swift-evolution/blob/.../proposals/0176-enforce-exclusive-access-to-memory.md
  16. ݫ͍͠ᶅɿΦʔμϦϯά͸ίϯύΠϧఆ਺Ͱʂ • ΦʔμϦϯάʢޙड़ʣΛࢦఆͰ͖Δ͕ɺఆ਺Ͱࢦఆ͠ͳ͍ͱΤϥʔʹͳΔ • swift-atomics΋ಉ༷ɹͭ·Γ👇͸݁ߏલ͔Β͋Δ • @_semantics("atomics.requires_constant_orderings")Ͱ࣮ݱ 34 var ordering

    = AtomicUpdateOrdering.relaxed atomic.add(1, ordering: ordering) Ordering argument must be a static method or property of ‘… 👆ઐ༻ͷΤϥʔϝοηʔδ͕͋Δ https://github.com/swiftlang/swift/blob/.../stdlib/public/Synchronization/Atomics/AtomicIntegers.swift.gyb#L110
 https://github.com/apple/swift-atomics/blob/.../Sources/Atomics/Types/ManagedAtomic.swift#L58
 https://github.com/swiftlang/swift/blob/.../test/Sema/diag_constantness_check.swift
 https://github.com/swiftlang/swift/blob/.../include/swift/AST/DiagnosticsSema.def#L7470-L7472
  17. @_semantics("...") • SwiftΦϓςΟϚΠβͰΑ͘࢖ΘΕͯΔΒ͍͠ • ௚༁ɿߴਫ४ͳ࠷దԽʢߴਫ४ݴޠͰ͸Ұൠతʣͱ௿ਫ४ͷ࠷దԽͷ྆ํ Λ࣮ݱ͢ΔͨΊɺ ඪ४ϥΠϒϥϦͷҰ෦ʹΞϊςʔγϣϯΛ͚ͭɺ Swiftඪ ४ϥΠϒϥϦͷσʔλܕʹ͓͚ΔυϝΠϯݻ༗ͷߴਫ४ͳૢ࡞ͷηϚϯ ςΟΫεΛهड़͢Δɻ

    • ΦϓςΟϚΠβ͸λάʹԠͨ͡ॲཧΛߦ͏ • Array΍String΍DictionaryͳͲͰ΋࢖ΘΕ͍ͯΔ 35 https://github.com/swiftlang/swift/blob/.../include/swift/AST/SemanticAttrs.def#L92
 https://github.com/swiftlang/swift/blob/.../docs/HighLevelSILOptimizations.rst
  18. HighLevelSILOptimizations.rstʹॻ͍ͯ͋ͬͨ͜ͱ • ࠷దԽͷύΠϓϥΠϯ 1. ·ͣhigh-levelͳ࠷దԽΛ͠·͢ 2. ͜͜ͰॳΊͯ@_semanticsͷ͍ͭͨؔ਺ΛΠϯϥΠϯԽ͠·͢ • ઌʹΠϯϥΠϯԽ͢ΔͱpassϞδϡʔϧ͕ͨͪ1. ͰࠔͬͪΌ͏͔Β

    3. ΠϯϥΠϯԽޙʹlow-levelͳ࠷దԽΛ͠·͢ 36 https://github.com/swiftlang/swift/blob/.../docs/HighLevelSILOptimizations.rst ⚠Atomicͱ͸ؔ܎ͳ͍Έ͍ͨ⚠
  19. ݁ہ͍ͭΦʔμϦϯάҾ਺ΛνΣοΫͯ͠Δͷ͔ • static Expr *checkConstantness(Expr *expr)ͷத • ḷ͍ͬͯ͘ͱTypeCheckConstraints.cppʹḷΓண͘ʢ͋Εʁʣ • Sema

    checkͱͯ͠΍ͬͯΔ 37 https://github.com/swiftlang/swift/blob/.../lib/Sema/ConstantnessSemaDiagnostics.cpp#L334
 ↑
 https://github.com/swiftlang/swift/blob/.../lib/Sema/MiscDiagnostics.cpp#L6233
 ↑
 https://github.com/swiftlang/swift/blob/.../lib/Sema/TypeCheckConstraints.cpp#L338-L342 hasSemanticsAttr(funcDecl, semantics::ATOMICS_REQUIRES_CONSTANT_ORDERINGS)
  20. lock-free, single-consumer stackΛ࡞Δ • ಺෦తʹ͸linked listͷߏ଄ • UnsafeMutablePointerͰͭͳ͕ͬͯΔ • push()͢Δͱϊʔυ͕૿͑ͯpop()͢Δͱϊʔυ͕ݮΔ

    • ෳ਺εϨου͔ΒͲΜͲΜpush()ͯ͠Α͍ • pop()ͷ࠷தʹผεϨου͔Βpop()͢Δͷ͸NGʢsingle-consumerʣ 42 ɿUnsafeMutablePointer Stack Node Node Node Node
  21. Atomicͷ࢖͍Ͳ͜Ζ2Օॴ • ελοΫ͔Βϊʔυ΁ͷUnsafeMutablePointer • pop()࡞ۀதͷconsumerͷ਺ 43 Stack Node Node Node

    Node Atomic<UnsafeMutablePointer<Node>?> pop()தͷ
 consumer͕
 Nਓ Atomic<Int>
  22. private let _consumerCount = Atomic<Int>(0) • pop()๯಄Ͱ+1ɺdeferͰ-1 • ๯಄Ͱ+1͢Δ͍ͭͰʹɺpreconditionͰ஋͕0Ͱ͋Δ͜ͱΛ͔֬ΊΔ 44

    ଞͷਓ͕pop()ͯ͠ͳ͍ t εϨουA ࠓͷ஋Λ
 ಡΉ औಘͨ͠஋ʹ
 +1ͨ͠஋Λ
 ॻ͘ ࠓͷ஋Λ
 ಡΉ औಘͨ͠஋ʹ
 -1ͨ͠஋Λ
 ॻ͘ popͷ࡞ۀத… +1 -1
  23. private let _consumerCount = Atomic<Int>(0) • pop()๯಄Ͱ+1ɺdeferͰ-1 • ๯಄Ͱ+1͢Δ͍ͭͰʹɺpreconditionͰ஋͕0Ͱ͋Δ͜ͱΛ͔֬ΊΔ 45

    ଞͷਓ͕pop()ͯ͠ͳ͍ t εϨουA ࠓͷ஋Λ
 ಡΉ औಘͨ͠஋ʹ
 +1ͨ͠஋Λ
 ॻ͘ ࠓͷ஋Λ
 ಡΉ औಘͨ͠஋ʹ
 -1ͨ͠஋Λ
 ॻ͘ t Count 0 0 1
  24. 54 • ॱ൪Λकͬͯ΄͍͠Օॴ΁ద੾ʹࢦఆ͢Δ ΦʔμϦϯάΛࢦࣔ͢Δ͜ͱͰղܾ εϨουA ϊʔυૢ࡞ͯ͠ Χ΢ϯτ0ʹ͢Δ 👆
 ଞͷม਺ΞΫηεͷ
 ޙʹ࣮ߦ͍ͨ͠


    ʢ.releasingʣ 👆
 ଞͷม਺ΞΫηεͷ
 લʹ࣮ߦ͍ͨ͠
 ʢ.acquiringʣ precondition(_consumerCount.wrappingAdd(1, ordering: .acquiring)以下略) defer { _consumerCount.wrappingSubtract(1, ordering: .releasing) }
  25. ϝϞϦΦʔμϦϯά 55 Swift Atomic*Ordering C++ std::memory_order S_Shimotoriͷཧղ .sequentiallyConsistent seq_cst Ϟσϧɿஞ࣍Ұ؏ੑ

    
 ஗͍ .acquiringͱ.releasing acquireͱrelease ϞσϧɿϦϦʔεҰ؏ੑ .relaxed relaxed Ͳ͏ͳͬͯ΋ߏΘΜ .acquiringAndReleasingͱ͍͏ͷ΋͋ΔΑ ݫ͍͠ ΏΔ͍
  26. Atomic<Value>ͱSendable • Conforms when Value conforms to Sendable and AtomicRepresentable.

    60 let counter = Atomic(MyCounter(0)) DispatchQueue.concurrentPerform(iterations: 10) { _ in for _ in 0 ..< 1_000_000 { counter } } public struct MyCounter: AtomicRepresentable {...} Capture of 'counter' with non-sendable type 'Atomic<MyCounter>' in a `@Sendable` closure
  27. func version1() async • ඇಉظؔ਺ͷݺͼग़͠φγͷ৔߹ 63 func version1() async {

    let counter = Atomic<Int>(0) let updatedCount = counter.load(...) } Stack … version1()ʹ͸
 தஅϙΠϯτ͕ͳ͍ͷͰ
 ྆ํͱ΋
 stackʹ͍Δ Heap … version1 ⚛︎︎
  28. func version2() async • version2()͕caller͔Βݺ͹ΕΔ௚લ 64 func version2() async {

    let counter = Atomic<Int>(0) let updater = Updater() await updater.update(counter) let updatedCount = counter.load(...) } Stack (caller) Heap … … (caller)
  29. func version2() async • version2()͕caller͔Βݺ͹Εͨ 65 func version2() async {

    let counter = Atomic<Int>(0) let updater = Updater() await updater.update(counter) let updatedCount = counter.load(...) } Stack version2 Heap … … (caller) (caller) ϛ version2 ⚛︎︎
  30. func version2() async • version2()͕caller͔Βݺ͹Εͨ 66 func version2() async {

    let counter = Atomic<Int>(0) let updater = Updater() await updater.update(counter) let updatedCount = counter.load(...) } Stack version2 Heap … … (caller) version2 தஅϙΠϯτͷޙ͚ͩͳͷͰͬͪ͜ தஅϙΠϯτͷલޙͰ࢖ͬͯΔͷͰ ⚛︎︎
  31. func version2() async • update()ΛݺΜͩ 67 func version2() async {

    let counter = Atomic<Int>(0) let updater = Updater() await updater.update(counter) let updatedCount = counter.load(...) } Stack update Heap … … (caller) version2 ϛ version2 update ⚛︎︎
  32. func version2() async • update()͔Βversion2()ʹ໭͖ͬͯͨ 68 func version2() async {

    let counter = Atomic<Int>(0) let updater = Updater() await updater.update(counter) let updatedCount = counter.load(...) } Stack version2 Heap … … (caller) version2 ⚛︎︎
  33. func version2() async • load()࡞ۀʹҠΔ 69 func version2() async {

    let counter = Atomic<Int>(0) let updater = Updater() await updater.update(counter) let updatedCount = counter.load(...) } Stack version2 Heap … … (caller) version2 load ⚛︎︎
  34. func version2() async • load()ऴΘΓ 70 func version2() async {

    let counter = Atomic<Int>(0) let updater = Updater() await updater.update(counter) let updatedCount = counter.load(...) } Stack version2 Heap … … (caller) version2 ⚛︎︎
  35. func version3() async 71 func version3() async { let counter

    = Atomic<Int>(0) let updater = Updater() await updater.doOtherWork() let updatedCount = counter.load(...) } Stack Heap … … version3 (caller) (caller) ϛ version3 ⚛︎︎ தஅϙΠϯτͷલޙͰ࢖ͬͯΔͷͰ
  36. ࢀߟจݙ • What’s new in Swift - WWDC24 - Videos

    - Apple Developer • https://developer.apple.com/videos/play/wwdc2024/10136/ • Var of Atomic type is not an error? - #2 by MahdiBM - Using Swift - Swift Forums • https://forums.swift.org/t/var-of-atomic-type-is-not-an-error/69885/2 • ಉظ • https://www.hpcs.cs.tsukuba.ac.jp/~tatebe/lecture/h27/dsys/5-sync.pdf • ϝϞϦϞσϧೖ໳ʢSequential ConsistencyͱTotal Store OrderΛཧղ͢Δʣ • https://techblog.lycorp.co.jp/ja/20231216a • Swift ʹಋೖ༧ఆͷ Ownership ػೳͷ঺հ #swtws - Qiita • https://qiita.com/omochimetaru/items/c5f0eabde516e4713367 • How to develop SIL Optimizer in Swift Language • https://gist.github.com/freddi-kit/459297734b37cb51bfb08f74ce944cab • swift-ownership-jp/0176-enforce-exclusive-access-to-memory.md at master · omochi/swift-ownership-jp • https://github.com/omochi/swift-ownership-jp/blob/master/0176-enforce-exclusive-access-to-memory.md 74