Skip to content

Replacement of Class Referenced with std::shared_ptr - #541

Closed
JanNiklasB wants to merge 53 commits into
CRPropa:masterfrom
JanNiklasB:ReferencedClassRemoval
Closed

JanNiklasB wants to merge 53 commits into
CRPropa:masterfrom
JanNiklasB:ReferencedClassRemoval

Conversation

@JanNiklasB

@JanNiklasB JanNiklasB commented May 11, 2026 •

Copy link
Copy Markdown
Member

Hi,

as discussed in the workshop I want to replace the Referenced class with std::shared_ptr.
Referenced was used to reference count in crpropa versions that required C++ standards lower then C++11, however since C++11 the std::shared_ptr template class was introduced as a better manageable smart pointer provided by C++.

To properly use std::shared_ptr instead of Referenced some other general modifications needed to be done:

  • All functions accepting a raw pointer needed to be deleted. This is because otherwise the ref_ptr might be converted into a raw pointer and then back into a ref_ptr which would cause the reference count of the std::shared_ptr to not be synced anymore and therefore might cause the unintended deletion of pointers.
  • All stack objects now need to be handed over directly:
// correct:
{
Candidate C;
ref_ptr<Candidate> cand_ptr(C);
}
// incorrect:
{
Candidate C;
ref_ptr<Candidate> cand_ptr(&C);  // will throw double free error
}
  • The plugin template changed due to the now required use of ref_ptr for the process functions. This breaks most plugins, but I provided an easy fix which can just be copy pasted in the README.md

The following will work the same as before:

  • Handing over heap allocated pointer:
ref_ptr<Candidate> c = new Candidate();
  • Initializing or adding modules directly over heap allocations (are converted to std::shared_ptr when converted to ref_ptr in function call)
ModuleList SIM;
SIM.add(new SimplePropagation());
  • usage of python

For that the SWIG header needed some adjustments so the pointer ownership is completely on the C++ side

Some other changes include:

  • Whitespace changes to consistently have tabs everywhere (as required by Contribute.md)
  • Replacement of AssocVec by std::unordered_map
  • Replacement of Clock by std::chrono::high_resolution_clock
  • New TEST(Candidate, clone) and TEST(Candidate, cloneRecursive)
  • New Plugin tests that test if it is still compatible with older crpropa versions
  • .vscode added to .gitignore, this is a folder that is automatically generated by Visual Studio Code and should never be committed
  • Version tag and configurable Version.h.in so plugins can easily check which code version they building against.

I marked this PR as Draft for now (currently CRPropa3.2.1) so it is clear that we only intend to add this after we release CRPropa3.3.0 . Furthermore, with the upcoming #535 and the heavy nuclei PR this PR would only delay the 3.3 release more, so considering that and the required changes to this PR when the mentioned PRs are merged I think a draft PR is the best choice.

(intern calls need to be adjusted)
ref_ptr now acts like a normal smart pointer and is able to hold a reference by using ref_ptr(T& obj)
comment previously commented test for now, search fix later
@JanNiklasB

JanNiklasB commented May 11, 2026 •

Copy link
Copy Markdown
Member Author

Turns out the issue was caused by the TEST(SynchrotronRadiation, PhotonEnergy) function in testInteraction.cpp , nothing went wrong internally but the memory required by the function was greater then the available amount in the runner (>2GiB), so limiting the samples solved the issue...

JanNiklasB and others added 2 commits May 11, 2026 20:35
The following changes are included:
- Optional python (now not required to build crpropa)
- Limit in secondary samples in tests to limit necessary memory
- Some performance adjustments
@JanNiklasB

Copy link
Copy Markdown
Member Author

I discussed the modification of SynchrotronRadiation.PhotonEnergy with @JulienDoerner and multiplied weights during the calculation of the average energy.

I also ran the test with the 1000 max sample limit 1000 times on our cluster to check if I encounter any fails (the test takes about 1ms) , and so far I did not encounter any.

@JulienDoerner JulienDoerner added this to the 3.4 milestone May 26, 2026
@rafaelab

Copy link
Copy Markdown
Member

Hi @JanNiklasB,

Thanks for the PR.

Out of curiosity: does this affect the performance?
Have you compared the native shared_ptr with CRPropa's handwritten ref_ptr for a couple of representative cases?
My intuition tells me the native C++ smart pointer should be faster, but who knows...

I haven't looked into the code in detail (nor do I think I will have time this week, so someone else might want to test it), but something I've noticed is that there seems to be a lot of outdated code in the commit (like deleting Eigen, lenses, etc), probably because you started working on this before 3.3.

@JanNiklasB

Copy link
Copy Markdown
Member Author

Yes, there is a lot of outdated code, I am currently working on migrating the new changes to this PR.
I will also try to add some perfomance plots that compare this PR and the last release when I am done with the cleanup.

@JanNiklasB
JanNiklasB marked this pull request as ready for review July 13, 2026 18:16
@JanNiklasB

