Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Synchronizationを支える技術

Sponsored · SiteGround - Reliable hosting with speed, security, and support you can count on.

 Synchronizationを支える技術

Avatar for Shigure Shimotori

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