Skip to content

Fixing compilation times for AD#1297

Open
zjwegert wants to merge 7 commits into
gridap:masterfrom
zjwegert:Tagging-fix
Open

Fixing compilation times for AD#1297
zjwegert wants to merge 7 commits into
gridap:masterfrom
zjwegert:Tagging-fix

Conversation

@zjwegert
Copy link
Copy Markdown
Contributor

@zjwegert zjwegert commented May 4, 2026

Fixes and updates for #1296 as I don't have access to branch.

@codecov
Copy link
Copy Markdown

codecov Bot commented May 4, 2026

Codecov Report

❌ Patch coverage is 91.80328% with 5 lines in your changes missing coverage. Please review.
✅ Project coverage is 88.92%. Comparing base (6bafc0b) to head (08657c5).

Files with missing lines Patch % Lines
src/FESpaces/FEAutodiff.jl 88.00% 3 Missing ⚠️
src/CellData/DomainContributions.jl 71.42% 2 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master    #1297      +/-   ##
==========================================
+ Coverage   88.90%   88.92%   +0.02%     
==========================================
  Files         228      228              
  Lines       30030    30034       +4     
==========================================
+ Hits        26698    26708      +10     
+ Misses       3332     3326       -6     
Flag Coverage Δ
drivers 39.62% <49.18%> (+0.01%) ⬆️
extensions 5.04% <0.00%> (-0.01%) ⬇️
unit-adaptivity 39.75% <9.83%> (+<0.01%) ⬆️
unit-basics 14.59% <19.67%> (-0.01%) ⬇️
unit-celldata 20.96% <11.47%> (+<0.01%) ⬆️
unit-fespaces-1 32.10% <24.59%> (+<0.01%) ⬆️
unit-fespaces-2 39.39% <59.01%> (+0.02%) ⬆️
unit-fields 17.58% <0.00%> (-0.01%) ⬇️
unit-geometry 28.53% <4.91%> (+<0.01%) ⬆️
unit-multifield 30.62% <80.32%> (+0.01%) ⬆️
unit-odes 28.50% <39.34%> (+0.01%) ⬆️
unit-referencefes 34.10% <4.91%> (+<0.01%) ⬆️
unit-visualization 11.75% <0.00%> (-0.01%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@zjwegert
Copy link
Copy Markdown
Contributor Author

zjwegert commented May 4, 2026

@JordiManyer This should be ready to go. Let me know if you want any changes

@JordiManyer
Copy link
Copy Markdown
Member

JordiManyer commented May 4, 2026

Has anyone though that compilation times for AD being so high is maybe because of these tags? Isn't there a better way of doing things?

Just to illustrate: By putting tags out of the inner functions and making them only once per call, we have reduced the multifield tests from 129 mins (master) down to 82 mins (in this PR). This is crazy, and we have basically only reduced the number of tags to one per AD call. Could we push this further?

For instance: I don't see why uh has to be embedded in the tag (maybe I am wrong, I have not thought this 100% through). Then we would specialize on AD function and weakform, which means consecutive calls to the same AD operation for different uh would not recompile (which they currently do). Again, maybe I am missing something, but it seems like it could work.

Going even further: Could we fully infer the "AD level" from the code, and then create a predictable, reusable tag? Something like

  struct FDTag{L} end 

FDTag(level::Integer) = FDTag{Val{level}}()

If we can reliably predict the level, this means we would only compile things once and thats it. It would also mean that we could precompile the code up to a certain level of AD and the precompiled code would be useful, not like now.

Does this make sense? @zjwegert @ConnorMallon

Comment thread src/Arrays/Autodiff.jl Outdated
autodiff_array_gradient(a,i_to_x,tag)
end

function autodiff_array_gradient(a,i_to_x,tag::Function)
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.

In all functions: Could we unify into a single signature? Something like

function default_tag(f,a)
   tag = x -> f(a,x)
end

function autodiff_array_gradient(a,i_to_x,tag=default_tag(ForwardDiff.gradient,a))
  ...
end

Note it is still an argument, so the call to this function stays the same. I would also not restrict the tag to be a function, it could be anything.

Comment thread NEWS.md
@Antoinemarteau
Copy link
Copy Markdown
Collaborator

I was curious about these tags and investigated a bit. The "tag" is used as the first argument of ForwardDiff.GradientConfig, which wants the function you differentiate, its not just a matter of AD level ("perturbation confusion").

The docstring mentions the output of GradientConfig depends of the type of the function and not the function itself, so it might be possible to encapsulate a dictionary into this default_tag function to store only one anonymous function x-> gradient(f,x) per typeof(f).

@zjwegert
Copy link
Copy Markdown
Contributor Author

zjwegert commented May 7, 2026

Some notes after discussion with @JordiManyer:

  • Add defaults to every level & remove specification of uh from tag
  • Add an @optim_ad macro that embeds the AD level into the tags - e.g., @optim_ad jacobian(ph->gradient(uh->ener(uh,ph),uh),ph) -> jacobian(ph->gradient(uh->ener(uh,ph),uh,tag=...),ph,tag=...)

@JordiManyer
Copy link
Copy Markdown
Member

@zjwegert I was thinking about this this weekend, and I think I found an elegant way to make this automatic. Could we add a single integer to DomainContribution that represents the AD level? By default we make it zero, and > 0 would be AD.

We instantiate the original contribution when we evaluate the gradients right? That is, _gradient(f,uh,duh) takes duh. This means we can keep a consistent count of the level, I think. We basically return a DomainContribution with level wich is 1-higher than the level of duh.

The only thing that we need to be careful of is DomainContribution operations. I think in general we can take the maximum and it should be fine. Also, if we want to keep the possibility to modify DomainContributions in place, we would need to a) make DC mutable or b) store a reference to this integer instead of the integer itself. I think both should be fine since DCs are really not performance critical.