Copy link
Copy Markdown
Member Author

This PR is now ready for review!
I will add the performance tests probably tomorrow.

@JulienDoerner JulienDoerner left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Hey @JanNiklasB, I think this PR already looks quite good. I have only a couple of minor comments.

"Replace it with a more appropriate turbulent field model instance.";
HelicalGridTurbulence::initTurbulence(grid, Brms, lMin, lMax, alpha, seed,
H);
<< "initTurbulence is deprecated and will be removed in the future. "

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

should we remove this one? It is already long time deprecated?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

oh, I am confused how that is there, I do not remember changing this (it has nothing to do with this PR and must come from an older version or something).

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Yes it is already older and you just applied the style convention. But I noticed it now, and we should remove it.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

We should remove it in another PR, I will set this line as it is in the master currently for now.

void setPhotonField(ref_ptr<PhotonField> photonField, bool superheavy = false);

// decide if secondary photons are added to the simulation
/// decide if secondary photons are added to the simulation

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

typo


/** Merge other maps, add pdfs */
void merge(const EmissionMap *other);
void merge(ref_ptr<const EmissionMap> other);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Are those EmissionMap Pointers available in swig?

* before continuing with the primary candidate if secondariesFirst is set to true
*/
void run(SourceInterface* source, size_t count, bool recursive = true, bool secondariesFirst = true, bool waitForSecondaries = true);
void run(ref_ptr<SourceInterface> source, size_t count, bool recursive = true, bool secondariesFirst = false, bool waitForSecondaries = true);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Why are you changing the default behavior of the secondariesFirst flag?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

no reason, this was by mistake during the last merge

Comment thread test/testCore.cpp
TEST(Candidate, clone){
Candidate c(
nucleusId(1, 1), // id
1*keV, // energy

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Although this test will work as intended, I would suggest using relativistic energies here (e.g. 1 EeV). At the moment, an energy of $E = 1 \ \mathrm{keV}$ does not reflect CRPropa's application.

Comment thread test/testCore.cpp
TEST(Candidate, cloneRecursive){
Candidate c(
nucleusId(1, 1), // id
1*keV, // energy

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

see above

- name: Checkout repository
uses: actions/checkout@v6
with:
ref: "3.3"

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

If I understand the logic correctly, this will test the old CRPropa version 3.3 with the plugin template at this point, not the new plugin with your compatibility check.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

yes, somehow I did not think about this, but yes the plugin template is also set to 3.3 .
Pulling the 3.3 repository manually and installing it should fix it.

@JanNiklasB

Copy link
Copy Markdown
Member Author

While doing the performance tests I noticed how slow std::shared_ptr actually is. Using ref_ptr like I use it in this PR would slow down the code quite a lot.

This is mainly caused by the excessive use of ref_ptr I introduced, which makes our code very memory safe (and thread safe I think) but at the cost of overall slower code, which is unacceptable.

Here are some plots that compare the current PR with the last release, there you can see how much slower the code would get (the code which I used to create these can be found in this codespace.

CPPSimComparison PythonSimComparison ref_ptr_PerformanceAnalysis

I want to see if I can find a workaround where we have the same or better performance as with the Referenced class since I really want to get rid of it. If I do not find anything I will close this PR.

@JanNiklasB

JanNiklasB commented Jul 15, 2026 •

Copy link
Copy Markdown
Member Author

I went through possible Ideas together with @JulienDoerner and thought about what I could do to achieve similar performance to the current release:

  1. Keep the current version of Referenced.h and change all performance critical functions back to raw pointer handling. For that I would also need to change some internal handling of ref_ptr like in ModuleList.cpp where modules are de-referenced in each process step.
  2. Rewrite the std::shared_ptr so that it is faster (remove everything that we do not need)
  3. Keep old Referenced.h

Option 1 and 2 would require massive amounts of work just to achieve similar performance, since after all, it is always faster to use a raw pointer then any smart pointer since the smart pointer always needs to do at least additional checks.
So for now I would just keep the old Referenced.h and the Referenced class.

Besides from just removing the class Referenced dependencies and enabling a ref_ptr from every object instead of just objects that inherit class Referenced, this PR introduced some other related changes that I would like to still merge:

  • Removal of AssocVector.h and Clock.h
  • Double free error message bug fix
  • Performance improvements through noexcept flags in ref_ptr

I will make a PR in the next few days for each of those points.

@JulienDoerner and I think it would be best to set this PR as a draft again until the next CRPropa call instead of just closing it.

As a conclusion, I think this PR shows us that the std::shared_ptr is not a good enough replacement for class Referenced. However, if another smart pointer that is as fast as class Referenced will be developed, std::shared_ptr can just be replaced by it.

@JanNiklasB
JanNiklasB marked this pull request as draft July 15, 2026 12:35
@JanNiklasB JanNiklasB closed this Sep 25, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants