Skip to content

Implement heterogeneous Association Map based on SoA blocks - #49995

Open
sbaldu wants to merge 3 commits into
cms-sw:masterfrom
sbaldu:feature/heterogeneous-associator
Open

Implement heterogeneous Association Map based on SoA blocks#49995
sbaldu wants to merge 3 commits into
cms-sw:masterfrom
sbaldu:feature/heterogeneous-associator

Conversation

@sbaldu

@sbaldu sbaldu commented Feb 1, 2026

Copy link
Copy Markdown
Contributor

PR description:

This PR implements an association map based on SoA blocks. In order to provide the accessors to the map's properties and content, the PR also enables the definition of SoA view methods.

PR validation:

The PR includes a test of the construction and fill of the map and checks the correctness of the content and the accessor methods.

FYI @felicepantaleo

@cmsbuild

cmsbuild commented Feb 1, 2026

Copy link
Copy Markdown
Contributor

cms-bot internal usage

@cmsbuild

cmsbuild commented Feb 1, 2026

Copy link
Copy Markdown
Contributor

@cmsbuild

cmsbuild commented Feb 1, 2026

Copy link
Copy Markdown
Contributor

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

It involves the following packages:

  • DataFormats/AssociationMap (****)
  • DataFormats/SoATemplate (heterogeneous)

The following packages do not have a category, yet:

DataFormats/AssociationMap
Please create a PR for https://github.com/cms-sw/cms-bot/blob/master/categories_map.py to assign category

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

cms-bot commands are listed here

@fwyzard

fwyzard commented Feb 2, 2026

Copy link
Copy Markdown
Contributor

type ngt

@cmsbuild cmsbuild added the ngt label Feb 2, 2026
Comment thread DataFormats/TICL/BuildFile.xml
Comment thread DataFormats/AssociationMap/interface/AssociationMap.h Outdated
#include <cstdint>
#include <type_traits>

namespace ticl {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Is this utility specific to TICL or intended for more general usage?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

As of now the main usage is TICL

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Would it be feasible to include this code in a package that already uses TICL?

Or, the motivation to place this code in its own package is not clear.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Thanks for the suggestion. I've now put the map under a new DataFormats/TICL package

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

With @felicepantaleo we decided to add a new package for TICL data formats and we'll start moving the structures specific for TICL there

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

With @felicepantaleo we decided to add a new package for TICL data formats and we'll start moving the structures specific for TICL there

Ok. I guess at least @cms-sw/reconstruction-l2 should sign it? What about @cms-sw/heterogeneous-l2?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

What about @cms-sw/heterogeneous-l2?

This too, I guess 🤷🏻‍♂️

Comment thread DataFormats/AssociationMap/interface/FillAssociator.h Outdated
Comment thread DataFormats/AssociationMap/interface/detail/FillAssociator.h Outdated
Comment thread DataFormats/AssociationMap/interface/FillAssociator.h Outdated
@cmsbuild

cmsbuild commented Feb 2, 2026

Copy link
Copy Markdown
Contributor

@cmsbuild

cmsbuild commented Feb 2, 2026

Copy link
Copy Markdown
Contributor

Pull request #49995 was updated. @cmsbuild, @fwyzard, @makortel can you please check and sign again.

Comment thread DataFormats/AssociationMap/interface/FillAssociator.h Outdated
Comment thread DataFormats/AssociationMap/interface/FillAssociator.h Outdated
@cmsbuild

cmsbuild commented Feb 3, 2026

Copy link
Copy Markdown
Contributor

@cmsbuild

cmsbuild commented Feb 3, 2026

Copy link
Copy Markdown
Contributor

Pull request #49995 was updated. @cmsbuild, @fwyzard, @makortel can you please check and sign again.

@Moanwar

Moanwar commented Aug 17, 2026

Copy link
Copy Markdown
Contributor

+1

Comment thread DataFormats/TICL/interface/detail/FillAssociator.h Outdated
Comment thread DataFormats/TICL/interface/detail/FillAssociator.h Outdated
@Parsifal-2045

Copy link
Copy Markdown
Contributor

I think that fixing the previous off-by-one issues has introduced some new unwanted behaviour (that escaped the current testing). Now the OffsetsLayout correctly stores the requested nkeys + 1 so that the count() method works as expected, then AssociationMap just returns size() - 1 to compensate (AssociationMap.h:62-64).
The issue is that now the number passed by the caller and what the block holds are different and the value stored in the SoABlocks depends on which constructor was used:

  • The constructor relying on the user-provided layout (link) copies what the caller passed to the constructor (let's say nkeys)
  • The constructor using per-block views asks each view how many elements it actually contains (which would be nkeys + 1).

The override of the SoABlocks sizes in AssociationMap makes the two disagree so that building a map with nkeys = 2 one would get:

  • map.view().metadata().size()[1] == 2
  • map.view().offsets().metadata().size() == 3
  • AssociationMap<>::const_cast_View(map.const_view()).metadata().size()[1] == 3
    which can be easily checked via something like
CHECK( map.view().metadata().size()[1] == map.view().offsets().metadata().size() )

I think this "disagreement" might have multiple repercussions (e.g. probably in the deepCopy check) so I would suggest to drop the OffsetsLayout override, prefer building always with nkeys + 1 and having keys() stay size() - 1 for consistent size reporting. Note that dropping the override would most likely need to go together with #49995 (comment) to not break fill()

@cmsbuild

Copy link
Copy Markdown
Contributor

+code-checks

Logs: https://cmssdt.cern.ch/SDT/code-checks/cms-sw-PR-49995/50648

@cmsbuild

Copy link
Copy Markdown
Contributor

Pull request #49995 was updated. @Moanwar, @cmsbuild, @fwyzard, @jfernan2, @makortel, @mandrenguyen, @srimanob can you please check and sign again.

@cmsbuild

Copy link
Copy Markdown
Contributor

+code-checks

Logs: https://cmssdt.cern.ch/SDT/code-checks/cms-sw-PR-49995/50664

@cmsbuild

Copy link
Copy Markdown
Contributor

Pull request #49995 was updated. @Moanwar, @cmsbuild, @fwyzard, @jfernan2, @makortel, @mandrenguyen, @srimanob can you please check and sign again.

@felicepantaleo

Copy link
Copy Markdown
Contributor

please test

@cmsbuild

Copy link
Copy Markdown
Contributor

-1

Failed Tests: RelVals-AMD_MI300X
Size: This PR adds an extra 24KB to repository
Summary: https://cmssdt.cern.ch/SDT/jenkins-artifacts/pull-request-integration/PR-9aee20/55469/summary.html
COMMIT: 9b6e60a
CMSSW: CMSSW_20_1_X_2026-08-19-1100/el9_amd64_gcc14
Additional Tests: GPU,AMD_MI300X,AMD_W7900,NVIDIA_H100,NVIDIA_L40S,NVIDIA_T4
User test area: For local testing, you can use /cvmfs/cms-ci.cern.ch/week1/cms-sw/cmssw/49995/55469/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.

Failed RelVals-AMD_MI300X

  • 37634.40237634.402_TTbar_14TeV+Run4D127_Patatrack_PixelOnlyAlpaka/step2_TTbar_14TeV+Run4D127_Patatrack_PixelOnlyAlpaka.log
  • 37634.40437634.404_TTbar_14TeV+Run4D127_Patatrack_PixelOnlyAlpaka_Profiling/step2_TTbar_14TeV+Run4D127_Patatrack_PixelOnlyAlpaka_Profiling.log
  • 37634.750337634.7503_TTbar_14TeV+Run4D127_HLTHeterogeneousValid/step2_TTbar_14TeV+Run4D127_HLTHeterogeneousValid.log
Expand to see more relval errors ...

Comparison Summary

Summary:

  • You potentially removed 1 lines from the logs
  • Reco comparison results: 4 differences found in the comparisons
  • DQMHistoTests: Total files compared: 45
  • DQMHistoTests: Total histograms compared: 3592269
  • DQMHistoTests: Total failures: 3
  • DQMHistoTests: Total nulls: 0
  • DQMHistoTests: Total successes: 3592248
  • DQMHistoTests: Total skipped: 18
  • DQMHistoTests: Total Missing objects: 0
  • DQMHistoSizes: Histogram memory added: 0.0 KiB( 44 files compared)
  • Checked 195 log files, 163 edm output root files, 45 DQM output files
  • TriggerResults: no differences found

NVIDIA_H100 Comparison Summary

Summary:

  • No significant changes to the logs found
  • Reco comparison results: 83 differences found in the comparisons
  • DQMHistoTests: Total files compared: 7
  • DQMHistoTests: Total histograms compared: 173739
  • DQMHistoTests: Total failures: 8632
  • DQMHistoTests: Total nulls: 0
  • DQMHistoTests: Total successes: 165107
  • DQMHistoTests: Total skipped: 0
  • DQMHistoTests: Total Missing objects: 0
  • DQMHistoSizes: Histogram memory added: 0.0 KiB( 6 files compared)
  • Checked 25 log files, 20 edm output root files, 7 DQM output files
  • TriggerResults: no differences found

NVIDIA_L40S Comparison Summary

Summary:

NVIDIA_T4 Comparison Summary

Summary:

  • No significant changes to the logs found
  • Reco comparison results: 86 differences found in the comparisons
  • DQMHistoTests: Total files compared: 7
  • DQMHistoTests: Total histograms compared: 173739
  • DQMHistoTests: Total failures: 8970
  • DQMHistoTests: Total nulls: 0
  • DQMHistoTests: Total successes: 164769
  • DQMHistoTests: Total skipped: 0
  • DQMHistoTests: Total Missing objects: 0
  • DQMHistoSizes: Histogram memory added: 0.0 KiB( 6 files compared)
  • Checked 25 log files, 20 edm output root files, 7 DQM output files
  • TriggerResults: no differences found

Max Memory Comparisons exceeding threshold NVIDIA_H100

@cms-sw/core-l2 , I found 1 workflow step(s) with memory usage exceeding the error threshold:

Expand to see workflows ...
  • Error: Workflow 37634.7503_TTbar_14TeV+Run4D127_HLTHeterogeneousValid step2 max memory diff -200.1 exceeds +/- 30.0 MiB

Max Memory Comparisons exceeding threshold NVIDIA_T4

@cms-sw/core-l2 , I found 1 workflow step(s) with memory usage exceeding the error threshold:

Expand to see workflows ...
  • Error: Workflow 37634.7503_TTbar_14TeV+Run4D127_HLTHeterogeneousValid step2 max memory diff 30.1 exceeds +/- 30.0 MiB

@fwyzard

fwyzard commented Aug 20, 2026

Copy link
Copy Markdown
Contributor

ignore tests-rejected with ib-failure

@sbaldu

sbaldu commented Aug 20, 2026

Copy link
Copy Markdown
Contributor Author

I think that fixing the previous off-by-one issues has introduced some new unwanted behaviour (that escaped the current testing). Now the OffsetsLayout correctly stores the requested nkeys + 1 so that the count() method works as expected, then AssociationMap just returns size() - 1 to compensate (AssociationMap.h:62-64). The issue is that now the number passed by the caller and what the block holds are different and the value stored in the SoABlocks depends on which constructor was used:

  • The constructor relying on the user-provided layout (link) copies what the caller passed to the constructor (let's say nkeys)
  • The constructor using per-block views asks each view how many elements it actually contains (which would be nkeys + 1).

The override of the SoABlocks sizes in AssociationMap makes the two disagree so that building a map with nkeys = 2 one would get:

  • map.view().metadata().size()[1] == 2
  • map.view().offsets().metadata().size() == 3
  • AssociationMap<>::const_cast_View(map.const_view()).metadata().size()[1] == 3
    which can be easily checked via something like
CHECK( map.view().metadata().size()[1] == map.view().offsets().metadata().size() )

I think this "disagreement" might have multiple repercussions (e.g. probably in the deepCopy check) so I would suggest to drop the OffsetsLayout override, prefer building always with nkeys + 1 and having keys() stay size() - 1 for consistent size reporting. Note that dropping the override would most likely need to go together with #49995 (comment) to not break fill()

I added a test specifically for the deepCopy and it works as expected. With @Electricks94 we also tested reading the map from ROOT files, and that also works correctly, producing a map with the correct content and sizes.
Regarding the different sizes, I agree that it could be confusing, but that's also why we provided the keys and size methods for the View, so in this case that would be the intended interface rather than using metadata().size(), also because the offsets layout and its actual size are implementation details.

Comment thread DataFormats/TICL/interface/AssociationMap.h
Comment thread DataFormats/TICL/test/alpaka/TestAssociationMap.dev.cc Outdated
Comment on lines +53 to +54
std::span<const uint32_t>{device_associations.data(), nvalues},
std::span<const uint32_t>{device_values.data(), nvalues});

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I'm curious if explicitly building a std::span is really needed, given that an alpaka buffer should be automatically convertible to std::span after alpaka-group/alpaka#2579 ?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I tried but it doesn't compile. I think it's because associator::fill is a template and templates are deduced before the implicit conversion operators are checked

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Ah... OK, I still have to figure out what can and cannot (yet) be done with concepts and templates.

Can you try with a simpler constructor or a static_cast ?

Suggested change
std::span<const uint32_t>{device_associations.data(), nvalues},
std::span<const uint32_t>{device_values.data(), nvalues});
std::span<const uint32_t>{device_associations},
std::span<const uint32_t>{device_values});

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Yes that works, I'll add it together with the additions to the tests discussed below

