Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Print ES module dependences from Tracer #46929

Open
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

wddgit
Copy link
Contributor

@wddgit wddgit commented Dec 12, 2024

PR description:

The Tracer service will print out information about the dependences between modules when the dumpPathsAndConsumes configuration parameter is set true. Before this PR, this information only covered dependences between EDFilters, EDAnalyzers and EDProducers. This PR extends that to also include EventSetup modules. The dependency information it prints is based upon the consumes calls in the C++ code.

Issue #12126 requests this capability. This is an old request, originally from 2015.

For the most part, this PR adds and doesn't modify existing code. It shouldn't modify the behavior of the EventSetup. It shouldn't affect output of the program. Mostly it is additional code to get data out of existing data structures that are somewhat difficult to access.

When reviewing this PR, one might benefit by first looking at the following file that contains reference output of a unit test:

FWCore/Integration/test/unit_test_outputs/testConsumesInfo_1.log

There are 5 main parts in the output.

  1. Paths and the modules on paths
  2. ED modules and modules they depend on
  3. EventSetup modules and modules they depend on
  4. ED modules with individual data products they consume and more detail
  5. EventSetup modules with individual data products they consume and more detail

Parts 3 and 5 are new. Parts 2 and 4 have EventSetup dependences added. Note that parts 4 and 5 have detail that includes both consumer and producer transitionID. The transitionID in the EventSetup is the count of the associated setWhatProduced function call in the ESProducer. This might be important because prefetching depends on this and is not done at the level of an entire ESProducer module.

Resolves cms-sw/framework-team#1187
Resolves #12126

PR validation:

The existing unit test for this dump of information is extended to cover the various EventSetup cases and the output of the dump is compared to a saved reference file known to be correct by manual inspection.

@cmsbuild
Copy link
Contributor

cmsbuild commented Dec 12, 2024

cms-bot internal usage

@cmsbuild
Copy link
Contributor

+code-checks

Logs: https://cmssdt.cern.ch/SDT/code-checks/cms-sw-PR-46929/42995

@cmsbuild
Copy link
Contributor

A new Pull Request was created by @wddgit for master.

It involves the following packages:

  • FWCore/Framework (core)
  • FWCore/Integration (core)
  • FWCore/ServiceRegistry (core)
  • FWCore/Services (core)

@Dr15Jones, @cmsbuild, @makortel, @smuzaffar can you please review it and eventually sign? Thanks.
@fwyzard, @makortel, @missirol this is something you requested to watch as well.
@antoniovilela, @mandrenguyen, @rappoccio, @sextonkennedy you are the release manager for this.

cms-bot commands are listed here

@wddgit
Copy link
Contributor Author

wddgit commented Dec 12, 2024

please test

@cmsbuild
Copy link
Contributor

+1

Size: This PR adds an extra 196KB to repository
Summary: https://cmssdt.cern.ch/SDT/jenkins-artifacts/pull-request-integration/PR-b428b4/43422/summary.html
COMMIT: 66bc924
CMSSW: CMSSW_15_0_X_2024-12-12-1700/el8_amd64_gcc12
User test area: For local testing, you can use /cvmfs/cms-ci.cern.ch/week1/cms-sw/cmssw/46929/43422/install.sh to create a dev area with all the needed externals and cmssw changes.

  • DAS Queries: The DAS query tests failed, see the summary page for details.

Comparison Summary

Summary:

Copy link
Contributor

@makortel makortel left a comment

Choose a reason for hiding this comment

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

Comments from a first pass

Comment on lines 153 to 154
ComponentDescription const* getComponentDescription(ESResolverIndex iResolverIndex) const;
unsigned int getTransitionID(ESResolverIndex iResolverIndex) const;
Copy link
Contributor

Choose a reason for hiding this comment

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

What would you think about dropping the get prefix?

