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

Update deserialize function to return unique_ptr #25126

Merged
merged 2 commits into from Nov 21, 2018

Conversation

wddgit
Copy link
Contributor

@wddgit wddgit commented Nov 5, 2018

The effects of this pull request on code outside the file Serialization.h are small. Any code that fully specializes the deserialization function template needs to have its return type changed to unique_ptr. In addition, there is one file in CMSSW containing code like "auto x = deserialize ...", where on a subsequent line x is assigned to a shared_ptr. In this particular case, the variable could just be removed. It wasn't needed. An alternative in this case and other similar cases, would be the addition of std::move at the assignment. These problems are easy to find because they cause compilation to fail. All such cases in the CMSSW repository are fixed in this PR. There are not many. Calling deserialize directly is not common in user code, so I suspect there are not many problematic calls to deserialize outside the repository. In most cases I saw, code that uses deserialize will continue to work without any modification or performance problem.

Internally, the deserialize function does not save a copy of the shared_ptr. It does not actually manage the memory of the object. It is hard to imagine modifying the deserialize function in the future to manage this memory. That would imply the internals of deserialize would know when to delete the memory and also hold on to a copy of the shared_ptr.

Externally, we are making deserialize more flexible. Previously, the interface forced the client to use a shared_ptr or make an expensive deep copy. By returning a unique_ptr, the client has a choice. It can use a unique_ptr to manage the memory. But just as easily, it can convert to a shared_ptr. And because it is a move there is no performance penalty. (The inverse, converting from shared_ptr to unique_ptr does not work!)

In general, a unique_ptr performs better than a shared_ptr. With a shared_ptr there are two counters, we use CPU to increment/decrement them, and they use memory. If you do not use make_shared, there is an extra memory allocation. Also, the counters are thread safe and at run time must be synchronized across threads, which can be significant. I do not know if in this case the performance difference is significant.

Unique_ptr's are also easier to understand and think about for both developers and maintainers. See https://herbsutter.com/2013/05/29/gotw-89-solution-smart-pointers/ for some discussion related to this in general.

The immediate motivation that started the core group looking at this relates to our goal of running multiple IOVs concurrently. Let's says an EDProducer processes two events and is using objects from the EventSetup from different IOVs. Assume that the values stored in the object for these different IOVs are different. When we ran one IOV at a time, one could have both objects actually be the same object at the same memory location. This allowed the ESProducer to implement some optimizations such as not having to reallocate the object at an IOV boundary or maybe only parts of the object would change at an IOV boundary. This worked fine when only one IOV is processed at a time. But when both IOVs are running simultaneously, this does not work. Unless you implement a very complex thread safe design for the object, it cannot hold two sets of values at the same time. The same object in the same memory cannot be used simultaneously with two different sets of values. In the past, the EventSetup implemented this reuse of objects by allowing the produce function to return a shared_ptr. The shared_ptr allows the ESProducer module to save a copy of the shared_ptr and reuse the object on the next call to produce. We now need to be careful that all ESProducers are operating in a thread safe manner. And it is important that we be able to verify this quickly. If we allocate new objects on each call to produce and return a unique_ptr, then we can verify this by simply looking at the return type of the produce function. But if a shared_ptr is returned we have to look inside the ESProducer at the details of how that ESProducer is using the shared memory internally and it gets complicated.

ESProducers can still return shared_ptr in some special cases and still work with concurrent IOVs, but in most cases it involves use of the new ReusableObjectHolder class which manages multiple copies of the object, one copy for each IOV. And even this will not work well if handed a shared_ptr that lower level code created.

If you look down the call stack from the produce function of many ESProducers you will find the deserialize function down near the bottom. There is no way to return a unique_ptr from these produce functions without changing the return type of the deserialize function (unless you do an expensive deep copy of the object or implement some horrible hack somewhere in between).

If this PR is approved, additional PRs will follow that modify the code higher in the stack above deserialize. However, this PR is not dependent on those changes. It could be approved alone. Even if none of the other changes are ever made, it would still be an improvement.