Comment thread DataFormats/TICL/test/alpaka/TestAssociationMap.dev.cc Outdated
Comment thread DataFormats/TICL/test/alpaka/TestAssociationMap.dev.cc Outdated
Comment thread DataFormats/TICL/test/alpaka/TestAssociationMap.dev.cc Outdated
@cmsbuild

Copy link
Copy Markdown
Contributor

+code-checks

Logs: https://cmssdt.cern.ch/SDT/code-checks/cms-sw-PR-49995/50673

@cmsbuild

Copy link
Copy Markdown
Contributor

Pull request #49995 was updated. @Moanwar, @cmsbuild, @fwyzard, @jfernan2, @makortel, @mandrenguyen, @srimanob can you please check and sign again.

Comment thread DataFormats/TICL/interface/detail/FillAssociator.h

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Can you add or modify the tests to check some more cases:

  • the map is not completely full (can it happen for the keys, or only for the values ?)

  • the map is exactly full (I think all current tests assume this)

  • the keys overflow (should assert ?)

  • the values overflow (should assert ?)

  • the number of keys is 30

  • the number of keys is 31 (so n+1 is 32 which is on the alignment boundary)

  • the number of keys is 32

  • different keys have different number of elements

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

In the effort to increase the robustness of the tests I would also opt to add a ROOT streamer test that stores and reads a map. There we rely on the interplay between SoABlocks and the layouts it contains. Everything is correct as it is but we should make sure that future developements don't destroy anything here. Especially, since we plan to support RNTuple soon things might change and I think it will be important to test that these changes don't affect anything that is developed in this PR

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I have written a test case for this a while ago here. I think it just needs to be added to right place in this PR if found useful