Any comments? Do you see any issues with this? I think we can fully infer the level from this right?

@zjwegert
Copy link
Copy Markdown
Contributor Author

@zjwegert I was thinking about this this weekend, and I think I found an elegant way to make this automatic. Could we add a single integer to DomainContribution that represents the AD level? By default we make it zero, and > 0 would be AD.

We instantiate the original contribution when we evaluate the gradients right? That is, _gradient(f,uh,duh) takes duh. This means we can keep a consistent count of the level, I think. We basically return a DomainContribution with level wich is 1-higher than the level of duh.

The only thing that we need to be careful of is DomainContribution operations. I think in general we can take the maximum and it should be fine. Also, if we want to keep the possibility to modify DomainContributions in place, we would need to a) make DC mutable or b) store a reference to this integer instead of the integer itself. I think both should be fine since DCs are really not performance critical.

Any comments? Do you see any issues with this? I think we can fully infer the level from this right?

Hmm interesting idea. I think we can probably use a reference. I'll look into this -- hopefully this week!

@JordiManyer JordiManyer changed the title Tagging (fix #1296) Fixing compilation times for AD May 11, 2026
@zjwegert
Copy link
Copy Markdown
Contributor Author

@JordiManyer @Antoinemarteau, I think this solution is on the right track. Let me know what you think and if there are any improvements that can be made!

@Antoinemarteau
Copy link
Copy Markdown
Collaborator

Minor: 
if I'm not missing something, most where N clauses are not needed, you could write GridapADTag{<:Val} instead.

@zjwegert
Copy link
Copy Markdown
Contributor Author

Some benchmarking:

NAME Master PR
CI / Extensions - 1 - ubuntu 1m 4m
CI / Prime cache - 1 - ubuntu 28s 24s
CI / Prime cache - lts - macos 25s 22s
CI / Prime cache - lts - ubuntu 22s 24s
CI / Prime cache - lts - ubuntu 27s 26s
CI / Prime cache - lts - windows 50s 56s
CI / drivers - 1 - ubuntu 45m 47m
CI / drivers - lts - ubuntu 32m 31m
CI / unit-adaptivity - 1 - ubuntu 19m 21m
CI / unit-adaptivity - lts - ubuntu 13m 13m
CI / unit-basics - 1 - ubuntu 4m 7m
CI / unit-basics - lts - macos 3m 3m
CI / unit-basics - lts - ubuntu 5m 4m
CI / unit-basics - lts - ubuntu 5m 3m
CI / unit-basics - lts - windows 4m 5m
CI / unit-celldata - 1 - ubuntu 6m 7m
CI / unit-celldata - lts - macos 3m 2m
CI / unit-celldata - lts - ubuntu 4m 5m
CI / unit-celldata - lts - ubuntu 4m 5m
CI / unit-celldata - lts - windows 4m 4m
CI / unit-fespaces-1 - 1 - ubuntu 16m 18m
CI / unit-fespaces-1 - lts - macos 7m 7m
CI / unit-fespaces-1 - lts - ubuntu 11m 13m
CI / unit-fespaces-1 - lts - ubuntu 11m 13m
CI / unit-fespaces-1 - lts - windows 12m 11m
CI / unit-fespaces-2 - 1 - ubuntu 33m 30m
CI / unit-fespaces-2 - lts - macos 19m 17m
CI / unit-fespaces-2 - lts - ubuntu 18m 21m
CI / unit-fespaces-2 - lts - ubuntu 20m 20m
CI / unit-fespaces-2 - lts - windows 20m 19m
CI / unit-fields - 1 - ubuntu 6m 6m
CI / unit-fields - lts - macos 3m 5m
CI / unit-fields - lts - ubuntu 3m 5m
CI / unit-fields - lts - ubuntu 4m 6m
CI / unit-fields - lts - windows 5m 5m
CI / unit-geometry - 1 - ubuntu 7m 7m
CI / unit-geometry - lts - macos 3m 2m
CI / unit-geometry - lts - ubuntu 4m 5m
CI / unit-geometry - lts - ubuntu 4m 6m
CI / unit-geometry - lts - windows 4m 5m
CI / unit-multifield - 1 - ubuntu 132m 101m
CI / unit-multifield - lts - ubuntu 58m 42m
CI / unit-odes - 1 - ubuntu 22m 15m
CI / unit-odes - lts - ubuntu 17m 12m
CI / unit-referencefes - 1 - ubuntu 9m 10m
CI / unit-referencefes - lts - macos 6m 3m
CI / unit-referencefes - lts - ubuntu 7m 8m
CI / unit-referencefes - lts - ubuntu 7m 9m
CI / unit-referencefes - lts - windows 5m 6m
CI / unit-visualization - 1 - ubuntu 2m 4m
CI / unit-visualization - lts - macos 1m 2m
CI / unit-visualization - lts - ubuntu 2m 2m
CI / unit-visualization - lts - ubuntu 2m 2m
CI / unit-visualization - lts - windows 3m 4m

@JordiManyer
Copy link
Copy Markdown
Member

JordiManyer commented May 13, 2026

Hi @zjwegert Ill probably change some stuff because I am a bit anal about things, but I would say the general gist is what I imagined.

Curiously enough, the test times are all over the place (some up, some down, mostly similar) compared to the first attempt at the fix but I assume this might be because of github ci servers. I presume with precompilation we should really get a speedup now that we can mostly predict the tags.

At least the goods new is that we dont seem to get confusion on nested AD calls, right?

@zjwegert
Copy link
Copy Markdown
Contributor Author

Sure! You have access to my fork so feel free to make any changes.

I think this is probably because of the github servers. The tests that take a long time are generally better. Looking forward to the precompilation changes!

Yes, these changes seem to handle tag confusion. I still need to test in GridapTopOpt

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.

4 participants