There is also another change included in this PR. There is a single one line change that resolves a clang warning. It is in a separate commit and unrelated.

@cmsbuild
Copy link
Contributor

cmsbuild commented Nov 5, 2018

The code-checks are being triggered in jenkins.

@cmsbuild
Copy link
Contributor

cmsbuild commented Nov 5, 2018

@cmsbuild
Copy link
Contributor

cmsbuild commented Nov 5, 2018

A new Pull Request was created by @wddgit (W. David Dagenhart) for master.

It involves the following packages:

CondCore/CSCPlugins
CondCore/CalibPlugins
CondCore/CondDB
CondCore/DTPlugins
CondCore/PhysicsToolsPlugins
CondCore/PopCon
CondCore/Utilities

@tocheng, @cmsbuild, @franzoni, @ggovi, @pohsun, @lpernie can you please review it and eventually sign? Thanks.
@mmusich, @tocheng this is something you requested to watch as well.
@davidlange6, @slava77, @fabiocos you are the release manager for this.

cms-bot commands are listed here

@wddgit
Copy link
Contributor Author

wddgit commented Nov 5, 2018

please test

FYI @Dr15Jones

@cmsbuild
Copy link
Contributor

cmsbuild commented Nov 5, 2018

The tests are being triggered in jenkins.
https://cmssdt.cern.ch/jenkins/job/ib-any-integration/31480/console Started: 2018/11/05 21:41

@cmsbuild
Copy link
Contributor

cmsbuild commented Nov 6, 2018

@cmsbuild
Copy link
Contributor

cmsbuild commented Nov 6, 2018

Comparison job queued.

@cmsbuild
Copy link
Contributor

cmsbuild commented Nov 6, 2018

Comparison is ready
https://cmssdt.cern.ch/SDT/jenkins-artifacts/pull-request-integration/PR-25126/31480/summary.html

Comparison Summary:

  • No significant changes to the logs found
  • Reco comparison results: 0 differences found in the comparisons
  • DQMHistoTests: Total files compared: 32
  • DQMHistoTests: Total histograms compared: 2993155
  • DQMHistoTests: Total failures: 2
  • DQMHistoTests: Total nulls: 0
  • DQMHistoTests: Total successes: 2992956
  • DQMHistoTests: Total skipped: 197
  • DQMHistoTests: Total Missing objects: 0
  • DQMHistoSizes: Histogram memory added: 0.0 KiB( 31 files compared)
  • Checked 134 log files, 14 edm output root files, 32 DQM output files

@pohsun
Copy link

pohsun commented Nov 12, 2018

+1
Many thanks for detailed PR message. I learned a lot.

@ggovi
Copy link
Contributor

ggovi commented Nov 20, 2018

@wddgit
I agree with most of your points. The main one remains less convincing: "Assume that the values stored in the object for these different IOVs are different. When we ran one IOV at a time, one could have both objects actually be the same object at the same memory location. This allowed the ESProducer to implement some optimizations such as not having to reallocate the object at an IOV boundary or maybe only parts of the object would change at an IOV boundary. This worked fine when only one IOV is processed at a time."
That's certainly not what happens with the Conditions. A Payload is either valid for the current IOV, or recreated from scratch for any other IOVs.
Anyhow, even if this PR would be actually required within a GENERAL redesign of the framework, the change in itself is reasonable and I've got no objection in approving it.

@ggovi
Copy link
Contributor

ggovi commented Nov 20, 2018

+1

@cmsbuild
Copy link
Contributor

This pull request is fully signed and it will be integrated in one of the next master IBs (tests are also fine). This pull request will now be reviewed by the release team before it's merged. @davidlange6, @slava77, @smuzaffar, @fabiocos (and backports should be raised in the release meeting by the corresponding L2)

@fabiocos
Copy link
Contributor

+1

@cmsbuild cmsbuild merged commit b538697 into cms-sw:master Nov 21, 2018
@wddgit wddgit deleted the deserializeReturnsUnique branch March 20, 2019 19:53
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.

None yet

5 participants