the detector clocks service
play

The detector-clocks service A case study in determining thread-safe - PowerPoint PPT Presentation

The detector-clocks service A case study in determining thread-safe service access patterns Kyle J. Knoepfel 17 December 2019 LArSoft coordination meeting Services The SciSoft team has been working toward making LArSoft code thread-safe.


  1. The detector-clocks service A case study in determining thread-safe service access patterns Kyle J. Knoepfel 17 December 2019 LArSoft coordination meeting

  2. Services • The SciSoft team has been working toward making LArSoft code thread-safe. • Services are problematic due to widespread use of non-const mutable data. – DetectorClocks and DetectorProperties suffer from this malady. • In this talk, I will present: – A pattern that can be adopted for both services to make them thread-safe. – My work toward that end for the DetectorClocks service. – A proposal for adopting the pattern. 2 12/17/19 Kyle J. Knoepfel | LArSoft coordination meeting

  3. Thread-unsafe approach • Monolithic data structures are often chosen for managing mutable data corresponding to different processing granularities. • This is true for various LArSoft facilities (e.g. Job-level data DetectorClocks and DetectorProperties ). • It is inherently thread- unsafe as it often relies on the Run-level data notion of “current”, which is ill-defined in multi-threaded environments. Event-level data 3 12/17/19 Kyle J. Knoepfel | LArSoft coordination meeting

  4. Thread-unsafe approach Thread 1 Thread 2 Create service Job-level data Run-level data Event-level data 4 12/17/19 Kyle J. Knoepfel | LArSoft coordination meeting

  5. Thread-unsafe approach Thread 1 Thread 2 Begin Run 1 Job-level data Run-level data Event-level data 5 12/17/19 Kyle J. Knoepfel | LArSoft coordination meeting

  6. Thread-unsafe approach Thread 1 Thread 2 Process Event 1 Job-level data Run-level data Event-level data 6 12/17/19 Kyle J. Knoepfel | LArSoft coordination meeting

  7. Thread-unsafe approach Thread 1 Thread 2 Process Event 1 Job-level data Process Event 2 Run-level data Eve d nt-l a eve t l a Data race 7 12/17/19 Kyle J. Knoepfel | LArSoft coordination meeting

  8. Thread-unsafe approach Thread 1 Thread 2 Process Event 1 Job-level data Process Event 2 Run-level data Eve d nt-l a eve t l a Data race • To solve this problem for the DetectorClocks provider/service, I have adopted the “persistent data structure” approach. – Data structures broken up according to the processing steps required. – In what follows, all boxes represent immutable objects. 8 12/17/19 Kyle J. Knoepfel | LArSoft coordination meeting

  9. Persistent data structure approach Thread 1 Thread 2 9 12/17/19 Kyle J. Knoepfel | LArSoft coordination meeting

  10. Persistent data structure approach Thread 1 Thread 2 Job-level data Create service 10 12/17/19 Kyle J. Knoepfel | LArSoft coordination meeting

  11. Persistent data structure approach Thread 1 Thread 2 Job-level data uses creates Begin Run 1 Run-level data 11 12/17/19 Kyle J. Knoepfel | LArSoft coordination meeting

  12. Persistent data structure approach Thread 1 Thread 2 Job-level data uses creates Run-level data Process Event 1 uses creates Event-level data 12 12/17/19 Kyle J. Knoepfel | LArSoft coordination meeting

  13. Persistent data structure approach Thread 1 Thread 2 Job-level data uses creates Run-level data Process Event 1 uses creates Process Event 2 Event-level data Event-level data 13 12/17/19 Kyle J. Knoepfel | LArSoft coordination meeting

  14. Persistent data structure approach Thread 1 Thread 2 Job-level data uses creates Run-level data uses creates Process Event 2 Finish Event 1 Event-level data 14 12/17/19 Kyle J. Knoepfel | LArSoft coordination meeting

  15. Persistent data structure approach Thread 1 Thread 2 Job-level data uses creates Run-level data uses creates Process Event 2 Begin Run 2 Event-level data Run-level data 15 12/17/19 Kyle J. Knoepfel | LArSoft coordination meeting

  16. Persistent data structure approach Thread 1 Thread 2 Job-level data uses creates Run-level data uses creates Process Event 2 Event-level data Process Event 3 Run-level data uses creates Event-level data 16 12/17/19 Kyle J. Knoepfel | LArSoft coordination meeting

  17. Persistent data structure approach • Why does this work? – All objects are immutable. Job-level data – Object construction/destruction uses creates happens on one thread. Run-level data – Object of one processing level refers to uses creates the object directly above it (via pointer or reference). – Assuming data corresponding to each processing levels is small, extra Event-level data overhead is minimal wrt. thread-unsafe option. Run-level data uses creates Event-level data 17 12/17/19 Kyle J. Knoepfel | LArSoft coordination meeting

  18. Persistent data structure approach • Why does this work? – All objects are immutable. Job-level data – Object construction/destruction uses creates happens on one thread. Run-level data – Object of one processing level refers to uses creates the object directly above it (via pointer or reference). – Assuming data corresponding to each processing levels is small, extra Event-level data overhead is minimal wrt. thread-unsafe option. • Downsides to this approach Run-level data – May require caching of data across uses creates threads. Not so much an issue for Event-level data DetectorClocks . 18 12/17/19 Kyle J. Knoepfel | LArSoft coordination meeting

  19. Example: Thread-unsafe code class ClockService { public: ClockService(ParameterSet const& pset, ActivityRegistry& reg); string const& mode() const noexcept { return mode_; } RunNumber_t run() const noexcept { return run_; } Clock const* clock() const noexcept { return clock_.get(); } private: void prepareRun(Run const& r); void prepareEvent(Event const& e, ScheduleID); string const mode_; bool goodRun_{false}; // Updated per run unique_ptr<Clock const> clock_{nullptr}; // Updated per event }; 19 12/17/19 Kyle J. Knoepfel | LArSoft coordination meeting

  20. Example: Thread-unsafe code ClockService::ClockService(ParameterSet const& pset, ActivityRegistry& reg) class ClockService { : mode_{pset.get<string>("mode")} public: { ClockService(ParameterSet const& pset, reg.sPreProcessRun.watch(this, &ClockService::prepareRun); ActivityRegistry& reg); reg.sPreProcessEvent.watch(this, &ClockService::prepareEvent); } string const& mode() const noexcept { return mode_; } RunNumber_t run() const noexcept { return run_; } Clock const* clock() const noexcept { return clock_.get(); } private: void prepareRun(Run const& r); void prepareEvent(Event const& e, ScheduleID); string const mode_; bool goodRun_{false}; // Updated per run unique_ptr<Clock const> clock_{nullptr}; // Updated per event }; 20 12/17/19 Kyle J. Knoepfel | LArSoft coordination meeting

  21. Example: Thread-unsafe code ClockService::ClockService(ParameterSet const& pset, ActivityRegistry& reg) class ClockService { : mode_{pset.get<string>("mode")} public: { ClockService(ParameterSet const& pset, reg.sPreProcessRun.watch(this, &ClockService::prepareRun); ActivityRegistry& reg); reg.sPreProcessEvent.watch(this, &ClockService::prepareEvent); } string const& mode() const noexcept { return mode_; } RunNumber_t run() const noexcept { return run_; } Clock const* clock() const noexcept { return clock_.get(); } void ClockService::prepareRun(Run const& r) private: { void prepareRun(Run const& r); goodRun_ = clock_is_valid_for(r); void prepareEvent(Event const& e, ScheduleID); } string const mode_; bool goodRun_{false}; // Updated per run void unique_ptr<Clock const> clock_{nullptr}; // Updated per event ClockService::prepareEvent(Event const& e, ScheduleID) }; { clock_ = get_clock(mode_, goodRun_, e); } 21 12/17/19 Kyle J. Knoepfel | LArSoft coordination meeting

  22. Example: Thread-unsafe code ClockService::ClockService(ParameterSet const& pset, ActivityRegistry& reg) class ClockService { : mode_{pset.get<string>("mode")} public: { ClockService(ParameterSet const& pset, reg.sPreProcessRun.watch(this, &ClockService::prepareRun); ActivityRegistry& reg); reg.sPreProcessEvent.watch(this, &ClockService::prepareEvent); } string const& mode() const noexcept { return mode_; } RunNumber_t run() const noexcept { return run_; } Clock const* clock() const noexcept { return clock_.get(); } void ClockService::prepareRun(Run const& r) private: { void prepareRun(Run const& r); goodRun_ = clock_is_valid_for(r); void prepareEvent(Event const& e, ScheduleID); } string const mode_; bool goodRun_{false}; // Updated per run void unique_ptr<Clock const> clock_{nullptr}; // Updated per event ClockService::prepareEvent(Event const& e, ScheduleID) }; { clock_ = get_clock(mode_, goodRun_, e); } Not everything is const . L 22 12/17/19 Kyle J. Knoepfel | LArSoft coordination meeting

  23. Example: Thread-safe code (using persistent data structures) class ClockService { public: ClockService(ParameterSet const& pset) : mode_{pset.get<string>("mode")} {} string const& mode() const noexcept { return mode_; } class RunData; class EventData; RunData DataForRun(Run const& r) const; private: string const mode_; }; 23 12/17/19 Kyle J. Knoepfel | LArSoft coordination meeting

Download Presentation
Download Policy: The content available on the website is offered to you 'AS IS' for your personal information and use only. It cannot be commercialized, licensed, or distributed on other websites without prior consent from the author. To download a presentation, simply click this link. If you encounter any difficulties during the download process, it's possible that the publisher has removed the file from their server.

Recommend


More recommend