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

Fix: mutable to immutable object conversion with python bindings #564

Merged
merged 4 commits into from
Feb 24, 2024

Conversation

m-fila
Copy link
Contributor

@m-fila m-fila commented Feb 22, 2024

BEGINRELEASENOTES

  • Fix mutable to immutable object conversion with python bindings

ENDRELEASENOTES

As reported in key4hep/EDM4hep#270 the python bindings were unable to implicitly convert mutable to immutable objects with the converting constructor. An alternative approach with converting constructor instead of conversion function is viable.

Converting constructor was added and conversion function removed to avoid ambiguity in precedence.

I'm not sure if the test is placed in a correct place

Closes key4hep/EDM4hep#270

@m-fila m-fila marked this pull request as draft February 22, 2024 16:40
cleaned unnecessary import magic
@tmadlener
Copy link
Collaborator

It looks like having a converting constructor instead of a conversion operator makes clang-tidy a bit more sensitive: https://github.com/AIDASoft/podio/actions/runs/8016200074/job/21897701922?pr=564#step:4:450

@m-fila
Copy link
Contributor Author

m-fila commented Feb 23, 2024

wlav/cppyy#218 explains inefficiencies of implicit casting in cppyy.
I tried a naive benchmark:

from timeit import timeit
from ROOT import MutableExampleMC
from cppyy.ll import static_cast
 
def bench_cast():
    parent = MutableExampleMC()
    daughter = MutableExampleMC()
    daughter.addparents(static_cast['ExampleMC'](parent))
 
def bench_no_cast():
    parent = MutableExampleMC()
    daughter = MutableExampleMC()
    daughter.addparents(parent)

def report(result, reps, name):
    print(f'Average time: {result/reps:.1E} seconds\t({name})')

if __name__ == '__main__':
    reps= 100000
    result = timeit('bench_cast()', setup='from __main__ import bench_cast; bench_cast()', number=reps)
    report(result, reps, 'explicit cast')
    result = timeit('bench_no_cast()', setup='from __main__ import bench_no_cast; bench_no_cast()', number=reps)
    report(result, reps, 'implicit cast')

With the results:

Average time: 2.7E-06 seconds   (explicit cast)
Average time: 2.2E-06 seconds   (implicit cast)

Not sure why implicit seems to be faster (very little overloads to consider?).
Regardless, I'd say there is no visible drop in performance for this case

@m-fila m-fila marked this pull request as ready for review February 23, 2024 09:39
@tmadlener
Copy link
Collaborator

Thanks for checking some potential performance implications. That looks reasonable to me. In the end, I don't think performance is the main concern when you go to the python bindings ;)

@jmcarcell
Copy link
Member

This works fine in EDM4hep without the casts, I tested the script that is in key4hep/EDM4hep#272. Re the benchmarks wlav mentions all the process where you have to first fail (in a try - except block probably) and then find the correct operator and then use it. So it's going to be worse than your .5 / 2.2 = 23% loss

@tmadlener
Copy link
Collaborator

@wdconinc this should be a completely transparent change as far as we are aware. Would you like to give it a go before we merge, or should we just go ahead and you will report back in case you find an issue on your side?

wdconinc added a commit to wdconinc/JANA2 that referenced this pull request Feb 23, 2024
Due to AIDASoft/podio#564 we get compilation errors:
```
  >> 322    /home/wdconinc/git/JANA2/src/examples/PodioExample/PodioExample.cc:46:51: error: call of overloaded 'ExampleHit(<brace-enclosed initializer list>)' is ambiguous
     323       46 |     hits2.push_back(ExampleHit({42, 5, -5, 5, 7.6}));
     324          |                                                   ^
     325    In file included from /home/wdconinc/git/JANA2/src/examples/PodioExample/datamodel/MutableExampleHit.h:8,
     326                     from /home/wdconinc/git/JANA2/src/examples/PodioExample/PodioExample.cc:7:
     327    /home/wdconinc/git/JANA2/src/examples/PodioExample/./datamodel/ExampleHit.h:59:3: note: candidate: 'ExampleHit::ExampleHit(const MutableExampleHit&)'
     328       59 |   ExampleHit(const MutableExampleHit& other);
     329          |   ^~~~~~~~~~
     330    /home/wdconinc/git/JANA2/src/examples/PodioExample/./datamodel/ExampleHit.h:47:3: note: candidate: 'ExampleHit::ExampleHit(const ExampleHit&)'
     331       47 |   ExampleHit(const ExampleHit& other) = default;
     332          |   ^~~~~~~~~~
```
This commit remove the ambiguity that is present by passing an initializer list to a constructor.

After this commit, JANA2 compiles both before and after podio PR 564 (and also with v00-17-03 which is before AIDASoft/podio#514 which led to JeffersonLab#269). There is no minimum podio version specified in CMakeLists.txt to test with.
@wdconinc
Copy link
Contributor

this should be a completely transparent change as far as we are aware

Hyrum's law strikes again. Go ahead and merge. We'll consider it a bug on our end :-)

@tmadlener tmadlener merged commit d12cf45 into AIDASoft:master Feb 24, 2024
16 of 18 checks passed
@m-fila m-fila deleted the object_conversion branch February 27, 2024 10:31
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.

The conversion operator is not being used in python to go from Mutable to the non-mutable type
4 participants