MuladdMacro.jl

This package provides the @muladd macro. It automatically converts expressions with multiplications and additions or subtractions to calls with muladd which then fuse via FMA when it would increase the performance of the code. The @muladd macro can be placed on code blocks, and it will automatically find the appropriate expressions and nest muladd expressions when necessary. In mixed expressions, summands without multiplication will be grouped together and evaluated first, but otherwise the order of evaluation of multiplications and additions is not changed.

Tutorials and Documentation

For information on using the package, see the stable documentation. Use the in-development documentation for the version of the documentation, which contains the unreleased features.

Examples

julia> using MuladdMacro

julia> @macroexpand(@muladd k3 = f(t + c3 * dt, @. uprev + dt * (a031 * k1 + a032 * k2)))
:(k3 = f((muladd)(c3, dt, t), (muladd).(dt, (muladd).(a032, k2, (*).(a031, k1)), uprev)))

julia> @macroexpand(@muladd integrator.EEst = integrator.opts.internalnorm((update -
                                                                            dt * (bhat1 * k1 +
                                                                             bhat4 * k4 +
                                                                             bhat5 * k5 +
                                                                             bhat6 * k6 +
                                                                             bhat7 * k7 +
                                                                             bhat10 * k10)) ./
                                                                           @. (integrator.opts.abstol +
                                                                               max(abs(uprev),
           abs(u)) * integrator.opts.reltol)))
:(integrator.EEst = integrator.opts.internalnorm((muladd)(-dt, (muladd)(bhat10, k10, (muladd)(bhat7, k7, (muladd)(bhat6, k6, (muladd)(bhat5, k5, (muladd)(bhat4, k4, bhat1 * k1))))), update) ./ (muladd).(max.(abs.(uprev), abs.(u)), integrator.opts.reltol, integrator.opts.abstol)))

Broadcasting

A muladd call will be broadcasted if both the * and the + or - are broadcasted. If either one is not broadcasted, then the expression will be converted to a non-dotted muladd.

Limitations

Currently, @muladd handles only explicit calls of + and *. In particular, assignments using += or literal power such as ^2 are not supported. Thus, you need to rewrite them, e.g.

julia> using MuladdMacro

julia> a = 1.0;
       b = 2.0;
       c = 3.0;

julia> @macroexpand @muladd a += b * c # does not work
:(a += b * c)

julia> @macroexpand @muladd a = a + b * c # good alternative
:(a = (muladd)(b, c, a))

julia> @macroexpand @muladd a + b^2 # does not work
:(a + b ^ 2)

julia> @macroexpand @muladd a + b * b # good alternative
:((muladd)(b, b, a))

Credit

Most of the credit goes to @fcard and @devmotion for building the first version and greatly refining the macro. These contributions are not directly shown as this was developed in Gitter chats and in the DiffEqBase.jl repository, but these two individuals did almost all the work.

Reproducibility

The documentation of this SciML package was built using these direct dependencies,
Status `~/work/MuladdMacro.jl/MuladdMacro.jl/docs/Project.toml`
  [e30172f5] Documenter v1.12.0
  [46d2c3a1] MuladdMacro v0.2.4 `~/work/MuladdMacro.jl/MuladdMacro.jl`
and using this machine and Julia version.
Julia Version 1.11.5
Commit 760b2e5b739 (2025-04-14 06:53 UTC)
Build Info:
  Official https://julialang.org/ release
Platform Info:
  OS: Linux (x86_64-linux-gnu)
  CPU: 4 × AMD EPYC 7763 64-Core Processor
  WORD_SIZE: 64
  LLVM: libLLVM-16.0.6 (ORCJIT, znver3)
Threads: 1 default, 0 interactive, 1 GC (on 4 virtual cores)
Environment:
  JULIA_DEBUG = Documenter
A more complete overview of all dependencies and their versions is also provided.
Status `~/work/MuladdMacro.jl/MuladdMacro.jl/docs/Manifest.toml`
  [a4c015fc] ANSIColoredPrinters v0.0.1
  [1520ce14] AbstractTrees v0.4.5
  [944b1d66] CodecZlib v0.7.8
  [ffbed154] DocStringExtensions v0.9.5
  [e30172f5] Documenter v1.12.0
  [d7ba0133] Git v1.4.0
  [b5f81e59] IOCapture v0.2.5
  [692b3bcd] JLLWrappers v1.7.0
  [682c06a0] JSON v0.21.4
  [0e77f7df] LazilyInitializedFields v1.3.0
  [d0879d2d] MarkdownAST v0.1.2
  [46d2c3a1] MuladdMacro v0.2.4 `~/work/MuladdMacro.jl/MuladdMacro.jl`
  [69de0a69] Parsers v2.8.3
⌅ [aea7be01] PrecompileTools v1.2.1
  [21216c6a] Preferences v1.4.3
  [2792f1a3] RegistryInstances v0.1.0
  [3bb67fe8] TranscodingStreams v0.11.3
  [2e619515] Expat_jll v2.6.5+0
  [f8c6e375] Git_jll v2.49.0+0
  [94ce4f54] Libiconv_jll v1.18.0+0
  [9bd350c2] OpenSSH_jll v10.0.1+0
  [458c3c95] OpenSSL_jll v3.5.0+0
  [0dad84c5] ArgTools v1.1.2
  [56f22d72] Artifacts v1.11.0
  [2a0f44e3] Base64 v1.11.0
  [ade2ca70] Dates v1.11.0
  [f43a241f] Downloads v1.6.0
  [7b1f6079] FileWatching v1.11.0
  [b77e0a4c] InteractiveUtils v1.11.0
  [b27032c2] LibCURL v0.6.4
  [76f85450] LibGit2 v1.11.0
  [8f399da3] Libdl v1.11.0
  [56ddb016] Logging v1.11.0
  [d6f4376e] Markdown v1.11.0
  [a63ad114] Mmap v1.11.0
  [ca575930] NetworkOptions v1.2.0
  [44cfe95a] Pkg v1.11.0
  [de0858da] Printf v1.11.0
  [3fa0cd96] REPL v1.11.0
  [9a3f8284] Random v1.11.0
  [ea8e919c] SHA v0.7.0
  [9e88b42a] Serialization v1.11.0
  [6462fe0b] Sockets v1.11.0
  [f489334b] StyledStrings v1.11.0
  [fa267f1f] TOML v1.0.3
  [a4e569a6] Tar v1.10.0
  [8dfed614] Test v1.11.0
  [cf7118a7] UUIDs v1.11.0
  [4ec0a83e] Unicode v1.11.0
  [deac9b47] LibCURL_jll v8.6.0+0
  [e37daf67] LibGit2_jll v1.7.2+0
  [29816b5a] LibSSH2_jll v1.11.0+1
  [c8ffd9c3] MbedTLS_jll v2.28.6+0
  [14a3606d] MozillaCACerts_jll v2023.12.12
  [efcefdf7] PCRE2_jll v10.42.0+1
  [83775a58] Zlib_jll v1.2.13+1
  [8e850ede] nghttp2_jll v1.59.0+0
  [3f19e933] p7zip_jll v17.4.0+2
Info Packages marked with ⌅ have new versions available but compatibility constraints restrict them from upgrading. To see why use `status --outdated -m`

You can also download the manifest file and the project file.