Skip to content

Commit

Permalink
Merge pull request #29438 from schneiml/patch-12
Browse files Browse the repository at this point in the history
DQM: Add a local vs. global section to the README
  • Loading branch information
cmsbuild authored Apr 9, 2020
2 parents a2a280c + 1ec79d7 commit 977cf44
Showing 1 changed file with 12 additions and 0 deletions.
12 changes: 12 additions & 0 deletions DQMServices/Core/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,18 @@ DQM promises that all data dependencies across the `DQMStore` are visible to EDM
- `DQMGenerationQTest` is produced only by the `QualityTester` module (of which we typically have many instances). The `QualityTester` has fully configurable dependencies to allow the more complicated setups sometimes required in harvesting, but typically consumes `DQMGenerationHarvesting`.
- There is a hidden dependency between end run and end job in harvesting: Many harvesters effectively depend on `DQMGenerationQTest` products (the QTest results) and do not specify that, but things still work because they do their work in `endJob`.

#### Local `MonitorElement`s vs. global `MonitorElement`s

The distiction between _global_ and _local_ MEs is the key part to having `edm::stream` based processing in DQM. This distinction bridges the gap between the module's view (`MonitorElement`s are objects that can be used at any time) and the frameworks view (`MonitorElement`s are datastructures that hold data for one run/lumisection/job).

To do this, we have _local_ MEs, which are owned by the modules, and remain valid over the full lifetime of a module (that will be a full job). These are the objects that normal `book*` calls return. Internally, we handle _global_ MEs, which are only valid over a single run or lumisection or job: They are created at the beginning, then filled, then saved to an output file, then destroyed. Modules *can* handle global MEs as well, they are returned by the `get*` calls in the `IGetter` interface of the `DQMStore`. (`book*` calls in _legacy_ and harvesting modules, that is those booking calls that do not happen inside a _booking transaction_ and therefore are booked with module id 0, also return global MEs. Local MEs are still internally created and could be returned as well, but by returning the global objects we get the useful property that `book*` and `get*` for the same name return _the same_ object.)

Global and local MEs use the same type (or rather types), `MonitorElement`. This is questionable and may be slightly confusing, but it keeps things simpler in harvesting code. The difference between global and local MEs is that the global MEs *own* a histogram (in the form of a `MonitorElementData` instance), while the local MEs only *have access* to the histogram, which is owned by the global ME.

Effectively, each local ME is connected to a global ME, by sharing its `MonitorElementData`. There can be many local MEs sharing data of the same global ME (this happens e.g. with `edm::stream` modules, and this is why we need locking in the `MonitorElement` methods). There can also be multiple global MEs with the same name, e.g. if there are concurrent lumisections. In this case, the local MEs must be connected to the *correct* global ME. This is handled by the `enterLumi` method. It can happen that a local ME is not connected to *any* global ME, accessing it in this situation will lead to a crash. This should only happen during transitions.

Related to multi-threading and legacy modules, we guarantee two things: First, it is safe to access _local_ MEs during the callbacks of `DQM*EDAnalyzer`. This is true no matter how many threads and concurrent accesses there are. Second, if a job runs single-threaded and without concurrent lumisections, then local and global MEs are interchangeable (though not identical) and valid for the entire job. There is exactly one global ME for each name, over the entire job, and all local MEs for the same name are always attached to this global ME. This is achieved by _recycling_ run and lumi MEs (that is, the global MEs for a new run/lumi will re-use the already existing objects, so that existing pointers remain valid) and carefully placed `Reset` calls, so that even `fill` calls that reach the MEs before or between runs/lumisections will be saved in the next run/lumi. Those guarantees cannot be held when there are multiple threads (modules on different threads might see invalid states during transitons) or concurrent lumisections (per-lumi global MEs may be created and deleted during the job).

### Digging Deeper

For more details about how things are exactly implemented, refer to the comments in the source code; primarily `DQMStore.h`, `DQMStore.cc`, `MonitorElement.h` and `MonitorElement.cc`. For hints on how to use the APIs, refer to the sample and test code in `DQMServices/Demo`. The module header files `DQM*EDAnalyzer.h`, `DQMEDHarvester.h` can also be worth a look, especially to understand the interaction with EDM.
Expand Down

0 comments on commit 977cf44

Please sign in to comment.