Performance vs Echoes
Before this page
Performance notes, for the paths timed below, and Cross-validation against Echoes, for the accuracy of the same runs.
Every number on this page is read from a JSON file written by a script of the repository, and can be measured again by anyone with the commands of the last section. Both codes run on the same machine, one thread each, timed by BenchmarkTools' @belapsed (the minimum of the samples); Echoes is called through its Python API, from Python modules that also run on their own. MeanFieldHomogenization is faster; a ratio below 1 is in bold. The gap is the largest relative difference between the two results, so that each ratio compares two computations of the same number.
Kernels
Measured on AMD EPYC-Milan Processor, Julia 1.13.3, MeanFieldHomogenization 0.23.1, TensND 0.8.5, 2 thread; BenchmarkTools @belapsed minimum of 20 samples, one thread each; Echoes through PythonCall.
Hill tensor
The Julia back-end is the default one unless the case names another.
| Case | Echoes | gap | |||
|---|---|---|---|---|---|
| Sphere a=b=c=1 / ISO | analytical | 13.8 ns | 72 ms | 5200000× | 6.6e-10 |
| Prolate a=5,b=c=1 / ISO | analytical | 85.7 ns | 108 ms | 1300000× | 5.3e-10 |
| Oblate a=b=5,c=1 / ISO | analytical | 73.3 ns | 67.1 ms | 920000× | 3.6e-10 |
| Prolate a=3,b=c=1 / cubic | NUMINT3D | 1.17 ms | 98.3 ms | 84.1× | 9.8e-09 |
| Prolate a=3,b=c=1 / cubic (Julia :decuhr) | NUMINT3D | 1.17 ms | 98.3 ms | 84.3× | 6.0e-06 |
| Prolate a=3,b=c=1 / cubic (Julia :nestedquadgk) | NUMINT3D | 224 µs | 98 ms | 440× | 1.6e-09 |
| Triaxial a=2,b=1,c=0.5 / triclinic | RESIDUES | 3.67 ms | 4.98 ms | 1.36× | 1.1e-07 |
| Triaxial a=2,b=1,c=0.5 / triclinic | NUMINT3D | 3.67 ms | 89.8 ms | 24.5× | 2.5e-09 |
| Triaxial a=2,b=1,c=0.5 / triclinic (Julia :decuhr) | RESIDUES | 4.39 ms | 4.98 ms | 1.13× | 1.1e-07 |
| Triaxial a=2,b=1,c=0.5 / triclinic (Julia :decuhr) | NUMINT3D | 4.39 ms | 89.6 ms | 20.4× | 2.5e-09 |
| Triaxial a=2,b=1,c=0.5 / triclinic (Julia :nestedquadgk) | RESIDUES | 906 µs | 4.97 ms | 5.49× | 1.1e-07 |
| Triaxial a=2,b=1,c=0.5 / triclinic (Julia :nestedquadgk) | NUMINT3D | 906 µs | 89.5 ms | 98.8× | 2.5e-09 |
| Rotated prolate a=5,b=c=1 / ISO (pi/4,pi/6,pi/3) | analytical | 91.5 ns | 108 ms | 1200000× | 3.0e-10 |
| Rotated triaxial a=2,b=1,c=0.5 / triclinic (pi/4,pi/6,pi/3) | RESIDUES | 2.19 ms | 3.12 ms | 1.43× | 1.4e-12 |
| Rotated triaxial a=2,b=1,c=0.5 / triclinic (pi/4,pi/6,pi/3) | NUMINT3D | 2.19 ms | 90.4 ms | 41.4× | 1.9e-10 |
| Rotated triaxial a=2,b=1,c=0.5 / triclinic :decuhr (pi/4,pi/6,pi/3) | RESIDUES | 2.99 ms | 3.11 ms | 1.04× | 2.0e-10 |
| Rotated triaxial a=2,b=1,c=0.5 / triclinic :decuhr (pi/4,pi/6,pi/3) | NUMINT3D | 2.99 ms | 90.6 ms | 30.3× | 5.5e-14 |
Crack compliance
| Case | Echoes | gap | |||
|---|---|---|---|---|---|
| Penny / ISO | analytical | 350 ns | 57.3 ms | 160000× | 5.2e-10 |
| Penny / cubic | NUMINT3D | 2.5 ms | 62.2 ms | 24.9× | 7.1e-09 |
| Penny / triclinic | RESIDUES | 1.71 ms | 3.58 ms | 2.1× | 6.9e-10 |
| Penny / triclinic | NUMINT3D | 1.71 ms | 105 ms | 61.6× | 6.9e-10 |
Conductivity
| Case | Echoes | gap | |||
|---|---|---|---|---|---|
| Sphere a=b=c=1 / K iso | analytical | 8.02 ns | 4.84 µs | 600× | 0.0e+00 |
| Prolate a=5,b=c=1 / K iso | analytical | 165 ns | 4.98 µs | 30.2× | 1.1e-16 |
| Oblate a=b=5,c=1 / K iso | analytical | 150 ns | 4.94 µs | 32.9× | 1.8e-16 |
| Triaxial a=2,b=1,c=0.5 / K aniso | RESIDUES | 4.05 µs | 8.58 µs | 2.12× | 2.2e-16 |
| Triaxial a=2,b=1,c=0.5 / K aniso | NUMINT3D | 4.05 µs | 8.8 µs | 2.17× | 2.2e-16 |
| Rotated triaxial a=2,b=1,c=0.5 / K aniso (pi/4,pi/6,pi/3) | RESIDUES | 7.35 µs | 9.13 µs | 1.24× | 1.0e-15 |
| Rotated triaxial a=2,b=1,c=0.5 / K aniso (pi/4,pi/6,pi/3) | NUMINT3D | 7.35 µs | 9.17 µs | 1.25× | 1.0e-15 |
Derivative
ForwardDiff through hill_tensor on the Julia side, Echoes' hill_derivative on the other (a closed form for an isotropic medium).
| Case | Echoes | gap | |||
|---|---|---|---|---|---|
| Sphere a=b=c=1 / ISO | κ=3k | ISO | 557 ns | 8.47 µs | 15.2× | 4.8e-16 |
| Sphere a=b=c=1 / ISO | η=2μ | ISO | 557 ns | 8.47 µs | 15.2× | 9.8e-17 |
| Prolate a=5,b=c=1 / ISO | κ=3k | ISO | 1.25 µs | 8.82 µs | 7.04× | 2.9e-16 |
| Prolate a=5,b=c=1 / ISO | η=2μ | ISO | 1.25 µs | 8.79 µs | 7.03× | 6.2e-16 |
| Oblate a=b=5,c=1 / ISO | κ=3k | ISO | 1.23 µs | 8.64 µs | 7.05× | 1.5e-16 |
| Oblate a=b=5,c=1 / ISO | η=2μ | ISO | 1.23 µs | 8.55 µs | 6.98× | 4.8e-16 |
| Prolate a=3,b=c=1 / cubic-as-ORTHO | C11 | NUMINT3D | 2.93 ms | 108 ms | 36.7× | 5.1e-05 |
| Prolate a=3,b=c=1 / cubic-as-ORTHO | C22 | NUMINT3D | 2.95 ms | 111 ms | 37.5× | 1.8e-06 |
| Prolate a=3,b=c=1 / cubic-as-ORTHO | C33 | NUMINT3D | 2.93 ms | 112 ms | 38.2× | 2.1e-05 |
| Prolate a=3,b=c=1 / cubic-as-ORTHO | C12 | NUMINT3D | 2.91 ms | 123 ms | 42.3× | 1.3e-05 |
| Prolate a=3,b=c=1 / cubic-as-ORTHO | C13 | NUMINT3D | 2.95 ms | 121 ms | 40.8× | 1.3e-04 |
| Prolate a=3,b=c=1 / cubic-as-ORTHO | C23 | NUMINT3D | 2.91 ms | 128 ms | 43.9× | 9.3e-06 |
| Prolate a=3,b=c=1 / cubic-as-ORTHO | μ₂₃ | NUMINT3D | 2.97 ms | 153 ms | 51.5× | 5.2e-06 |
| Prolate a=3,b=c=1 / cubic-as-ORTHO | μ₁₃ | NUMINT3D | 2.94 ms | 133 ms | 45.3× | 3.7e-05 |
| Prolate a=3,b=c=1 / cubic-as-ORTHO | μ₁₂ | NUMINT3D | 2.94 ms | 130 ms | 44.3× | 1.0e-05 |
Schemes
Whole homogenizations of the same RVEs, with the same tolerances on both sides: numerical Hill tensors to a relative
Measured on AMD EPYC-Milan Processor, Julia 1.13.2, MeanFieldHomogenization 0.20.1, TensND 0.8.3, 1 thread; BenchmarkTools @belapsed minimum, one thread; Hill tensors to rtol 1e-6, atol 1e-8; fixed points to rtol 1e-8.
| Case | Scheme | gap | |||
|---|---|---|---|---|---|
| isotropic matrix, spheres | mori tanaka | 791 ns | 169 ms | 210000× | 1.9e-13 |
| isotropic matrix, spheres | self consistent | 9.8 µs | 1.35 s | 140000× | 1.8e-13 |
| isotropic matrix, spheres | differential | 127 µs | 85.7 s | 680000× | 5.5e-05 |
| isotropic matrix, aligned prolate spheroids 5:1:1 | mori tanaka | 942 ns | 225 ms | 240000× | 2.1e-12 |
| isotropic matrix, aligned prolate spheroids 5:1:1 | self consistent | 75.7 µs | 1.88 s | 25000× | 3.4e-13 |
| isotropic matrix, oblate pores 1:1:0.1, isotropic average | mori tanaka | 916 ns | 165 ms | 180000× | 1.7e-13 |
| isotropic matrix, oblate pores 1:1:0.1, isotropic average | self consistent | 36.7 µs | 6.65 s | 180000× | 3.0e-12 |
| anisotropic matrix, two rotated triaxial families | dilute | 1.13 ms | 226 ms | 200× | 1.4e-11 |
| anisotropic matrix, two rotated triaxial families | mori tanaka | 1.17 ms | 316 ms | 270× | 1.4e-11 |
| conduction, isotropic matrix, spheres | mori tanaka | 792 ns | 35.8 µs | 45.2× | 1.4e-16 |
| conduction, isotropic matrix, spheres | self consistent | 22.4 µs | 185 µs | 8.26× | 2.6e-16 |
| conduction, anisotropic matrix, rotated triaxial ellipsoids | dilute | 8.44 µs | 48.4 µs | 5.73× | 9.7e-17 |
| conduction, anisotropic matrix, rotated triaxial ellipsoids | mori tanaka | 9.9 µs | 56.1 µs | 5.67× | 3.5e-16 |
| conduction, anisotropic matrix, rotated triaxial ellipsoids | self consistent | 161 µs | 288 µs | 1.79× | 4.6e-16 |
| ALV: Maxwell matrix, elastic spheres | mori tanaka | 107 µs | 1.03 ms | 9.66× | 2.9e-16 |
| ALV: Maxwell matrix, elastic spheres | self consistent | 1.18 ms | 6.58 ms | 5.59× | 2.1e-16 |
| ALV: Maxwell matrix, oblate soft inclusions 1:1:0.1, isotropic average | mori tanaka | 180 µs | 1.05 ms | 5.85× | 7.9e-16 |
| ALV: Maxwell matrix, oblate soft inclusions 1:1:0.1, isotropic average | self consistent | 1.92 ms | 6.69 ms | 3.48× | 6.7e-16 |
The differential scheme is not the same algorithm on both sides: Echoes takes 1000 explicit first-order steps, MeanFieldHomogenization integrates adaptively to a relative
The three-scale strength model: the same route, two stopping rules
The model of Quasi-brittle strength of cement paste and mortar computes a strength and its derivative with respect to the shear modulus of one family of hydrate needles. Both codes take the same route, described on the page of the model: the self-consistent foam is first converged with the needles averaged isotropically, whose Hill tensors are closed forms, and the 20 transversely isotropic families start from that estimate. They stop differently. Echoes takes a single step over the families and obtains the derivative there, with numerical Hill tensors to MeanFieldHomogenization iterates the families to convergence, the derivative included through dual numbers, every Hill tensor to
| Case | Scheme | gap | |||
|---|---|---|---|---|---|
| three-scale strength model (20 needle families), value and derivative | SC + MT + MT, from the isotropic foam | 2.7 s | 2.29 s | 0.85× | 6.0e-04 |
| three-scale strength model (20 needle families), value and derivative | SC + MT + MT, direct | 4.51 s | 2.29 s | 0.51× | 6.0e-04 |
What the numbers say
Closed forms against Echoes' calls. On an isotropic medium both codes evaluate a closed form, and a Hill tensor costs tens of nanoseconds in Julia against tens of milliseconds through Echoes' Python API; the isotropic schemes inherit the factor.
Same algorithm, comparable speed. Residues against residues on a triclinic medium, and the anisotropic conductivity, differ by a factor 2 to 2.5. The one case where Echoes is faster,
:decuhron the rotated triclinic ellipsoid against Echoes' residues, is a cubature against residues.The cubatures are the fastest general back-ends.
:nestedquadgkagrees with NUMINT3D toand is 80 to 400 times faster. Derivatives by automatic differentiation cost no more than closed forms. ForwardDiff through
hill_tensorbeats Echoes' closed-form derivative on the isotropic cases.Aging viscoelasticity: 3.6 to 10 times faster on 11 instants, the self-consistent schemes in the cheapest algebra of the reference kernel.
Reproducing
The Julia scripts call the Echoes side through PythonCall, from Python modules next to them. Install Echoes in a Python environment of your choice, which must also have sympy (pip install sympy), activate it, then start Julia from the same shell: scripts/bench_echoes/echoes_python.jl, included by every script before PythonCall loads, takes the active python3 (or the interpreter named by ECHOES_PYTHON), checks that it imports echoes and sympy, and tells PythonCall to use it and CondaPkg to manage nothing. The explicit alternative is JULIA_CONDAPKG_BACKEND=Null and JULIA_PYTHONCALL_EXE=<path to that python>. CondaPkg then touches nothing, so the numpy version Echoes was built against (often numpy 1.x, numpy < 2) is kept; never let CondaPkg manage an environment where Echoes is installed, or pin that numpy in its CondaPkg.toml. Details in scripts/bench_echoes/README.md. From the root of the repository, alone on the machine:
julia --project=scripts/bench_echoes scripts/bench_echoes/benchmark.jl
julia --project=scripts/bench_echoes scripts/bench_echoes/benchmark_schemes_speed.jlThey write scripts/bench_echoes/figures/benchmark_kernels.json and benchmark_schemes_speed.json, which this page reads. The Echoes side runs on its own as well, with the cases defined once in these modules:
python3 -I scripts/bench_echoes/kernels_echoes.py
python3 -I scripts/bench_echoes/schemes_speed_echoes.pywith python3 the interpreter in which import echoes works.
Internal (Julia-only) regression benchmarking is separate: scripts/bench/ holds a gated suite with a committed baseline and a bitwise checksum gate, described in its own README.md.
Where to go next
Roadmap lists what remains to be implemented.