@cmsbuild

Copy link
Copy Markdown
Contributor

+code-checks

Logs: https://cmssdt.cern.ch/SDT/code-checks/cms-sw-PR-49995/50674

@cmsbuild

Copy link
Copy Markdown
Contributor

Pull request #49995 was updated. @Moanwar, @cmsbuild, @fwyzard, @jfernan2, @makortel, @mandrenguyen, @srimanob can you please check and sign again.

@cmsbuild

Copy link
Copy Markdown
Contributor

+code-checks

Logs: https://cmssdt.cern.ch/SDT/code-checks/cms-sw-PR-49995/50675

@cmsbuild

Copy link
Copy Markdown
Contributor

Pull request #49995 was updated. @Moanwar, @cmsbuild, @fwyzard, @jfernan2, @makortel, @mandrenguyen, @srimanob can you please check and sign again.

@fwyzard

fwyzard commented Aug 20, 2026

Copy link
Copy Markdown
Contributor

I am lost among the discussion in #49995 (comment) and #49995 (comment) .

I agree with @Parsifal-2045 that having an potentially public inconsistent interface is Bad.

I understand that we need nkeys + 1 entries in the buffer.
(well, maybe not, maybe we could rely on the fact that the first offset is always 0 and store one less offset? but the code would be come more complicated and somewhat less efficient).

I would like to understand what code would produce inconsistent results, and discuss how to address the issue.

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.

9 participants