Suggested change
ComponentDescription const* getComponentDescription(ESResolverIndex iResolverIndex) const;
unsigned int getTransitionID(ESResolverIndex iResolverIndex) const;
ComponentDescription const* componentDescription(ESResolverIndex iResolverIndex) const;
unsigned int transitionID(ESResolverIndex iResolverIndex) const;

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done. That is better. Thanks. (I didn't push the new code yet, but hopefully later today I will do that after I finish some last checks)

namespace edm {
class EventSetupConsumesInfo {
public:
EventSetupConsumesInfo(std::string_view const& iEventSetupRecordType,
Copy link
Contributor

Choose a reason for hiding this comment

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

string_view can be considered as a lightweight type that would be better passed by value

Suggested change
EventSetupConsumesInfo(std::string_view const& iEventSetupRecordType,
EventSetupConsumesInfo(std::string_view iEventSetupRecordType,

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I changed this class into a struct and removed that function entirely. CMS coding guidelines recommend this for things like this. It seemed cleaner and less error prone than trying to get the order of the arguments correct every time I called the constructor. Although I agree with the comment in general, string_view should be passed by value not by reference.

Comment on lines 10 to 11
Produces new WhatsIt products for the EventSetup by copying
the products it read.
Copy link
Contributor

Choose a reason for hiding this comment

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

To me it would be more clear to describe already here that the input ES data products are not actually copied.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I renamed the class ConsumeWhatsIt and there is also a new MayConsumeWhatsIt. I also edited the comment along the lines of your suggestion.

@@ -75,6 +75,8 @@ namespace edm::eventsetup {
CallbackProductResolver(const CallbackProductResolver&) = delete;
const CallbackProductResolver& operator=(const CallbackProductResolver&) = delete;

unsigned int transitionID() const final { return callback_->transitionID(); }
Copy link
Contributor

Choose a reason for hiding this comment

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

This is the "counter" for the setWhatProduced() calls in the ESProducer, right? I was going to suggest if we could think of a better word (I can see potential confusion with the framework transitions), but I see we already use the transitionID() term for this purpose.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes. It is the counter for the setWhatProduced calls in ESProducer. Adding this function to the resolver interface does make the name more visible, but we were already using this name. The transitionID function in CallbackBase was already there and this PR didn't modify that. It also was present in several other files already.

I could change it. We could also leave that for a separate PR. But I can't think of a better name and in some places it is convenient to use the same name as we use in EDConsumerBase. But I agree it leads to some confusion. It has been that way a while.

Copy link
Contributor

Choose a reason for hiding this comment

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

Since this is nominally the order of the setWhatProduced method calls, how about we change it to producedID? If we keep using the transitionID name for this one it is very confusing with the use of transition already in the ED system.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I am OK with changing this to producedID. I can't think of anything better. @makortel Are you in agreement also? Just checking before I start spending time implementing that change.

Just to repeat what I said earlier, this name was in use before this PR. I'll have to search for all existing uses and change them. But I agree that the name transitionID is confusing in this context.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Actually, what about produceMethodID instead.

Copy link
Contributor

Choose a reason for hiding this comment

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

produceMethodID does seem better to me.

Copy link
Contributor

Choose a reason for hiding this comment

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

I'm good with produceMethodID().

@@ -197,6 +197,16 @@ namespace edm {
void watchPostEndJob(PostEndJob::slot_type const& iSlot) { postEndJobSignal_.connect_front(iSlot); }
AR_WATCH_USING_METHOD_0(watchPostEndJob)

typedef signalslot::Signal<void(PathsAndConsumesOfModulesBase const&, ProcessContext const&)>
Copy link
Contributor

Choose a reason for hiding this comment

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

What is the state of PathsAndConsumesOfModuleBase object in the existing PreBeginJob signal?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The PathsAndConsumes object is not completely filled. Most of the information related to EventSetup isn't filled yet.

I was trying to minimize perturbations to the existing code and that led me to the current implementation. I didn't really want to change the interface of the preBeginJob signal or figure out all the consequences of moving the sequence in existing code around (afraid of the time it would take and possible errors I could inadvertently introduce and it is not obvious what to move).

I'm not completely happy with this.

The current sequence is (most of this is in EventProcessor::beginJob):

  1. Initialize info related to ED modules
  2. Use that for EDModule deletion
  3. The EventSetup modules go through finalizeConfiguration (updateLookup can't be called yet)
  4. eventSetupConfiguration signal (a couple things watch this)
  5. preBeginJob signal (partially filled PathsAndConsumes passed as argument, more things watch this)
  6. inside WorkerManager::beginJob there is a loop, for each worker updateLookup is called then beginJob
  7. looper calls updateLookup
  8. initializeForEventSetup (uses results of updateLookup)
  9. new signal used by the Tracer with all the EventSetup information filled

I could try to improve this. I'm nearing completion on the rest of the comments and the may consumes stuff. Mostly that seemed straightforward. I'm not sure if it is worth the effort to change this. I don't like giving access to a partially filled object in preBeginJob, but... What do you think?

Copy link
Contributor

Choose a reason for hiding this comment

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

Thanks for explaining the details.

I'm not sure if it is worth the effort to change this. I don't like giving access to a partially filled object in preBeginJob, but... What do you think?

I share your dislike, more specifically I'm concerned partially filled object would lead to confusion. Also code using PathsAndConsumesOfModulesBase would potentially want to watch the new signal. Ideally, I think we should eventually give only a fully filled PathsAndConsumesOfModulesBase object.

On a quick look I noticed only a few Services that really use it, in addition to Tracer (NVProfilerService, ProcessCallGraph, FastTimerService, DependencyGraph), so I'd expect migrating those Services to use the new signal would be straightforward. But in order to avoid feature creep, it would probably be better to migrate those in subsequent PR(s), given that this PR doesn't impact their present functionality.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I could easily add a boolean data member to the class PathsAndConsumesOfModules that indicates whether the EventSetup part has been initialized or not. If a member function using EventSetup info is called too early, it could throw an exception. Later we could remove that boolean if we migrate everything and remove the PathsAndConsumes argument from the preBeginJob transition signal function interface. Would that help?

Copy link
Contributor

Choose a reason for hiding this comment

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

I think the boolean and exception would be useful to add some safety on the interface during the transition period.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done. I implemented the boolean and exception.

// that is held in memory throughout the job, while the following
// function returns a newly created object each time. We do not
// expect this to be called during a normal production job where
std::vector<eventsetup::ComponentDescription const*> const& esModulesWhoseProductsAreConsumedBy(
Copy link
Contributor

Choose a reason for hiding this comment

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

Could you add a comment describing the return value in similar way to the modulesWhoseProductsAreConsumedBy() above?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I added a comment for that function and all the other similar functions in that class. Better. Thanks.

@makortel
Copy link
Contributor

One weakness of this PR is that the dump is not going to give much insight into what goes on inside PoolDBESSource.

Do you mean whatever PoolDBESSource itself does is not shown? But the dependencies from other (ES or ED) modules to ES data products produced by PoolDBESSource are included in the dump?

@makortel
Copy link
Contributor

Comparison differences are related to #39803 and #46416

@wddgit
Copy link
Contributor Author

wddgit commented Dec 18, 2024

I noticed that I neglected to implement code to handle the "may consumes" case even though I had thought about it and had a plan for how to do it. I am working on that now. It is mostly just additions to what is already here, not much significant changes in the existing PR code. I'll put it into a separate commit. This version just silently ignores those cases. I need to add a test module using "may consumes". Our current test relies on a lower level unit test.

@wddgit
Copy link
Contributor Author

wddgit commented Jan 2, 2025

One weakness of this PR is that the dump is not going to give much insight into what goes on inside PoolDBESSource.

Do you mean whatever PoolDBESSource itself does is not shown? But the dependencies from other (ES or ED) modules to ES data products produced by PoolDBESSource are included in the dump?

I am going to edit the comment at the top and remove those sentences. They do not make much sense. PoolDBESSource and its data products will show up in the Tracer output. The dump will not give any insight into the internal workings of PoolDBESSource and PoolDBESSource will not consume or depend on anything, but that is expected.

@cmsbuild
Copy link
Contributor

cmsbuild commented Jan 3, 2025

+code-checks

Logs: https://cmssdt.cern.ch/SDT/code-checks/cms-sw-PR-46929/43163

@cmsbuild
Copy link
Contributor

cmsbuild commented Jan 3, 2025

Pull request #46929 was updated. @Dr15Jones, @cmsbuild, @makortel, @smuzaffar can you please check and sign again.

@wddgit
Copy link
Contributor Author

wddgit commented Jan 3, 2025

I just force pushed all the updates. This should complete all of the following:

  • implement "may consumes" cases with new unit test extensions
  • implement changes in response to Matti's comments
  • EventSetupConsumesInfo is now a struct
  • Renamed the test module to ConsumesWhatsIt
  • Refactored the huge Tracer function into 6 smaller functions
  • Maybe some other minor changes I'm forgetting about but most of the rest is the same

Let me know if there are any more review comments or questions.
Looking at the reference test file may be a useful place to start as
that output is the whole point of this (in FWCore/Integration/test/unit_test_outputs).
The parts related to the test module MayConsumeWhatsIt are the
most significant new part.

@wddgit
Copy link
Contributor Author

wddgit commented Jan 3, 2025

please test

@wddgit
Copy link
Contributor Author

wddgit commented Jan 3, 2025

I also rebased the working area and squashed the commits.

(Although there is a branch from before I rebased and before I squashed
https://github.com/wddgit/cmssw/tree/tempBranchEventSetupDep
first commit is the original version of the PR and the second is the
changes)

@cmsbuild
Copy link
Contributor

cmsbuild commented Jan 4, 2025

+1

Size: This PR adds an extra 100KB to repository
Summary: https://cmssdt.cern.ch/SDT/jenkins-artifacts/pull-request-integration/PR-b428b4/43620/summary.html
COMMIT: d5fbf7d
CMSSW: CMSSW_15_0_X_2025-01-03-1100/el8_amd64_gcc12
User test area: For local testing, you can use /cvmfs/cms-ci.cern.ch/week0/cms-sw/cmssw/46929/43620/install.sh to create a dev area with all the needed externals and cmssw changes.

Comparison Summary

Summary:

@wddgit
Copy link
Contributor Author

wddgit commented Jan 9, 2025

This one is done and just waiting for more comments or approval. I probably won't get to responding to any new comments until Monday so no rush. Just don't want it to be forgotten because of the holidays.

Copy link
Contributor

@makortel makortel left a comment

Choose a reason for hiding this comment

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

A few more comments (also my last unless something comes up during further discussion)

// entries would be in the same order in m_esTokenInfo and
// esItemsToGetFromTransition_. This is something for future
// development and might require a change to SoATuple to support
// inserts in the middle of the data structure.
Copy link
Contributor

Choose a reason for hiding this comment

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

Is this TODO idea addressed in this PR, or you think it is not worth it anymore? (I see you added the comment in 2019)


namespace edm {
struct EventSetupConsumesInfo {
public:
Copy link
Contributor

Choose a reason for hiding this comment

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

Given the class being struct, this line is redundant

Suggested change
public:

// module label does not match an exception will be thrown
// if there is an attempt to actually get the data (or
// when the ESHandle is dereferenced if one gets that).
std::string_view eventSetupRecordType_;
Copy link
Contributor

Choose a reason for hiding this comment

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

If the public interface of this class are the public data members, I'd be inclined to drop the trailing underscore (like in ProductLabels)

But thinking out loud a bit

  • Event system ConsumesInfo has a member-function interface
  • With public data members it is possible to accidentally write to any of the data members
    • Not sure how big risk this point really is
  • Adding trivial getters/setters like in ConsumesInfo doesn't seem good either (also C++ Code Guidelines C.131), and adding all of these via constructor would just beg for a mistake

So probably a struct would indeed be the least bad option.

Should we consider making the interface patterns of EventSetupConsumesInfo and ConsumesInfo similar (some time in the future, if ever)?

// matches the EventSetupRecordKey type and product type. If there are no
// matches there is still one entry with a flag indicating there were
// no matches.
std::vector<std::vector<EventSetupConsumesInfo>> eventSetupConsumesInfo(ESProducer const& esProducer) const {
Copy link
Contributor

Choose a reason for hiding this comment

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

I have a little bit uneasy feeling of adding a dependence on ESProducer type here (although it is only forward-declared).

With EDModules I suppose we deal with these dependencies by passing in the module ID (that is just and integer) in this abstract interface, whereas the implementation (in FWCore/Framework) gets access to the actual worker/module objects via Schedule. I wonder if something similar would be feasible for ESProducers? (even for possible subsequent work if would be too much for this PR)

Copy link
Contributor

@Dr15Jones Dr15Jones left a comment

Choose a reason for hiding this comment

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

I have issues with how EventSetup internals are being exposed so I'll pause my review until we can discuss what I have.

void esModulesWhoseProductsAreConsumed(
eventsetup::EventSetupProvider const&,
std::array<std::vector<eventsetup::ComponentDescription const*>*,
static_cast<unsigned int>(Transition::NumberOfEventSetupTransitions)>& esModules) const;
Copy link
Contributor

Choose a reason for hiding this comment

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

Given how often static_cast<unsigned int>(Transition::NumberOfEventSetupTransitions) appears in the code, maybe a constexpr should be defined maybe in the file holding Transition?

constexpr unsigned int kNumberOfEventSetupTransitions = static_cast<unsigned int>(Transition::NumberOfEventSetupTransitions);

using ResolverIndexContainer =
std::array<std::vector<ESResolverIndex>,
static_cast<unsigned int>(edm::Transition::NumberOfEventSetupTransitions)>;
ResolverIndexContainer esItemsToGetFromTransition_;
Copy link
Contributor

Choose a reason for hiding this comment

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

Given the full type is used above in the function signature, how about moving the using to before the function and then make use of the using there as well?


// Ordered same as m_esTokenInfo, contents are indexes into esItemsToGetFromTransition_
std::vector<std::pair<ResolverIndexContainer::size_type, std::vector<ESResolverIndex>::size_type>>
consumesIndexToTransitionAndTokenIndex_;
Copy link
Contributor

Choose a reason for hiding this comment

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

Is this only used during the filling of the information for the Service callback? If so, I'm not a fan of it hanging around long term.

class ESRecordsToProductResolverIndices;
}
class EventSetupProvider;
Copy link
Contributor

Choose a reason for hiding this comment

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

I do not like everything suddenly becoming dependent upon EventSetupProvider. What appears to be needed is just a way to lookup for each EventSetup Record index what ComponentDescriptions are associated with a given index. I'd prefer a dedicated structure to lookup such information (could be a virtual interface which just goes back to the EventSetupProvider hidden inside of it).

In fact, maybe just returning a std::map<ESRecordIndex, std::vector<ESProductResolverIndex>> and then have the call which setups the data for the Service callback use that might be sufficient.

std::array<std::vector<ESResolverIndex>, static_cast<unsigned int>(edm::Transition::NumberOfEventSetupTransitions)>
esItemsToGetFromTransition_;

using ResolverIndexContainer =
Copy link
Contributor

Choose a reason for hiding this comment

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

This should probably be ESResolverIndexContainer

@@ -75,6 +75,8 @@ namespace edm::eventsetup {
CallbackProductResolver(const CallbackProductResolver&) = delete;
const CallbackProductResolver& operator=(const CallbackProductResolver&) = delete;

unsigned int transitionID() const final { return callback_->transitionID(); }
Copy link
Contributor

Choose a reason for hiding this comment

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

Since this is nominally the order of the setWhatProduced method calls, how about we change it to producedID? If we keep using the transitionID name for this one it is very confusing with the use of transition already in the ED system.

@@ -150,6 +150,9 @@ namespace edm {
void invalidateResolvers();
void resetIfTransientInResolvers();

ComponentDescription const* componentDescription(ESResolverIndex iResolverIndex) const;
unsigned int transitionID(ESResolverIndex iResolverIndex) const;

Copy link
Contributor

Choose a reason for hiding this comment

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

Should there be one function which returns both (rather than 2 functions)?

@@ -54,10 +64,20 @@ namespace edm {
std::vector<ModuleDescription const*> const& doModulesWhoseProductsAreConsumedBy(
unsigned int moduleID, BranchType branchType) const override;

std::vector<eventsetup::ComponentDescription const*> const& doESModulesWhoseProductsAreConsumedBy(
unsigned int moduleID, Transition) const override;
Copy link
Contributor

Choose a reason for hiding this comment

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

Should we change doModulesWhoseProductsAreConsumedBy so it also uses Transition instead of BranchType?


unsigned int doLargestModuleID() const override;

std::vector<eventsetup::ESProductResolverProvider const*> const& doAllESProductResolverProviders() const override;
Copy link
Contributor

Choose a reason for hiding this comment

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

I don't like exposing even via forward reference eventsetup::ESProductResolverProvider to everything that deals with a PathsAndConsumesOfModulesBase.


Schedule const* schedule_;
eventsetup::EventSetupProvider const* eventSetupProvider_;
Copy link
Contributor

Choose a reason for hiding this comment

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

I think we should use a different data structure and avoid exposing the inner details of the EventSetup system.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@Dr15Jones We are agreed that I need to remove the dependence on EventSetupProvider and ESProductResolverProvider from the interface available to Services. There are a couple of options I can think of to accomplish this.

  • I could remove the dependence on EventSetupProvider and ESProductResolverProvider from PathsAndConsumesOfModules entirely. The downside to this that we would need to calculate all elements of the std::vector<std::vector<EventSetupConsumesInfo>> always when initializing PathsAndConsumesOfModules and it just saves the result. Currently, most of the time we never calculate this at all, only when Tracer is printing out the dependences. So this involves extra CPU work, memory churn, and temporarily some extra memory to calculate this.
  • I could allow PathsAndConsumesOfModules to contain pointers to EventSetupProvider and ESProductResolverProvider, but only as private members that are used but not accessible. The Services would not have access to them. Note that PathsAndConsumesOfModules is a Framework class so its dependence on EventSetupProvider and ESProductResolverProvider is less of a problem than allowing Services to depend on them.

I can implement this either of those ways, just let me know what you prefer. Or maybe you have a better idea?

Copy link
Contributor

Choose a reason for hiding this comment

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

How about an abstract virtual class which has the job to return that info? Then Services would only depend on that interface and not on any parts of the ES system itself.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

PathsAndConsumesOfModulesBase is an abstract virtual base class and that is what the Services currently have access to (before this PR). Other than the constructor/destructor, all the base class functions have the pattern non-virtual function calls pure virtual function which is defined in PathsAndConsumesOfModules. PathsAndConsumesOfModules is the derived class. The only things with access to the derived class are EventProcessor and SubProcess. Is that good enough?

Copy link
Contributor

Choose a reason for hiding this comment

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

Sure.

@wddgit
Copy link
Contributor Author

wddgit commented Jan 15, 2025

I agree that it would be better to remove any dependence on ESProducer and ESProductResolverProvider from PathsAndConsumesOfModulesBase. There should not even be a forward reference. This will also remove that dependence from Tracer and also any other Service that uses this interface in the future. I think the dependence on ComponentDescription is OK and should remain.

That said, I think it is not a problem that the derived class PathsAndConsumesOfModules depends on ESProducer, ESProductResolverProvider and EventSetupProvider. PathsAndConsumesOfModules is a class defined in FWCore/Framework and the only things with access to it are EventProcessor and SubProcess. It is a data structure designed to isolate the Services from the internal workings of the Framework. So my initial suggestion would be to move the dependences in the base class to the derived class (along with whatever other changes are necessary to accomplish that).

I'm thinking it is OK for ESProducer and EDConsumerBase to use EventSetupProvider to get information related to the items they declare they consume. A new function in EventSetupProvider that returns a ComponentDescription given an EventSetupRecordKey and ESResolverIndex would remove some of that dependence from ESProducer.cc and EDConsumerBase.cc, but they would need to call that function. The alternatives seem worse to me. EventSetupProvider depending on EDConsumerBase and ESProducer or a new class that depends all three classes.

I didn't understand the suggestion with std::map<ESRecordIndex, std::vector<ESProductResolverIndex>>

Should I proceed to implement this and we can see how it looks? Or should I approach this in a different way?

@Dr15Jones
Copy link
Contributor

I didn't understand the suggestion with std::map<ESRecordIndex, std::vector>

Sorry, probably should have been more like std::map<ESRecordIndex, std::map< ESProductResolverIndex, ComponentDescription const*> >

I.e. a way to lookup ComponentDescription based on ESRecordIndex and ESProductResolverIndex.

@wddgit
Copy link
Contributor Author

wddgit commented Jan 15, 2025

Actually, instead of creating this new map, I think I can use ESRecordsToProductResolverIndices. That class already exists. ESProducer and EDConsumerBase already depend on it. I'll need to add another accessor to it and I am looking at the details carefully to figure out how to make it work. But it looks promising. Then EDConsumerBase and ESProducer will not need to depend on EventSetupProvider.

@wddgit
Copy link
Contributor Author

wddgit commented Jan 17, 2025

Some more detail on the solution I am working towards:

Add this data member to ESRecordsToProductResolverIndices.

    std::vector<unsigned int> produceMethodIDs_;

And add this accessor to the same class:

  std::tuple<ComponentDescription const*, unsigned int>
  ESRecordsToProductResolverIndices::componentAndProduceMethodID(
      EventSetupRecordKey const& iRK, ESResolverIndex esResolverIndex) const noexcept {
    auto const recIndex = recordIndexFor(iRK);
    if (recIndex == missingRecordIndex()) {
      return {nullptr, 0};
    }
    auto const beginIndex = recordOffsets_[recIndex.value()];
    auto const endIndex = recordOffsets_[recIndex.value() + 1];

    int resolverIndex = esResolverIndex.value();
    if (resolverIndex < 0 || static_cast<unsigned int>(resolverIndex) >= endIndex - beginIndex) {
      return {nullptr, 0};
    }
    auto index = beginIndex + resolverIndex;
    return { components_[index], produceMethodIDs_[index] };
  }

Then wherever possibly propagate this instead of EventSetupProvider. This will reduce dependence on that and some of these classes already depend on that type, e.g. EDConsumerBase and ESProducer.

I haven't tested or debugged this yet, but let me know if I'm headed in the wrong direction. I could implement the map you suggested but this seems better because it already exists and there is already code to fill it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Main PR implementing the feature Determine which modules depend on which conditions
4 participants