From c4745e8cd13bf01dbe3809000c46705e488c733a Mon Sep 17 00:00:00 2001 From: Jukka Aho Date: Fri, 12 Dec 2025 21:41:48 +0200 Subject: [PATCH] chore: remove benchmarks directory Remove benchmarks directory as it has been moved to another repository - Remove benchmarks/ directory and all contents - 37 files removed including benchmark scripts and results --- benchmarks/README_GPU_BENCHMARKS.md | 244 ----- benchmarks/VALIDATION_RESULTS.md | 85 -- benchmarks/basis_function_access_patterns.jl | 412 -------- benchmarks/basis_function_access_tet10.jl | 504 ---------- benchmarks/deformation_gradient_analysis.jl | 240 ----- benchmarks/element_immutability_benchmark.jl | 396 -------- benchmarks/field_storage_comparison.jl | 334 ------- benchmarks/gpu_state_management_benchmark.jl | 468 --------- benchmarks/integration_points_benchmark.jl | 301 ------ benchmarks/linear_elastic_analysis.jl | 272 ------ benchmarks/material_models_benchmark.jl | 865 ---------------- .../material_models_benchmark_results.txt | 922 ------------------ benchmarks/matrix_free_gpu_benchmark.jl | 835 ---------------- benchmarks/multigpu_mpi_benchmark.jl | 571 ----------- benchmarks/multigpu_results_2025-11-09.md | 329 ------- benchmarks/neo_hookean_analysis.jl | 260 ----- benchmarks/nodal_assembly_scalability.jl | 438 --------- benchmarks/perfect_plasticity_analysis.jl | 320 ------ benchmarks/results/benchmark_output.txt | 348 ------- .../results/combined_20251109_201256.png | Bin 156594 -> 0 bytes .../results/copy_cost_20251109_201256.png | Bin 36593 -> 0 bytes benchmarks/results/copy_cost_linear.png | Bin 53976 -> 0 bytes .../results/field_access_20251109_201256.png | Bin 34661 -> 0 bytes benchmarks/results/field_access_scaling.png | Bin 47022 -> 0 bytes .../results/field_update_20251109_201256.png | Bin 32128 -> 0 bytes benchmarks/results/field_update_scaling.png | Bin 56813 -> 0 bytes .../results/iteration_20251109_201256.png | Bin 37113 -> 0 bytes benchmarks/results/iteration_scaling.png | Bin 45409 -> 0 bytes .../speedup_factors_20251109_201256.png | Bin 55104 -> 0 bytes benchmarks/results/speedup_ratios.png | Bin 55795 -> 0 bytes .../struct_scaling_20251109_201256.csv | 13 - .../struct_scaling_20251109_201256.json | 187 ---- benchmarks/results/struct_size_scaling.csv | 13 - benchmarks/results/struct_size_scaling.json | 196 ---- benchmarks/struct_size_scaling.jl | 731 -------------- benchmarks/test_gpu_benchmarks.sh | 159 --- benchmarks/tet10_derivatives_benchmark.jl | 284 ------ 37 files changed, 9727 deletions(-) delete mode 100644 benchmarks/README_GPU_BENCHMARKS.md delete mode 100644 benchmarks/VALIDATION_RESULTS.md delete mode 100644 benchmarks/basis_function_access_patterns.jl delete mode 100644 benchmarks/basis_function_access_tet10.jl delete mode 100644 benchmarks/deformation_gradient_analysis.jl delete mode 100644 benchmarks/element_immutability_benchmark.jl delete mode 100755 benchmarks/field_storage_comparison.jl delete mode 100644 benchmarks/gpu_state_management_benchmark.jl delete mode 100644 benchmarks/integration_points_benchmark.jl delete mode 100644 benchmarks/linear_elastic_analysis.jl delete mode 100644 benchmarks/material_models_benchmark.jl delete mode 100644 benchmarks/material_models_benchmark_results.txt delete mode 100644 benchmarks/matrix_free_gpu_benchmark.jl delete mode 100755 benchmarks/multigpu_mpi_benchmark.jl delete mode 100644 benchmarks/multigpu_results_2025-11-09.md delete mode 100644 benchmarks/neo_hookean_analysis.jl delete mode 100755 benchmarks/nodal_assembly_scalability.jl delete mode 100644 benchmarks/perfect_plasticity_analysis.jl delete mode 100644 benchmarks/results/benchmark_output.txt delete mode 100644 benchmarks/results/combined_20251109_201256.png delete mode 100644 benchmarks/results/copy_cost_20251109_201256.png delete mode 100644 benchmarks/results/copy_cost_linear.png delete mode 100644 benchmarks/results/field_access_20251109_201256.png delete mode 100644 benchmarks/results/field_access_scaling.png delete mode 100644 benchmarks/results/field_update_20251109_201256.png delete mode 100644 benchmarks/results/field_update_scaling.png delete mode 100644 benchmarks/results/iteration_20251109_201256.png delete mode 100644 benchmarks/results/iteration_scaling.png delete mode 100644 benchmarks/results/speedup_factors_20251109_201256.png delete mode 100644 benchmarks/results/speedup_ratios.png delete mode 100644 benchmarks/results/struct_scaling_20251109_201256.csv delete mode 100644 benchmarks/results/struct_scaling_20251109_201256.json delete mode 100644 benchmarks/results/struct_size_scaling.csv delete mode 100644 benchmarks/results/struct_size_scaling.json delete mode 100644 benchmarks/struct_size_scaling.jl delete mode 100755 benchmarks/test_gpu_benchmarks.sh delete mode 100644 benchmarks/tet10_derivatives_benchmark.jl diff --git a/benchmarks/README_GPU_BENCHMARKS.md b/benchmarks/README_GPU_BENCHMARKS.md deleted file mode 100644 index bc9accc..0000000 --- a/benchmarks/README_GPU_BENCHMARKS.md +++ /dev/null @@ -1,244 +0,0 @@ -# GPU Benchmark Suite - -Comprehensive benchmarks demonstrating GPU-friendly FEM architecture strategies. - -## Prerequisites - -```bash -# Add required packages -julia --project=. -e 'using Pkg; Pkg.add(["CUDA", "Tensors", "BenchmarkTools", "IterativeSolvers"])' -``` - -## Benchmarks - -### 1. State Management Strategy Comparison - -**File:** `gpu_state_management_benchmark.jl` - -**What it tests:** - -- **Strategy 1:** Immutable elements (Array of Structs - AoS) -- **Strategy 2:** Separate mutable state (Structure of Arrays - SoA) - -**Metrics:** - -- Memory bandwidth (GB/s) -- Execution time -- Allocation counts - -**Run:** - -```bash -julia --project=. benchmarks/gpu_state_management_benchmark.jl -``` - -**Expected Results:** - -- CPU: Strategy 2 is 3-5× faster (better cache utilization) -- GPU: Strategy 2 is 5-10× faster (coalesced memory access) -- Strategy 2 bandwidth: 500-900 GB/s (depending on GPU) -- Strategy 1 bandwidth: 50-150 GB/s (non-coalesced) - -### 2. Matrix-Free Newton-Krylov - -**File:** `matrix_free_gpu_benchmark.jl` - -**What it tests:** - -- **Traditional Newton:** Full Jacobian assembly + direct solve -- **Matrix-Free NK:** Jacobian-free with GMRES -- **Anderson-Accelerated:** Matrix-Free + Anderson acceleration - -**Metrics:** - -- Total time -- Iterations to convergence -- Time per iteration - -**Run:** - -```bash -julia --project=. benchmarks/matrix_free_gpu_benchmark.jl -``` - -**Expected Results:** - -- Matrix-Free: 2-4× faster than traditional (no assembly) -- Anderson: 2-3× fewer iterations (superlinear convergence) -- GPU: Additional 3-10× speedup for large problems (>10K DOFs) -- **Total:** 5-10× speedup with all optimizations - -## Understanding the Results - -### State Management Benchmark Output - -```text -GPU Benchmark: 1000000 elements -==================================================================== - -📊 Strategy 1 (Immutable Elements - AoS on GPU): - Time: 12.456 ms - Bandwidth: 87.3 GB/s - -📊 Strategy 2 (Separate State - SoA on GPU): - Time: 1.234 ms - Bandwidth: 743.2 GB/s - -✅ GPU Speedup (Strategy 2 / Strategy 1): 10.09× -✅ Bandwidth Improvement: 8.51× - Strategy 1: 87.3 GB/s (non-coalesced) - Strategy 2: 743.2 GB/s (coalesced) -``` - -**Interpretation:** - -- Strategy 2 achieves 8-10× higher bandwidth -- Approaching GPU memory bandwidth limit (~1000 GB/s for high-end GPUs) -- Coalesced memory access is critical for GPU performance - -### Matrix-Free Benchmark Output - -```text -CPU Benchmark: 10000 DOFs -==================================================================== - -📊 Traditional Newton (Full Jacobian): - Time: 456.78 ms - Iterations: 8 - Time/iter: 57.10 ms - -📊 Matrix-Free Newton-Krylov: - Time: 123.45 ms - Iterations: 10 - Time/iter: 12.35 ms - -📊 Anderson-Accelerated Matrix-Free: - Time: 67.89 ms - Iterations: 5 - Time/iter: 13.58 ms - -✅ CPU Speedups: - Matrix-Free vs Traditional: 3.70× - Anderson vs Traditional: 6.73× - Anderson vs Matrix-Free: 1.82× -``` - -**Interpretation:** - -- Matrix-Free eliminates expensive Jacobian assembly -- Anderson reduces Newton iterations (superlinear convergence) -- Combined: 6-10× speedup on CPU, more on GPU - -## Hardware Requirements - -### Minimum - -- CPU: x86_64 with AVX2 -- RAM: 8 GB -- Julia: 1.9+ - -### Recommended for GPU Benchmarks - -- GPU: NVIDIA with CUDA Compute Capability 7.0+ (RTX 20XX or newer) -- VRAM: 4 GB+ -- CUDA: 11.0+ - -### Tested On - -- NVIDIA RTX 4090 (24 GB VRAM) -- NVIDIA RTX 3090 (24 GB VRAM) -- NVIDIA RTX 3080 (10 GB VRAM) - -## Troubleshooting - -### "CUDA not available" - -If you see this warning, the benchmark will run CPU-only comparison: - -```julia -⚠️ CUDA not available! Running CPU-only comparison. -``` - -**Solution:** - -1. Check GPU is recognized: `nvidia-smi` -2. Verify CUDA installation: `julia -e 'using CUDA; CUDA.versioninfo()'` -3. Rebuild CUDA.jl: `julia --project=. -e 'using Pkg; Pkg.build("CUDA")'` - -### Out of Memory Errors - -If benchmarks crash with OOM: - -```julia -ERROR: Out of memory -``` - -**Solution:** - -- Reduce problem sizes in benchmark scripts -- Edit `sizes = [10_000, 100_000, 1_000_000]` → smaller values -- Close other GPU applications - -### Slow CPU Benchmarks - -Matrix-Free benchmark may be slow on CPU for large problems (>100K DOFs). - -**Solution:** - -- Use smaller test sizes for CPU -- Focus on GPU results for large problems -- Enable BLAS threading: `export JULIA_NUM_THREADS=8` - -## Performance Expectations - -### State Management (1M elements) - -| Strategy | CPU Time | GPU Time | GPU Bandwidth | -|----------|----------|----------|---------------| -| Strategy 1 (AoS) | 18 ms | 12 ms | 80-150 GB/s | -| Strategy 2 (SoA) | 5 ms | 1.2 ms | 500-900 GB/s | -| **Speedup** | **3.6×** | **10×** | **6-10×** | - -### Matrix-Free Newton-Krylov (100K DOFs) - -| Method | CPU Time | GPU Time | Iterations | -|--------|----------|----------|------------| -| Traditional Newton | 8200 ms | N/A | 20 | -| Matrix-Free NK | 2100 ms | 450 ms | 20 | -| MF-NK + Anderson | 1050 ms | 180 ms | 8 | -| **Speedup** | **7.8×** | **45×** | **2.5× fewer** | - -## Validation - -Both benchmarks validate correctness: - -1. **State Management:** - - Verifies final state matches between strategies - - Checks zero allocations for Strategy 2 - -2. **Matrix-Free:** - - Compares solution accuracy (||u_traditional - u_matrixfree|| < 1e-6) - - Validates convergence to same residual norm - -## Citation - -If you use these benchmarks in research, please cite: - -```bibtex -@software{juliafem2025, - title = {JuliaFEM: GPU-Accelerated Finite Element Method}, - author = {Aho, Jukka}, - year = {2025}, - url = {https://github.com/JuliaFEM/JuliaFEM.jl} -} -``` - -## References - -1. **Knoll & Keyes (2004):** "Jacobian-free Newton–Krylov methods" -2. **Walker & Ni (2011):** "Anderson acceleration for fixed-point iterations" -3. **CUDA Programming Guide:** - -## Contact - -Questions or issues? Open an issue at: diff --git a/benchmarks/VALIDATION_RESULTS.md b/benchmarks/VALIDATION_RESULTS.md deleted file mode 100644 index 146dc73..0000000 --- a/benchmarks/VALIDATION_RESULTS.md +++ /dev/null @@ -1,85 +0,0 @@ -# Benchmark Validation Results - -**Date:** November 9, 2025 -**Platform:** Julia 1.12.1 -**Document:** `docs/book/zero_allocation_fields.md` -**Benchmark:** `benchmarks/field_storage_comparison.jl` - -## Summary - -✅ **All performance claims validated** - -The zero-allocation field storage design achieves **9-92× speedup** over `Dict{String,Any}` with **zero allocations in hot paths**. - -## Measured Results - -| Test | OLD (Dict) | NEW (Typed) | Speedup | -|------|------------|-------------|---------| -| Constant field access | 19.2ns, 0 allocs | 2.1ns, 0 allocs | **9×** | -| Nodal field access | 262ns, 3 allocs | 6.5ns, 0 allocs | **40×** | -| Interpolation (uncached) | 2.6μs, 50 allocs | 44ns, 2 allocs | **59×** | -| Interpolation (cached) | 2.6μs, 50 allocs | 53ns, **0 allocs** ✅ | **49×** | -| Assembly (1000 elem) | 109μs, 4000 allocs | 1.2μs, **0 allocs** ✅ | **92×** | - -## Key Achievements - -1. ✅ **Zero allocations** in cached interpolation (53ns) -2. ✅ **Zero allocations** in assembly loop (1.2μs vs 109μs) -3. ✅ **Type stability** eliminates runtime dispatch -4. ✅ **9-92× speedup** across all operations -5. ✅ **Simple implementation** (~200 LOC for field types) - -## Design Validated - -The `NamedTuple` + typed field structs approach is proven effective: - -```julia -# Simple field types -struct ConstantField{T} - value::T -end - -struct NodalField{T} - values::Matrix{T} -end - -# Type-stable container -fields = ( - youngs_modulus = ConstantField(210e3), - displacement = NodalField(zeros(3, 1000)), -) - -# Fast access (zero allocations) -E = fields.youngs_modulus.value # 2.1ns, 0 allocs -u = @view fields.displacement.values[:, nodes] # 6.5ns, 0 allocs -``` - -## Claims Verification - -| Claim | Measured | Status | -|-------|----------|--------| -| 50× faster | 9-92× across operations | ✅ VALIDATED | -| 0 allocations | 0 allocs in hot paths | ✅ VALIDATED | -| Type stability | No runtime dispatch | ✅ VALIDATED | -| Simple implementation | ~200 LOC field types | ✅ VALIDATED | - -## Reproduction - -```bash -cd /home/juajukka/dev/JuliaFEM.jl -julia --project=. benchmarks/field_storage_comparison.jl -``` - -## Next Steps - -1. ✅ Document written and validated -2. ⏭️ Implement field types in `src/fields/types.jl` -3. ⏭️ Update `Element` struct for `ElementSet` pattern -4. ⏭️ Add CI benchmarks to prevent regression -5. ⏭️ Migrate examples to new field system - -## Conclusion - -The zero-allocation field storage design is **ready for v1.0 implementation**. Measured performance exceeds targets with 9-92× speedup and zero allocations in hot paths. - -**Design Decision:** Use `NamedTuple` of typed field structs for JuliaFEM v1.0 diff --git a/benchmarks/basis_function_access_patterns.jl b/benchmarks/basis_function_access_patterns.jl deleted file mode 100644 index b72a413..0000000 --- a/benchmarks/basis_function_access_patterns.jl +++ /dev/null @@ -1,412 +0,0 @@ -# Benchmark: Basis Function Access Patterns for Tet10 (Realistic 3D Case) -# -# Focus: 10-node quadratic tetrahedron (Tet10) - the workhorse for 3D simulations -# -# Goal: Find the fastest way to access basis functions and their DERIVATIVES with: -# 1. Return all 10 basis functions as tuple (zero allocation) -# 2. Return single basis function by index (must be inlineable) -# 3. Return all 10 derivatives as tuple of Vec{3} (zero allocation) -# 4. Return single derivative by index (must be inlineable) -# 5. Pass topology separately (separation of concerns) -# 6. Must be type-stable and superfast -# -# Use Case: -# - Stiffness matrix assembly: Need derivatives (B matrix construction) -# - Mass matrix assembly: Need basis functions (M matrix construction) -# - Nodal assembly: Need single basis function/derivative at a time -# -# Usage: julia --project=. benchmarks/basis_function_access_patterns.jl - -using BenchmarkTools -using Tensors - -# ============================================================================ -# Define minimal types for testing -# ============================================================================ - -abstract type AbstractTopology end -struct Tetrahedron <: AbstractTopology end - -abstract type AbstractBasis end -struct Lagrange{P} <: AbstractBasis end - -# Vec type comes from Tensors.jl (already available in JuliaFEM) -# Vec{3,Float64} for 3D gradients - -# ============================================================================ -# Strategy 1: Return tuple, index with getindex -# ============================================================================ -# Advantages: Natural Julia syntax, type-stable -# Disadvantages: Might not inline getindex? - -""" -Get all basis functions for Triangle, P1 Lagrange (3 functions). -Returns tuple of 3 Float64 values. -""" -@inline function get_basis_functions_v1(::Triangle, ::Lagrange{1}, xi::Vec{2,T}) where T - u, v = xi - # P1 triangle: N1 = 1-u-v, N2 = u, N3 = v - return (1 - u - v, u, v) -end - -""" -Get single basis function by index (1-based). -""" -@inline function get_basis_function_v1(topology::Triangle, basis::Lagrange{1}, xi::Vec{2,T}, i::Int) where T - N_all = get_basis_functions_v1(topology, basis, xi) - return N_all[i] # Tuple indexing -end - -# ============================================================================ -# Strategy 2: Generated function for single access -# ============================================================================ -# Advantages: Compiler can specialize for each index -# Disadvantages: More complex code - -@inline function get_basis_functions_v2(::Triangle, ::Lagrange{1}, xi::Vec{2,T}) where T - u, v = xi - return (1 - u - v, u, v) -end - -""" -Use @generated to create specialized code for each index at compile time. -""" -@generated function get_basis_function_v2(::Triangle, ::Lagrange{1}, xi::Vec{2,T}, ::Val{I}) where {T,I} - if I == 1 - return :(1 - xi[1] - xi[2]) - elseif I == 2 - return :(xi[1]) - elseif I == 3 - return :(xi[2]) - else - return :(error("Invalid basis function index: $I")) - end -end - -# ============================================================================ -# Strategy 3: Manual dispatch on Val (type-stable index) -# ============================================================================ -# Advantages: Explicit, clear what's happening -# Disadvantages: Verbose, need to write each case - -@inline function get_basis_functions_v3(::Triangle, ::Lagrange{1}, xi::Vec{2,T}) where T - u, v = xi - return (1 - u - v, u, v) -end - -@inline get_basis_function_v3(t::Triangle, b::Lagrange{1}, xi::Vec{2,T}, ::Val{1}) where T = 1 - xi[1] - xi[2] -@inline get_basis_function_v3(t::Triangle, b::Lagrange{1}, xi::Vec{2,T}, ::Val{2}) where T = xi[1] -@inline get_basis_function_v3(t::Triangle, b::Lagrange{1}, xi::Vec{2,T}, ::Val{3}) where T = xi[2] - -# ============================================================================ -# Strategy 4: Struct with getindex (most Julian) -# ============================================================================ -# Advantages: Can use N[i] syntax naturally -# Disadvantages: Extra struct allocation? - -struct BasisFunctions{N,T} - data::NTuple{N,T} -end - -Base.@propagate_inbounds Base.getindex(bf::BasisFunctions, i::Int) = bf.data[i] -Base.length(::BasisFunctions{N}) where N = N - -@inline function get_basis_functions_v4(::Triangle, ::Lagrange{1}, xi::Vec{2,T}) where T - u, v = xi - return BasisFunctions((1 - u - v, u, v)) -end - -# Can use natural indexing -@inline function get_basis_function_v4(topology::Triangle, basis::Lagrange{1}, xi::Vec{2,T}, i::Int) where T - N = get_basis_functions_v4(topology, basis, xi) - return N[i] -end - -# ============================================================================ -# Strategy 5: Separate implementation per basis function (extreme specialization) -# ============================================================================ -# Advantages: Maximum performance, no tuple allocation at all -# Disadvantages: Lots of code duplication - -@inline function get_basis_function_1_v5(::Triangle, ::Lagrange{1}, xi::Vec{2,T}) where T - return 1 - xi[1] - xi[2] -end - -@inline function get_basis_function_2_v5(::Triangle, ::Lagrange{1}, xi::Vec{2,T}) where T - return xi[1] -end - -@inline function get_basis_function_3_v5(::Triangle, ::Lagrange{1}, xi::Vec{2,T}) where T - return xi[2] -end - -@inline function get_basis_functions_v5(::Triangle, ::Lagrange{1}, xi::Vec{2,T}) where T - u, v = xi - return (1 - u - v, u, v) -end - -# ============================================================================ -# Benchmark: Access all basis functions (typical in assembly loop) -# ============================================================================ - -function benchmark_all_access() - println("\n" * "="^80) - println("BENCHMARK: Access ALL basis functions") - println("="^80) - - topology = Triangle() - basis = Lagrange{1}() - xi = Vec(0.25, 0.25) - - println("\nStrategy 1: Tuple return + getindex") - @btime get_basis_functions_v1($topology, $basis, $xi) - - println("\nStrategy 2: Generated function") - @btime get_basis_functions_v2($topology, $basis, $xi) - - println("\nStrategy 3: Val dispatch") - @btime get_basis_functions_v3($topology, $basis, $xi) - - println("\nStrategy 4: BasisFunctions struct") - @btime get_basis_functions_v4($topology, $basis, $xi) - - println("\nStrategy 5: Separate functions") - @btime get_basis_functions_v5($topology, $basis, $xi) - - # Verify all return same values - r1 = get_basis_functions_v1(topology, basis, xi) - r2 = get_basis_functions_v2(topology, basis, xi) - r3 = get_basis_functions_v3(topology, basis, xi) - r4 = get_basis_functions_v4(topology, basis, xi).data - r5 = get_basis_functions_v5(topology, basis, xi) - - @assert r1 == r2 == r3 == r4 == r5 "Results don't match!" - println("\n✓ All strategies return identical values: $r1") -end - -# ============================================================================ -# Benchmark: Access SINGLE basis function (for nodal assembly) -# ============================================================================ - -function benchmark_single_access() - println("\n" * "="^80) - println("BENCHMARK: Access SINGLE basis function (nodal assembly)") - println("="^80) - - topology = Triangle() - basis = Lagrange{1}() - xi = Vec(0.25, 0.25) - - println("\nStrategy 1: Tuple + runtime index") - @btime get_basis_function_v1($topology, $basis, $xi, 2) - - println("\nStrategy 2: Generated function with Val{2}") - @btime get_basis_function_v2($topology, $basis, $xi, Val(2)) - - println("\nStrategy 3: Val dispatch") - @btime get_basis_function_v3($topology, $basis, $xi, Val(2)) - - println("\nStrategy 4: BasisFunctions struct + index") - @btime get_basis_function_v4($topology, $basis, $xi, 2) - - println("\nStrategy 5: Direct function call") - @btime get_basis_function_2_v5($topology, $basis, $xi) - - # Verify all return same value - r1 = get_basis_function_v1(topology, basis, xi, 2) - r2 = get_basis_function_v2(topology, basis, xi, Val(2)) - r3 = get_basis_function_v3(topology, basis, xi, Val(2)) - r4 = get_basis_function_v4(topology, basis, xi, 2) - r5 = get_basis_function_2_v5(topology, basis, xi) - - @assert r1 == r2 == r3 == r4 == r5 "Results don't match!" - println("\n✓ All strategies return identical value: $r1") -end - -# ============================================================================ -# Benchmark: Typical assembly loop pattern -# ============================================================================ - -function benchmark_assembly_loop() - println("\n" * "="^80) - println("BENCHMARK: Typical assembly loop (iterate over all basis functions)") - println("="^80) - - topology = Triangle() - basis = Lagrange{1}() - xi = Vec(0.25, 0.25) - - # Pattern 1: Get all, iterate over tuple - println("\nPattern 1: Get all as tuple, iterate") - function assemble_v1() - N_all = get_basis_functions_v1(topology, basis, xi) - s = 0.0 - for N_i in N_all - s += N_i * N_i # Dummy computation - end - return s - end - @btime assemble_v1() - - # Pattern 2: Get all, index in loop - println("\nPattern 2: Get all, index with i") - function assemble_v2() - N_all = get_basis_functions_v1(topology, basis, xi) - s = 0.0 - for i in 1:3 - s += N_all[i] * N_all[i] - end - return s - end - @btime assemble_v2() - - # Pattern 3: Get one at a time (nodal assembly style) - println("\nPattern 3: Get one at a time with Val") - function assemble_v3() - s = 0.0 - # Unrolled loop (what compiler would do with Val) - N1 = get_basis_function_v3(topology, basis, xi, Val(1)) - s += N1 * N1 - N2 = get_basis_function_v3(topology, basis, xi, Val(2)) - s += N2 * N2 - N3 = get_basis_function_v3(topology, basis, xi, Val(3)) - s += N3 * N3 - return s - end - @btime assemble_v3() - - # Pattern 4: Direct function calls (strategy 5) - println("\nPattern 4: Direct function calls (extreme specialization)") - function assemble_v4() - s = 0.0 - N1 = get_basis_function_1_v5(topology, basis, xi) - s += N1 * N1 - N2 = get_basis_function_2_v5(topology, basis, xi) - s += N2 * N2 - N3 = get_basis_function_3_v5(topology, basis, xi) - s += N3 * N3 - return s - end - @btime assemble_v4() - - # Verify all compute same result - r1 = assemble_v1() - r2 = assemble_v2() - r3 = assemble_v3() - r4 = assemble_v4() - @assert r1 == r2 == r3 == r4 "Assembly results don't match!" - println("\n✓ All patterns compute same result: $r1") -end - -# ============================================================================ -# Benchmark: Basis derivatives (return Vec) -# ============================================================================ - -function benchmark_derivatives() - println("\n" * "="^80) - println("BENCHMARK: Basis function DERIVATIVES (return Vec)") - println("="^80) - - topology = Triangle() - basis = Lagrange{1}() - xi = Vec(0.25, 0.25) - - # Triangle P1 derivatives (constant): - # dN1/d(u,v) = (-1, -1) - # dN2/d(u,v) = (1, 0) - # dN3/d(u,v) = (0, 1) - - println("\nStrategy 1: Return tuple of Vecs") - @inline function get_basis_derivatives_v1(::Triangle, ::Lagrange{1}, xi::Vec{2,T}) where T - return (Vec(-1.0, -1.0), Vec(1.0, 0.0), Vec(0.0, 1.0)) - end - @btime get_basis_derivatives_v1($topology, $basis, $xi) - - println("\nStrategy 2: Return single Vec with Val indexing") - @inline get_basis_derivative_v2(::Triangle, ::Lagrange{1}, xi::Vec{2,T}, ::Val{1}) where T = Vec(-1.0, -1.0) - @inline get_basis_derivative_v2(::Triangle, ::Lagrange{1}, xi::Vec{2,T}, ::Val{2}) where T = Vec(1.0, 0.0) - @inline get_basis_derivative_v2(::Triangle, ::Lagrange{1}, xi::Vec{2,T}, ::Val{3}) where T = Vec(0.0, 1.0) - @btime get_basis_derivative_v2($topology, $basis, $xi, Val(2)) - - # Verify - all_derivs = get_basis_derivatives_v1(topology, basis, xi) - single_deriv = get_basis_derivative_v2(topology, basis, xi, Val(2)) - @assert all_derivs[2] == single_deriv - println("\n✓ Derivatives match: $single_deriv") -end - -# ============================================================================ -# Main execution -# ============================================================================ - -function main() - println("\n") - println("╔" * "="^78 * "╗") - println("║" * " "^78 * "║") - println("║" * " "^20 * "BASIS FUNCTION ACCESS PATTERNS BENCHMARK" * " "^18 * "║") - println("║" * " "^78 * "║") - println("╚" * "="^78 * "╝") - - println("\nGoal: Find fastest way to access basis functions for nodal assembly") - println("Requirements:") - println(" - Zero allocation") - println(" - Type stable") - println(" - Inlineable") - println(" - Support both 'all at once' and 'one at a time' access") - - benchmark_all_access() - benchmark_single_access() - benchmark_assembly_loop() - benchmark_derivatives() - - println("\n" * "="^80) - println("SUMMARY & RECOMMENDATIONS") - println("="^80) - println(""" - - For TRADITIONAL ASSEMBLY (get all basis functions at integration point): - → Use Strategy 1 or 4: Simple tuple return - → Should be 0-5 ns, zero allocation - - For NODAL ASSEMBLY (get single basis function): - → Use Strategy 3: Val dispatch for compile-time index - → Should be 0-2 ns, zero allocation, fully inlined - → Usage: get_basis_function(Triangle(), Lagrange{1}(), xi, Val(i)) - - For DERIVATIVES: - → Return tuple of Vec for all derivatives - → Use Val indexing for single derivative - → Same performance as basis functions - - RECOMMENDED API: - ```julia - # Get all basis functions (returns tuple) - N_all = get_basis_functions(Triangle(), Lagrange{1}(), xi) - - # Get single basis function (Val for compile-time specialization) - N_i = get_basis_function(Triangle(), Lagrange{1}(), xi, Val(i)) - - # Get all derivatives (returns tuple of Vec) - dN_all = get_basis_derivatives(Triangle(), Lagrange{1}(), xi) - - # Get single derivative (returns Vec) - dN_i = get_basis_derivative(Triangle(), Lagrange{1}(), xi, Val(i)) - ``` - - WHY Val? - - Compiler knows index at compile time - - Can generate optimal code for each basis function - - Zero runtime overhead - - Type stable - - NOTE: For runtime indexing (i not known at compile time), tuple indexing - is still very fast (typically 1-2 ns overhead). - """) - - println("\n" * "="^80) -end - -# Run benchmarks -if abspath(PROGRAM_FILE) == @__FILE__ - main() -end diff --git a/benchmarks/basis_function_access_tet10.jl b/benchmarks/basis_function_access_tet10.jl deleted file mode 100644 index 0dd6f12..0000000 --- a/benchmarks/basis_function_access_tet10.jl +++ /dev/null @@ -1,504 +0,0 @@ -# Benchmark: Basis Function Access Patterns for Tet10 (Realistic 3D Case) -# -# Focus: 10-node quadratic tetrahedron (Tet10) - the workhorse for 3D simulations -# -# Goal: Find the fastest way to access basis functions and their DERIVATIVES with: -# 1. Return all 10 basis functions as tuple (zero allocation) -# 2. Return single basis function by index (must be inlineable) -# 3. Return all 10 derivatives as tuple of Vec{3} (zero allocation) -# 4. Return single derivative by index (must be inlineable) -# 5. Pass topology separately (separation of concerns: Element(Tetrahedron, Lagrange{2}, ...)) -# 6. Must be type-stable and superfast -# -# Use Case: -# - Stiffness matrix assembly: Need derivatives (B matrix construction) -# - Mass matrix assembly: Need basis functions (M matrix construction) -# - Nodal assembly: Need single basis function/derivative at a time -# -# Usage: julia --project=. benchmarks/basis_function_access_tet10.jl - -using BenchmarkTools -using Tensors - -# ============================================================================ -# Define minimal types for testing -# ============================================================================ - -abstract type AbstractTopology end -struct Tetrahedron <: AbstractTopology end - -abstract type AbstractBasis end -struct Lagrange{P} <: AbstractBasis end - -# ============================================================================ -# Tet10 Basis Functions (Quadratic Tetrahedron, 10 nodes) -# ============================================================================ -# Node numbering: -# 1-4: vertices -# 5-10: edge midpoints (5: 1-2, 6: 2-3, 7: 3-1, 8: 1-4, 9: 2-4, 10: 3-4) -# -# Parametric coordinates: (u, v, w) where u+v+w ≤ 1 -# Reference element: vertices at (0,0,0), (1,0,0), (0,1,0), (0,0,1) - -""" -Get all 10 basis functions for Tet10 at parametric point (u,v,w). -Returns NTuple{10, Float64}. - -The basis functions are: -- N1 = (1-u-v-w)(1-2u-2v-2w) [vertex 1] -- N2 = u(2u-1) [vertex 2] -- N3 = v(2v-1) [vertex 3] -- N4 = w(2w-1) [vertex 4] -- N5 = 4u(1-u-v-w) [edge 1-2] -- N6 = 4uv [edge 2-3] -- N7 = 4v(1-u-v-w) [edge 3-1] -- N8 = 4w(1-u-v-w) [edge 1-4] -- N9 = 4uw [edge 2-4] -- N10= 4vw [edge 3-4] -""" -@inline function get_basis_functions(::Tetrahedron, ::Lagrange{2}, xi::Vec{3,T}) where T - u, v, w = xi - λ = 1 - u - v - w # barycentric coordinate for vertex 1 - - # Vertex nodes (1-4) - N1 = λ * (2λ - 1) - N2 = u * (2u - 1) - N3 = v * (2v - 1) - N4 = w * (2w - 1) - - # Edge midpoint nodes (5-10) - N5 = 4 * u * λ - N6 = 4 * u * v - N7 = 4 * v * λ - N8 = 4 * w * λ - N9 = 4 * u * w - N10 = 4 * v * w - - return (N1, N2, N3, N4, N5, N6, N7, N8, N9, N10) -end - -""" -Get all 10 basis function derivatives for Tet10. -Returns NTuple{10, Vec{3,Float64}}. - -Each derivative is ∇N_i = (∂N_i/∂u, ∂N_i/∂v, ∂N_i/∂w) -""" -@inline function get_basis_derivatives(::Tetrahedron, ::Lagrange{2}, xi::Vec{3,T}) where T - u, v, w = xi - λ = 1 - u - v - w - - # Derivatives of vertex nodes - dN1 = Vec(-3 + 4u + 4v + 4w, -3 + 4u + 4v + 4w, -3 + 4u + 4v + 4w) - dN2 = Vec(4u - 1, 0.0, 0.0) - dN3 = Vec(0.0, 4v - 1, 0.0) - dN4 = Vec(0.0, 0.0, 4w - 1) - - # Derivatives of edge midpoint nodes - dN5 = Vec(4λ - 4u, -4u, -4u) - dN6 = Vec(4v, 4u, 0.0) - dN7 = Vec(-4v, 4λ - 4v, -4v) - dN8 = Vec(-4w, -4w, 4λ - 4w) - dN9 = Vec(4w, 0.0, 4u) - dN10 = Vec(0.0, 4w, 4v) - - return (dN1, dN2, dN3, dN4, dN5, dN6, dN7, dN8, dN9, dN10) -end - -# ============================================================================ -# Strategy 1: Tuple indexing (runtime index) -# ============================================================================ - -@inline function get_basis_function_v1(topology::Tetrahedron, basis::Lagrange{2}, - xi::Vec{3,T}, i::Int) where T - N_all = get_basis_functions(topology, basis, xi) - return N_all[i] -end - -@inline function get_basis_derivative_v1(topology::Tetrahedron, basis::Lagrange{2}, - xi::Vec{3,T}, i::Int) where T - dN_all = get_basis_derivatives(topology, basis, xi) - return dN_all[i] -end - -# ============================================================================ -# Strategy 2: Val dispatch (compile-time index) -# ============================================================================ - -@inline function get_basis_function_v2(t::Tetrahedron, b::Lagrange{2}, - xi::Vec{3,T}, ::Val{I}) where {T,I} - N_all = get_basis_functions(t, b, xi) - return N_all[I] -end - -@inline function get_basis_derivative_v2(t::Tetrahedron, b::Lagrange{2}, - xi::Vec{3,T}, ::Val{I}) where {T,I} - dN_all = get_basis_derivatives(t, b, xi) - return dN_all[I] -end - -# ============================================================================ -# Strategy 3: Generated function (compute only requested basis function) -# ============================================================================ - -@generated function get_basis_function_v3(::Tetrahedron, ::Lagrange{2}, - xi::Vec{3,T}, ::Val{I}) where {T,I} - # Generate specialized code for each index - if I == 1 - return quote - u, v, w = xi - λ = 1 - u - v - w - return λ * (2λ - 1) - end - elseif I == 2 - return quote - u = xi[1] - return u * (2u - 1) - end - elseif I == 3 - return quote - v = xi[2] - return v * (2v - 1) - end - elseif I == 4 - return quote - w = xi[3] - return w * (2w - 1) - end - elseif I == 5 - return quote - u, v, w = xi - λ = 1 - u - v - w - return 4 * u * λ - end - elseif I == 6 - return quote - u, v = xi[1], xi[2] - return 4 * u * v - end - elseif I == 7 - return quote - u, v, w = xi - λ = 1 - u - v - w - return 4 * v * λ - end - elseif I == 8 - return quote - u, v, w = xi - λ = 1 - u - v - w - return 4 * w * λ - end - elseif I == 9 - return quote - u, w = xi[1], xi[3] - return 4 * u * w - end - elseif I == 10 - return quote - v, w = xi[2], xi[3] - return 4 * v * w - end - else - return :(error("Invalid basis function index: $I for Tet10")) - end -end - -@generated function get_basis_derivative_v3(::Tetrahedron, ::Lagrange{2}, - xi::Vec{3,T}, ::Val{I}) where {T,I} - if I == 1 - return quote - u, v, w = xi - return Vec(-3 + 4u + 4v + 4w, -3 + 4u + 4v + 4w, -3 + 4u + 4v + 4w) - end - elseif I == 2 - return quote - u = xi[1] - return Vec(4u - 1, 0.0, 0.0) - end - elseif I == 3 - return quote - v = xi[2] - return Vec(0.0, 4v - 1, 0.0) - end - elseif I == 4 - return quote - w = xi[3] - return Vec(0.0, 0.0, 4w - 1) - end - elseif I == 5 - return quote - u, v, w = xi - λ = 1 - u - v - w - return Vec(4λ - 4u, -4u, -4u) - end - elseif I == 6 - return quote - u, v = xi[1], xi[2] - return Vec(4v, 4u, 0.0) - end - elseif I == 7 - return quote - u, v, w = xi - λ = 1 - u - v - w - return Vec(-4v, 4λ - 4v, -4v) - end - elseif I == 8 - return quote - u, v, w = xi - λ = 1 - u - v - w - return Vec(-4w, -4w, 4λ - 4w) - end - elseif I == 9 - return quote - u, w = xi[1], xi[3] - return Vec(4w, 0.0, 4u) - end - elseif I == 10 - return quote - v, w = xi[2], xi[3] - return Vec(0.0, 4w, 4v) - end - else - return :(error("Invalid basis function index: $I for Tet10")) - end -end - -# ============================================================================ -# Benchmark Functions -# ============================================================================ - -function benchmark_all_basis_functions() - println("\n" * "="^80) - println("BENCHMARK 1: Get ALL 10 basis functions") - println("="^80) - println("Use case: Mass matrix assembly, need all N_i at integration point") - - topology = Tetrahedron() - basis = Lagrange{2}() - xi = Vec(0.25, 0.25, 0.2) # Typical integration point - - println("\nAccess all 10 basis functions:") - @btime get_basis_functions($topology, $basis, $xi) - - result = get_basis_functions(topology, basis, xi) - println("\n✓ Result (10 values): ", result) - println("✓ Sum of basis functions (partition of unity): ", sum(result)) - @assert abs(sum(result) - 1.0) < 1e-10 "Partition of unity violated!" -end - -function benchmark_single_basis_function() - println("\n" * "="^80) - println("BENCHMARK 2: Get SINGLE basis function (nodal assembly)") - println("="^80) - println("Use case: Nodal assembly, need N_i for specific node") - - topology = Tetrahedron() - basis = Lagrange{2}() - xi = Vec(0.25, 0.25, 0.2) - node_idx = 5 # Edge midpoint node - - println("\nStrategy 1: Tuple + runtime index") - @btime get_basis_function_v1($topology, $basis, $xi, $node_idx) - - println("\nStrategy 2: Val dispatch (compile-time index)") - @btime get_basis_function_v2($topology, $basis, $xi, Val($node_idx)) - - println("\nStrategy 3: @generated function (minimal computation)") - @btime get_basis_function_v3($topology, $basis, $xi, Val($node_idx)) - - # Verify all return same value - r1 = get_basis_function_v1(topology, basis, xi, node_idx) - r2 = get_basis_function_v2(topology, basis, xi, Val(node_idx)) - r3 = get_basis_function_v3(topology, basis, xi, Val(node_idx)) - @assert r1 ≈ r2 ≈ r3 "Strategies return different values!" - println("\n✓ All strategies return: N_$node_idx = $r1") -end - -function benchmark_all_derivatives() - println("\n" * "="^80) - println("BENCHMARK 3: Get ALL 10 basis function derivatives") - println("="^80) - println("Use case: Stiffness matrix assembly (B matrix construction)") - println("Most important benchmark for 3D simulations!") - - topology = Tetrahedron() - basis = Lagrange{2}() - xi = Vec(0.25, 0.25, 0.2) - - println("\nAccess all 10 derivatives (each is Vec{3}):") - @btime get_basis_derivatives($topology, $basis, $xi) - - result = get_basis_derivatives(topology, basis, xi) - println("\n✓ Result (10 Vec{3} gradients):") - for (i, dN) in enumerate(result) - println(" ∇N_$i = $dN") - end -end - -function benchmark_single_derivative() - println("\n" * "="^80) - println("BENCHMARK 4: Get SINGLE basis function derivative") - println("="^80) - println("Use case: Nodal assembly for stiffness matrix") - - topology = Tetrahedron() - basis = Lagrange{2}() - xi = Vec(0.25, 0.25, 0.2) - node_idx = 5 - - println("\nStrategy 1: Tuple + runtime index") - @btime get_basis_derivative_v1($topology, $basis, $xi, $node_idx) - - println("\nStrategy 2: Val dispatch") - @btime get_basis_derivative_v2($topology, $basis, $xi, Val($node_idx)) - - println("\nStrategy 3: @generated function") - @btime get_basis_derivative_v3($topology, $basis, $xi, Val($node_idx)) - - # Verify - r1 = get_basis_derivative_v1(topology, basis, xi, node_idx) - r2 = get_basis_derivative_v2(topology, basis, xi, Val(node_idx)) - r3 = get_basis_derivative_v3(topology, basis, xi, Val(node_idx)) - @assert r1 ≈ r2 ≈ r3 "Strategies return different values!" - println("\n✓ All strategies return: ∇N_$node_idx = $r1") -end - -function benchmark_stiffness_assembly_pattern() - println("\n" * "="^80) - println("BENCHMARK 5: Realistic stiffness matrix assembly loop") - println("="^80) - println("Use case: Compute element stiffness K_e (typical FEM inner loop)") - println("Pattern: B^T D B where B = strain-displacement matrix") - - topology = Tetrahedron() - basis = Lagrange{2}() - xi = Vec(0.25, 0.25, 0.2) # Integration point - - # Typical pattern: Get all derivatives, compute B matrix terms - println("\nPattern 1: Get all derivatives at once (traditional)") - stiffness_loop_v1 = let topology = topology, basis = basis, xi = xi - () -> begin - dN_all = get_basis_derivatives(topology, basis, xi) - s = 0.0 - # Simplified: compute trace of K (just for benchmarking) - for i in 1:10 - for j in 1:10 - dNi = dN_all[i] - dNj = dN_all[j] - s += dot(dNi, dNj) # Simplified K_ij computation - end - end - return s - end - end - @btime $stiffness_loop_v1() - - println("\nPattern 2: Get derivatives with @generated (manual unroll)") - stiffness_loop_v3 = let topology = topology, basis = basis, xi = xi - () -> begin - s = 0.0 - # Unrolled loop (compiler would do this with Val) - dN1 = get_basis_derivative_v3(topology, basis, xi, Val(1)) - dN2 = get_basis_derivative_v3(topology, basis, xi, Val(2)) - dN3 = get_basis_derivative_v3(topology, basis, xi, Val(3)) - dN4 = get_basis_derivative_v3(topology, basis, xi, Val(4)) - dN5 = get_basis_derivative_v3(topology, basis, xi, Val(5)) - dN6 = get_basis_derivative_v3(topology, basis, xi, Val(6)) - dN7 = get_basis_derivative_v3(topology, basis, xi, Val(7)) - dN8 = get_basis_derivative_v3(topology, basis, xi, Val(8)) - dN9 = get_basis_derivative_v3(topology, basis, xi, Val(9)) - dN10 = get_basis_derivative_v3(topology, basis, xi, Val(10)) - - # Compute all pairs (100 dot products) - for dNi in (dN1, dN2, dN3, dN4, dN5, dN6, dN7, dN8, dN9, dN10) - for dNj in (dN1, dN2, dN3, dN4, dN5, dN6, dN7, dN8, dN9, dN10) - s += dot(dNi, dNj) - end - end - return s - end - end - @btime $stiffness_loop_v3() - - # Verify all compute same result - r1 = stiffness_loop_v1() - r3 = stiffness_loop_v3() - @assert abs(r1 - r3) < 1e-10 "Assembly patterns give different results!" - println("\n✓ Assembly result: $r1") -end - -# ============================================================================ -# Main execution -# ============================================================================ - -function main() - println("\n") - println("╔" * "="^78 * "╗") - println("║" * " "^78 * "║") - println("║" * " "^15 * "TET10 BASIS FUNCTION ACCESS BENCHMARK" * " "^25 * "║") - println("║" * " "^20 * "(10-node Quadratic Tetrahedron)" * " "^26 * "║") - println("║" * " "^78 * "║") - println("╚" * "="^78 * "╝") - - println("\nElement: Tet10 (10-node quadratic tetrahedron)") - println("Nodes: 4 vertices + 6 edge midpoints") - println("Polynomial degree: P2 (quadratic)") - println("Dimension: 3D") - println("\nSeparation of concerns API:") - println(" Element(Tetrahedron, Lagrange{2}, connectivity)") - println(" get_basis_functions(Tetrahedron(), Lagrange{2}(), xi)") - println(" get_basis_derivatives(Tetrahedron(), Lagrange{2}(), xi)") - - benchmark_all_basis_functions() - benchmark_single_basis_function() - benchmark_all_derivatives() - benchmark_single_derivative() - benchmark_stiffness_assembly_pattern() - - println("\n" * "="^80) - println("SUMMARY & RECOMMENDATIONS FOR 3D SIMULATIONS") - println("="^80) - println(""" - - FOR STIFFNESS MATRIX ASSEMBLY (derivatives): - → Use get_basis_derivatives() - returns all 10 gradients as tuple - → Expected: ~10-30 ns, zero allocation - → This is the HOT PATH for 3D FEM! - - FOR MASS MATRIX ASSEMBLY (basis functions): - → Use get_basis_functions() - returns all 10 values as tuple - → Expected: ~5-15 ns, zero allocation - - FOR NODAL ASSEMBLY (single node operations): - → Use Val dispatch: get_basis_derivative(t, b, xi, Val(i)) - → @generated gives minimal computation (only compute requested function) - → Expected: ~5-10 ns per node - - API DESIGN DECISION: - ```julia - # Separation of concerns (RECOMMENDED): - Element(Tetrahedron, Lagrange{2}, connectivity) - - # Functions take topology explicitly: - dN_all = get_basis_derivatives(Tetrahedron(), Lagrange{2}(), xi) - dN_i = get_basis_derivative(Tetrahedron(), Lagrange{2}(), xi, Val(i)) - ``` - - WHY THIS API? - - Clear separation: topology is geometry, basis is interpolation - - Topology passed to basis evaluation (no redundancy in type parameters) - - Type-stable, zero-allocation, fully inlined - - Works with any basis type (Lagrange, Hierarchical, Nedelec, etc.) - - PERFORMANCE TARGET: - - 10-node Tet10 derivatives: < 30 ns (achieved!) - - 100× faster than old Dict-based approach - - Ready for million-element meshes - """) - - println("\n" * "="^80) -end - -# Run benchmarks -if abspath(PROGRAM_FILE) == @__FILE__ - main() -end diff --git a/benchmarks/deformation_gradient_analysis.jl b/benchmarks/deformation_gradient_analysis.jl deleted file mode 100644 index 057bc0a..0000000 --- a/benchmarks/deformation_gradient_analysis.jl +++ /dev/null @@ -1,240 +0,0 @@ -# Performance Analysis for Deformation Gradient Implementation -# This script analyzes the machine code generated and validates zero-allocation claims - -using JuliaFEM -using Tensors -using BenchmarkTools -using InteractiveUtils - -# Load the deformation gradient code -include("../src/physics/deformation_gradient.jl") - -println("="^80) -println("DEFORMATION GRADIENT PERFORMANCE ANALYSIS") -println("="^80) -println() - -# Setup test data -X_nodes = ( - Vec(0.0, 0.0, 0.0), - Vec(1.0, 0.0, 0.0), - Vec(1.0, 1.0, 0.0), - Vec(0.0, 1.0, 0.0), - Vec(0.0, 0.0, 1.0), - Vec(1.0, 0.0, 1.0), - Vec(1.0, 1.0, 1.0), - Vec(0.0, 1.0, 1.0) -) - -u_nodes = ( - Vec(0.0, 0.0, 0.0), - Vec(0.1, 0.0, 0.0), - Vec(0.1, 0.0, 0.0), - Vec(0.0, 0.0, 0.0), - Vec(0.0, 0.0, 0.0), - Vec(0.1, 0.0, 0.0), - Vec(0.1, 0.0, 0.0), - Vec(0.0, 0.0, 0.0) -) - -ξ = Vec(0.0, 0.0, 0.0) -dN_dξ = get_basis_derivatives(Hexahedron(), Lagrange{Hexahedron,1}(), ξ) - -# Compute Jacobian -function compute_jacobian(X_nodes, dN_dξ) - J = zero(Tensor{2,3,Float64,9}) - for i in 1:8 - J += X_nodes[i] ⊗ dN_dξ[i] - end - return J -end - -J = compute_jacobian(X_nodes, dN_dξ) - -# ============================================================================ -# 1. ALLOCATION ANALYSIS -# ============================================================================ -println("1. ALLOCATION ANALYSIS") -println("-"^80) - -# Warm up (compile) -F = compute_deformation_gradient(X_nodes, u_nodes, dN_dξ, J, FiniteStrain()) - -# Measure allocations -allocs = @allocated compute_deformation_gradient(X_nodes, u_nodes, dN_dξ, J, FiniteStrain()) -println("Allocations: $allocs bytes") - -if allocs == 0 - println("✅ ZERO ALLOCATIONS CONFIRMED!") -else - println("❌ WARNING: Found $allocs bytes allocated!") -end -println() - -# ============================================================================ -# 2. PERFORMANCE BENCHMARKING -# ============================================================================ -println("2. PERFORMANCE BENCHMARKING") -println("-"^80) - -println("Benchmarking compute_deformation_gradient...") -result = @benchmark compute_deformation_gradient($X_nodes, $u_nodes, $dN_dξ, $J, FiniteStrain()) -println(result) -println() - -median_time_ns = median(result.times) -println("Median time: $(median_time_ns) ns = $(median_time_ns/1000) μs") -println() - -# ============================================================================ -# 3. LLVM IR ANALYSIS -# ============================================================================ -println("3. LLVM IR ANALYSIS") -println("-"^80) -println("Examining LLVM IR for signs of optimization...") -println() - -io_llvm = IOBuffer() -code_llvm(io_llvm, compute_deformation_gradient, - typeof((X_nodes, u_nodes, dN_dξ, J, FiniteStrain()))) -llvm_code = String(take!(io_llvm)) - -# Count key indicators -n_allocations = count(r"@julia.gc_alloc_obj", llvm_code) -n_stores = count(r"store", llvm_code) -n_loads = count(r"load", llvm_code) -n_vector_ops = count(r"<\d+ x ", llvm_code) # SIMD vector operations - -println("LLVM IR Statistics:") -println(" - GC allocations: $n_allocations") -println(" - Store operations: $n_stores") -println(" - Load operations: $n_loads") -println(" - Vector operations (SIMD): $n_vector_ops") -println() - -if n_allocations == 0 - println("✅ No GC allocations in LLVM IR!") -else - println("❌ WARNING: Found $n_allocations GC allocation calls!") -end - -if n_vector_ops > 0 - println("✅ SIMD vectorization detected!") -end -println() - -# Print full LLVM (first 100 lines) -println("Full LLVM IR (first 100 lines):") -println("-"^80) -llvm_lines = split(llvm_code, '\n') -for (i, line) in enumerate(llvm_lines[1:min(100, length(llvm_lines))]) - println(line) -end -println() - -# ============================================================================ -# 4. NATIVE ASSEMBLY ANALYSIS -# ============================================================================ -println("4. NATIVE ASSEMBLY ANALYSIS") -println("-"^80) -println("Examining native assembly...") -println() - -io_native = IOBuffer() -code_native(io_native, compute_deformation_gradient, - typeof((X_nodes, u_nodes, dN_dξ, J, FiniteStrain()))) -native_code = String(take!(io_native)) - -# Count key assembly features -n_movsd = count(r"movsd", native_code) # Scalar moves -n_movapd = count(r"movapd", native_code) # Aligned packed moves -n_movupd = count(r"movupd", native_code) # Unaligned packed moves -n_mulpd = count(r"mulpd", native_code) # Packed multiply -n_addpd = count(r"addpd", native_code) # Packed add -n_call = count(r"call", native_code) # Function calls - -println("Native Assembly Statistics:") -println(" - Scalar moves (movsd): $n_movsd") -println(" - Aligned packed moves (movapd): $n_movapd") -println(" - Unaligned packed moves (movupd): $n_movupd") -println(" - Packed multiplies (mulpd): $n_mulpd") -println(" - Packed adds (addpd): $n_addpd") -println(" - Function calls: $n_call") -println() - -if n_mulpd > 0 || n_addpd > 0 - println("✅ SSE/AVX SIMD instructions detected!") -end - -if n_call == 0 - println("✅ Fully inlined - no function calls!") -else - println("⚠️ Note: $n_call function calls detected (may include math library)") -end -println() - -# Print full assembly (first 100 lines) -println("Full Native Assembly (first 100 lines):") -println("-"^80) -native_lines = split(native_code, '\n') -for (i, line) in enumerate(native_lines[1:min(100, length(native_lines))]) - println(line) -end -println() - -# ============================================================================ -# 5. TYPE STABILITY ANALYSIS -# ============================================================================ -println("5. TYPE STABILITY ANALYSIS") -println("-"^80) -println("Checking type stability with @code_warntype...") -println() - -io_warntype = IOBuffer() -code_warntype(io_warntype, compute_deformation_gradient, - typeof((X_nodes, u_nodes, dN_dξ, J, FiniteStrain()))) -warntype_output = String(take!(io_native)) - -# Check for type instabilities -has_any = contains(warntype_output, "Any") -has_union = contains(warntype_output, "Union{") - -if has_any - println("⚠️ WARNING: 'Any' types detected (type instability)") -else - println("✅ No 'Any' types detected!") -end - -if has_union - println("⚠️ Note: Union types detected (may be intentional)") -else - println("✅ No Union types detected!") -end -println() - -# Print warntype output (first 50 lines) -println("@code_warntype output (first 50 lines):") -println("-"^80) -warntype_lines = split(warntype_output, '\n') -for (i, line) in enumerate(warntype_lines[1:min(50, length(warntype_lines))]) - println(line) -end -println() - -# ============================================================================ -# 6. SUMMARY -# ============================================================================ -println("="^80) -println("PERFORMANCE SUMMARY") -println("="^80) -println() -println("✅ Implementation validated as:") -println(" - Zero allocation (confirmed)") -println(" - Type stable") -println(" - SIMD optimized ($(n_vector_ops) vector ops in LLVM)") -println(" - Median execution time: $(round(median_time_ns, digits=2)) ns") -println() -println("This implementation achieves the best possible performance for") -println("deformation gradient computation in Julia.") -println() -println("="^80) diff --git a/benchmarks/element_immutability_benchmark.jl b/benchmarks/element_immutability_benchmark.jl deleted file mode 100644 index f86f3df..0000000 --- a/benchmarks/element_immutability_benchmark.jl +++ /dev/null @@ -1,396 +0,0 @@ -# ============================================================================== -# ELEMENT IMMUTABILITY BENCHMARK -# ============================================================================== -# -# Purpose: Demonstrate why immutable elements with type-stable fields are faster -# than mutable elements with Dict-based fields, despite seeming -# counterintuitive. -# -# Hypothesis: Immutable + type-stable >> Mutable + Dict -# -# What we measure: -# 1. Field access time (reading) -# 2. Field update time (writing) -# 3. Memory allocations -# 4. Assembly loop performance (realistic FEM workload) -# -# Expected results: -# - Dict lookup: O(1) amortized, but ~100ns overhead per access -# - Type-stable access: O(1), but ~1ns (inlined, no overhead) -# - Immutable update: Allocates new struct, but compiler optimizes away -# - Dict update: Mutates in-place, but loses type stability -# -# Conclusion: For FEM assembly (tight loops, millions of field accesses), -# type stability dominates. Immutability enables GPU/HPC. -# -# ============================================================================== - -using BenchmarkTools -using Statistics - -println("="^80) -println("ELEMENT IMMUTABILITY BENCHMARK") -println("="^80) -println() -println("Comparing two implementations of P2 Lagrange Tetrahedron (Tet10):") -println(" 1. Mutable element with Dict-based fields (OLD API)") -println(" 2. Immutable element with NamedTuple fields (NEW API)") -println() -println("Measuring: field access, field update, assembly loop") -println("="^80) -println() - -# ============================================================================== -# IMPLEMENTATION 1: Mutable Element with Dict-based Fields (OLD) -# ============================================================================== - -""" -Mutable element: fields stored in Dict{Symbol,Any} -- Pro: Can add/remove fields dynamically -- Con: Type-unstable, Dict lookup overhead, no GPU support -""" -mutable struct MutableElement - id::UInt - connectivity::Vector{UInt} - fields::Dict{Symbol,Any} # Type-unstable! -end - -function MutableElement(connectivity::Vector{UInt}) - return MutableElement(UInt(0), connectivity, Dict{Symbol,Any}()) -end - -# Old-style update: mutate in-place -function update_field!(elem::MutableElement, field_name::Symbol, value) - elem.fields[field_name] = value - return nothing -end - -# Old-style access: Dict lookup -function get_field(elem::MutableElement, field_name::Symbol) - return elem.fields[field_name] -end - -# ============================================================================== -# IMPLEMENTATION 2: Immutable Element with NamedTuple Fields (NEW) -# ============================================================================== - -""" -Immutable element: fields stored in NamedTuple -- Pro: Type-stable, zero overhead access, GPU-compatible -- Con: Cannot mutate, must create new element (but compiler optimizes!) -""" -struct ImmutableElement{F} - id::UInt - connectivity::NTuple{10,UInt} # Fixed size, stack-allocated - fields::F # Type-stable! (NamedTuple) -end - -function ImmutableElement(connectivity::NTuple{10,UInt}, fields::NamedTuple) - return ImmutableElement{typeof(fields)}(UInt(0), connectivity, fields) -end - -# New-style update: return new element (immutable) -function update_field(elem::ImmutableElement, updates::NamedTuple) - new_fields = merge(elem.fields, updates) - return ImmutableElement(elem.connectivity, new_fields) -end - -# New-style access: direct field access (inlined!) -function get_field(elem::ImmutableElement, field_name::Symbol) - return getfield(elem.fields, field_name) -end - -# ============================================================================== -# BENCHMARK 1: Field Access (Read Performance) -# ============================================================================== - -println("BENCHMARK 1: Field Access (Reading E, ν, ρ in tight loop)") -println("-"^80) - -# Setup test elements -connectivity_vec = UInt.(1:10) -connectivity_tuple = ntuple(i -> UInt(i), 10) - -mutable_elem = MutableElement(connectivity_vec) -update_field!(mutable_elem, :E, 210e9) -update_field!(mutable_elem, :ν, 0.3) -update_field!(mutable_elem, :ρ, 7850.0) - -immutable_elem = ImmutableElement(connectivity_tuple, (E=210e9, ν=0.3, ρ=7850.0)) - -# Benchmark: Read fields 1000 times (simulating assembly loop) -function read_fields_mutable(elem, n) - sum_val = 0.0 - for _ in 1:n - E = get_field(elem, :E) - ν = get_field(elem, :ν) - ρ = get_field(elem, :ρ) - sum_val += E + ν + ρ - end - return sum_val -end - -function read_fields_immutable(elem, n) - sum_val = 0.0 - for _ in 1:n - E = get_field(elem, :E) - ν = get_field(elem, :ν) - ρ = get_field(elem, :ρ) - sum_val += E + ν + ρ - end - return sum_val -end - -n_reads = 1000 - -println("Reading fields $n_reads times:") -println() - -t_mutable = @benchmark read_fields_mutable($mutable_elem, $n_reads) -println("Mutable (Dict): ", minimum(t_mutable.times) / n_reads, " ns/read") -println(" Median: ", median(t_mutable.times) / n_reads, " ns/read") -println(" Allocs: ", t_mutable.allocs) - -t_immutable = @benchmark read_fields_immutable($immutable_elem, $n_reads) -println("Immutable (Tuple): ", minimum(t_immutable.times) / n_reads, " ns/read") -println(" Median: ", median(t_immutable.times) / n_reads, " ns/read") -println(" Allocs: ", t_immutable.allocs) - -speedup_read = minimum(t_mutable.times) / minimum(t_immutable.times) -println() -println("Speedup: ", round(speedup_read, digits=1), "x faster") -println() - -# ============================================================================== -# BENCHMARK 2: Field Update (Write Performance) -# ============================================================================== - -println("BENCHMARK 2: Field Update (Updating temperature field)") -println("-"^80) - -# Benchmark: Update temperature field 100 times -function update_temperature_mutable(elem, n) - for i in 1:n - update_field!(elem, :temperature, Float64(i) * 293.15) - end - return get_field(elem, :temperature) -end - -function update_temperature_immutable(elem, n) - current = elem - for i in 1:n - current = update_field(current, (temperature=Float64(i) * 293.15,)) - end - return get_field(current, :temperature) -end - -n_updates = 100 - -println("Updating temperature field $n_updates times:") -println() - -# Reset elements -mutable_elem2 = MutableElement(connectivity_vec) -update_field!(mutable_elem2, :E, 210e9) -update_field!(mutable_elem2, :ν, 0.3) - -immutable_elem2 = ImmutableElement(connectivity_tuple, (E=210e9, ν=0.3)) - -t_mutable_update = @benchmark update_temperature_mutable($mutable_elem2, $n_updates) -println("Mutable (mutate): ", minimum(t_mutable_update.times) / n_updates, " ns/update") -println(" Median: ", median(t_mutable_update.times) / n_updates, " ns/update") -println(" Allocs: ", t_mutable_update.allocs) -println(" Memory: ", t_mutable_update.memory, " bytes") - -t_immutable_update = @benchmark update_temperature_immutable($immutable_elem2, $n_updates) -println("Immutable (copy): ", minimum(t_immutable_update.times) / n_updates, " ns/update") -println(" Median: ", median(t_immutable_update.times) / n_updates, " ns/update") -println(" Allocs: ", t_immutable_update.allocs) -println(" Memory: ", t_immutable_update.memory, " bytes") - -println() -println("Note: Immutable creates new structs, but compiler optimizes stack allocation") -println() - -# ============================================================================== -# BENCHMARK 3: Realistic Assembly Loop (FEM Workload) -# ============================================================================== - -println("BENCHMARK 3: Realistic FEM Assembly Loop") -println("-"^80) -println("Simulating element stiffness matrix assembly:") -println(" - Read E, ν from element fields") -println(" - Compute 10 Gauss integration points") -println(" - Each point: read fields, compute B matrix, add to K") -println() - -# Simplified assembly kernel -function assemble_stiffness_mutable(elem) - E = get_field(elem, :E) - ν = get_field(elem, :ν) - - # Compute material matrix (simplified) - λ = E * ν / ((1 + ν) * (1 - 2ν)) - μ = E / (2 * (1 + ν)) - - K = 0.0 - # Simulate 10 integration points - for ip in 1:10 - # Simulate field reads at integration point - E_ip = get_field(elem, :E) - ν_ip = get_field(elem, :ν) - - # Simplified stiffness contribution - detJ = 1.0 + 0.1 * ip # Fake Jacobian - weight = 0.1 - K += (λ + 2μ) * detJ * weight - end - - return K -end - -function assemble_stiffness_immutable(elem) - E = get_field(elem, :E) - ν = get_field(elem, :ν) - - # Compute material matrix (simplified) - λ = E * ν / ((1 + ν) * (1 - 2ν)) - μ = E / (2 * (1 + ν)) - - K = 0.0 - # Simulate 10 integration points - for ip in 1:10 - # Simulate field reads at integration point - E_ip = get_field(elem, :E) - ν_ip = get_field(elem, :ν) - - # Simplified stiffness contribution - detJ = 1.0 + 0.1 * ip # Fake Jacobian - weight = 0.1 - K += (λ + 2μ) * detJ * weight - end - - return K -end - -t_assembly_mutable = @benchmark assemble_stiffness_mutable($mutable_elem) -t_assembly_immutable = @benchmark assemble_stiffness_immutable($immutable_elem) - -println("Assembly time per element:") -println() -println("Mutable (Dict): ", minimum(t_assembly_mutable.times), " ns") -println(" Median: ", median(t_assembly_mutable.times), " ns") -println(" Allocs: ", t_assembly_mutable.allocs) - -println("Immutable (Tuple): ", minimum(t_assembly_immutable.times), " ns") -println(" Median: ", median(t_assembly_immutable.times), " ns") -println(" Allocs: ", t_assembly_immutable.allocs) - -speedup_assembly = minimum(t_assembly_mutable.times) / minimum(t_assembly_immutable.times) -println() -println("Speedup: ", round(speedup_assembly, digits=1), "x faster") -println() - -# ============================================================================== -# BENCHMARK 4: Large-Scale Mesh (1000 elements) -# ============================================================================== - -println("BENCHMARK 4: Large-Scale Assembly (1000 elements)") -println("-"^80) - -n_elements = 1000 - -# Create mesh -mutable_mesh = [ - begin - elem = MutableElement(UInt.(1:10) .+ UInt(i * 10)) - update_field!(elem, :E, 210e9) - update_field!(elem, :ν, 0.3) - elem - end for i in 1:n_elements -] - -immutable_mesh = [ - begin - conn = ntuple(j -> UInt(j + i * 10), 10) - ImmutableElement(conn, (E=210e9, ν=0.3)) - end for i in 1:n_elements -] - -function assemble_mesh_mutable(mesh) - K_total = 0.0 - for elem in mesh - K_total += assemble_stiffness_mutable(elem) - end - return K_total -end - -function assemble_mesh_immutable(mesh) - K_total = 0.0 - for elem in mesh - K_total += assemble_stiffness_immutable(elem) - end - return K_total -end - -println("Assembling $n_elements elements:") -println() - -t_mesh_mutable = @benchmark assemble_mesh_mutable($mutable_mesh) -println("Mutable (Dict): ", minimum(t_mesh_mutable.times) / 1e6, " ms") -println(" Median: ", median(t_mesh_mutable.times) / 1e6, " ms") -println(" Allocs: ", t_mesh_mutable.allocs) -println(" Memory: ", t_mesh_mutable.memory / 1024, " KB") - -t_mesh_immutable = @benchmark assemble_mesh_immutable($immutable_mesh) -println("Immutable (Tuple): ", minimum(t_mesh_immutable.times) / 1e6, " ms") -println(" Median: ", median(t_mesh_immutable.times) / 1e6, " ms") -println(" Allocs: ", t_mesh_immutable.allocs) -println(" Memory: ", t_mesh_immutable.memory / 1024, " KB") - -speedup_mesh = minimum(t_mesh_mutable.times) / minimum(t_mesh_immutable.times) -println() -println("Speedup: ", round(speedup_mesh, digits=1), "x faster") -println() - -# ============================================================================== -# SUMMARY -# ============================================================================== - -println("="^80) -println("SUMMARY") -println("="^80) -println() -println("Key findings:") -println() -println("1. Field Access:") -println(" - Type-stable (immutable) is ", round(speedup_read, digits=1), "x faster") -println(" - Dict lookup: ~100-200ns overhead per access") -println(" - NamedTuple: ~1ns (inlined, zero overhead)") -println() -println("2. Assembly Performance:") -println(" - Single element: ", round(speedup_assembly, digits=1), "x faster") -println(" - Large mesh: ", round(speedup_mesh, digits=1), "x faster") -println() -println("3. Memory:") -println(" - Immutable elements: no allocations in hot path") -println(" - Mutable elements: Dict overhead + dynamic dispatch") -println() -println("4. GPU/HPC Compatibility:") -println(" - Immutable: ✓ All bits types, can transfer to GPU") -println(" - Mutable: ✗ Pointers, heap allocations, no GPU support") -println() -println("CONCLUSION:") -println("-"^80) -println("Despite appearing counterintuitive, IMMUTABLE elements with type-stable") -println("fields are SIGNIFICANTLY FASTER for FEM assembly. The key insight:") -println() -println(" • Dict lookup cost dominates in tight loops (millions of accesses)") -println(" • Type stability enables compiler optimizations (inlining, SIMD)") -println(" • Immutability enables GPU/HPC parallelization (no race conditions)") -println(" • Modern compilers optimize away struct copies on stack") -println() -println("For FEM with millions of field accesses per assembly, type stability") -println("is the critical factor. Immutability is a small price for 10-100x speedup.") -println() -println("="^80) diff --git a/benchmarks/field_storage_comparison.jl b/benchmarks/field_storage_comparison.jl deleted file mode 100755 index c7df4c1..0000000 --- a/benchmarks/field_storage_comparison.jl +++ /dev/null @@ -1,334 +0,0 @@ -#!/usr/bin/env julia -# -# Benchmark: Dict{String,Any} vs Type-Stable Field Storage -# -# This benchmark validates the performance claims in: -# docs/book/zero_allocation_fields.md -# -# Expected results: -# - Constant field access: 50× faster, 0 allocations -# - Nodal field access: 50× faster, 0 allocations -# - Interpolation: 16× faster with zero allocations (cached) -# - -using BenchmarkTools -using LinearAlgebra - -# ============================================================================ -# Field Type Definitions (from proposal) -# ============================================================================ - -abstract type AbstractField{T} end - -struct ConstantField{T} <: AbstractField{T} - value::T -end - -struct NodalField{T} <: AbstractField{T} - values::Matrix{T} # N_components × N_nodes -end - -# Accessors -@inline value(f::ConstantField) = f.value -@inline function value(f::NodalField, node_ids::AbstractVector{Int}) - return @view f.values[:, node_ids] -end - -# ============================================================================ -# Mock Element (simplified for benchmarking) -# ============================================================================ - -struct MockElement - connectivity::Vector{Int} -end - -function mock_eval_basis(x::Vector{Float64}) - # Mock basis function values for 8-node element - return [0.1, 0.15, 0.05, 0.1, 0.2, 0.15, 0.15, 0.1] -end - -# ============================================================================ -# Setup: OLD (Dict-based) vs NEW (Typed) -# ============================================================================ - -const OLD_FIELDS = Dict{String,Any}( - "youngs_modulus" => 210e3, - "poissons_ratio" => 0.3, - "displacement" => zeros(3, 8), -) - -const NEW_FIELDS = ( - youngs_modulus=ConstantField(210e3), - poissons_ratio=ConstantField(0.3), - displacement=NodalField(zeros(3, 8)), -) - -# ============================================================================ -# Benchmark 1: Constant Field Access -# ============================================================================ - -println("="^70) -println("Benchmark 1: Constant Field Access") -println("="^70) - -println("\nOLD (Dict{String,Any}):") -old_constant = @benchmark $OLD_FIELDS["youngs_modulus"] -display(old_constant) - -println("\nNEW (ConstantField):") -new_constant = @benchmark value($NEW_FIELDS.youngs_modulus) -display(new_constant) - -old_time_1 = median(old_constant).time -new_time_1 = median(new_constant).time -speedup_1 = old_time_1 / new_time_1 -allocs_old_1 = median(old_constant).allocs -allocs_new_1 = median(new_constant).allocs - -println("\n📊 Results:") -println(" OLD: $(round(old_time_1, digits=1)) ns, $(allocs_old_1) allocations") -println(" NEW: $(round(new_time_1, digits=1)) ns, $(allocs_new_1) allocations") -println(" Speedup: $(round(speedup_1, digits=1))×") -println(" Allocation reduction: $(allocs_old_1 - allocs_new_1)") - -# ============================================================================ -# Benchmark 2: Nodal Field Access -# ============================================================================ - -println("\n" * "="^70) -println("Benchmark 2: Nodal Field Access (4 nodes)") -println("="^70) - -node_ids = [1, 2, 3, 4] - -println("\nOLD (Dict with Array slicing):") -old_nodal = @benchmark $OLD_FIELDS["displacement"][:, $node_ids] -display(old_nodal) - -println("\nNEW (NodalField with @view):") -new_nodal = @benchmark value($NEW_FIELDS.displacement, $node_ids) -display(new_nodal) - -old_time_2 = median(old_nodal).time -new_time_2 = median(new_nodal).time -speedup_2 = old_time_2 / new_time_2 -allocs_old_2 = median(old_nodal).allocs -allocs_new_2 = median(new_nodal).allocs - -println("\n📊 Results:") -println(" OLD: $(round(old_time_2, digits=1)) ns, $(allocs_old_2) allocations") -println(" NEW: $(round(new_time_2, digits=1)) ns, $(allocs_new_2) allocations") -println(" Speedup: $(round(speedup_2, digits=1))×") -println(" Allocation reduction: $(allocs_old_2 - allocs_new_2)") - -# ============================================================================ -# Benchmark 3: Interpolation (Without Cache) -# ============================================================================ - -println("\n" * "="^70) -println("Benchmark 3: Spatial Interpolation (No Cache)") -println("="^70) - -element = MockElement([1, 2, 3, 4, 5, 6, 7, 8]) -x = [0.1, 0.2, 0.3] -N = mock_eval_basis(x) - -function interpolate_old(element, N, fields_dict) - u = fields_dict["displacement"] # Type: Any - result = zeros(3) - for i in 1:length(N) - result .+= N[i] .* u[:, element.connectivity[i]] - end - return result -end - -function interpolate_new(element, N, fields) - u_nodal = value(fields.displacement, element.connectivity) - result = zeros(3) - for i in 1:length(N) - result .+= N[i] .* @view u_nodal[:, i] - end - return result -end - -println("\nOLD (Dict-based):") -old_interp = @benchmark interpolate_old($element, $N, $OLD_FIELDS) -display(old_interp) - -println("\nNEW (Typed fields):") -new_interp = @benchmark interpolate_new($element, $N, $NEW_FIELDS) -display(new_interp) - -old_time_3 = median(old_interp).time -new_time_3 = median(new_interp).time -speedup_3 = old_time_3 / new_time_3 -allocs_old_3 = median(old_interp).allocs -allocs_new_3 = median(new_interp).allocs - -println("\n📊 Results:") -println(" OLD: $(round(old_time_3/1000, digits=1)) μs, $(allocs_old_3) allocations") -println(" NEW: $(round(new_time_3, digits=1)) ns, $(allocs_new_3) allocations") -println(" Speedup: $(round(speedup_3, digits=1))×") -println(" Allocation reduction: $(allocs_old_3 - allocs_new_3)") - -# ============================================================================ -# Benchmark 4: Interpolation (With Cache - Zero Allocation Target) -# ============================================================================ - -println("\n" * "="^70) -println("Benchmark 4: Spatial Interpolation (WITH Cache)") -println("="^70) - -struct InterpolationCache - result::Vector{Float64} -end - -function interpolate_cached!(cache, element, N, fields) - u_nodal = value(fields.displacement, element.connectivity) - fill!(cache.result, 0.0) - for i in eachindex(N) - cache.result .+= N[i] .* @view u_nodal[:, i] - end - return cache.result -end - -cache = InterpolationCache(zeros(3)) - -println("\nNEW (Cached - Zero Allocation Target):") -cached_interp = @benchmark interpolate_cached!($cache, $element, $N, $NEW_FIELDS) -display(cached_interp) - -cached_time = median(cached_interp).time -cached_allocs = median(cached_interp).allocs -speedup_4 = old_time_3 / cached_time - -println("\n📊 Results:") -println(" Cached: $(round(cached_time, digits=1)) ns, $(cached_allocs) allocations") -println(" Speedup vs OLD: $(round(speedup_4, digits=1))×") -println(" Zero allocation target: $(cached_allocs == 0 ? "✅ MET" : "❌ FAILED")") - -# ============================================================================ -# Benchmark 5: Assembly Loop (1000 elements) -# ============================================================================ - -println("\n" * "="^70) -println("Benchmark 5: Assembly Loop (1000 elements)") -println("="^70) - -n_elements = 1000 -elements = [MockElement(collect(1:8)) for _ in 1:n_elements] - -function assemble_old_style(elements, fields_dict) - total = 0.0 - for element in elements - E = fields_dict["youngs_modulus"] # Type-unstable access - ν = fields_dict["poissons_ratio"] - - # Mock stiffness computation - K_local = E * (1 - ν^2) - total += K_local - end - return total -end - -function assemble_new_style(elements, fields) - E = value(fields.youngs_modulus) # Type-stable access (once) - ν = value(fields.poissons_ratio) - - total = 0.0 - for element in elements - # Mock stiffness computation - K_local = E * (1 - ν^2) - total += K_local - end - return total -end - -println("\nOLD (Dict access in loop):") -old_assembly = @benchmark assemble_old_style($elements, $OLD_FIELDS) -display(old_assembly) - -println("\nNEW (Typed fields, hoisted access):") -new_assembly = @benchmark assemble_new_style($elements, $NEW_FIELDS) -display(new_assembly) - -old_time_5 = median(old_assembly).time -new_time_5 = median(new_assembly).time -speedup_5 = old_time_5 / new_time_5 -allocs_old_5 = median(old_assembly).allocs -allocs_new_5 = median(new_assembly).allocs - -println("\n📊 Results:") -println(" OLD: $(round(old_time_5/1000, digits=1)) μs, $(allocs_old_5) allocations") -println(" NEW: $(round(new_time_5/1000, digits=1)) μs, $(allocs_new_5) allocations") -println(" Speedup: $(round(speedup_5, digits=1))×") -println(" Allocation reduction: $(allocs_old_5 - allocs_new_5)") - -# ============================================================================ -# Summary and Validation -# ============================================================================ - -println("\n" * "="^70) -println("SUMMARY - Validation Against Claims") -println("="^70) - -validation_passed = true - -# Claim 1: Constant field access should be ~50× faster, 0 allocations -println("\n1. Constant Field Access:") -println(" Claimed: ~50× faster, 0 allocations") -println(" Actual: $(round(speedup_1, digits=1))× faster, $(allocs_new_1) allocations") -if speedup_1 >= 10 && allocs_new_1 == 0 - println(" Status: ✅ VALIDATED ($(round(speedup_1, digits=1))× > 10× threshold)") -else - println(" Status: ⚠️ PARTIAL (speedup or allocation target not met)") - validation_passed = false -end - -# Claim 2: Nodal field access should be ~50× faster, 0 allocations -println("\n2. Nodal Field Access:") -println(" Claimed: ~50× faster, 0 allocations") -println(" Actual: $(round(speedup_2, digits=1))× faster, $(allocs_new_2) allocations") -if speedup_2 >= 10 && allocs_new_2 == 0 - println(" Status: ✅ VALIDATED ($(round(speedup_2, digits=1))× > 10× threshold)") -else - println(" Status: ⚠️ PARTIAL (speedup or allocation target not met)") - validation_passed = false -end - -# Claim 3: Cached interpolation should be ~16× faster, 0 allocations -println("\n3. Interpolation (Cached):") -println(" Claimed: ~16× faster, 0 allocations") -println(" Actual: $(round(speedup_4, digits=1))× faster, $(cached_allocs) allocations") -if speedup_4 >= 10 && cached_allocs == 0 - println(" Status: ✅ VALIDATED ($(round(speedup_4, digits=1))× > 10× threshold)") -else - println(" Status: ⚠️ PARTIAL (speedup or allocation target not met)") - validation_passed = false -end - -# Claim 4: Assembly should be 10-100× faster -println("\n4. Assembly Loop:") -println(" Claimed: 10-100× faster") -println(" Actual: $(round(speedup_5, digits=1))× faster") -if speedup_5 >= 10 - println(" Status: ✅ VALIDATED ($(round(speedup_5, digits=1))× > 10× threshold)") -else - println(" Status: ⚠️ PARTIAL (speedup target not met)") - validation_passed = false -end - -println("\n" * "="^70) -if validation_passed - println("✅ ALL PERFORMANCE CLAIMS VALIDATED") -else - println("⚠️ SOME CLAIMS NOT FULLY VALIDATED (but likely still significant improvement)") -end -println("="^70) - -println("\nKey Insights:") -println(" • Type stability (NamedTuple) eliminates runtime dispatch") -println(" • Zero allocations achieved with @view and pre-allocated caches") -println(" • Hoisting invariant access out of loops provides massive speedup") -println(" • The combination gives 10-100× speedup in realistic scenarios") -println("\n✅ This validates the NamedTuple + typed fields design for v1.0") diff --git a/benchmarks/gpu_state_management_benchmark.jl b/benchmarks/gpu_state_management_benchmark.jl deleted file mode 100644 index 4eac1a6..0000000 --- a/benchmarks/gpu_state_management_benchmark.jl +++ /dev/null @@ -1,468 +0,0 @@ -""" -GPU State Management Benchmark - -Demonstrates Strategy 1 (immutable elements) vs Strategy 2 (separate mutable state) -and validates memory coalescing patterns on actual GPU hardware. - -Run with: - julia --project=. benchmarks/gpu_state_management_benchmark.jl -""" - -using CUDA -using Tensors -using BenchmarkTools -using Printf - -# Check GPU availability -if !CUDA.functional() - error("CUDA not available! This benchmark requires a CUDA-capable GPU.") -end - -println("GPU Device: $(CUDA.device())") -println("GPU Memory: $(CUDA.name(CUDA.device())) - $(round(CUDA.total_memory()/1e9, digits=1)) GB") -println() - -# ============================================================================ -# Strategy 1: Immutable Elements (Array of Structs - AoS) -# ============================================================================ - -""" -Strategy 1: Element contains its own state (immutable). -Update creates new element (allocation + copy). -""" -struct Element_Strategy1{T} - connectivity::NTuple{8,Int32} - material_id::Int32 - # State (plastic strain, hardening) - ε_p::SymmetricTensor{2,3,T,6} - α::T -end - -""" -Update state for Strategy 1 (returns new element - allocation!). -""" -function update_element_strategy1(elem::Element_Strategy1{T}, Δε_p, Δα) where T - return Element_Strategy1( - elem.connectivity, - elem.material_id, - elem.ε_p + Δε_p, - elem.α + Δα - ) -end - -""" -CPU kernel: Update all elements (Strategy 1). -""" -function update_elements_strategy1_cpu!( - elements::Vector{Element_Strategy1{T}}, - strain_increments::Vector{SymmetricTensor{2,3,T,6}}, - hardening_increments::Vector{T} -) where T - n = length(elements) - for i in 1:n - elements[i] = update_element_strategy1( - elements[i], - strain_increments[i], - hardening_increments[i] - ) - end -end - -""" -GPU kernel: Update all elements (Strategy 1). - -Problem: Each thread accesses scattered memory (pointer chasing). -""" -function update_elements_strategy1_kernel!( - elements::CuDeviceVector{Element_Strategy1{T}}, - strain_increments::CuDeviceVector{SymmetricTensor{2,3,T,6}}, - hardening_increments::CuDeviceVector{T} -) where T - i = (blockIdx().x - 1) * blockDim().x + threadIdx().x - - if i <= length(elements) - elem = elements[i] # Non-coalesced read! - Δε_p = strain_increments[i] - Δα = hardening_increments[i] - - # Update (creates new element - allocation on GPU!) - new_elem = Element_Strategy1( - elem.connectivity, - elem.material_id, - elem.ε_p + Δε_p, - elem.α + Δα - ) - - elements[i] = new_elem # Non-coalesced write! - end - - return nothing -end - -function update_elements_strategy1_gpu!( - elements::CuVector{Element_Strategy1{T}}, - strain_increments::CuVector{SymmetricTensor{2,3,T,6}}, - hardening_increments::CuVector{T} -) where T - n = length(elements) - threads = 256 - blocks = cld(n, threads) - - @cuda threads = threads blocks = blocks update_elements_strategy1_kernel!( - elements, strain_increments, hardening_increments - ) - CUDA.synchronize() -end - -# ============================================================================ -# Strategy 2: Separate Mutable State (Structure of Arrays - SoA) -# ============================================================================ - -""" -Strategy 2: Geometry is immutable, state is separate and mutable. -""" -struct ElementGeometry - connectivity::NTuple{8,Int32} - material_id::Int32 -end - -""" -Mutable state storage (flat arrays for GPU coalescing). -""" -mutable struct AssemblyState{T,VecT} - # Plastic strain (Voigt notation: 6 components per state) - ε_p_flat::VecT # [N_states × 6] - - # Hardening variable (1 component per state) - α_flat::VecT # [N_states] - - n_states::Int -end - -function AssemblyState{T}(n_states::Int) where T - return AssemblyState{T,Vector{T}}( - zeros(T, n_states * 6), - zeros(T, n_states), - n_states - ) -end - -""" -CPU kernel: Update state (Strategy 2 - in-place!). -""" -function update_state_strategy2_cpu!( - state::AssemblyState{T,Vector{T}}, - strain_increments::Vector{SymmetricTensor{2,3,T,6}}, - hardening_increments::Vector{T} -) where T - n = state.n_states - - for i in 1:n - # Flat indexing (cache-friendly!) - offset = (i - 1) * 6 - - Δε_p = strain_increments[i] - - # Update in-place (no allocation!) - state.ε_p_flat[offset+1] += Δε_p[1, 1] - state.ε_p_flat[offset+2] += Δε_p[2, 2] - state.ε_p_flat[offset+3] += Δε_p[3, 3] - state.ε_p_flat[offset+4] += Δε_p[1, 2] - state.ε_p_flat[offset+5] += Δε_p[1, 3] - state.ε_p_flat[offset+6] += Δε_p[2, 3] - - state.α_flat[i] += hardening_increments[i] - end -end - -""" -GPU kernel: Update state (Strategy 2). - -Advantage: Coalesced memory access! -- Thread 0 accesses state.ε_p_flat[0:5] -- Thread 1 accesses state.ε_p_flat[6:11] -- Thread 2 accesses state.ε_p_flat[12:17] -All consecutive in memory! -""" -function update_state_strategy2_kernel!( - ε_p_flat::CuDeviceVector{T}, - α_flat::CuDeviceVector{T}, - strain_increments_flat::CuDeviceVector{T}, - hardening_increments::CuDeviceVector{T}, - n_states::Int -) where T - i = (blockIdx().x - 1) * blockDim().x + threadIdx().x - - if i <= n_states - # Flat indexing (coalesced access!) - offset = (i - 1) * 6 - strain_offset = (i - 1) * 6 - - # Update plastic strain (6 consecutive reads/writes) - ε_p_flat[offset+1] += strain_increments_flat[strain_offset+1] - ε_p_flat[offset+2] += strain_increments_flat[strain_offset+2] - ε_p_flat[offset+3] += strain_increments_flat[strain_offset+3] - ε_p_flat[offset+4] += strain_increments_flat[strain_offset+4] - ε_p_flat[offset+5] += strain_increments_flat[strain_offset+5] - ε_p_flat[offset+6] += strain_increments_flat[strain_offset+6] - - # Update hardening (1 read/write) - α_flat[i] += hardening_increments[i] - end - - return nothing -end - -function update_state_strategy2_gpu!( - state_gpu::AssemblyState{T,<:CuVector{T}}, - strain_increments_flat::CuVector{T}, - hardening_increments::CuVector{T} -) where T - n = state_gpu.n_states - threads = 256 - blocks = cld(n, threads) - - @cuda threads = threads blocks = blocks update_state_strategy2_kernel!( - state_gpu.ε_p_flat, - state_gpu.α_flat, - strain_increments_flat, - hardening_increments, - n - ) - CUDA.synchronize() -end - -# ============================================================================ -# Benchmark Setup -# ============================================================================ - -function setup_benchmark(n_elements::Int) - T = Float64 - - # Create random strain increments - Δε_p_tensors = [SymmetricTensor{2,3}(( - rand(T) * 1e-5, - rand(T) * 1e-5, - rand(T) * 1e-5, - rand(T) * 1e-6, - rand(T) * 1e-6, - rand(T) * 1e-6 - )) for _ in 1:n_elements] - - Δα = rand(T, n_elements) .* 1e-5 - - # Strategy 1: Array of immutable elements - elements_s1 = [Element_Strategy1( - ntuple(j -> Int32(j), 8), - Int32(1), - zero(SymmetricTensor{2,3,T}), - zero(T) - ) for _ in 1:n_elements] - - # Strategy 2: Separate geometry and state - geometry_s2 = [ElementGeometry( - ntuple(j -> Int32(j), 8), - Int32(1) - ) for _ in 1:n_elements] - - state_s2 = AssemblyState{T}(n_elements) - - return Δε_p_tensors, Δα, elements_s1, geometry_s2, state_s2 -end - -# ============================================================================ -# CPU Benchmarks -# ============================================================================ - -function benchmark_cpu(n_elements::Int) - println("="^70) - println("CPU Benchmark: $n_elements elements") - println("="^70) - - Δε_p, Δα, elements_s1, geometry_s2, state_s2 = setup_benchmark(n_elements) - - # Strategy 1: Update immutable elements - println("\n📊 Strategy 1 (Immutable Elements - AoS):") - elements_s1_copy = copy(elements_s1) - t1 = @belapsed update_elements_strategy1_cpu!( - $elements_s1_copy, $Δε_p, $Δα - ) samples = 10 - - println(" Time: $(round(t1 * 1000, digits=3)) ms") - println(" Bandwidth: N/A (CPU cache)") - - # Check allocations - allocs = @allocated update_elements_strategy1_cpu!(elements_s1_copy, Δε_p, Δα) - println(" Allocations: $(allocs) bytes ($(allocs ÷ n_elements) bytes/element)") - - # Strategy 2: Update mutable state - println("\n📊 Strategy 2 (Separate State - SoA):") - state_s2_copy = deepcopy(state_s2) - t2 = @belapsed update_state_strategy2_cpu!( - $state_s2_copy, $Δε_p, $Δα - ) samples = 10 - - println(" Time: $(round(t2 * 1000, digits=3)) ms") - println(" Bandwidth: N/A (CPU cache)") - - # Check allocations - allocs2 = @allocated update_state_strategy2_cpu!(state_s2_copy, Δε_p, Δα) - println(" Allocations: $(allocs2) bytes") - - # Speedup - speedup = t1 / t2 - println("\n✅ CPU Speedup (Strategy 2 / Strategy 1): $(round(speedup, digits=2))×") - - println() -end - -# ============================================================================ -# GPU Benchmarks -# ============================================================================ - -function benchmark_gpu(n_elements::Int) - println("="^70) - println("GPU Benchmark: $n_elements elements") - println("="^70) - - T = Float64 - Δε_p, Δα, elements_s1, geometry_s2, state_s2 = setup_benchmark(n_elements) - - # ======================================================================== - # Strategy 1: GPU - # ======================================================================== - println("\n📊 Strategy 1 (Immutable Elements - AoS on GPU):") - - # Transfer to GPU - elements_s1_gpu = CuArray(elements_s1) - Δε_p_gpu = CuArray(Δε_p) - Δα_gpu = CuArray(Δα) - - # Warmup - update_elements_strategy1_gpu!(elements_s1_gpu, Δε_p_gpu, Δα_gpu) - - # Benchmark - t1_gpu = CUDA.@elapsed begin - update_elements_strategy1_gpu!(elements_s1_gpu, Δε_p_gpu, Δα_gpu) - end - - println(" Time: $(round(t1_gpu * 1000, digits=3)) ms") - - # Estimate bandwidth (reading + writing entire element) - bytes_per_elem = sizeof(Element_Strategy1{T}) - total_bytes = bytes_per_elem * n_elements * 2 # Read + write - bandwidth_s1 = total_bytes / t1_gpu / 1e9 - println(" Bandwidth: $(round(bandwidth_s1, digits=1)) GB/s") - - # ======================================================================== - # Strategy 2: GPU - # ======================================================================== - println("\n📊 Strategy 2 (Separate State - SoA on GPU):") - - # Transfer to GPU (flat arrays!) - state_s2_gpu = AssemblyState{T,CuVector{T}}( - CuArray(state_s2.ε_p_flat), - CuArray(state_s2.α_flat), - state_s2.n_states - ) - - # Flatten strain increments for GPU - Δε_p_flat = zeros(T, n_elements * 6) - for i in 1:n_elements - offset = (i - 1) * 6 - ε = Δε_p[i] - Δε_p_flat[offset+1] = ε[1, 1] - Δε_p_flat[offset+2] = ε[2, 2] - Δε_p_flat[offset+3] = ε[3, 3] - Δε_p_flat[offset+4] = ε[1, 2] - Δε_p_flat[offset+5] = ε[1, 3] - Δε_p_flat[offset+6] = ε[2, 3] - end - - Δε_p_flat_gpu = CuArray(Δε_p_flat) - Δα_flat_gpu = CuArray(Δα) - - # Warmup - update_state_strategy2_gpu!(state_s2_gpu, Δε_p_flat_gpu, Δα_flat_gpu) - - # Benchmark - t2_gpu = CUDA.@elapsed begin - update_state_strategy2_gpu!(state_s2_gpu, Δε_p_flat_gpu, Δα_flat_gpu) - end - - println(" Time: $(round(t2_gpu * 1000, digits=3)) ms") - - # Estimate bandwidth (only state data, not geometry!) - bytes_per_state = 6 * sizeof(T) + sizeof(T) # 6 strain + 1 hardening - total_bytes_s2 = bytes_per_state * n_elements * 2 # Read + write - bandwidth_s2 = total_bytes_s2 / t2_gpu / 1e9 - println(" Bandwidth: $(round(bandwidth_s2, digits=1)) GB/s") - - # ======================================================================== - # Comparison - # ======================================================================== - speedup = t1_gpu / t2_gpu - bandwidth_ratio = bandwidth_s2 / bandwidth_s1 - - println("\n✅ GPU Speedup (Strategy 2 / Strategy 1): $(round(speedup, digits=2))×") - println("✅ Bandwidth Improvement: $(round(bandwidth_ratio, digits=2))×") - println(" Strategy 1: $(round(bandwidth_s1, digits=1)) GB/s (non-coalesced)") - println(" Strategy 2: $(round(bandwidth_s2, digits=1)) GB/s (coalesced)") - - # Theoretical peak (example: RTX 4090 = ~1000 GB/s) - gpu_name = CUDA.name(CUDA.device()) - println("\n💡 GPU Memory Bandwidth:") - println(" Achieved: $(round(bandwidth_s2, digits=1)) GB/s") - println(" Device: $gpu_name") - - println() - - # Cleanup - CUDA.unsafe_free!(elements_s1_gpu) - CUDA.unsafe_free!(Δε_p_gpu) - CUDA.unsafe_free!(Δα_gpu) - CUDA.unsafe_free!(state_s2_gpu.ε_p_flat) - CUDA.unsafe_free!(state_s2_gpu.α_flat) - CUDA.unsafe_free!(Δε_p_flat_gpu) - CUDA.unsafe_free!(Δα_flat_gpu) -end - -# ============================================================================ -# Main Benchmark -# ============================================================================ - -function main() - println("\n" * "=" * 70) - println("GPU State Management Strategy Benchmark") - println("=" * 70) - println() - - # Test sizes - sizes = [10_000, 100_000, 1_000_000] - - for n in sizes - # CPU benchmark - benchmark_cpu(n) - - # GPU benchmark - benchmark_gpu(n) - - println() - end - - println("="^70) - println("Benchmark Complete!") - println("="^70) - println() - println("Key Findings:") - println(" - Strategy 1 (AoS): Non-coalesced memory access on GPU") - println(" - Strategy 2 (SoA): Coalesced memory access on GPU") - println(" - Strategy 2 achieves 5-10× higher memory bandwidth") - println(" - Strategy 2 has zero allocations (in-place update)") - println() -end - -# Run benchmark -if abspath(PROGRAM_FILE) == @__FILE__ - main() -end diff --git a/benchmarks/integration_points_benchmark.jl b/benchmarks/integration_points_benchmark.jl deleted file mode 100644 index 3898e51..0000000 --- a/benchmarks/integration_points_benchmark.jl +++ /dev/null @@ -1,301 +0,0 @@ -# Benchmark: Integration Point Access Patterns -# ============================================ -# -# This benchmark compares different approaches to storing and accessing -# integration points in finite element assembly loops. -# -# Key Question: What's the fastest way to get integration points? -# -# Approaches tested: -# 1. OLD: Runtime dispatch + mutable struct with Dict (type-unstable) -# 2. NEW Option A: Compile-time function (like eval_basis!) -# 3. NEW Option B: Store in element as NTuple -# 4. NEW Option C: Hybrid (compile-time generation + caching) - -using BenchmarkTools -using Tensors -using StaticArrays - -# ============================================================================ -# OLD APPROACH: Runtime dispatch with mutable struct -# ============================================================================ - -struct OldIP - id::UInt - weight::Float64 - coords::Tuple{Vararg{Float64}} - fields::Dict{String,Any} # Type-unstable! -end - -function get_integration_points_old(::Type{Val{:Tri3}}) - # Simulate runtime dispatch to get IPs - return [ - OldIP(UInt(1), 0.5, (1 / 3, 1 / 3), Dict{String,Any}()), - ] -end - -function assembly_loop_old() - sum_val = 0.0 - for _ in 1:1000 # Simulate 1000 elements - ips = get_integration_points_old(Val{:Tri3}) - for ip in ips - w = ip.weight - xi, eta = ip.coords - # Simulate some computation - sum_val += w * (xi + eta) - end - end - return sum_val -end - -# ============================================================================ -# NEW OPTION A: Compile-time function (zero allocation) -# ============================================================================ - -""" -Return integration points as tuple at compile time. -Similar to eval_basis! - zero allocation, fully inlined. -""" -@inline function get_integration_points!(::Type{Val{:Tri3_Gauss1}}) - # Return as tuple of (weight, coords) pairs - return ( - (0.5, (1 / 3, 1 / 3)), - ) -end - -@inline function get_integration_points!(::Type{Val{:Tet4_Gauss1}}) - return ( - (1 / 24, (0.25, 0.25, 0.25)), - ) -end - -function assembly_loop_option_a() - sum_val = 0.0 - for _ in 1:1000 - ips = get_integration_points!(Val{:Tri3_Gauss1}) - for (w, (xi, eta)) in ips - sum_val += w * (xi + eta) - end - end - return sum_val -end - -# ============================================================================ -# NEW OPTION B: Store in element as NTuple (what we have now) -# ============================================================================ - -struct IntegrationPoint{D} - ξ::NTuple{D,Float64} - weight::Float64 -end - -struct MockElement{NIP} - ips::NTuple{NIP,IntegrationPoint{2}} -end - -function create_element_b() - ips = ( - IntegrationPoint((1 / 3, 1 / 3), 0.5), - ) - return MockElement(ips) -end - -function assembly_loop_option_b() - elements = [create_element_b() for _ in 1:1000] - sum_val = 0.0 - for element in elements - for ip in element.ips - w = ip.weight - xi, eta = ip.ξ - sum_val += w * (xi + eta) - end - end - return sum_val -end - -# ============================================================================ -# NEW OPTION C: Compile-time with Tensors.jl Vec (recommended for FEM) -# ============================================================================ - -""" -Return integration points with Vec{D} coordinates (Tensors.jl). -This matches the golden standard from nodal assembly demos. -""" -@inline function get_integration_points_vec!(::Type{Val{:Tri3_Gauss1}}) - return ( - (0.5, Vec{2}((1 / 3, 1 / 3))), - ) -end - -@inline function get_integration_points_vec!(::Type{Val{:Tet4_Gauss1}}) - return ( - (1 / 24, Vec{3}((0.25, 0.25, 0.25))), - ) -end - -function assembly_loop_option_c() - sum_val = 0.0 - for _ in 1:1000 - ips = get_integration_points_vec!(Val{:Tri3_Gauss1}) - for (w, xi) in ips - # Vec arithmetic is optimized by Tensors.jl - sum_val += w * sum(xi) - end - end - return sum_val -end - -# ============================================================================ -# NEW OPTION D: Pre-generated global constants (ultimate zero-cost) -# ============================================================================ - -const TRI3_GAUSS1_IPS = ( - (0.5, Vec{2}((1 / 3, 1 / 3))), -) - -const TET4_GAUSS1_IPS = ( - (1 / 24, Vec{3}((0.25, 0.25, 0.25))), -) - -function assembly_loop_option_d() - sum_val = 0.0 - for _ in 1:1000 - for (w, xi) in TRI3_GAUSS1_IPS - sum_val += w * sum(xi) - end - end - return sum_val -end - -# ============================================================================ -# OPTION E: Hybrid - Function returns pre-computed constant -# ============================================================================ - -@inline get_ips_tri3_gauss1() = TRI3_GAUSS1_IPS -@inline get_ips_tet4_gauss1() = TET4_GAUSS1_IPS - -function assembly_loop_option_e() - sum_val = 0.0 - for _ in 1:1000 - for (w, xi) in get_ips_tri3_gauss1() - sum_val += w * sum(xi) - end - end - return sum_val -end - -# ============================================================================ -# Run Benchmarks -# ============================================================================ - -println("="^80) -println("Integration Point Access Pattern Benchmark") -println("="^80) -println() - -println("OLD APPROACH: Runtime dispatch + mutable struct with Dict") -println("-"^80) -@btime assembly_loop_old() -println() - -println("OPTION A: Compile-time function returning tuples") -println("-"^80) -@btime assembly_loop_option_a() -println() - -println("OPTION B: Store in element as NTuple (current approach)") -println("-"^80) -@btime assembly_loop_option_b() -println() - -println("OPTION C: Compile-time function with Vec{D} (Tensors.jl)") -println("-"^80) -@btime assembly_loop_option_c() -println() - -println("OPTION D: Pre-generated global constants") -println("-"^80) -@btime assembly_loop_option_d() -println() - -println("OPTION E: Function returning pre-computed constant") -println("-"^80) -@btime assembly_loop_option_e() -println() - -# ============================================================================ -# Realistic FEM Assembly Benchmark -# ============================================================================ - -println() -println("="^80) -println("REALISTIC FEM ASSEMBLY COMPARISON") -println("="^80) -println() - -# Simulate realistic element stiffness computation -function compute_element_stiffness_old(element_type::Type{Val{:Tri3}}) - K_elem = zeros(6, 6) - ips = get_integration_points_old(element_type) - for ip in ips - w = ip.weight - xi, eta = ip.coords - # Simulate shape function evaluation and stiffness computation - N1 = 1 - xi - eta - N2 = xi - N3 = eta - # Accumulate (simplified) - K_elem[1, 1] += w * (N1^2) - end - return K_elem[1, 1] -end - -function compute_element_stiffness_new() - K_elem = 0.0 - for (w, xi) in get_integration_points_vec!(Val{:Tri3_Gauss1}) - # Vec arithmetic - xi_val = xi[1] - eta_val = xi[2] - N1 = 1 - xi_val - eta_val - K_elem += w * (N1^2) - end - return K_elem -end - -println("OLD: Realistic element stiffness assembly") -@btime begin - sum_val = 0.0 - for _ in 1:1000 - sum_val += compute_element_stiffness_old(Val{:Tri3}) - end - sum_val -end - -println() -println("NEW: Realistic element stiffness assembly") -@btime begin - sum_val = 0.0 - for _ in 1:1000 - sum_val += compute_element_stiffness_new() - end - sum_val -end - -println() -println("="^80) -println("SUMMARY") -println("="^80) -println() -println("Expected ranking (fastest to slowest):") -println("1. Option D/E: Pre-computed constants (ultimate zero-cost)") -println("2. Option C: Compile-time with Vec{D} (recommended for FEM)") -println("3. Option A: Compile-time with plain tuples") -println("4. Option B: Stored in element NTuple (slight overhead)") -println("5. OLD: Runtime dispatch + Dict (type-unstable)") -println() -println("RECOMMENDATION:") -println(" Use Option C or D/E for integration points:") -println(" - Compile-time generation like eval_basis!") -println(" - Return as Tuple of (weight, Vec{D}) pairs") -println(" - Zero allocation, fully inlined") -println(" - Matches golden standard architecture") diff --git a/benchmarks/linear_elastic_analysis.jl b/benchmarks/linear_elastic_analysis.jl deleted file mode 100644 index 29ed274..0000000 --- a/benchmarks/linear_elastic_analysis.jl +++ /dev/null @@ -1,272 +0,0 @@ -""" -Performance analysis for LinearElastic material model. - -Analyzes: -1. Execution time (@btime) -2. Memory allocations (@allocated) -3. Type stability (@code_warntype) -4. LLVM IR optimization (code_llvm) -5. Native assembly (code_native) -""" - -using BenchmarkTools -using Tensors -using InteractiveUtils - -# Load implementation -include("../src/materials/linear_elastic.jl") - -println("="^80) -println("LINEAR ELASTIC MATERIAL - PERFORMANCE ANALYSIS") -println("="^80) -println() - -# Test material (steel) -steel = LinearElastic(E=200e9, ν=0.3) - -# Test strain (uniaxial extension) -ε = SymmetricTensor{2,3}((0.001, 0.0, 0.0, 0.0, 0.0, 0.0)) - -println("Material: Steel (E = 200 GPa, ν = 0.3)") -println("Strain: Uniaxial extension (ε₁₁ = 0.001)") -println() - -# ============================================================================ -# BENCHMARK 1: Execution Time -# ============================================================================ -println("BENCHMARK 1: Execution Time") -println("-"^80) - -# Warmup -compute_stress(steel, ε, nothing, 0.0) - -# Benchmark -println("Running @btime compute_stress(steel, ε, nothing, 0.0)...") -t = @benchmark compute_stress($steel, $ε, nothing, 0.0) -println() -display(t) -println() -println() - -# ============================================================================ -# BENCHMARK 2: Memory Allocations -# ============================================================================ -println("BENCHMARK 2: Memory Allocations") -println("-"^80) - -# First call to compile -compute_stress(steel, ε, nothing, 0.0) - -# Check allocations -allocs = @allocated compute_stress(steel, ε, nothing, 0.0) -println("Allocations: $allocs bytes") - -if allocs == 0 - println("✅ ZERO ALLOCATIONS (stack-only computation)") -else - println("⚠️ WARNING: Non-zero allocations detected!") -end -println() -println() - -# ============================================================================ -# BENCHMARK 3: Type Stability -# ============================================================================ -println("BENCHMARK 3: Type Stability") -println("-"^80) - -println("Running @code_warntype compute_stress(steel, ε, nothing, 0.0)...") -println() -@code_warntype compute_stress(steel, ε, nothing, 0.0) -println() -println() - -# ============================================================================ -# BENCHMARK 4: LLVM IR Analysis -# ============================================================================ -println("BENCHMARK 4: LLVM IR Analysis") -println("-"^80) - -println("Running @code_llvm compute_stress(steel, ε, nothing, 0.0)...") -println() -@code_llvm compute_stress(steel, ε, nothing, 0.0) -println() -println() - -# ============================================================================ -# BENCHMARK 5: Native Assembly -# ============================================================================ -println("BENCHMARK 5: Native Assembly") -println("-"^80) - -println("Running @code_native compute_stress(steel, ε, nothing, 0.0)...") -println() -@code_native compute_stress(steel, ε, nothing, 0.0) -println() -println() - -# ============================================================================ -# LLVM IR INSPECTION (Detailed Analysis) -# ============================================================================ -println("LLVM IR INSPECTION") -println("-"^80) - -# Get LLVM IR as string -llvm_ir = sprint(io -> code_llvm(io, compute_stress, typeof.((steel, ε, nothing, 0.0)))) - -# Count key operations -n_fadd = count(r"fadd", llvm_ir) -n_fmul = count(r"fmul", llvm_ir) -n_load = count(r"load", llvm_ir) -n_store = count(r"store", llvm_ir) -n_call = count(r"call", llvm_ir) -n_alloca = count(r"alloca", llvm_ir) - -# Count vector operations (SIMD) -n_vector_ops = count(r"<\d+ x ", llvm_ir) -n_shufflevector = count(r"shufflevector", llvm_ir) -n_insertelement = count(r"insertelement", llvm_ir) -n_extractelement = count(r"extractelement", llvm_ir) - -println("LLVM Operations Count:") -println(" Floating-point additions: $n_fadd") -println(" Floating-point multiplications: $n_fmul") -println(" Memory loads: $n_load") -println(" Memory stores: $n_store") -println(" Function calls: $n_call") -println(" Stack allocations (alloca): $n_alloca") -println() -println("SIMD Vectorization:") -println(" Vector operations: $n_vector_ops") -println(" Shuffle operations: $n_shufflevector") -println(" Insert element operations: $n_insertelement") -println(" Extract element operations: $n_extractelement") -println() - -if n_call == 0 - println("✅ No function calls (fully inlined)") -else - println("⚠️ Contains $n_call function calls (may not be fully inlined)") -end - -if n_alloca == 0 - println("✅ No stack allocations (register-only computation)") -else - println("ℹ️ Contains $n_alloca stack allocations") -end -println() -println() - -# ============================================================================ -# NATIVE ASSEMBLY INSPECTION -# ============================================================================ -println("NATIVE ASSEMBLY INSPECTION") -println("-"^80) - -# Get native assembly as string -native_asm = sprint(io -> code_native(io, compute_stress, typeof.((steel, ε, nothing, 0.0)))) - -# Count SIMD instructions (AVX/SSE) -n_vmul = count(r"vmul", native_asm) -n_vadd = count(r"vadd", native_asm) -n_vsub = count(r"vsub", native_asm) -n_vfma = count(r"vfma", native_asm) -n_vmov = count(r"vmov", native_asm) -n_vbroadcast = count(r"vbroadcast", native_asm) - -# Count total vector instructions -n_total_simd = n_vmul + n_vadd + n_vsub + n_vfma + n_vmov + n_vbroadcast - -println("x86-64 Assembly SIMD Instructions:") -println(" vmulpd/vmulsd: $n_vmul") -println(" vaddpd/vaddsd: $n_vadd") -println(" vsubpd/vsubsd: $n_vsub") -println(" vfmadd/vfmsub: $n_vfma (fused multiply-add)") -println(" vmovapd/vmovsd: $n_vmov") -println(" vbroadcast: $n_vbroadcast") -println(" Total SIMD ops: $n_total_simd") -println() - -if n_vfma > 0 - println("✅ FMA (Fused Multiply-Add) instructions detected (optimal)") -end - -if n_total_simd > 0 - println("✅ SIMD vectorization active (AVX/AVX2)") -else - println("⚠️ No SIMD instructions detected") -end -println() -println() - -# ============================================================================ -# PERFORMANCE SUMMARY -# ============================================================================ -println("="^80) -println("PERFORMANCE SUMMARY") -println("="^80) -println() - -# Extract median time from benchmark -median_time = median(t.times) -median_ns = median_time # Already in nanoseconds - -println("Execution Time:") -println(" Median: $(round(median_ns, digits=2)) ns") -println(" Mean: $(round(mean(t.times), digits=2)) ns") -println(" Minimum: $(round(minimum(t.times), digits=2)) ns") -println() - -println("Memory:") -println(" Allocations: $allocs bytes") -if allocs == 0 - println(" ✅ Zero allocation (confirmed)") -end -println() - -println("Code Quality:") -if n_call == 0 - println(" ✅ Fully inlined (no function calls)") -end -if n_alloca == 0 - println(" ✅ Register-only computation (no stack usage)") -end -if n_total_simd > 0 - println(" ✅ SIMD optimized ($n_total_simd vector instructions)") -end -if n_vfma > 0 - println(" ✅ FMA instructions ($n_vfma fused multiply-adds)") -end -println() - -println("Expected Operations:") -println(" Hooke's law: σ = λ·tr(ε)·I + 2μ·ε") -println(" - 1 trace computation: 3 additions") -println(" - 1 scalar multiplication: 1 multiply") -println(" - 6 scalar multiplications for diagonal") -println(" - 6 additions for final stress") -println(" Tangent: 𝔻 = λ·I⊗I + 2μ·𝕀ˢʸᵐ") -println(" - Constant tensor construction (may be compile-time)") -println() - -# Theoretical lower bound -theoretical_flops = 3 + 1 + 6 + 6 # From expected operations -println("Theoretical minimum FLOPs: ~$theoretical_flops") -println("LLVM FLOPs: $(n_fadd + n_fmul)") -println() - -# Throughput calculation -elements_per_second = 1e9 / median_ns -println("Throughput:") -println(" ~$(round(elements_per_second / 1e6, digits=1)) million stress evaluations/second/core") -println() - -println("✅ Implementation validated as:") -println(" - Zero allocation (confirmed)") -println(" - Type stable") -if n_total_simd > 0 - println(" - SIMD optimized ($n_total_simd vector ops)") -end -println(" - Median execution time: $(round(median_ns, digits=2)) ns") -println() -println("="^80) diff --git a/benchmarks/material_models_benchmark.jl b/benchmarks/material_models_benchmark.jl deleted file mode 100644 index d8e4b14..0000000 --- a/benchmarks/material_models_benchmark.jl +++ /dev/null @@ -1,865 +0,0 @@ -""" -Material Models Performance Benchmark (Extended Version) - -Validates performance claims from docs/book/material_modeling.md: -- Zero allocation claims -- 5-50× speedup over Voigt/Dict approach -- Type stability analysis (especially 'nothing' return for stateless materials) -- Manual vs automatic differentiation for Neo-Hookean -- Material state handling for Newton iterations - -Compares: -1. New approach: Tensors.jl with SymmetricTensor -2. Old approach: Voigt notation with arrays/Dict -3. Neo-Hookean: Manual derivatives vs automatic differentiation - -Materials tested: -- Linear Elastic (Hookean) - Stateless -- Neo-Hookean Hyperelasticity - Stateless (AD and manual versions) -- Perfect Plasticity (von Mises) - Stateful - -Type hierarchy: -- AbstractMaterial - Base type for all materials -- AbstractMaterialState - Base type for material internal state - - NoState - For stateless materials - - PlasticityState - For plasticity with history -""" - -using Tensors -using BenchmarkTools -using LinearAlgebra -using InteractiveUtils # For @code_warntype - -println("="^80) -println("Material Models Performance Benchmark (Extended)") -println("="^80) -println() - -#============================================================================= -TYPE HIERARCHY -=============================================================================# - -""" -Abstract base type for all materials. - -All concrete materials must implement: -- `compute_stress(material, ε, state_old, Δt) -> (σ, 𝔻, state_new)` -- `initial_state(material) -> AbstractMaterialState` -""" -abstract type AbstractMaterial end - -""" -Abstract base type for material internal state. - -Used to track history-dependent variables during Newton iterations: -- Old state (beginning of time step) -- Trial state (current Newton iteration) -- New state (converged solution) -""" -abstract type AbstractMaterialState end - -""" -State for stateless materials (no history dependence). - -Using singleton type instead of `nothing` for type hierarchy consistency. -Performance identical to `nothing` (zero-sized type). -""" -struct NoState <: AbstractMaterialState end - -""" -Initial state for stateless materials. -""" -initial_state(::AbstractMaterial) = NoState() - -#============================================================================= -NEW APPROACH: Tensors.jl Implementation -=============================================================================# - -# --------------------------------------------------------------------------- -# 1. Linear Elastic (Hookean) -# --------------------------------------------------------------------------- - -"""Linear elastic material with Tensors.jl""" -struct LinearElastic <: AbstractMaterial - E::Float64 # Young's modulus [Pa] - ν::Float64 # Poisson's ratio [-] -end - -LinearElastic(; E, ν) = LinearElastic(E, ν) - -λ(mat::LinearElastic) = mat.E * mat.ν / ((1 + mat.ν) * (1 - 2mat.ν)) -μ(mat::LinearElastic) = mat.E / (2(1 + mat.ν)) - -"""Compute stress for linear elastic material.""" -function compute_stress( - material::LinearElastic, - ε::SymmetricTensor{2,3,T}, - state_old::NoState, - Δt::Float64 -) where T - - # Lamé parameters - λ_val = λ(material) - μ_val = μ(material) - - # Identity tensor - I = one(ε) - - # Hooke's law: σ = λ·tr(ε)·I + 2μ·ε - σ = λ_val * tr(ε) * I + 2μ_val * ε - - # Tangent modulus: 𝔻 = λ I⊗I + 2μ 𝕀ˢʸᵐ - 𝕀ˢʸᵐ = one(SymmetricTensor{4,3,T}) # Symmetric 4th order identity - 𝔻 = λ_val * I ⊗ I + 2μ_val * 𝕀ˢʸᵐ - - return σ, 𝔻, NoState() # No state change (stateless) -end# --------------------------------------------------------------------------- -# 2. Neo-Hookean Hyperelasticity (Automatic Differentiation) -# --------------------------------------------------------------------------- - -"""Neo-Hookean hyperelastic material (using automatic differentiation).""" -struct NeoHookeanAD <: AbstractMaterial - μ::Float64 # Shear modulus [Pa] - λ::Float64 # Lamé parameter [Pa] -end - -function NeoHookeanAD(; E, ν) - μ = E / (2(1 + ν)) - λ = E * ν / ((1 + ν) * (1 - 2ν)) - return NeoHookeanAD(μ, λ) -end - -"""Strain energy density for Neo-Hookean model.""" -function strain_energy(material::NeoHookeanAD, C::SymmetricTensor{2,3}) - μ, λ = material.μ, material.λ - - # Invariants - I₁ = tr(C) - J = √(det(C)) - - # Strain energy: ψ = μ/2(I₁ - 3) - μln(J) + λ/2·ln²(J) - ψ = μ / 2 * (I₁ - 3) - μ * log(J) + λ / 2 * log(J)^2 - - return ψ -end - -"""Compute stress for Neo-Hookean material using automatic differentiation.""" -function compute_stress( - material::NeoHookeanAD, - E::SymmetricTensor{2,3,T}, # Green-Lagrange strain - state_old::NoState, - Δt::Float64 -) where T - - # Right Cauchy-Green tensor: C = 2E + I - I = one(E) - C = 2E + I - - # Strain energy function (closure capturing material) - ψ(C_) = strain_energy(material, C_) - - # Automatic differentiation! - 𝔻, S = hessian(ψ, C, :all) # Returns both hessian and gradient! - - # Note: We want S = 2·∂ψ/∂C, 𝔻 = 4·∂²ψ/∂C² - S = 2 * S - 𝔻 = 4 * 𝔻 - - return S, 𝔻, NoState() # No state change (stateless) -end - -# --------------------------------------------------------------------------- -# 3. Neo-Hookean Hyperelasticity (Manual Derivatives) -# --------------------------------------------------------------------------- - -""" -Neo-Hookean hyperelastic material (hand-coded derivatives). - -Strain energy: ψ(C) = μ/2(I₁ - 3) - μln(J) + λ/2·ln²(J) - -Where: -- I₁ = tr(C) - First invariant -- J = √det(C) - Jacobian determinant - -Derivatives (computed by hand): -- S = 2∂ψ/∂C = μ(I - C⁻¹) + λln(J)C⁻¹ -- 𝔻 = 4∂²ψ/∂C² = λ(C⁻¹⊗C⁻¹) + 2(μ - λln(J))∂C⁻¹/∂C - -The second derivative uses the identity: -∂C⁻¹/∂C : X = -C⁻¹:(X:C⁻¹) for any symmetric X -""" -struct NeoHookeanManual <: AbstractMaterial - μ::Float64 # Shear modulus [Pa] - λ::Float64 # Lamé parameter [Pa] -end - -function NeoHookeanManual(; E, ν) - μ = E / (2(1 + ν)) - λ = E * ν / ((1 + ν) * (1 - 2ν)) - return NeoHookeanManual(μ, λ) -end - -"""Compute stress for Neo-Hookean material with manual derivatives.""" -function compute_stress( - material::NeoHookeanManual, - E::SymmetricTensor{2,3,T}, # Green-Lagrange strain - state_old::NoState, - Δt::Float64 -) where T - μ, λ = material.μ, material.λ - - # Right Cauchy-Green tensor: C = 2E + I - I = one(E) - C = 2E + I - - # Invariants - J = √(det(C)) - C_inv = inv(C) - - # Second Piola-Kirchhoff stress: S = μ(I - C⁻¹) + λln(J)C⁻¹ - S = μ * (I - C_inv) + λ * log(J) * C_inv - - # Material tangent: 𝔻 = 4∂²ψ/∂C² - # Term 1: λ(C⁻¹⊗C⁻¹) - 𝔻₁ = λ * (C_inv ⊗ C_inv) - - # Term 2: 2(μ - λln(J))∂C⁻¹/∂C - # The derivative ∂C⁻¹/∂C can be computed as: - # (∂C⁻¹/∂C)ᵢⱼₖₗ = -1/2(C⁻¹ᵢₖC⁻¹ⱼₗ + C⁻¹ᵢₗC⁻¹ⱼₖ) - # - # For SymmetricTensor, we build this fourth-order tensor - # by exploiting the symmetry structure - - # Build the symmetric fourth-order tensor manually - # This is the most expensive part of the computation - 𝕀ˢʸᵐ = one(SymmetricTensor{4,3,T}) - - # For compressible Neo-Hookean, the full tangent is: - # 𝔻 = λ(C⁻¹⊗C⁻¹) - 2(μ - λln(J))(C⁻¹⊙C⁻¹) - # where ⊙ is the symmetric dyadic product for fourth-order tensors - - # Construct C⁻¹⊗C⁻¹ part (already have 𝔻₁) - # Construct symmetric part: use Tensors.jl identity operations - # The fourth-order identity for symmetric tensors handles this - - coeff = 2(μ - λ * log(J)) - - # For the symmetric outer product of C⁻¹ with itself, - # we can use the following approach: - # Build component-wise using Voigt ordering - - # Simplified: Use the property that for small strains, - # this reduces to a simpler form. For full nonlinear case: - 𝔻₂ = -coeff * inv_symmetric_outer(C_inv) - - 𝔻 = 𝔻₁ + 𝔻₂ - - return S, 𝔻, NoState() -end - -""" -Compute symmetric fourth-order tensor from inverse: ∂C⁻¹/∂C - -For symmetric second-order tensor C⁻¹, compute the fourth-order tensor: -(∂C⁻¹/∂C)ᵢⱼₖₗ = -1/2(C⁻¹ᵢₖC⁻¹ⱼₗ + C⁻¹ᵢₗC⁻¹ⱼₖ) - -This appears in the material tangent of hyperelastic materials. -""" -function inv_symmetric_outer(C_inv::SymmetricTensor{2,3,T}) where T - # Extract components (Voigt notation: 11, 22, 33, 12, 23, 13) - c = [C_inv[1, 1], C_inv[2, 2], C_inv[3, 3], - C_inv[1, 2], C_inv[2, 3], C_inv[1, 3]] - - # Build fourth-order tensor in Voigt notation (6x6 matrix representation) - # Then convert to SymmetricTensor{4,3} - # - # This is the -1/2(CᵢₖCⱼₗ + CᵢₗCⱼₖ) tensor - - # For now, use a simpler approximation that works for Neo-Hookean - # Full implementation would build all 36 components - - # Use outer product and symmetrize - result = C_inv ⊗ C_inv - - # Add symmetric component - # (This is a simplified version - full implementation needs more care) - return result -end - -# --------------------------------------------------------------------------- -# 4. Perfect Plasticity (von Mises) -# --------------------------------------------------------------------------- - -"""Perfect plasticity with von Mises yield criterion.""" -struct PerfectPlasticity <: AbstractMaterial - E::Float64 # Young's modulus [Pa] - ν::Float64 # Poisson's ratio [-] - σ_y::Float64 # Yield stress [Pa] -end - -PerfectPlasticity(; E, ν, σ_y) = PerfectPlasticity(E, ν, σ_y) - -λ(mat::PerfectPlasticity) = mat.E * mat.ν / ((1 + mat.ν) * (1 - 2mat.ν)) -μ(mat::PerfectPlasticity) = mat.E / (2(1 + mat.ν)) - -""" -Internal state for plasticity (history-dependent variables). - -This struct is passed through Newton iterations: -- state_old: State at beginning of time step (t_n) -- state_trial: Trial state during iteration (may not converge) -- state_new: Updated state for next iteration (t_n+1) -""" -struct PlasticityState{T} <: AbstractMaterialState - ε_p::SymmetricTensor{2,3,T} # Plastic strain - α::T # Equivalent plastic strain -end - -"""Initial state for plasticity (zero plastic strain).""" -initial_state(::PerfectPlasticity) = PlasticityState(zero(SymmetricTensor{2,3}), 0.0) - -"""Von Mises equivalent stress.""" -function von_mises_stress(σ::SymmetricTensor{2,3}) - s = dev(σ) # Deviatoric stress - return √(3 / 2 * s ⊡ s) -end - -"""Compute stress for perfectly plastic material with radial return.""" -function compute_stress( - material::PerfectPlasticity, - ε::SymmetricTensor{2,3,T}, - state_old::PlasticityState{T}, - Δt::Float64 -) where T - - # Material parameters - λ_val = λ(material) - μ_val = μ(material) - σ_y = material.σ_y - - # Elastic constitutive tensor - I = one(ε) - 𝕀ˢʸᵐ = one(SymmetricTensor{4,3,T}) - 𝔻ᵉ = λ_val * I ⊗ I + 2μ_val * 𝕀ˢʸᵐ - - # Elastic predictor - ε_e = ε - state_old.ε_p - σ_trial = λ_val * tr(ε_e) * I + 2μ_val * ε_e - σ_eq_trial = von_mises_stress(σ_trial) - - # Yield function - f = σ_eq_trial - σ_y - - if f ≤ 0.0 - # Elastic step - σ = σ_trial - 𝔻 = 𝔻ᵉ - state_new = state_old - else - # Plastic step: Radial return - s_trial = dev(σ_trial) - p = tr(σ_trial) / 3 - - # Return to yield surface - σ = p * I + (σ_y / σ_eq_trial) * s_trial - - # Plastic multiplier - Δγ = f / (3μ_val) - - # Flow direction - n = √(3 / 2) * s_trial / σ_eq_trial - - # Update plastic strain - ε_p_new = state_old.ε_p + Δγ * n - α_new = state_old.α + Δγ - - state_new = PlasticityState(ε_p_new, α_new) - - # Algorithmic tangent (simplified) - θ = 1 - σ_y / σ_eq_trial - β = 6μ_val^2 / (3μ_val + θ * 3μ_val) - - 𝔻 = 𝔻ᵉ - β * (n ⊗ n) - end - - return σ, 𝔻, state_new -end - -#============================================================================= -OLD APPROACH: Voigt Notation + Array Implementation -=============================================================================# - -"""Old-style linear elastic with Voigt notation.""" -struct LinearElasticOld - E::Float64 - ν::Float64 -end - -"""Compute 6×6 constitutive matrix (Voigt notation).""" -function constitutive_matrix(mat::LinearElasticOld) - E, ν = mat.E, mat.ν - λ = E * ν / ((1 + ν) * (1 - 2ν)) - μ = E / (2(1 + ν)) - - D = zeros(6, 6) - D[1:3, 1:3] .= λ - D[1, 1] = D[2, 2] = D[3, 3] = λ + 2μ - D[4, 4] = D[5, 5] = D[6, 6] = μ - - return D -end - -"""Compute stress (old approach with arrays).""" -function compute_stress_old( - material::LinearElasticOld, - ε_vec::Vector{Float64}, # [ε11, ε22, ε33, 2ε12, 2ε23, 2ε13] - state_old::Dict{String,Any}, - Δt::Float64 -) - D = constitutive_matrix(material) - σ_vec = D * ε_vec - return σ_vec, D, state_old -end - -"""Old-style Neo-Hookean (manual derivatives).""" -struct NeoHookeanOld - μ::Float64 - λ::Float64 -end - -"""Compute stress manually (simplified, no actual derivatives for brevity).""" -function compute_stress_old( - material::NeoHookeanOld, - E_vec::Vector{Float64}, - state_old::Dict{String,Any}, - Δt::Float64 -) - # This would normally have 50+ lines of manual derivative calculations - # For benchmark purposes, just do some array operations - D = zeros(6, 6) - for i in 1:6 - D[i, i] = material.μ + material.λ / 3 - end - σ_vec = D * E_vec - return σ_vec, D, state_old -end - -"""Old-style plasticity with Dict storage.""" -struct PerfectPlasticityOld - E::Float64 - ν::Float64 - σ_y::Float64 -end - -"""Compute stress with Dict field storage.""" -function compute_stress_old( - material::PerfectPlasticityOld, - ε_vec::Vector{Float64}, - state_old::Dict{String,Any}, - Δt::Float64 -) - # Get plastic strain from Dict (type instability!) - if haskey(state_old, "epsilon_plastic") - ε_p_vec = state_old["epsilon_plastic"] - else - ε_p_vec = zeros(6) - end - - # Elastic trial - D = constitutive_matrix(LinearElasticOld(material.E, material.ν)) - ε_e_vec = ε_vec - ε_p_vec - σ_trial_vec = D * ε_e_vec - - # Von Mises check (manual calculation with arrays) - s11, s22, s33 = σ_trial_vec[1:3] - s12, s23, s13 = σ_trial_vec[4:6] - p = (s11 + s22 + s33) / 3 - dev_vec = [s11 - p, s22 - p, s33 - p, s12, s23, s13] - σ_eq = √(3 / 2 * (dev_vec[1]^2 + dev_vec[2]^2 + dev_vec[3]^2 + - 2 * (dev_vec[4]^2 + dev_vec[5]^2 + dev_vec[6]^2))) - - f = σ_eq - material.σ_y - - state_new = copy(state_old) - - if f > 0.0 - # Plastic correction - factor = material.σ_y / σ_eq - σ_vec = [p, p, p, 0.0, 0.0, 0.0] + factor * dev_vec - - # Update state in Dict - Δγ = f / (3 * material.E / (2(1 + material.ν))) - n_vec = √(3 / 2) * dev_vec / σ_eq - state_new["epsilon_plastic"] = ε_p_vec + Δγ * n_vec - else - σ_vec = σ_trial_vec - end - - return σ_vec, D, state_new -end - -#============================================================================= -MATERIAL STATE HANDLING FOR NEWTON ITERATIONS -=============================================================================# - -""" -Example: How to handle material state during Newton-Raphson iterations. - -In FEM nonlinear analysis, each time step requires iterative solution: - -1. **Beginning of time step (t_n):** - - state_old = converged state from previous time step - -2. **During Newton iterations (t_n → t_n+1):** - - For each iteration k = 1, 2, ... - - Compute: σ, 𝔻, state_trial = compute_stress(material, ε_k, state_old, Δt) - - state_trial is NOT committed yet (iteration may not converge) - -3. **After convergence:** - - state_new = state_trial from final iteration - - Commit: state_old ← state_new for next time step - -This ensures: -- Failed iterations don't corrupt material history -- Material state is consistent with converged solution -- Internal variables (plastic strain, damage, etc.) evolve correctly -""" - -""" -Simulate Newton-Raphson iteration with material state handling. - -Returns: -- converged: Whether iterations converged -- n_iter: Number of iterations -- state_converged: Final material state (only valid if converged) -""" -function newton_with_material_state( - material::AbstractMaterial, - ε_target::SymmetricTensor{2,3}, - state_old::AbstractMaterialState, - Δt::Float64; - max_iter=10, - tol=1e-8 -) - println(" Newton iteration with material state tracking:") - println(" " * "="^60) - - # Initial guess - ε_k = zero(ε_target) - - for k in 1:max_iter - # Compute stress and tangent (state_trial is NOT committed yet!) - σ_k, 𝔻_k, state_trial = compute_stress(material, ε_k, state_old, Δt) - - println(" Iteration $k:") - println(" strain: $(norm(ε_k))") - println(" stress: $(norm(σ_k))") - println(" state: $(state_trial)") - - # Residual (simplified: just strain error) - r = norm(ε_k - ε_target) - - if r < tol - println(" → Converged!") - println(" Final state committed: $(state_trial)") - return true, k, state_trial - end - - # Newton update (simplified) - ε_k = ε_k + 0.5 * (ε_target - ε_k) - end - - println(" → Failed to converge!") - println(" State NOT committed (keeping state_old)") - return false, max_iter, state_old # Keep old state on failure! -end - -println() -println("="^80) -println("NEWTON ITERATION STATE HANDLING EXAMPLE") -println("="^80) -println() - -# Example 1: Stateless material (LinearElastic) -println("Example 1: Stateless Material (LinearElastic)") -println("-"^80) -steel_example = LinearElastic(E=200e9, ν=0.3) -state_stateless = initial_state(steel_example) -ε_test = SymmetricTensor{2,3}((0.001, 0.0, 0.0, 0.0, 0.0, 0.0)) - -converged, n_iter, state_final = newton_with_material_state( - steel_example, ε_test, state_stateless, 1.0, max_iter=3 -) -println("Result: state_final = $state_final (NoState, always)") -println() - -# Example 2: Stateful material (PerfectPlasticity) -println("Example 2: Stateful Material (PerfectPlasticity)") -println("-"^80) -plastic_example = PerfectPlasticity(E=200e9, ν=0.3, σ_y=250e6) -state_stateful = initial_state(plastic_example) -ε_test_plastic = SymmetricTensor{2,3}((0.002, 0.0, 0.0, 0.0, 0.0, 0.0)) # Large strain → plastic - -converged, n_iter, state_final = newton_with_material_state( - plastic_example, ε_test_plastic, state_stateful, 1.0, max_iter=3 -) -println("Result: state_final = $state_final (plastic strain accumulated)") -println() - -println("Key insight: State handling is IDENTICAL for all materials due to") -println("AbstractMaterialState type hierarchy. Assembly code doesn't need") -println("to know whether material is stateless or stateful!") -println() - -#============================================================================= -BENCHMARK SETUP -=============================================================================# - -println("Setting up materials and test cases...") -println() - -# Materials (realistic steel properties) -steel_new = LinearElastic(E=200e9, ν=0.3) -steel_old = LinearElasticOld(200e9, 0.3) - -rubber_ad = NeoHookeanAD(E=10e6, ν=0.45) -rubber_manual = NeoHookeanManual(E=10e6, ν=0.45) -rubber_old = NeoHookeanOld(10e6 / (2 * 1.45), 10e6 * 0.45 / (1.45 * 0.1)) - -plastic_new = PerfectPlasticity(E=200e9, ν=0.3, σ_y=250e6) -plastic_old = PerfectPlasticityOld(200e9, 0.3, 250e6) - -# Test strain (small elastic deformation) -ε11, ε22, ε33 = 0.001, -0.0003, -0.0003 # Uniaxial tension with Poisson effect -ε12, ε23, ε13 = 0.0, 0.0, 0.0 - -# New approach: SymmetricTensor -ε_tensor = SymmetricTensor{2,3}((ε11, ε12, ε13, ε22, ε23, ε33)) -E_tensor = ε_tensor # For Neo-Hookean (Green-Lagrange ≈ small strain here) - -# Old approach: Voigt vector (note factor of 2 for shear!) -ε_voigt = [ε11, ε22, ε33, 2 * ε12, 2 * ε23, 2 * ε13] - -# States (using proper type hierarchy) -state_nostate = NoState() -state_dict_empty = Dict{String,Any}() -state_plastic_new = initial_state(plastic_new) -state_plastic_old = Dict{String,Any}("epsilon_plastic" => zeros(6)) - -println("Materials configured:") -println(" - Linear Elastic: E = 200 GPa, ν = 0.3") -println(" - Neo-Hookean (AD): μ ≈ 3.4 MPa, λ ≈ 45 MPa (automatic differentiation)") -println(" - Neo-Hookean (Manual): μ ≈ 3.4 MPa, λ ≈ 45 MPa (hand-coded derivatives)") -println(" - Perfect Plasticity: E = 200 GPa, σ_y = 250 MPa") -println() -println("Test strain: ε11 = 0.001 (uniaxial tension)") -println() - -#============================================================================= -TYPE STABILITY CHECK -=============================================================================# - -println("="^80) -println("TYPE STABILITY ANALYSIS") -println("="^80) -println() - -println("Checking for type instabilities...") -println() - -# Check LinearElastic -println("1. Linear Elastic (Tensors.jl):") -@code_warntype compute_stress(steel_new, ε_tensor, state_nostate, 0.0) -println() - -println("2. Linear Elastic (Old Voigt/Dict):") -@code_warntype compute_stress_old(steel_old, ε_voigt, state_dict_empty, 0.0) -println() - -println("3. Neo-Hookean AD (Tensors.jl with automatic differentiation):") -@code_warntype compute_stress(rubber_ad, E_tensor, state_nostate, 0.0) -println() - -println("4. Neo-Hookean Manual (Tensors.jl with hand-coded derivatives):") -@code_warntype compute_stress(rubber_manual, E_tensor, state_nostate, 0.0) -println() - -println("5. Perfect Plasticity (Tensors.jl):") -@code_warntype compute_stress(plastic_new, ε_tensor, state_plastic_new, 0.0) -println() - -println("6. Perfect Plasticity (Old Dict):") -@code_warntype compute_stress_old(plastic_old, ε_voigt, state_plastic_old, 0.0) -println() - -#============================================================================= -ALLOCATION TESTS -=============================================================================# - -println("="^80) -println("ALLOCATION TESTS") -println("="^80) -println() - -println("Testing for allocations (should be 0 for new approach)...") -println() - -# Linear Elastic -println("1. Linear Elastic") -println(" NEW (Tensors.jl):") -allocs_le_new = @allocated compute_stress(steel_new, ε_tensor, state_nostate, 0.0) -println(" Allocations: $allocs_le_new bytes") - -println(" OLD (Voigt/Dict):") -allocs_le_old = @allocated compute_stress_old(steel_old, ε_voigt, state_dict_empty, 0.0) -println(" Allocations: $allocs_le_old bytes") -println() - -# Neo-Hookean -println("2. Neo-Hookean") -println(" NEW (Tensors.jl + AD):") -allocs_nh_ad = @allocated compute_stress(rubber_ad, E_tensor, state_nostate, 0.0) -println(" Allocations: $allocs_nh_ad bytes") - -println(" NEW (Tensors.jl + Manual):") -allocs_nh_manual = @allocated compute_stress(rubber_manual, E_tensor, state_nostate, 0.0) -println(" Allocations: $allocs_nh_manual bytes") - -println(" OLD (Array):") -allocs_nh_old = @allocated compute_stress_old(rubber_old, ε_voigt, state_dict_empty, 0.0) -println(" Allocations: $allocs_nh_old bytes") -println() - -# Perfect Plasticity -println("3. Perfect Plasticity (elastic branch)") -println(" NEW (Tensors.jl):") -allocs_pp_new = @allocated compute_stress(plastic_new, ε_tensor, state_plastic_new, 0.0) -println(" Allocations: $allocs_pp_new bytes") - -println(" OLD (Dict):") -allocs_pp_old = @allocated compute_stress_old(plastic_old, ε_voigt, state_plastic_old, 0.0) -println(" Allocations: $allocs_pp_old bytes") -println() - -#============================================================================= -PERFORMANCE BENCHMARKS -=============================================================================# - -println("="^80) -println("PERFORMANCE BENCHMARKS") -println("="^80) -println() - -println("Running detailed benchmarks (this may take a minute)...") -println() - -# Linear Elastic -println("1. LINEAR ELASTIC") -println("-"^40) -println("NEW (Tensors.jl):") -bench_le_new = @benchmark compute_stress($steel_new, $ε_tensor, $state_nostate, 0.0) -display(bench_le_new) -println() - -println("OLD (Voigt/Dict):") -bench_le_old = @benchmark compute_stress_old($steel_old, $ε_voigt, $state_dict_empty, 0.0) -display(bench_le_old) -println() - -speedup_le = median(bench_le_old.times) / median(bench_le_new.times) -println("SPEEDUP: $(round(speedup_le, digits=1))×") -println() - -# Neo-Hookean -println("2. NEO-HOOKEAN") -println("-"^40) -println("NEW (Tensors.jl + Automatic Differentiation):") -bench_nh_ad = @benchmark compute_stress($rubber_ad, $E_tensor, $state_nostate, 0.0) -display(bench_nh_ad) -println() - -println("NEW (Tensors.jl + Manual Derivatives):") -bench_nh_manual = @benchmark compute_stress($rubber_manual, $E_tensor, $state_nostate, 0.0) -display(bench_nh_manual) -println() - -println("OLD (Array):") -bench_nh_old = @benchmark compute_stress_old($rubber_old, $ε_voigt, $state_dict_empty, 0.0) -display(bench_nh_old) -println() - -speedup_nh_ad = median(bench_nh_old.times) / median(bench_nh_ad.times) -speedup_nh_manual = median(bench_nh_old.times) / median(bench_nh_manual.times) -ad_overhead = median(bench_nh_ad.times) / median(bench_nh_manual.times) -println("SPEEDUP (AD): $(round(speedup_nh_ad, digits=1))×") -println("SPEEDUP (Manual): $(round(speedup_nh_manual, digits=1))×") -println("AD OVERHEAD: $(round(ad_overhead, digits=1))× (AD / Manual)") -println() - -# Perfect Plasticity -println("3. PERFECT PLASTICITY (elastic branch)") -println("-"^40) -println("NEW (Tensors.jl):") -bench_pp_new = @benchmark compute_stress($plastic_new, $ε_tensor, $state_plastic_new, 0.0) -display(bench_pp_new) -println() - -println("OLD (Dict):") -bench_pp_old = @benchmark compute_stress_old($plastic_old, $ε_voigt, $state_plastic_old, 0.0) -display(bench_pp_old) -println() - -speedup_pp = median(bench_pp_old.times) / median(bench_pp_new.times) -println("SPEEDUP: $(round(speedup_pp, digits=1))×") -println() - -#============================================================================= -SUMMARY -=============================================================================# - -println("="^80) -println("SUMMARY") -println("="^80) -println() - -println("ALLOCATIONS:") -println(" LinearElastic: NEW = $allocs_le_new bytes, OLD = $allocs_le_old bytes") -println(" NeoHookean (AD): NEW = $allocs_nh_ad bytes, OLD = $allocs_nh_old bytes") -println(" NeoHookean (Manual): NEW = $allocs_nh_manual bytes") -println(" PerfectPlasticity: NEW = $allocs_pp_new bytes, OLD = $allocs_pp_old bytes") -println() - -println("MEDIAN TIMING:") -println(" LinearElastic: NEW = $(median(bench_le_new.times)) ns, OLD = $(median(bench_le_old.times)) ns") -println(" NeoHookean (AD): NEW = $(median(bench_nh_ad.times)) ns, OLD = $(median(bench_nh_old.times)) ns") -println(" NeoHookean (Manual): NEW = $(median(bench_nh_manual.times)) ns") -println(" PerfectPlasticity: NEW = $(median(bench_pp_new.times)) ns, OLD = $(median(bench_pp_old.times)) ns") -println() - -println("SPEEDUP (OLD / NEW):") -println(" LinearElastic: $(round(speedup_le, digits=1))×") -println(" NeoHookean (AD): $(round(speedup_nh_ad, digits=1))×") -println(" NeoHookean (Manual): $(round(speedup_nh_manual, digits=1))×") -println(" PerfectPlasticity: $(round(speedup_pp, digits=1))×") -println() - -println("AD OVERHEAD:") -println(" NeoHookean: AD is $(round(ad_overhead, digits=1))× slower than manual derivatives") -println() - -avg_speedup = (speedup_le + speedup_nh_manual + speedup_pp) / 3 -println("AVERAGE SPEEDUP: $(round(avg_speedup, digits=1))× (using manual Neo-Hookean)") -println() - -# Validate claims -println("VALIDATION OF CLAIMS:") -println(" - Zero allocations for new approach: ", - allocs_le_new == 0 && allocs_nh_ad == 0 && allocs_nh_manual == 0 && allocs_pp_new == 0 ? "✓ PASS" : "✗ FAIL") -println(" - Manual derivatives outperform AD: ", - median(bench_nh_manual.times) < median(bench_nh_ad.times) ? "✓ PASS" : "✗ FAIL") -println(" - Type stability with NoState return: Check @code_warntype output above") -println() - -println("="^80) -println("Benchmark complete! Results saved to: material_models_benchmark_results.txt") -println("="^80) diff --git a/benchmarks/material_models_benchmark_results.txt b/benchmarks/material_models_benchmark_results.txt deleted file mode 100644 index c9a2b26..0000000 --- a/benchmarks/material_models_benchmark_results.txt +++ /dev/null @@ -1,922 +0,0 @@ -================================================================================ -Material Models Performance Benchmark (Extended) -================================================================================ - - -================================================================================ -NEWTON ITERATION STATE HANDLING EXAMPLE -================================================================================ - -Example 1: Stateless Material (LinearElastic) --------------------------------------------------------------------------------- - Newton iteration with material state tracking: - ============================================================ - Iteration 1: - strain: 0.0 - stress: 0.0 - state: NoState() - Iteration 2: - strain: 0.0005 - stress: 1.5741063022831637e8 - state: NoState() - Iteration 3: - strain: 0.00075 - stress: 2.3611594534247452e8 - state: NoState() - → Failed to converge! - State NOT committed (keeping state_old) -Result: state_final = NoState() (NoState, always) - -Example 2: Stateful Material (PerfectPlasticity) --------------------------------------------------------------------------------- - Newton iteration with material state tracking: - ============================================================ - Iteration 1: - strain: 0.0 - stress: 0.0 - state: PlasticityState{Float64}([0.0 0.0 0.0; 0.0 0.0 0.0; 0.0 0.0 0.0], 0.0) - Iteration 2: - strain: 0.001 - stress: 3.1482126045663273e8 - state: PlasticityState{Float64}([0.0 0.0 0.0; 0.0 0.0 0.0; 0.0 0.0 0.0], 0.0) - Iteration 3: - strain: 0.0015 - stress: 4.7223189068494904e8 - state: PlasticityState{Float64}([0.0 0.0 0.0; 0.0 0.0 0.0; 0.0 0.0 0.0], 0.0) - → Failed to converge! - State NOT committed (keeping state_old) -Result: state_final = PlasticityState{Float64}([0.0 0.0 0.0; 0.0 0.0 0.0; 0.0 0.0 0.0], 0.0) (plastic strain accumulated) - -Key insight: State handling is IDENTICAL for all materials due to -AbstractMaterialState type hierarchy. Assembly code doesn't need -to know whether material is stateless or stateful! - -Setting up materials and test cases... - -Materials configured: - - Linear Elastic: E = 200 GPa, ν = 0.3 - - Neo-Hookean (AD): μ ≈ 3.4 MPa, λ ≈ 45 MPa (automatic differentiation) - - Neo-Hookean (Manual): μ ≈ 3.4 MPa, λ ≈ 45 MPa (hand-coded derivatives) - - Perfect Plasticity: E = 200 GPa, σ_y = 250 MPa - -Test strain: ε11 = 0.001 (uniaxial tension) - -================================================================================ -TYPE STABILITY ANALYSIS -================================================================================ - -Checking for type instabilities... - -1. Linear Elastic (Tensors.jl): -MethodInstance for compute_stress(::LinearElastic, ::SymmetricTensor{2, 3, Float64, 6}, ::NoState, ::Float64) - from compute_stress(material::LinearElastic, ε::SymmetricTensor{2, 3, T}, state_old::NoState, Δt::Float64) where T @ Main ~/dev/JuliaFEM.jl/benchmarks/material_models_benchmark.jl:94 -Static Parameters - T = Float64 -Arguments - #self#::Core.Const(Main.compute_stress) - material::LinearElastic - ε::SymmetricTensor{2, 3, Float64, 6} - state_old::Core.Const(NoState()) - Δt::Float64 -Locals - 𝔻::SymmetricTensor{4, 3, Float64, 36} - 𝕀ˢʸᵐ::SymmetricTensor{4, 3, Float64, 36} - σ::SymmetricTensor{2, 3, Float64, 6} - I::SymmetricTensor{2, 3, Float64, 6} - μ_val::Float64 - λ_val::Float64 -Body::Tuple{SymmetricTensor{2, 3, Float64, 6}, SymmetricTensor{4, 3, Float64, 36}, NoState} -1 ─ %1 = Main.λ::Core.Const(Main.λ) -│ (λ_val = (%1)(material)) -│ %3 = Main.μ::Core.Const(Main.μ) -│ (μ_val = (%3)(material)) -│ %5 = Main.one::Core.Const(one) -│ (I = (%5)(ε)) -│ %7 = Main.:+::Core.Const(+) -│ %8 = Main.:*::Core.Const(*) -│ %9 = λ_val::Float64 -│ %10 = Main.tr::Core.Const(LinearAlgebra.tr) -│ %11 = (%10)(ε)::Float64 -│ %12 = I::Core.Const([1.0 0.0 0.0; 0.0 1.0 0.0; 0.0 0.0 1.0]) -│ %13 = (%8)(%9, %11, %12)::SymmetricTensor{2, 3, Float64, 6} -│ %14 = Main.:*::Core.Const(*) -│ %15 = Main.:*::Core.Const(*) -│ %16 = μ_val::Float64 -│ %17 = (%15)(2, %16)::Float64 -│ %18 = (%14)(%17, ε)::SymmetricTensor{2, 3, Float64, 6} -│ (σ = (%7)(%13, %18)) -│ %20 = Main.one::Core.Const(one) -│ %21 = Main.SymmetricTensor::Core.Const(SymmetricTensor) -│ %22 = $(Expr(:static_parameter, 1))::Core.Const(Float64) -│ %23 = Core.apply_type(%21, 4, 3, %22)::Core.Const(SymmetricTensor{4, 3, Float64}) -│ (𝕀ˢʸᵐ = (%20)(%23)) -│ %25 = Main.:+::Core.Const(+) -│ %26 = Main.:⊗::Core.Const(Tensors.otimes) -│ %27 = Main.:*::Core.Const(*) -│ %28 = λ_val::Float64 -│ %29 = I::Core.Const([1.0 0.0 0.0; 0.0 1.0 0.0; 0.0 0.0 1.0]) -│ %30 = (%27)(%28, %29)::SymmetricTensor{2, 3, Float64, 6} -│ %31 = I::Core.Const([1.0 0.0 0.0; 0.0 1.0 0.0; 0.0 0.0 1.0]) -│ %32 = (%26)(%30, %31)::SymmetricTensor{4, 3, Float64, 36} -│ %33 = Main.:*::Core.Const(*) -│ %34 = Main.:*::Core.Const(*) -│ %35 = μ_val::Float64 -│ %36 = (%34)(2, %35)::Float64 -│ %37 = 𝕀ˢʸᵐ::Core.Const([1.0 0.0 0.0; 0.0 0.0 0.0; 0.0 0.0 0.0;;; 0.0 0.5 0.0; 0.5 0.0 0.0; 0.0 0.0 0.0;;; 0.0 0.0 0.5; 0.0 0.0 0.0; 0.5 0.0 0.0;;;; 0.0 0.5 0.0; 0.5 0.0 0.0; 0.0 0.0 0.0;;; 0.0 0.0 0.0; 0.0 1.0 0.0; 0.0 0.0 0.0;;; 0.0 0.0 0.0; 0.0 0.0 0.5; 0.0 0.5 0.0;;;; 0.0 0.0 0.5; 0.0 0.0 0.0; 0.5 0.0 0.0;;; 0.0 0.0 0.0; 0.0 0.0 0.5; 0.0 0.5 0.0;;; 0.0 0.0 0.0; 0.0 0.0 0.0; 0.0 0.0 1.0]) -│ %38 = (%33)(%36, %37)::SymmetricTensor{4, 3, Float64, 36} -│ (𝔻 = (%25)(%32, %38)) -│ %40 = σ::SymmetricTensor{2, 3, Float64, 6} -│ %41 = 𝔻::SymmetricTensor{4, 3, Float64, 36} -│ %42 = Main.NoState::Core.Const(NoState) -│ %43 = (%42)()::Core.Const(NoState()) -│ %44 = Core.tuple(%40, %41, %43)::Tuple{SymmetricTensor{2, 3, Float64, 6}, SymmetricTensor{4, 3, Float64, 36}, NoState} -└── return %44 - - -2. Linear Elastic (Old Voigt/Dict): -MethodInstance for compute_stress_old(::LinearElasticOld, ::Vector{Float64}, ::Dict{String, Any}, ::Float64) - from compute_stress_old(material::LinearElasticOld, ε_vec::Vector{Float64}, state_old::Dict{String, Any}, Δt::Float64) @ Main ~/dev/JuliaFEM.jl/benchmarks/material_models_benchmark.jl:413 -Arguments - #self#::Core.Const(Main.compute_stress_old) - material::LinearElasticOld - ε_vec::Vector{Float64} - state_old::Dict{String, Any} - Δt::Float64 -Locals - σ_vec::Vector{Float64} - D::Matrix{Float64} -Body::Tuple{Vector{Float64}, Matrix{Float64}, Dict{String, Any}} -1 ─ %1 = Main.constitutive_matrix::Core.Const(Main.constitutive_matrix) -│ (D = (%1)(material)) -│ %3 = Main.:*::Core.Const(*) -│ %4 = D::Matrix{Float64} -│ (σ_vec = (%3)(%4, ε_vec)) -│ %6 = σ_vec::Vector{Float64} -│ %7 = D::Matrix{Float64} -│ %8 = Core.tuple(%6, %7, state_old)::Tuple{Vector{Float64}, Matrix{Float64}, Dict{String, Any}} -└── return %8 - - -3. Neo-Hookean AD (Tensors.jl with automatic differentiation): -MethodInstance for compute_stress(::NeoHookeanAD, ::SymmetricTensor{2, 3, Float64, 6}, ::NoState, ::Float64) - from compute_stress(material::NeoHookeanAD, E::SymmetricTensor{2, 3, T}, state_old::NoState, Δt::Float64) where T @ Main ~/dev/JuliaFEM.jl/benchmarks/material_models_benchmark.jl:147 -Static Parameters - T = Float64 -Arguments - #self#::Core.Const(Main.compute_stress) - material::NeoHookeanAD - E::SymmetricTensor{2, 3, Float64, 6} - state_old::Core.Const(NoState()) - Δt::Float64 -Locals - @_6::Int64 - S::SymmetricTensor{2, 3, Float64, 6} - 𝔻::SymmetricTensor{4, 3, Float64, 36} - ψ::var"#ψ#compute_stress##0"{NeoHookeanAD} - C::SymmetricTensor{2, 3, Float64, 6} - I::SymmetricTensor{2, 3, Float64, 6} -Body::Tuple{SymmetricTensor{2, 3, Float64, 6}, SymmetricTensor{4, 3, Float64, 36}, NoState} -1 ─ %1 = Main.one::Core.Const(one) -│ (I = (%1)(E)) -│ %3 = Main.:+::Core.Const(+) -│ %4 = Main.:*::Core.Const(*) -│ %5 = (%4)(2, E)::SymmetricTensor{2, 3, Float64, 6} -│ %6 = I::Core.Const([1.0 0.0 0.0; 0.0 1.0 0.0; 0.0 0.0 1.0]) -│ (C = (%3)(%5, %6)) -│ %8 = Main.:(var"#ψ#compute_stress##0")::Core.Const(var"#ψ#compute_stress##0") -│ %9 = Core._typeof_captured_variable(material)::Core.Const(NeoHookeanAD) -│ %10 = Core.apply_type(%8, %9)::Core.Const(var"#ψ#compute_stress##0"{NeoHookeanAD}) -│ (ψ = %new(%10, material)) -│ %12 = Main.hessian::Core.Const(Tensors.hessian) -│ %13 = ψ::var"#ψ#compute_stress##0"{NeoHookeanAD} -│ %14 = C::SymmetricTensor{2, 3, Float64, 6} -│ %15 = (%12)(%13, %14, :all)::Tuple{SymmetricTensor{4, 3, Float64, 36}, SymmetricTensor{2, 3, Float64, 6}, Float64} -│ %16 = Base.indexed_iterate(%15, 1)::Core.PartialStruct(Tuple{SymmetricTensor{4, 3, Float64, 36}, Int64}, Any[SymmetricTensor{4, 3, Float64, 36}, Core.Const(2)]) -│ (𝔻 = Core.getfield(%16, 1)) -│ (@_6 = Core.getfield(%16, 2)) -│ %19 = @_6::Core.Const(2) -│ %20 = Base.indexed_iterate(%15, 2, %19)::Core.PartialStruct(Tuple{SymmetricTensor{2, 3, Float64, 6}, Int64}, Any[SymmetricTensor{2, 3, Float64, 6}, Core.Const(3)]) -│ (S = Core.getfield(%20, 1)) -│ %22 = Main.:*::Core.Const(*) -│ %23 = S::SymmetricTensor{2, 3, Float64, 6} -│ (S = (%22)(2, %23)) -│ %25 = Main.:*::Core.Const(*) -│ %26 = 𝔻::SymmetricTensor{4, 3, Float64, 36} -│ (𝔻 = (%25)(4, %26)) -│ %28 = S::SymmetricTensor{2, 3, Float64, 6} -│ %29 = 𝔻::SymmetricTensor{4, 3, Float64, 36} -│ %30 = Main.NoState::Core.Const(NoState) -│ %31 = (%30)()::Core.Const(NoState()) -│ %32 = Core.tuple(%28, %29, %31)::Tuple{SymmetricTensor{2, 3, Float64, 6}, SymmetricTensor{4, 3, Float64, 36}, NoState} -└── return %32 - - -4. Neo-Hookean Manual (Tensors.jl with hand-coded derivatives): -MethodInstance for compute_stress(::NeoHookeanManual, ::SymmetricTensor{2, 3, Float64, 6}, ::NoState, ::Float64) - from compute_stress(material::NeoHookeanManual, E::SymmetricTensor{2, 3, T}, state_old::NoState, Δt::Float64) where T @ Main ~/dev/JuliaFEM.jl/benchmarks/material_models_benchmark.jl:203 -Static Parameters - T = Float64 -Arguments - #self#::Core.Const(Main.compute_stress) - material::NeoHookeanManual - E::SymmetricTensor{2, 3, Float64, 6} - state_old::Core.Const(NoState()) - Δt::Float64 -Locals - 𝔻::SymmetricTensor{4, 3, Float64, 36} - 𝔻₂::SymmetricTensor{4, 3, Float64, 36} - coeff::Float64 - 𝕀ˢʸᵐ::SymmetricTensor{4, 3, Float64, 36} - 𝔻₁::SymmetricTensor{4, 3, Float64, 36} - S::SymmetricTensor{2, 3, Float64, 6} - C_inv::SymmetricTensor{2, 3, Float64, 6} - J::Float64 - C::SymmetricTensor{2, 3, Float64, 6} - I::SymmetricTensor{2, 3, Float64, 6} - λ::Float64 - μ::Float64 -Body::Tuple{SymmetricTensor{2, 3, Float64, 6}, SymmetricTensor{4, 3, Float64, 36}, NoState} -1 ─ %1 = Base.getproperty(material, :μ)::Float64 -│ %2 = Base.getproperty(material, :λ)::Float64 -│ (μ = %1) -│ (λ = %2) -│ %5 = Main.one::Core.Const(one) -│ (I = (%5)(E)) -│ %7 = Main.:+::Core.Const(+) -│ %8 = Main.:*::Core.Const(*) -│ %9 = (%8)(2, E)::SymmetricTensor{2, 3, Float64, 6} -│ %10 = I::Core.Const([1.0 0.0 0.0; 0.0 1.0 0.0; 0.0 0.0 1.0]) -│ (C = (%7)(%9, %10)) -│ %12 = Main.:√::Core.Const(sqrt) -│ %13 = Main.det::Core.Const(LinearAlgebra.det) -│ %14 = C::SymmetricTensor{2, 3, Float64, 6} -│ %15 = (%13)(%14)::Float64 -│ (J = (%12)(%15)) -│ %17 = Main.inv::Core.Const(inv) -│ %18 = C::SymmetricTensor{2, 3, Float64, 6} -│ (C_inv = (%17)(%18)) -│ %20 = Main.:+::Core.Const(+) -│ %21 = Main.:*::Core.Const(*) -│ %22 = μ::Float64 -│ %23 = Main.:-::Core.Const(-) -│ %24 = I::Core.Const([1.0 0.0 0.0; 0.0 1.0 0.0; 0.0 0.0 1.0]) -│ %25 = C_inv::SymmetricTensor{2, 3, Float64, 6} -│ %26 = (%23)(%24, %25)::SymmetricTensor{2, 3, Float64, 6} -│ %27 = (%21)(%22, %26)::SymmetricTensor{2, 3, Float64, 6} -│ %28 = Main.:*::Core.Const(*) -│ %29 = λ::Float64 -│ %30 = Main.log::Core.Const(log) -│ %31 = J::Float64 -│ %32 = (%30)(%31)::Float64 -│ %33 = C_inv::SymmetricTensor{2, 3, Float64, 6} -│ %34 = (%28)(%29, %32, %33)::SymmetricTensor{2, 3, Float64, 6} -│ (S = (%20)(%27, %34)) -│ %36 = Main.:*::Core.Const(*) -│ %37 = λ::Float64 -│ %38 = Main.:⊗::Core.Const(Tensors.otimes) -│ %39 = C_inv::SymmetricTensor{2, 3, Float64, 6} -│ %40 = C_inv::SymmetricTensor{2, 3, Float64, 6} -│ %41 = (%38)(%39, %40)::SymmetricTensor{4, 3, Float64, 36} -│ (𝔻₁ = (%36)(%37, %41)) -│ %43 = Main.one::Core.Const(one) -│ %44 = Main.SymmetricTensor::Core.Const(SymmetricTensor) -│ %45 = $(Expr(:static_parameter, 1))::Core.Const(Float64) -│ %46 = Core.apply_type(%44, 4, 3, %45)::Core.Const(SymmetricTensor{4, 3, Float64}) -│ (𝕀ˢʸᵐ = (%43)(%46)) -│ %48 = Main.:*::Core.Const(*) -│ %49 = Main.:-::Core.Const(-) -│ %50 = μ::Float64 -│ %51 = Main.:*::Core.Const(*) -│ %52 = λ::Float64 -│ %53 = Main.log::Core.Const(log) -│ %54 = J::Float64 -│ %55 = (%53)(%54)::Float64 -│ %56 = (%51)(%52, %55)::Float64 -│ %57 = (%49)(%50, %56)::Float64 -│ (coeff = (%48)(2, %57)) -│ %59 = Main.:*::Core.Const(*) -│ %60 = Main.:-::Core.Const(-) -│ %61 = coeff::Float64 -│ %62 = (%60)(%61)::Float64 -│ %63 = Main.inv_symmetric_outer::Core.Const(Main.inv_symmetric_outer) -│ %64 = C_inv::SymmetricTensor{2, 3, Float64, 6} -│ %65 = (%63)(%64)::SymmetricTensor{4, 3, Float64, 36} -│ (𝔻₂ = (%59)(%62, %65)) -│ %67 = Main.:+::Core.Const(+) -│ %68 = 𝔻₁::SymmetricTensor{4, 3, Float64, 36} -│ %69 = 𝔻₂::SymmetricTensor{4, 3, Float64, 36} -│ (𝔻 = (%67)(%68, %69)) -│ %71 = S::SymmetricTensor{2, 3, Float64, 6} -│ %72 = 𝔻::SymmetricTensor{4, 3, Float64, 36} -│ %73 = Main.NoState::Core.Const(NoState) -│ %74 = (%73)()::Core.Const(NoState()) -│ %75 = Core.tuple(%71, %72, %74)::Tuple{SymmetricTensor{2, 3, Float64, 6}, SymmetricTensor{4, 3, Float64, 36}, NoState} -└── return %75 - - -5. Perfect Plasticity (Tensors.jl): -MethodInstance for compute_stress(::PerfectPlasticity, ::SymmetricTensor{2, 3, Float64, 6}, ::PlasticityState{Float64}, ::Float64) - from compute_stress(material::PerfectPlasticity, ε::SymmetricTensor{2, 3, T}, state_old::PlasticityState{T}, Δt::Float64) where T @ Main ~/dev/JuliaFEM.jl/benchmarks/material_models_benchmark.jl:328 -Static Parameters - T = Float64 -Arguments - #self#::Core.Const(Main.compute_stress) - material::PerfectPlasticity - ε::SymmetricTensor{2, 3, Float64, 6} - state_old::PlasticityState{Float64} - Δt::Float64 -Locals - 𝔻::SymmetricTensor{4, 3, Float64, 36} - β::Float64 - θ::Float64 - state_new::PlasticityState{Float64} - α_new::Float64 - ε_p_new::SymmetricTensor{2, 3, Float64, 6} - n::SymmetricTensor{2, 3, Float64, 6} - Δγ::Float64 - σ::SymmetricTensor{2, 3, Float64, 6} - p::Float64 - s_trial::SymmetricTensor{2, 3, Float64, 6} - f::Float64 - σ_eq_trial::Float64 - σ_trial::SymmetricTensor{2, 3, Float64, 6} - ε_e::SymmetricTensor{2, 3, Float64, 6} - 𝔻ᵉ::SymmetricTensor{4, 3, Float64, 36} - 𝕀ˢʸᵐ::SymmetricTensor{4, 3, Float64, 36} - I::SymmetricTensor{2, 3, Float64, 6} - σ_y::Float64 - μ_val::Float64 - λ_val::Float64 -Body::Tuple{SymmetricTensor{2, 3, Float64, 6}, SymmetricTensor{4, 3, Float64, 36}, PlasticityState{Float64}} -1 ─ Core.NewvarNode(:(𝔻)) -│ Core.NewvarNode(:(β)) -│ Core.NewvarNode(:(θ)) -│ Core.NewvarNode(:(state_new)) -│ Core.NewvarNode(:(α_new)) -│ Core.NewvarNode(:(ε_p_new)) -│ Core.NewvarNode(:(n)) -│ Core.NewvarNode(:(Δγ)) -│ Core.NewvarNode(:(σ)) -│ Core.NewvarNode(:(p)) -│ Core.NewvarNode(:(s_trial)) -│ %12 = Main.λ::Core.Const(Main.λ) -│ (λ_val = (%12)(material)) -│ %14 = Main.μ::Core.Const(Main.μ) -│ (μ_val = (%14)(material)) -│ (σ_y = Base.getproperty(material, :σ_y)) -│ %17 = Main.one::Core.Const(one) -│ (I = (%17)(ε)) -│ %19 = Main.one::Core.Const(one) -│ %20 = Main.SymmetricTensor::Core.Const(SymmetricTensor) -│ %21 = $(Expr(:static_parameter, 1))::Core.Const(Float64) -│ %22 = Core.apply_type(%20, 4, 3, %21)::Core.Const(SymmetricTensor{4, 3, Float64}) -│ (𝕀ˢʸᵐ = (%19)(%22)) -│ %24 = Main.:+::Core.Const(+) -│ %25 = Main.:⊗::Core.Const(Tensors.otimes) -│ %26 = Main.:*::Core.Const(*) -│ %27 = λ_val::Float64 -│ %28 = I::Core.Const([1.0 0.0 0.0; 0.0 1.0 0.0; 0.0 0.0 1.0]) -│ %29 = (%26)(%27, %28)::SymmetricTensor{2, 3, Float64, 6} -│ %30 = I::Core.Const([1.0 0.0 0.0; 0.0 1.0 0.0; 0.0 0.0 1.0]) -│ %31 = (%25)(%29, %30)::SymmetricTensor{4, 3, Float64, 36} -│ %32 = Main.:*::Core.Const(*) -│ %33 = Main.:*::Core.Const(*) -│ %34 = μ_val::Float64 -│ %35 = (%33)(2, %34)::Float64 -│ %36 = 𝕀ˢʸᵐ::Core.Const([1.0 0.0 0.0; 0.0 0.0 0.0; 0.0 0.0 0.0;;; 0.0 0.5 0.0; 0.5 0.0 0.0; 0.0 0.0 0.0;;; 0.0 0.0 0.5; 0.0 0.0 0.0; 0.5 0.0 0.0;;;; 0.0 0.5 0.0; 0.5 0.0 0.0; 0.0 0.0 0.0;;; 0.0 0.0 0.0; 0.0 1.0 0.0; 0.0 0.0 0.0;;; 0.0 0.0 0.0; 0.0 0.0 0.5; 0.0 0.5 0.0;;;; 0.0 0.0 0.5; 0.0 0.0 0.0; 0.5 0.0 0.0;;; 0.0 0.0 0.0; 0.0 0.0 0.5; 0.0 0.5 0.0;;; 0.0 0.0 0.0; 0.0 0.0 0.0; 0.0 0.0 1.0]) -│ %37 = (%32)(%35, %36)::SymmetricTensor{4, 3, Float64, 36} -│ (𝔻ᵉ = (%24)(%31, %37)) -│ %39 = Main.:-::Core.Const(-) -│ %40 = Base.getproperty(state_old, :ε_p)::SYMMETRICTENSOR{2, 3, FLOAT64} -│ (ε_e = (%39)(ε, %40)) -│ %42 = Main.:+::Core.Const(+) -│ %43 = Main.:*::Core.Const(*) -│ %44 = λ_val::Float64 -│ %45 = Main.tr::Core.Const(LinearAlgebra.tr) -│ %46 = ε_e::SymmetricTensor{2, 3, Float64, 6} -│ %47 = (%45)(%46)::Float64 -│ %48 = I::Core.Const([1.0 0.0 0.0; 0.0 1.0 0.0; 0.0 0.0 1.0]) -│ %49 = (%43)(%44, %47, %48)::SymmetricTensor{2, 3, Float64, 6} -│ %50 = Main.:*::Core.Const(*) -│ %51 = Main.:*::Core.Const(*) -│ %52 = μ_val::Float64 -│ %53 = (%51)(2, %52)::Float64 -│ %54 = ε_e::SymmetricTensor{2, 3, Float64, 6} -│ %55 = (%50)(%53, %54)::SymmetricTensor{2, 3, Float64, 6} -│ (σ_trial = (%42)(%49, %55)) -│ %57 = Main.von_mises_stress::Core.Const(Main.von_mises_stress) -│ %58 = σ_trial::SymmetricTensor{2, 3, Float64, 6} -│ (σ_eq_trial = (%57)(%58)) -│ %60 = Main.:-::Core.Const(-) -│ %61 = σ_eq_trial::Float64 -│ %62 = σ_y::Float64 -│ (f = (%60)(%61, %62)) -│ %64 = Main.:≤::Core.Const(<=) -│ %65 = f::Float64 -│ %66 = (%64)(%65, 0.0)::Bool -└── goto #3 if not %66 -2 ─ %68 = σ_trial::SymmetricTensor{2, 3, Float64, 6} -│ (σ = %68) -│ %70 = 𝔻ᵉ::SymmetricTensor{4, 3, Float64, 36} -│ (𝔻 = %70) -│ %72 = state_old::PlasticityState{Float64} -│ (state_new = %72) -└── goto #4 -3 ─ %75 = Main.dev::Core.Const(Tensors.dev) -│ %76 = σ_trial::SymmetricTensor{2, 3, Float64, 6} -│ (s_trial = (%75)(%76)) -│ %78 = Main.:/::Core.Const(/) -│ %79 = Main.tr::Core.Const(LinearAlgebra.tr) -│ %80 = σ_trial::SymmetricTensor{2, 3, Float64, 6} -│ %81 = (%79)(%80)::Float64 -│ (p = (%78)(%81, 3)) -│ %83 = Main.:+::Core.Const(+) -│ %84 = Main.:*::Core.Const(*) -│ %85 = p::Float64 -│ %86 = I::Core.Const([1.0 0.0 0.0; 0.0 1.0 0.0; 0.0 0.0 1.0]) -│ %87 = (%84)(%85, %86)::SymmetricTensor{2, 3, Float64, 6} -│ %88 = Main.:*::Core.Const(*) -│ %89 = Main.:/::Core.Const(/) -│ %90 = σ_y::Float64 -│ %91 = σ_eq_trial::Float64 -│ %92 = (%89)(%90, %91)::Float64 -│ %93 = s_trial::SymmetricTensor{2, 3, Float64, 6} -│ %94 = (%88)(%92, %93)::SymmetricTensor{2, 3, Float64, 6} -│ (σ = (%83)(%87, %94)) -│ %96 = Main.:/::Core.Const(/) -│ %97 = f::Float64 -│ %98 = Main.:*::Core.Const(*) -│ %99 = μ_val::Float64 -│ %100 = (%98)(3, %99)::Float64 -│ (Δγ = (%96)(%97, %100)) -│ %102 = Main.:/::Core.Const(/) -│ %103 = Main.:*::Core.Const(*) -│ %104 = Main.:√::Core.Const(sqrt) -│ %105 = Main.:/::Core.Const(/) -│ %106 = (%105)(3, 2)::Core.Const(1.5) -│ %107 = (%104)(%106)::Core.Const(1.224744871391589) -│ %108 = s_trial::SymmetricTensor{2, 3, Float64, 6} -│ %109 = (%103)(%107, %108)::SymmetricTensor{2, 3, Float64, 6} -│ %110 = σ_eq_trial::Float64 -│ (n = (%102)(%109, %110)) -│ %112 = Main.:+::Core.Const(+) -│ %113 = Base.getproperty(state_old, :ε_p)::SYMMETRICTENSOR{2, 3, FLOAT64} -│ %114 = Main.:*::Core.Const(*) -│ %115 = Δγ::Float64 -│ %116 = n::SymmetricTensor{2, 3, Float64, 6} -│ %117 = (%114)(%115, %116)::SymmetricTensor{2, 3, Float64, 6} -│ (ε_p_new = (%112)(%113, %117)) -│ %119 = Main.:+::Core.Const(+) -│ %120 = Base.getproperty(state_old, :α)::Float64 -│ %121 = Δγ::Float64 -│ (α_new = (%119)(%120, %121)) -│ %123 = Main.PlasticityState::Core.Const(PlasticityState) -│ %124 = ε_p_new::SymmetricTensor{2, 3, Float64, 6} -│ %125 = α_new::Float64 -│ (state_new = (%123)(%124, %125)) -│ %127 = Main.:-::Core.Const(-) -│ %128 = Main.:/::Core.Const(/) -│ %129 = σ_y::Float64 -│ %130 = σ_eq_trial::Float64 -│ %131 = (%128)(%129, %130)::Float64 -│ (θ = (%127)(1, %131)) -│ %133 = Main.:/::Core.Const(/) -│ %134 = Main.:*::Core.Const(*) -│ %135 = Main.:^::Core.Const(^) -│ %136 = μ_val::Float64 -│ %137 = Core.apply_type(Base.Val, 2)::Core.Const(Val{2}) -│ %138 = (%137)()::Core.Const(Val{2}()) -│ %139 = Base.literal_pow(%135, %136, %138)::Float64 -│ %140 = (%134)(6, %139)::Float64 -│ %141 = Main.:+::Core.Const(+) -│ %142 = Main.:*::Core.Const(*) -│ %143 = μ_val::Float64 -│ %144 = (%142)(3, %143)::Float64 -│ %145 = Main.:*::Core.Const(*) -│ %146 = θ::Float64 -│ %147 = Main.:*::Core.Const(*) -│ %148 = μ_val::Float64 -│ %149 = (%147)(3, %148)::Float64 -│ %150 = (%145)(%146, %149)::Float64 -│ %151 = (%141)(%144, %150)::Float64 -│ (β = (%133)(%140, %151)) -│ %153 = Main.:-::Core.Const(-) -│ %154 = 𝔻ᵉ::SymmetricTensor{4, 3, Float64, 36} -│ %155 = Main.:*::Core.Const(*) -│ %156 = β::Float64 -│ %157 = Main.:⊗::Core.Const(Tensors.otimes) -│ %158 = n::SymmetricTensor{2, 3, Float64, 6} -│ %159 = n::SymmetricTensor{2, 3, Float64, 6} -│ %160 = (%157)(%158, %159)::SymmetricTensor{4, 3, Float64, 36} -│ %161 = (%155)(%156, %160)::SymmetricTensor{4, 3, Float64, 36} -└── (𝔻 = (%153)(%154, %161)) -4 ┄ %163 = σ::SymmetricTensor{2, 3, Float64, 6} -│ %164 = 𝔻::SymmetricTensor{4, 3, Float64, 36} -│ %165 = state_new::PlasticityState{Float64} -│ %166 = Core.tuple(%163, %164, %165)::Tuple{SymmetricTensor{2, 3, Float64, 6}, SymmetricTensor{4, 3, Float64, 36}, PlasticityState{Float64}} -└── return %166 - - -6. Perfect Plasticity (Old Dict): -MethodInstance for compute_stress_old(::PerfectPlasticityOld, ::Vector{Float64}, ::Dict{String, Any}, ::Float64) - from compute_stress_old(material::PerfectPlasticityOld, ε_vec::Vector{Float64}, state_old::Dict{String, Any}, Δt::Float64) @ Main ~/dev/JuliaFEM.jl/benchmarks/material_models_benchmark.jl:455 -Arguments - #self#::Core.Const(Main.compute_stress_old) - material::PerfectPlasticityOld - ε_vec::Vector{Float64} - state_old::Dict{String, Any} - Δt::Float64 -Locals - @_6::ANY - @_7::ANY - σ_vec::ANY - n_vec::ANY - Δγ::ANY - factor::ANY - state_new::Dict{String, Any} - f::ANY - σ_eq::ANY - dev_vec::ANY - p::ANY - s13::ANY - s23::ANY - s12::ANY - s33::ANY - s22::ANY - s11::ANY - σ_trial_vec::ANY - ε_e_vec::ANY - D::Matrix{Float64} - ε_p_vec::ANY -Body::TUPLE{ANY, MATRIX{FLOAT64}, DICT{STRING, ANY}} -1 ─ Core.NewvarNode(:(@_6)) -│ Core.NewvarNode(:(@_7)) -│ Core.NewvarNode(:(σ_vec)) -│ Core.NewvarNode(:(n_vec)) -│ Core.NewvarNode(:(Δγ)) -│ Core.NewvarNode(:(factor)) -│ Core.NewvarNode(:(state_new)) -│ Core.NewvarNode(:(f)) -│ Core.NewvarNode(:(σ_eq)) -│ Core.NewvarNode(:(dev_vec)) -│ Core.NewvarNode(:(p)) -│ Core.NewvarNode(:(s13)) -│ Core.NewvarNode(:(s23)) -│ Core.NewvarNode(:(s12)) -│ Core.NewvarNode(:(s33)) -│ Core.NewvarNode(:(s22)) -│ Core.NewvarNode(:(s11)) -│ Core.NewvarNode(:(σ_trial_vec)) -│ Core.NewvarNode(:(ε_e_vec)) -│ Core.NewvarNode(:(D)) -│ Core.NewvarNode(:(ε_p_vec)) -│ %22 = Main.haskey::Core.Const(haskey) -│ %23 = (%22)(state_old, "epsilon_plastic")::Bool -└── goto #3 if not %23 -2 ─ (ε_p_vec = Base.getindex(state_old, "epsilon_plastic")) -└── goto #4 -3 ─ %27 = Main.zeros::Core.Const(zeros) -└── (ε_p_vec = (%27)(6)) -4 ┄ %29 = Main.constitutive_matrix::Core.Const(Main.constitutive_matrix) -│ %30 = Main.LinearElasticOld::Core.Const(LinearElasticOld) -│ %31 = Base.getproperty(material, :E)::Float64 -│ %32 = Base.getproperty(material, :ν)::Float64 -│ %33 = (%30)(%31, %32)::LinearElasticOld -│ (D = (%29)(%33)) -│ %35 = Main.:-::Core.Const(-) -│ %36 = ε_p_vec::ANY -│ (ε_e_vec = (%35)(ε_vec, %36)) -│ %38 = Main.:*::Core.Const(*) -│ %39 = D::Matrix{Float64} -│ %40 = ε_e_vec::ANY -│ (σ_trial_vec = (%38)(%39, %40)) -│ %42 = σ_trial_vec::ANY -│ %43 = Main.:(:)::Core.Const(Colon()) -│ %44 = (%43)(1, 3)::Core.Const(1:3) -│ %45 = Base.getindex(%42, %44)::ANY -│ %46 = Base.indexed_iterate(%45, 1)::ANY -│ (s11 = Core.getfield(%46, 1)) -│ (@_7 = Core.getfield(%46, 2)) -│ %49 = @_7::ANY -│ %50 = Base.indexed_iterate(%45, 2, %49)::ANY -│ (s22 = Core.getfield(%50, 1)) -│ (@_7 = Core.getfield(%50, 2)) -│ %53 = @_7::ANY -│ %54 = Base.indexed_iterate(%45, 3, %53)::ANY -│ (s33 = Core.getfield(%54, 1)) -│ %56 = σ_trial_vec::ANY -│ %57 = Main.:(:)::Core.Const(Colon()) -│ %58 = (%57)(4, 6)::Core.Const(4:6) -│ %59 = Base.getindex(%56, %58)::ANY -│ %60 = Base.indexed_iterate(%59, 1)::ANY -│ (s12 = Core.getfield(%60, 1)) -│ (@_6 = Core.getfield(%60, 2)) -│ %63 = @_6::ANY -│ %64 = Base.indexed_iterate(%59, 2, %63)::ANY -│ (s23 = Core.getfield(%64, 1)) -│ (@_6 = Core.getfield(%64, 2)) -│ %67 = @_6::ANY -│ %68 = Base.indexed_iterate(%59, 3, %67)::ANY -│ (s13 = Core.getfield(%68, 1)) -│ %70 = Main.:/::Core.Const(/) -│ %71 = Main.:+::Core.Const(+) -│ %72 = s11::ANY -│ %73 = s22::ANY -│ %74 = s33::ANY -│ %75 = (%71)(%72, %73, %74)::ANY -│ (p = (%70)(%75, 3)) -│ %77 = Main.:-::Core.Const(-) -│ %78 = s11::ANY -│ %79 = p::ANY -│ %80 = (%77)(%78, %79)::ANY -│ %81 = Main.:-::Core.Const(-) -│ %82 = s22::ANY -│ %83 = p::ANY -│ %84 = (%81)(%82, %83)::ANY -│ %85 = Main.:-::Core.Const(-) -│ %86 = s33::ANY -│ %87 = p::ANY -│ %88 = (%85)(%86, %87)::ANY -│ %89 = s12::ANY -│ %90 = s23::ANY -│ %91 = s13::ANY -│ (dev_vec = Base.vect(%80, %84, %88, %89, %90, %91)) -│ %93 = Main.:√::Core.Const(sqrt) -│ %94 = Main.:*::Core.Const(*) -│ %95 = Main.:/::Core.Const(/) -│ %96 = (%95)(3, 2)::Core.Const(1.5) -│ %97 = Main.:+::Core.Const(+) -│ %98 = Main.:^::Core.Const(^) -│ %99 = dev_vec::ANY -│ %100 = Base.getindex(%99, 1)::ANY -│ %101 = Core.apply_type(Base.Val, 2)::Core.Const(Val{2}) -│ %102 = (%101)()::Core.Const(Val{2}()) -│ %103 = Base.literal_pow(%98, %100, %102)::ANY -│ %104 = Main.:^::Core.Const(^) -│ %105 = dev_vec::ANY -│ %106 = Base.getindex(%105, 2)::ANY -│ %107 = Core.apply_type(Base.Val, 2)::Core.Const(Val{2}) -│ %108 = (%107)()::Core.Const(Val{2}()) -│ %109 = Base.literal_pow(%104, %106, %108)::ANY -│ %110 = Main.:^::Core.Const(^) -│ %111 = dev_vec::ANY -│ %112 = Base.getindex(%111, 3)::ANY -│ %113 = Core.apply_type(Base.Val, 2)::Core.Const(Val{2}) -│ %114 = (%113)()::Core.Const(Val{2}()) -│ %115 = Base.literal_pow(%110, %112, %114)::ANY -│ %116 = Main.:*::Core.Const(*) -│ %117 = Main.:+::Core.Const(+) -│ %118 = Main.:^::Core.Const(^) -│ %119 = dev_vec::ANY -│ %120 = Base.getindex(%119, 4)::ANY -│ %121 = Core.apply_type(Base.Val, 2)::Core.Const(Val{2}) -│ %122 = (%121)()::Core.Const(Val{2}()) -│ %123 = Base.literal_pow(%118, %120, %122)::ANY -│ %124 = Main.:^::Core.Const(^) -│ %125 = dev_vec::ANY -│ %126 = Base.getindex(%125, 5)::ANY -│ %127 = Core.apply_type(Base.Val, 2)::Core.Const(Val{2}) -│ %128 = (%127)()::Core.Const(Val{2}()) -│ %129 = Base.literal_pow(%124, %126, %128)::ANY -│ %130 = Main.:^::Core.Const(^) -│ %131 = dev_vec::ANY -│ %132 = Base.getindex(%131, 6)::ANY -│ %133 = Core.apply_type(Base.Val, 2)::Core.Const(Val{2}) -│ %134 = (%133)()::Core.Const(Val{2}()) -│ %135 = Base.literal_pow(%130, %132, %134)::ANY -│ %136 = (%117)(%123, %129, %135)::ANY -│ %137 = (%116)(2, %136)::ANY -│ %138 = (%97)(%103, %109, %115, %137)::ANY -│ %139 = (%94)(%96, %138)::ANY -│ (σ_eq = (%93)(%139)) -│ %141 = Main.:-::Core.Const(-) -│ %142 = σ_eq::ANY -│ %143 = Base.getproperty(material, :σ_y)::Float64 -│ (f = (%141)(%142, %143)) -│ %145 = Main.copy::Core.Const(copy) -│ (state_new = (%145)(state_old)) -│ %147 = Main.:>::Core.Const(>) -│ %148 = f::ANY -│ %149 = (%147)(%148, 0.0)::ANY -└── goto #6 if not %149 -5 ─ %151 = Main.:/::Core.Const(/) -│ %152 = Base.getproperty(material, :σ_y)::Float64 -│ %153 = σ_eq::ANY -│ (factor = (%151)(%152, %153)) -│ %155 = Main.:+::Core.Const(+) -│ %156 = p::ANY -│ %157 = p::ANY -│ %158 = p::ANY -│ %159 = Base.vect(%156, %157, %158, 0.0, 0.0, 0.0)::ANY -│ %160 = Main.:*::Core.Const(*) -│ %161 = factor::ANY -│ %162 = dev_vec::ANY -│ %163 = (%160)(%161, %162)::ANY -│ (σ_vec = (%155)(%159, %163)) -│ %165 = Main.:/::Core.Const(/) -│ %166 = f::ANY -│ %167 = Main.:/::Core.Const(/) -│ %168 = Main.:*::Core.Const(*) -│ %169 = Base.getproperty(material, :E)::Float64 -│ %170 = (%168)(3, %169)::Float64 -│ %171 = Main.:*::Core.Const(*) -│ %172 = Main.:+::Core.Const(+) -│ %173 = Base.getproperty(material, :ν)::Float64 -│ %174 = (%172)(1, %173)::Float64 -│ %175 = (%171)(2, %174)::Float64 -│ %176 = (%167)(%170, %175)::Float64 -│ (Δγ = (%165)(%166, %176)) -│ %178 = Main.:/::Core.Const(/) -│ %179 = Main.:*::Core.Const(*) -│ %180 = Main.:√::Core.Const(sqrt) -│ %181 = Main.:/::Core.Const(/) -│ %182 = (%181)(3, 2)::Core.Const(1.5) -│ %183 = (%180)(%182)::Core.Const(1.224744871391589) -│ %184 = dev_vec::ANY -│ %185 = (%179)(%183, %184)::ANY -│ %186 = σ_eq::ANY -│ (n_vec = (%178)(%185, %186)) -│ %188 = Main.:+::Core.Const(+) -│ %189 = ε_p_vec::ANY -│ %190 = Main.:*::Core.Const(*) -│ %191 = Δγ::ANY -│ %192 = n_vec::ANY -│ %193 = (%190)(%191, %192)::ANY -│ %194 = (%188)(%189, %193)::ANY -│ %195 = state_new::Dict{String, Any} -│ Base.setindex!(%195, %194, "epsilon_plastic") -└── goto #7 -6 ─ %198 = σ_trial_vec::ANY -└── (σ_vec = %198) -7 ┄ %200 = σ_vec::ANY -│ %201 = D::Matrix{Float64} -│ %202 = state_new::Dict{String, Any} -│ %203 = Core.tuple(%200, %201, %202)::TUPLE{ANY, MATRIX{FLOAT64}, DICT{STRING, ANY}} -└── return %203 - - -================================================================================ -ALLOCATION TESTS -================================================================================ - -Testing for allocations (should be 0 for new approach)... - -1. Linear Elastic - NEW (Tensors.jl): - Allocations: 0 bytes - OLD (Voigt/Dict): - Allocations: 496 bytes - -2. Neo-Hookean - NEW (Tensors.jl + AD): - Allocations: 0 bytes - NEW (Tensors.jl + Manual): - Allocations: 0 bytes - OLD (Array): - Allocations: 496 bytes - -3. Perfect Plasticity (elastic branch) - NEW (Tensors.jl): - Allocations: 0 bytes - OLD (Dict): - Allocations: 8828848 bytes - -================================================================================ -PERFORMANCE BENCHMARKS -================================================================================ - -Running detailed benchmarks (this may take a minute)... - -1. LINEAR ELASTIC ----------------------------------------- -NEW (Tensors.jl): -BenchmarkTools.Trial: 10000 samples with 997 evaluations per sample. - Range (min … max): 19.464 ns … 45.831 ns ┊ GC (min … max): 0.00% … 0.00% - Time (median): 19.577 ns ┊ GC (median): 0.00% - Time (mean ± σ): 19.670 ns ± 0.675 ns ┊ GC (mean ± σ): 0.00% ± 0.00% - - ▁█▄ - ███▇▄▃▂▂▂▂▂▁▁▂▂▂▂▂▂▂▂▂▂▂▂▁▁▁▂▁▁▁▂▁▁▁▂▂▁▁▁▁▂▁▂▂▂▂▂▂▂▂▂▂▂▂▂▂▂ ▂ - 19.5 ns Histogram: frequency by time 22.8 ns < - - Memory estimate: 0 bytes, allocs estimate: 0. - -OLD (Voigt/Dict): -BenchmarkTools.Trial: 10000 samples with 950 evaluations per sample. - Range (min … max): 93.356 ns … 8.107 μs ┊ GC (min … max): 0.00% … 97.34% - Time (median): 100.107 ns ┊ GC (median): 0.00% - Time (mean ± σ): 139.733 ns ± 249.840 ns ┊ GC (mean ± σ): 25.28% ± 13.73% - - █▂ ▁ ▁ - ██▄▄██▁▁▁▁▁▁▁▁▁▁▁▁▁▃▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▃▆▇▇▄▃▄▅▄▄▅▅▅▅▅▃▆ █ - 93.4 ns Histogram: log(frequency) by time 1.74 μs < - - Memory estimate: 496 bytes, allocs estimate: 4. - -SPEEDUP: 5.1× - -2. NEO-HOOKEAN ----------------------------------------- -NEW (Tensors.jl + Automatic Differentiation): -BenchmarkTools.Trial: 10000 samples with 23 evaluations per sample. - Range (min … max): 1.050 μs … 2.802 μs ┊ GC (min … max): 0.00% … 0.00% - Time (median): 1.051 μs ┊ GC (median): 0.00% - Time (mean ± σ): 1.055 μs ± 31.780 ns ┊ GC (mean ± σ): 0.00% ± 0.00% - - █ - █▄▂▂▁▂▁▂▂▁▁▁▁▁▁▁▁▁▂▁▁▂▁▁▁▁▁▁▁▁▁▁▁▂▁▂▂▂▂▂▂▂▂▂▂▂▂▂▂▂▂▂▂▂▂▂▂▂ ▂ - 1.05 μs Histogram: frequency by time 1.19 μs < - - Memory estimate: 0 bytes, allocs estimate: 0. - -NEW (Tensors.jl + Manual Derivatives): -BenchmarkTools.Trial: 10000 samples with 987 evaluations per sample. - Range (min … max): 49.806 ns … 1.787 μs ┊ GC (min … max): 0.00% … 0.00% - Time (median): 49.922 ns ┊ GC (median): 0.00% - Time (mean ± σ): 50.262 ns ± 17.399 ns ┊ GC (mean ± σ): 0.00% ± 0.00% - - ▅██▄▁ ▂ - █████▆▄▁▄▄▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▃▁▁▃▄▄▅▅▆▆▆▆▆▇▇█▇▇▇▇▇██▇▇▆▇▇ █ - 49.8 ns Histogram: log(frequency) by time 53.3 ns < - - Memory estimate: 0 bytes, allocs estimate: 0. - -OLD (Array): -BenchmarkTools.Trial: 10000 samples with 955 evaluations per sample. - Range (min … max): 91.182 ns … 9.723 μs ┊ GC (min … max): 0.00% … 97.56% - Time (median): 99.922 ns ┊ GC (median): 0.00% - Time (mean ± σ): 142.795 ns ± 307.090 ns ┊ GC (mean ± σ): 22.77% ± 11.92% - - █▃ ▄▁ ▁ - ██▆▄██▃▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▄▇█ █ - 91.2 ns Histogram: log(frequency) by time 1.77 μs < - - Memory estimate: 496 bytes, allocs estimate: 4. - -SPEEDUP (AD): 0.1× -SPEEDUP (Manual): 2.0× -AD OVERHEAD: 21.1× (AD / Manual) - -3. PERFECT PLASTICITY (elastic branch) ----------------------------------------- -NEW (Tensors.jl): -BenchmarkTools.Trial: 10000 samples with 976 evaluations per sample. - Range (min … max): 69.677 ns … 151.814 ns ┊ GC (min … max): 0.00% … 0.00% - Time (median): 70.389 ns ┊ GC (median): 0.00% - Time (mean ± σ): 70.566 ns ± 1.326 ns ┊ GC (mean ± σ): 0.00% ± 0.00% - - ▂▆▇▆▇▆▇▇▇█▇▆▇▆▅▃ ▁▁▁▁▁▁▂▁▁▁▁▁ ▃ - ▆███████████████████▇▇▇▆▇▆▆▄▃▁▁▁▁▁▆▅▅▆▇▆▇▇██████████████████ █ - 69.7 ns Histogram: log(frequency) by time 73.9 ns < - - Memory estimate: 0 bytes, allocs estimate: 0. - -OLD (Dict): -BenchmarkTools.Trial: 10000 samples with 10 evaluations per sample. - Range (min … max): 1.371 μs … 998.345 μs ┊ GC (min … max): 0.00% … 99.45% - Time (median): 1.480 μs ┊ GC (median): 0.00% - Time (mean ± σ): 1.701 μs ± 9.970 μs ┊ GC (mean ± σ): 5.84% ± 0.99% - - ▅█▆▂ - ▁▂▃▆████▆▅▄▃▃▃▃▂▂▂▂▂▁▁▂▁▁▁▁▁▂▂▂▂▂▂▂▂▂▂▃▃▃▃▂▂▂▂▂▂▂▂▂▂▁▁▁▁▁▁▁ ▂ - 1.37 μs Histogram: frequency by time 2.16 μs < - - Memory estimate: 1.98 KiB, allocs estimate: 53. - -SPEEDUP: 21.0× - -================================================================================ -SUMMARY -================================================================================ - -ALLOCATIONS: - LinearElastic: NEW = 0 bytes, OLD = 496 bytes - NeoHookean (AD): NEW = 0 bytes, OLD = 496 bytes - NeoHookean (Manual): NEW = 0 bytes - PerfectPlasticity: NEW = 0 bytes, OLD = 8828848 bytes - -MEDIAN TIMING: - LinearElastic: NEW = 19.576730190571716 ns, OLD = 100.10684210526315 ns - NeoHookean (AD): NEW = 1051.304347826087 ns, OLD = 99.92198952879582 ns - NeoHookean (Manual): NEW = 49.92198581560284 ns - PerfectPlasticity: NEW = 70.38934426229508 ns, OLD = 1479.55 ns - -SPEEDUP (OLD / NEW): - LinearElastic: 5.1× - NeoHookean (AD): 0.1× - NeoHookean (Manual): 2.0× - PerfectPlasticity: 21.0× - -AD OVERHEAD: - NeoHookean: AD is 21.1× slower than manual derivatives - -AVERAGE SPEEDUP: 9.4× (using manual Neo-Hookean) - -VALIDATION OF CLAIMS: - - Zero allocations for new approach: ✓ PASS - - Manual derivatives outperform AD: ✓ PASS - - Type stability with NoState return: Check @code_warntype output above - -================================================================================ -Benchmark complete! Results saved to: material_models_benchmark_results.txt -================================================================================ diff --git a/benchmarks/matrix_free_gpu_benchmark.jl b/benchmarks/matrix_free_gpu_benchmark.jl deleted file mode 100644 index 82cc9b9..0000000 --- a/benchmarks/matrix_free_gpu_benchmark.jl +++ /dev/null @@ -1,835 +0,0 @@ -""" -Matrix-Free Newton-Krylov GPU Benchmark - -Demonstrates traditional Newton vs Matrix-Free Newton-Krylov with Anderson -acceleration, running on GPU. - -Run with: - julia --project=. benchmarks/matrix_free_gpu_benchmark.jl -""" - -using CUDA -using LinearAlgebra -using IterativeSolvers -using Printf - -# Check GPU availability -if !CUDA.functional() - @warn "CUDA not available! Running CPU-only comparison." - USE_GPU = false -else - println("GPU Device: $(CUDA.name(CUDA.device()))") - println("GPU Memory: $(CUDA.total_memory() / 1e9) GB") - println() - USE_GPU = true -end - -# ============================================================================ -# Problem Setup: 3D Nonlinear Elasticity -# ============================================================================ - -""" -Residual for 3D nonlinear elasticity with cubic nonlinearity. - - r(u) = K·u + β·(K·u).^3 - f - -where K is stiffness matrix, β is nonlinearity parameter. -""" -struct NonlinearProblem{T,MatT,VecT} - K::MatT # Stiffness matrix (sparse or LinearMap) - f::VecT # Force vector - β::T # Nonlinearity parameter - n::Int # DOF count -end - -""" -Compute residual: r(u) = K·u + β·(K·u).^3 - f -""" -function compute_residual!(r::AbstractVector, prob::NonlinearProblem, u::AbstractVector) - # Linear part - mul!(r, prob.K, u) # r = K·u - - # Nonlinear part: r += β·(K·u).^3 - if prob.β != 0 - # Reuse r (which contains K·u) - @. r = r + prob.β * r^3 - end - - # Apply forcing - @. r = r - prob.f - - return r -end - -""" -Jacobian-vector product: J·v ≈ [r(u+ε·v) - r(u)] / ε (finite difference) -""" -function jacobian_vector_product!( - Jv::AbstractVector, - prob::NonlinearProblem, - u::AbstractVector, - v::AbstractVector, - r_u::AbstractVector, # Pre-computed r(u) - temp::AbstractVector # Workspace -) - ε = 1e-7 - - # temp = u + ε·v - @. temp = u + ε * v - - # Jv = r(u + ε·v) - compute_residual!(Jv, prob, temp) - - # Jv = [r(u + ε·v) - r(u)] / ε - @. Jv = (Jv - r_u) / ε - - return Jv -end - -# ============================================================================ -# Traditional Newton Solver -# ============================================================================ - -""" -Traditional Newton with full Jacobian assembly. - - u_{k+1} = u_k - J(u_k)^{-1} · r(u_k) - -Expensive: Assembles full Jacobian matrix at each iteration. -""" -function newton_traditional!( - u::AbstractVector{T}, - prob::NonlinearProblem{T}, - r::AbstractVector{T}, - du::AbstractVector{T}; - tol=1e-8, - max_iter=20, - verbose=true -) where T - - n = length(u) - - # Build Jacobian matrix (expensive!) - # J ≈ K + 3β·diag((K·u).^2)·K - Ku = prob.K * u - J = copy(prob.K) - - for iter in 1:max_iter - # Compute residual - compute_residual!(r, prob, u) - - norm_r = norm(r) - - if verbose - @printf(" Iter %2d: ||r|| = %.6e\n", iter, norm_r) - end - - if norm_r < tol - if verbose - println(" ✅ Converged!") - end - return iter - end - - # Update Jacobian (expensive!) - Ku .= prob.K * u - for i in 1:n - J[i, i] = prob.K[i, i] + 3 * prob.β * Ku[i]^2 * prob.K[i, i] - end - - # Solve linear system (expensive!) - du .= -(J \ r) - - # Update - u .+= du - end - - if verbose - println(" ⚠️ Did not converge in $max_iter iterations") - end - - return max_iter -end - -# ============================================================================ -# Helper: Matrix-free operator wrapper for GMRES -# ============================================================================ - -""" -Wrapper to make a function look like a matrix for GMRES. -""" -struct MatrixFreeOperator{F} - matvec!::F - n::Int -end - -Base.size(A::MatrixFreeOperator) = (A.n, A.n) -Base.size(A::MatrixFreeOperator, d::Int) = d <= 2 ? A.n : 1 -Base.eltype(::MatrixFreeOperator{F}) where F = Float64 - -function LinearAlgebra.mul!(y, A::MatrixFreeOperator, x) - A.matvec!(y, x) - return y -end - -# ============================================================================ -# Matrix-Free Newton-Krylov -# ============================================================================ - -""" -Matrix-Free Newton-Krylov with GMRES. - - J·v ≈ [r(u+ε·v) - r(u)] / ε (no matrix!) - du = gmres(Jv_op, -r) - u_{k+1} = u_k + du - -Cheap: Only residual evaluations, no Jacobian assembly. -""" -function newton_matrix_free!( - u::AbstractVector{T}, - prob::NonlinearProblem{T}, - r::AbstractVector{T}, - du::AbstractVector{T}, - temp::AbstractVector{T}, - Jv::AbstractVector{T}; - tol=1e-8, - max_iter=20, - gmres_tol=1e-6, - verbose=true -) where T - - for iter in 1:max_iter - # Compute residual - compute_residual!(r, prob, u) - - norm_r = norm(r) - - if verbose - @printf(" Iter %2d: ||r|| = %.6e", iter, norm_r) - end - - if norm_r < tol - if verbose - println(" ✅ Converged!") - end - return iter - end - - # Matrix-free operator: J·v - function Jv_matvec!(out, v) - jacobian_vector_product!(out, prob, u, v, r, temp) - return out - end - Jv_op = MatrixFreeOperator(Jv_matvec!, length(u)) - - # Solve J·du = -r using GMRES (matrix-free!) - du .= 0 - gmres!(du, Jv_op, -r; - abstol=gmres_tol, reltol=0, maxiter=50, verbose=false) - - gmres_iters = 50 # Would need to extract from gmres! return - - if verbose - @printf(" [GMRES: ~%d iters]\n", gmres_iters) - end - - # Update - u .+= du - end - - if verbose - println(" ⚠️ Did not converge in $max_iter iterations") - end - - return max_iter -end - -# ============================================================================ -# Anderson-Accelerated Newton-Krylov -# ============================================================================ - -""" -Anderson acceleration for Newton-Krylov. - -Combines m previous iterates via least-squares: - u_new = ∑ αᵢ·uᵢ where argmin ||∑ αᵢ·rᵢ||² s.t. ∑ αᵢ = 1 - -Transforms linear convergence → superlinear convergence. -""" -function anderson_newton_matrix_free!( - u::AbstractVector{T}, - prob::NonlinearProblem{T}, - r::AbstractVector{T}, - du::AbstractVector{T}, - temp::AbstractVector{T}, - Jv::AbstractVector{T}; - m=5, # Anderson history - tol=1e-8, - max_iter=20, - gmres_tol=1e-6, - verbose=true -) where T - - n = length(u) - - # Anderson history - U_history = [zeros(T, n) for _ in 1:m] - R_history = [zeros(T, n) for _ in 1:m] - history_count = 0 - - for iter in 1:max_iter - # Compute residual - compute_residual!(r, prob, u) - - norm_r = norm(r) - - if verbose - @printf(" Iter %2d: ||r|| = %.6e", iter, norm_r) - end - - if norm_r < tol - if verbose - println(" ✅ Converged!") - end - return iter - end - - # Matrix-free operator - function Jv_matvec!(out, v) - jacobian_vector_product!(out, prob, u, v, r, temp) - return out - end - Jv_op = MatrixFreeOperator(Jv_matvec!, n) - - # Solve J·du = -r using GMRES - du .= 0 - gmres!(du, Jv_op, -r; - abstol=gmres_tol, reltol=0, maxiter=50, verbose=false) - - # Store in history (circular buffer) - idx = mod1(history_count + 1, m) - U_history[idx] .= u - R_history[idx] .= r - history_count = min(history_count + 1, m) - - if verbose - @printf(" [GMRES: ~50 iters, history: %d]", history_count) - end - - # Anderson acceleration (if enough history) - if history_count >= 2 - # Build residual difference matrix - k = history_count - R_diff = zeros(T, n, k) - for i in 1:k - R_diff[:, i] .= R_history[i] .- r - end - - # Check condition number before QR - # If matrix is ill-conditioned, skip Anderson this iteration - R_norm = norm(R_diff) - if R_norm < 1e-10 - # Matrix too small, use standard update - u .+= du - if verbose - println(" [Anderson: skipped (residuals too small)]") - end - else - # Least-squares: min ||R_diff·α||² s.t. sum(α) = 1 - # Use QR factorization with regularization - try - Q, Rt = qr(R_diff) - - # Add small regularization to diagonal if needed - Rt_diag = diag(Rt) - if any(abs.(Rt_diag) .< 1e-12) - # Add Tikhonov regularization - λ = 1e-8 - Rt_reg = Rt + λ * I - α = Rt_reg \ (Q' * r) - else - α = Rt \ (Q' * r) - end - - α ./= sum(α) # Normalize - - # Combine previous iterates - u_combined = zeros(T, n) - for i in 1:k - u_combined .+= α[i] .* U_history[i] - end - - # Update with combination - u .= u_combined .+ du - - if verbose - println(" [Anderson: α=$(round.(α, digits=3))]") - end - catch e - # If QR fails, fall back to standard Newton - u .+= du - if verbose - println(" [Anderson: failed ($e), using standard update]") - end - end - end - else - # Standard Newton update - u .+= du - - if verbose - println() - end - end - end - - if verbose - println(" ⚠️ Did not converge in $max_iter iterations") - end - - return max_iter -end - -# ============================================================================ -# GPU Implementations -# ============================================================================ - -""" -GPU version of residual computation. -""" -function compute_residual_gpu!( - r::CuVector{T}, - K::CuMatrix{T}, - u::CuVector{T}, - f::CuVector{T}, - β::T -) where T - # r = K·u - mul!(r, K, u) - - # r = r + β·r³ - f - if β != 0 - r .= r .+ β .* r .^ 3 .- f - else - r .= r .- f - end - - return r -end - -""" -GPU version of Matrix-Free Newton-Krylov. -""" -function newton_matrix_free_gpu!( - u::CuVector{T}, - K::CuMatrix{T}, - f::CuVector{T}, - β::T; - tol=1e-8, - max_iter=20, - gmres_tol=1e-6, - verbose=true -) where T - - n = length(u) - r = CUDA.zeros(T, n) - du = CUDA.zeros(T, n) - temp = CUDA.zeros(T, n) - Jv = CUDA.zeros(T, n) - - for iter in 1:max_iter - # Compute residual on GPU - compute_residual_gpu!(r, K, u, f, β) - - norm_r = norm(Array(r)) # Transfer to CPU for norm - - if verbose - @printf(" Iter %2d: ||r|| = %.6e\n", iter, norm_r) - end - - if norm_r < tol - if verbose - println(" ✅ Converged!") - end - return iter - end - - # Jacobian-vector product (on GPU) - ε = T(1e-7) - function Jv_matvec_gpu!(out_cpu, v_cpu) - v = CuArray(v_cpu) - # temp = u + ε·v - temp .= u .+ ε .* v - # Jv = r(u + ε·v) - compute_residual_gpu!(Jv, K, temp, f, β) - # Jv = [r(u + ε·v) - r(u)] / ε - Jv .= (Jv .- r) ./ ε - out_cpu .= Array(Jv) - return out_cpu - end - Jv_op_gpu = MatrixFreeOperator(Jv_matvec_gpu!, n) - - # Solve on CPU (GMRES doesn't have GPU version in IterativeSolvers.jl) - r_cpu = Array(r) - du_cpu = zeros(T, n) - gmres!(du_cpu, Jv_op_gpu, -r_cpu; - abstol=gmres_tol, reltol=0, maxiter=50, verbose=false) - - # Update on GPU - du .= CuArray(du_cpu) - u .+= du - end - - if verbose - println(" ⚠️ Did not converge in $max_iter iterations") - end - - return max_iter -end - -# GPU Anderson-Accelerated Newton-Krylov -function anderson_newton_matrix_free_gpu!( - u::CuVector{T}, - K::CuMatrix{T}, - f::CuVector{T}, - β::T; - tol=1e-8, - max_iter=20, - gmres_tol=1e-6, - history_size=5, - verbose=true -) where T - - n = length(u) - r = CUDA.zeros(T, n) - du = CUDA.zeros(T, n) - temp = CUDA.zeros(T, n) - Jv = CUDA.zeros(T, n) - - # Anderson acceleration storage (CPU) - R_history = Vector{Vector{T}}() - U_history = Vector{Vector{T}}() - history_count = 0 - - for iter in 1:max_iter - # Compute residual on GPU - compute_residual_gpu!(r, K, u, f, β) - - norm_r = norm(Array(r)) # Transfer to CPU for norm - - if verbose - @printf(" Iter %2d: ||r|| = %.6e", iter, norm_r) - end - - if norm_r < tol - if verbose - println("\n ✅ Converged!") - end - return iter - end - - # Jacobian-vector product (on GPU) - ε = T(1e-7) - function Jv_matvec_gpu!(out_cpu, v_cpu) - v = CuArray(v_cpu) - # temp = u + ε·v - temp .= u .+ ε .* v - # Jv = r(u + ε·v) - compute_residual_gpu!(Jv, K, temp, f, β) - # Jv = [r(u + ε·v) - r(u)] / ε - Jv .= (Jv .- r) ./ ε - out_cpu .= Array(Jv) - return out_cpu - end - Jv_op_gpu = MatrixFreeOperator(Jv_matvec_gpu!, n) - - # Solve on CPU (GMRES doesn't have GPU version) - r_cpu = Array(r) - u_cpu = Array(u) - du_cpu = zeros(T, n) - gmres!(du_cpu, Jv_op_gpu, -r_cpu; - abstol=gmres_tol, reltol=0, maxiter=50, verbose=false) - - # Anderson acceleration (on CPU) - if history_count >= 2 - # Build residual difference matrix - k = history_count - R_diff = zeros(T, n, k) - for i in 1:k - R_diff[:, i] .= R_history[i] .- r_cpu - end - - # Check condition number before QR - R_norm = norm(R_diff) - if R_norm < 1e-10 - # Matrix too small, use standard update - u .+= CuArray(du_cpu) - if verbose - println(" [Anderson: skipped (residuals too small)]") - end - else - # Least-squares with regularization - try - Q, Rt = qr(R_diff) - - # Add small regularization to diagonal if needed - Rt_diag = diag(Rt) - if any(abs.(Rt_diag) .< 1e-12) - # Add Tikhonov regularization - λ = 1e-8 - Rt_reg = Rt + λ * I - α = Rt_reg \ (Q' * r_cpu) - else - α = Rt \ (Q' * r_cpu) - end - - α ./= sum(α) # Normalize - - # Combine previous iterates - u_combined = zeros(T, n) - for i in 1:k - u_combined .+= α[i] .* U_history[i] - end - - # Update with combination (transfer to GPU) - u .= CuArray(u_combined .+ du_cpu) - - if verbose - println(" [Anderson: α=$(round.(α, digits=3))]") - end - catch e - # If QR fails, fall back to standard Newton - u .+= CuArray(du_cpu) - if verbose - println(" [Anderson: failed ($e), using standard update]") - end - end - end - else - # Standard Newton update (transfer du to GPU) - u .+= CuArray(du_cpu) - if verbose - println() - end - end - - # Store history (on CPU to avoid GPU memory overhead) - push!(R_history, copy(r_cpu)) - push!(U_history, copy(u_cpu)) - history_count += 1 - - # Maintain history size - if history_count > history_size - popfirst!(R_history) - popfirst!(U_history) - history_count = history_size - end - end - - if verbose - println(" ⚠️ Did not converge in $max_iter iterations") - end - - return max_iter -end - -# ============================================================================ -# Benchmark Runners -# ============================================================================ - -function benchmark_cpu(n::Int) - println("="^70) - println("CPU Benchmark: $n DOFs") - println("="^70) - - # Setup problem - T = Float64 - K = Matrix(Tridiagonal( - -ones(T, n - 1), - 2ones(T, n), - -ones(T, n - 1) - )) - f = ones(T, n) * 0.1 - β = T(1e-3) # Nonlinearity - - prob = NonlinearProblem(K, f, β, n) - - # Initial guess - u0 = zeros(T, n) - - # Allocate workspace - r = zeros(T, n) - du = zeros(T, n) - temp = zeros(T, n) - Jv = zeros(T, n) - - # Benchmark Traditional Newton - println("\n📊 Traditional Newton (Full Jacobian):") - u_trad = copy(u0) - t_trad = @elapsed iters_trad = newton_traditional!(u_trad, prob, r, du; verbose=false) - println(" Time: $(round(t_trad * 1000, digits=2)) ms") - println(" Iterations: $iters_trad") - println(" Time/iter: $(round(t_trad / iters_trad * 1000, digits=2)) ms") - - # Benchmark Matrix-Free - println("\n📊 Matrix-Free Newton-Krylov:") - u_mf = copy(u0) - t_mf = @elapsed iters_mf = newton_matrix_free!( - u_mf, prob, r, du, temp, Jv; verbose=false - ) - println(" Time: $(round(t_mf * 1000, digits=2)) ms") - println(" Iterations: $iters_mf") - println(" Time/iter: $(round(t_mf / iters_mf * 1000, digits=2)) ms") - - # Benchmark Anderson-Accelerated - println("\n📊 Anderson-Accelerated Matrix-Free:") - u_anderson = copy(u0) - t_anderson = @elapsed iters_anderson = anderson_newton_matrix_free!( - u_anderson, prob, r, du, temp, Jv; m=5, verbose=false - ) - println(" Time: $(round(t_anderson * 1000, digits=2)) ms") - println(" Iterations: $iters_anderson") - println(" Time/iter: $(round(t_anderson / iters_anderson * 1000, digits=2)) ms") - - # Speedups - println("\n✅ CPU Speedups:") - println(" Matrix-Free vs Traditional: $(round(t_trad / t_mf, digits=2))×") - println(" Anderson vs Traditional: $(round(t_trad / t_anderson, digits=2))×") - println(" Anderson vs Matrix-Free: $(round(t_mf / t_anderson, digits=2))×") - - println() -end - -function benchmark_gpu(n::Int) - if !USE_GPU - println("⚠️ GPU not available, skipping GPU benchmark\n") - return - end - - println("="^70) - println("GPU Benchmark: $n DOFs") - println("="^70) - - # Setup problem - T = Float64 - K_cpu = Matrix(Tridiagonal( - -ones(T, n - 1), - 2ones(T, n), - -ones(T, n - 1) - )) - f_cpu = ones(T, n) * 0.1 - β = T(1e-3) - - # Transfer to GPU - K_gpu = CuArray(K_cpu) - f_gpu = CuArray(f_cpu) - u0_gpu = CUDA.zeros(T, n) - - # Benchmark Matrix-Free on GPU - println("\n📊 Matrix-Free Newton-Krylov (GPU):") - u_gpu = copy(u0_gpu) - - # Warmup - newton_matrix_free_gpu!(u_gpu, K_gpu, f_gpu, β; max_iter=2, verbose=false) - - # Benchmark - CUDA.synchronize() - t_gpu = CUDA.@elapsed begin - iters_gpu = newton_matrix_free_gpu!(u_gpu, K_gpu, f_gpu, β; verbose=false) - CUDA.synchronize() - end - - println(" Time: $(round(t_gpu * 1000, digits=2)) ms") - println(" Iterations: $iters_gpu") - println(" Time/iter: $(round(t_gpu / iters_gpu * 1000, digits=2)) ms") - - # Compare with CPU - prob_cpu = NonlinearProblem(K_cpu, f_cpu, β, n) - u_cpu = zeros(T, n) - r = zeros(T, n) - du = zeros(T, n) - temp = zeros(T, n) - Jv = zeros(T, n) - - t_cpu = @elapsed iters_cpu = newton_matrix_free!( - u_cpu, prob_cpu, r, du, temp, Jv; verbose=false - ) - - println("\n✅ GPU vs CPU Speedup: $(round(t_cpu / t_gpu, digits=2))×") - println(" CPU: $(round(t_cpu * 1000, digits=2)) ms") - println(" GPU: $(round(t_gpu * 1000, digits=2)) ms") - - # Benchmark Anderson-Accelerated on GPU - println("\n📊 Anderson-Accelerated Newton-Krylov (GPU):") - u_gpu_anderson = copy(u0_gpu) - - # Warmup - anderson_newton_matrix_free_gpu!(u_gpu_anderson, K_gpu, f_gpu, β; max_iter=2, verbose=false) - - # Benchmark - CUDA.synchronize() - t_gpu_anderson = CUDA.@elapsed begin - iters_gpu_anderson = anderson_newton_matrix_free_gpu!(u_gpu_anderson, K_gpu, f_gpu, β; verbose=false) - CUDA.synchronize() - end - - println(" Time: $(round(t_gpu_anderson * 1000, digits=2)) ms") - println(" Iterations: $iters_gpu_anderson") - println(" Time/iter: $(round(t_gpu_anderson / iters_gpu_anderson * 1000, digits=2)) ms") - - # Compare with CPU Anderson - u_cpu_anderson = zeros(T, n) - t_cpu_anderson = @elapsed iters_cpu_anderson = anderson_newton_matrix_free!( - u_cpu_anderson, prob_cpu, r, du, temp, Jv; verbose=false - ) - - println("\n✅ GPU vs CPU Speedup (Anderson): $(round(t_cpu_anderson / t_gpu_anderson, digits=2))×") - println(" CPU: $(round(t_cpu_anderson * 1000, digits=2)) ms") - println(" GPU: $(round(t_gpu_anderson * 1000, digits=2)) ms") - - # Overall comparison - println("\n📊 Summary:") - println(" Matrix-Free GPU speedup: $(round(t_cpu / t_gpu, digits=2))×") - println(" Anderson GPU speedup: $(round(t_cpu_anderson / t_gpu_anderson, digits=2))×") - - println() -end - -# ============================================================================ -# Main -# ============================================================================ - -function main() - println("\n" * "="^70) - println("Matrix-Free Newton-Krylov GPU Benchmark") - println("="^70) - println() - - # Test sizes (reasonable for demonstration) - sizes = [1000, 5_000, 10_000] - - for n in sizes - # CPU comparison - benchmark_cpu(n) - - # GPU benchmark - if USE_GPU - benchmark_gpu(n) - end - end - - println("="^70) - println("Benchmark Complete!") - println("="^70) - println() - println("Key Findings:") - println(" - Matrix-Free eliminates Jacobian assembly cost") - println(" - Anderson acceleration reduces Newton iterations") - println(" - GPU provides additional speedup for large problems") - println(" - Total speedup: 5-10× depending on problem size") - println() -end - -if abspath(PROGRAM_FILE) == @__FILE__ - main() -end diff --git a/benchmarks/multigpu_mpi_benchmark.jl b/benchmarks/multigpu_mpi_benchmark.jl deleted file mode 100755 index 99b31b5..0000000 --- a/benchmarks/multigpu_mpi_benchmark.jl +++ /dev/null @@ -1,571 +0,0 @@ -#!/usr/bin/env julia -# -# Multi-GPU Nodal Assembly Benchmark with MPI + CUDA -# -# Usage: -# mpirun -np 2 julia --project=. benchmarks/multigpu_mpi_benchmark.jl -# mpirun -np 4 julia --project=. benchmarks/multigpu_mpi_benchmark.jl -# -# Each MPI rank gets one GPU - -using MPI -using CUDA -using LinearAlgebra -using Printf - -MPI.Init() - -const comm = MPI.COMM_WORLD -const rank = MPI.Comm_rank(comm) -const nranks = MPI.Comm_size(comm) - -# Set GPU device based on rank -if CUDA.functional() - CUDA.device!(rank % CUDA.ndevices()) - if rank == 0 - println("="^70) - println("Multi-GPU Nodal Assembly Benchmark (MPI + CUDA)") - println("="^70) - println("MPI ranks: $nranks") - println("CUDA devices: $(CUDA.ndevices())") - println("CUDA functional: $(CUDA.functional())") - println("="^70) - println() - end -else - if rank == 0 - println("ERROR: CUDA not functional!") - println("Install CUDA.jl: using Pkg; Pkg.add(\"CUDA\")") - end - MPI.Finalize() - exit(1) -end - -# ============================================================================ -# Data Structures -# ============================================================================ - -struct Node - id::Int32 - x::Float32 - y::Float32 - z::Float32 -end - -struct Element - id::Int32 - connectivity::NTuple{8,Int32} # Hex8 -end - -struct Partition - rank::Int - owned_nodes::UnitRange{Int} - ghost_nodes::Vector{Int} - local_elements::Vector{Int} - node_to_elements::Vector{Vector{Int}} - interface_neighbors::Vector{Int} # Neighbor ranks - interface_send::Dict{Int,Vector{Int}} # rank → local DOF indices to send - interface_recv::Dict{Int,Vector{Int}} # rank → local DOF indices to receive -end - -# ============================================================================ -# Mesh Generation -# ============================================================================ - -function create_hex_mesh(nx, ny, nz) - """Create structured hexahedral mesh""" - n_nodes = nx * ny * nz - n_elements = (nx - 1) * (ny - 1) * (nz - 1) - - nodes = Node[] - for k in 1:nz, j in 1:ny, i in 1:nx - node_id = Int32((k - 1) * nx * ny + (j - 1) * nx + i) - push!(nodes, Node(node_id, Float32(i), Float32(j), Float32(k))) - end - - elements = Element[] - for k in 1:(nz-1), j in 1:(ny-1), i in 1:(nx-1) - n1 = Int32((k - 1) * nx * ny + (j - 1) * nx + i) - n2 = n1 + 1 - n3 = n2 + nx - n4 = n1 + nx - n5 = n1 + nx * ny - n6 = n2 + nx * ny - n7 = n3 + nx * ny - n8 = n4 + nx * ny - - elem_id = Int32(length(elements) + 1) - push!(elements, Element(elem_id, (n1, n2, n3, n4, n5, n6, n7, n8))) - end - - return nodes, elements -end - -function build_node_to_elements(nodes, elements) - node_to_elems = [Int[] for _ in 1:length(nodes)] - - for (elem_id, element) in enumerate(elements) - for node_id in element.connectivity - push!(node_to_elems[node_id], elem_id) - end - end - - return node_to_elems -end - -# ============================================================================ -# Partitioning -# ============================================================================ - -function partition_mesh_for_rank(nodes, elements, my_rank, n_ranks) - """Create partition for this MPI rank""" - n_nodes = length(nodes) - nodes_per_rank = ceil(Int, n_nodes / n_ranks) - - # Owned nodes - start_node = my_rank * nodes_per_rank + 1 - end_node = min((my_rank + 1) * nodes_per_rank, n_nodes) - owned_nodes = start_node:end_node - - node_to_elems = build_node_to_elements(nodes, elements) - - # Find local elements (touching owned nodes) - local_elements = Int[] - ghost_nodes = Set{Int}() - - for (elem_id, element) in enumerate(elements) - if any(Int(nid) in owned_nodes for nid in element.connectivity) - push!(local_elements, elem_id) - - for nid in element.connectivity - if !(Int(nid) in owned_nodes) - push!(ghost_nodes, Int(nid)) - end - end - end - end - - # Build local node_to_elements - local_node_to_elems = [ - filter(eid -> eid in local_elements, node_to_elems[nid]) - for nid in owned_nodes - ] - - # Find interface nodes with each neighbor - interface_send = Dict{Int,Vector{Int}}() - interface_recv = Dict{Int,Vector{Int}}() - - for neighbor_rank in 0:(n_ranks-1) - if neighbor_rank == my_rank - continue - end - - neighbor_start = neighbor_rank * nodes_per_rank + 1 - neighbor_end = min((neighbor_rank + 1) * nodes_per_rank, n_nodes) - neighbor_owned = neighbor_start:neighbor_end - - # Nodes I own that neighbor needs (I send) - send_nodes = Int[] - for elem_id in local_elements - element = elements[elem_id] - has_neighbor = any(Int(nid) in neighbor_owned for nid in element.connectivity) - if has_neighbor - for nid in element.connectivity - if Int(nid) in owned_nodes && !(Int(nid) in send_nodes) - push!(send_nodes, Int(nid)) - end - end - end - end - - # Nodes neighbor owns that I need (I receive) - recv_nodes = Int[] - for nid in ghost_nodes - if Int(nid) in neighbor_owned - push!(recv_nodes, Int(nid)) - end - end - - if !isempty(send_nodes) || !isempty(recv_nodes) - # Convert to local DOF indices - send_dofs = Int[] - for nid in send_nodes - local_idx = nid - start_node + 1 - for d in 0:2 - push!(send_dofs, (local_idx - 1) * 3 + d + 1) - end - end - - recv_dofs = Int[] - for nid in recv_nodes - ghost_idx = findfirst(==(nid), sort(collect(ghost_nodes))) - for d in 0:2 - # Ghost DOFs come after owned DOFs - push!(recv_dofs, length(owned_nodes) * 3 + (ghost_idx - 1) * 3 + d + 1) - end - end - - if !isempty(send_dofs) - interface_send[neighbor_rank] = send_dofs - end - if !isempty(recv_dofs) - interface_recv[neighbor_rank] = recv_dofs - end - end - end - - interface_neighbors = sort(collect(keys(interface_send) ∪ keys(interface_recv))) - - return Partition( - my_rank, - owned_nodes, - sort(collect(ghost_nodes)), - local_elements, - local_node_to_elems, - interface_neighbors, - interface_send, - interface_recv - ) -end - -# ============================================================================ -# GPU Kernel: Nodal Assembly -# ============================================================================ - -function gpu_matvec_kernel!( - y::CuDeviceArray{Float32,1}, - x::CuDeviceArray{Float32,1}, - nodes::CuDeviceArray{Node,1}, - elements::CuDeviceArray{Element,1}, - node_to_elems_offsets::CuDeviceArray{Int32,1}, - node_to_elems_data::CuDeviceArray{Int32,1}, - n_owned_nodes::Int32, -) - idx = (blockIdx().x - 1) * blockDim().x + threadIdx().x - - if idx > n_owned_nodes - return - end - - # This thread processes owned node idx - node = nodes[idx] - - dof_start = (idx - 1) * 3 + 1 - - # Initialize nodal contribution - y1 = Float32(0.0) - y2 = Float32(0.0) - y3 = Float32(0.0) - - # Get connected elements using CSR-like format (1-based indexing) - if idx + Int32(1) > length(node_to_elems_offsets) - return - end - - elem_start = node_to_elems_offsets[idx] + Int32(1) - elem_end = node_to_elems_offsets[idx+Int32(1)] - - for i in elem_start:elem_end - if i > length(node_to_elems_data) - return - end - elem_id = node_to_elems_data[i] - if elem_id > length(elements) - return - end - element = elements[elem_id] - - # Add contribution from all nodes in this element - for j in 1:8 - nid = element.connectivity[j] - x_dof_start = (nid - 1) * 3 + 1 - - # Mock stiffness contribution - y1 += Float32(0.1) * x[x_dof_start] - y2 += Float32(0.1) * x[x_dof_start+1] - y3 += Float32(0.1) * x[x_dof_start+2] - end - end - - # Write to output - y[dof_start] = y1 - y[dof_start+1] = y2 - y[dof_start+2] = y3 - - return nothing -end - -# ============================================================================ -# Multi-GPU Communication -# ============================================================================ - -function exchange_ghost_values!( - x_local::CuArray{Float32,1}, - partition::Partition, - comm::MPI.Comm -) - """Exchange interface DOF values between MPI ranks""" - - # Prepare send/recv buffers on CPU - send_bufs = Dict{Int,Vector{Float32}}() - recv_bufs = Dict{Int,Vector{Float32}}() - - # Copy data from GPU to CPU for sending - x_cpu = Array(x_local) - - for neighbor in partition.interface_neighbors - if haskey(partition.interface_send, neighbor) - send_dofs = partition.interface_send[neighbor] - send_bufs[neighbor] = x_cpu[send_dofs] - end - - if haskey(partition.interface_recv, neighbor) - recv_dofs = partition.interface_recv[neighbor] - recv_bufs[neighbor] = zeros(Float32, length(recv_dofs)) - end - end - - # MPI communication - requests = MPI.Request[] - - # Post receives - for neighbor in partition.interface_neighbors - if haskey(recv_bufs, neighbor) - req = MPI.Irecv!(recv_bufs[neighbor], comm; source=neighbor, tag=neighbor) - push!(requests, req) - end - end - - # Post sends - for neighbor in partition.interface_neighbors - if haskey(send_bufs, neighbor) - req = MPI.Isend(send_bufs[neighbor], comm; dest=neighbor, tag=partition.rank) - push!(requests, req) - end - end - - # Wait for all communications - MPI.Waitall(requests) - - # Copy received data back to GPU - for neighbor in partition.interface_neighbors - if haskey(partition.interface_recv, neighbor) - recv_dofs = partition.interface_recv[neighbor] - x_cpu[recv_dofs] .= recv_bufs[neighbor] - end - end - - # Update GPU array - copyto!(x_local, x_cpu) -end - -# ============================================================================ -# Benchmark -# ============================================================================ - -function run_multigpu_benchmark(nx, ny, nz, n_warmup=5, n_runs=10) - if rank == 0 - println("\n" * "="^70) - println("Multi-GPU Benchmark: $nx × $ny × $nz mesh") - println("="^70) - end - - # Create full mesh on all ranks - nodes, elements = create_hex_mesh(nx, ny, nz) - - if rank == 0 - println(" Total nodes: ", length(nodes)) - println(" Total elements: ", length(elements)) - println(" Total DOFs: ", 3 * length(nodes)) - end - - # Partition for this rank - partition = partition_mesh_for_rank(nodes, elements, rank, nranks) - - n_owned = length(partition.owned_nodes) - n_ghost = length(partition.ghost_nodes) - n_local_dofs = 3 * (n_owned + n_ghost) - - println("Rank $rank: $n_owned owned nodes, $n_ghost ghost nodes, " * - "$(length(partition.local_elements)) elements") - - # Prepare GPU data - local_nodes = [nodes[i] for i in vcat(collect(partition.owned_nodes), partition.ghost_nodes)] - - # Create mapping from global node ID to local index - global_to_local_node = Dict{Int,Int32}() - for (local_idx, global_nid) in enumerate(vcat(collect(partition.owned_nodes), partition.ghost_nodes)) - global_to_local_node[global_nid] = Int32(local_idx) - end - - # Remap element connectivity to local node indices - local_elements = Element[] - for global_eid in partition.local_elements - element = elements[global_eid] - # Convert global node IDs to local indices - local_conn = ntuple(8) do i - global_nid = Int(element.connectivity[i]) - global_to_local_node[global_nid] - end - push!(local_elements, Element(element.id, local_conn)) - end - - # Create mapping from global element ID to local index (for CSR data) - global_to_local_elem = Dict{Int,Int}() - for (local_idx, global_id) in enumerate(partition.local_elements) - global_to_local_elem[global_id] = local_idx - end - - # Convert node_to_elements to GPU-friendly flat format - # Format: offsets array + flat data array (CSR-like) - # IMPORTANT: Convert global element IDs to local indices - node_to_elems_offsets = Int32[0] - node_to_elems_data = Int32[] - for arr in partition.node_to_elements - # Map global element IDs to local indices - local_indices = [global_to_local_elem[global_id] for global_id in arr] - append!(node_to_elems_data, Int32.(local_indices)) - push!(node_to_elems_offsets, length(node_to_elems_data)) - end - - # Debug: check element ID range - if rank == 0 && length(node_to_elems_data) > 0 - min_elem_id = minimum(node_to_elems_data) - max_elem_id = maximum(node_to_elems_data) - println("\nCSR data element ID range: $min_elem_id to $max_elem_id") - println("Local elements array size: $(length(local_elements))") - if max_elem_id > length(local_elements) - println("❌ WARNING: Element ID $max_elem_id > array size $(length(local_elements))") - end - end - - # Transfer to GPU - nodes_gpu = CuArray(local_nodes) - elements_gpu = CuArray(local_elements) - node_to_elems_offsets_gpu = CuArray(node_to_elems_offsets) - node_to_elems_data_gpu = CuArray(node_to_elems_data) - - # Debug: print array sizes - if rank == 0 - println("\nArray sizes on GPU:") - println(" nodes: $(length(nodes_gpu))") - println(" elements: $(length(elements_gpu))") - println(" node_to_elems_offsets: $(length(node_to_elems_offsets_gpu))") - println(" node_to_elems_data: $(length(node_to_elems_data_gpu))") - println(" Expected offsets length: $(n_owned + 1)") - end - - # Test vectors - x_local = CUDA.rand(Float32, n_local_dofs) - y_local = CUDA.zeros(Float32, n_local_dofs) - - # Kernel launch parameters - threads_per_block = 256 - n_blocks = cld(n_owned, threads_per_block) - - if rank == 0 - println("\nGPU configuration:") - println(" Threads per block: $threads_per_block") - println(" Blocks per rank: $n_blocks") - end - - # Warmup - for _ in 1:n_warmup - exchange_ghost_values!(x_local, partition, comm) - CUDA.@sync @cuda threads = threads_per_block blocks = n_blocks gpu_matvec_kernel!( - y_local, x_local, nodes_gpu, elements_gpu, - node_to_elems_offsets_gpu, node_to_elems_data_gpu, Int32(n_owned) - ) - end - - MPI.Barrier(comm) - - # Benchmark - times = Float64[] - comm_times = Float64[] - compute_times = Float64[] - - for _ in 1:n_runs - t_start = time_ns() - - # Communication - t_comm_start = time_ns() - exchange_ghost_values!(x_local, partition, comm) - MPI.Barrier(comm) - t_comm_end = time_ns() - - # Computation - t_compute_start = time_ns() - CUDA.@sync @cuda threads = threads_per_block blocks = n_blocks gpu_matvec_kernel!( - y_local, x_local, nodes_gpu, elements_gpu, - node_to_elems_offsets_gpu, node_to_elems_data_gpu, Int32(n_owned) - ) - MPI.Barrier(comm) - t_compute_end = time_ns() - - t_end = time_ns() - - push!(times, (t_end - t_start) / 1e9) - push!(comm_times, (t_comm_end - t_comm_start) / 1e9) - push!(compute_times, (t_compute_end - t_compute_start) / 1e9) - end - - # Gather results - local_time = minimum(times) - local_comm = minimum(comm_times) - local_compute = minimum(compute_times) - - all_times = MPI.Gather(local_time, 0, comm) - all_comm = MPI.Gather(local_comm, 0, comm) - all_compute = MPI.Gather(local_compute, 0, comm) - - if rank == 0 - println("\nResults:") - println(" Rank | Owned Nodes | Total Time | Comm Time | Compute Time | Comm %") - println(" " * "-"^70) - for r in 0:(nranks-1) - nodes_str = lpad(string(length(partition.owned_nodes)), 11) - total_str = @sprintf("%.3f ms", all_times[r+1] * 1000) - comm_str = @sprintf("%.3f ms", all_comm[r+1] * 1000) - compute_str = @sprintf("%.3f ms", all_compute[r+1] * 1000) - comm_pct = @sprintf("%.1f%%", all_comm[r+1] / all_times[r+1] * 100) - - println(" $r | $nodes_str | $(lpad(total_str, 10)) | " * - "$(lpad(comm_str, 9)) | $(lpad(compute_str, 12)) | $(lpad(comm_pct, 6))") - end - - max_time = maximum(all_times) - avg_compute = sum(all_compute) / length(all_compute) - avg_comm = sum(all_comm) / length(all_comm) - - println("\n Maximum time: ", @sprintf("%.3f ms", max_time * 1000)) - println(" Average compute: ", @sprintf("%.3f ms", avg_compute * 1000)) - println(" Average communication: ", @sprintf("%.3f ms", avg_comm * 1000)) - println(" Communication overhead: ", @sprintf("%.1f%%", avg_comm / max_time * 100)) - - throughput = length(nodes) / max_time / 1e6 - println(" Throughput: ", @sprintf("%.2f Mnodes/s", throughput)) - end -end - -# ============================================================================ -# Main -# ============================================================================ - -if rank == 0 - println("Starting benchmarks...") - println() -end - -# Run benchmarks with increasing mesh sizes -run_multigpu_benchmark(30, 30, 30) -run_multigpu_benchmark(50, 50, 50) -run_multigpu_benchmark(70, 70, 70) - -if rank == 0 - println("\n" * "="^70) - println("✓ Multi-GPU Benchmark Complete") - println("="^70) -end - -MPI.Finalize() diff --git a/benchmarks/multigpu_results_2025-11-09.md b/benchmarks/multigpu_results_2025-11-09.md deleted file mode 100644 index 928bf54..0000000 --- a/benchmarks/multigpu_results_2025-11-09.md +++ /dev/null @@ -1,329 +0,0 @@ -# Multi-GPU Nodal Assembly Results - -**Date:** November 9, 2025 -**Hardware:** 2× MPI ranks, 1× NVIDIA RTX A2000 12GB (shared between ranks) -**Software:** Julia 1.12.1, CUDA.jl, MPI.jl - ---- - -## Executive Summary - -**✅ Multi-GPU implementation WORKS!** Successfully ran multi-GPU nodal assembly with MPI + CUDA. - -**Key Achievement:** Implemented nodal assembly on GPU with: -- CSR-format node-to-elements connectivity (zero allocation) -- Proper global-to-local index remapping for elements and nodes -- MPI ghost value exchange for domain interfaces -- Validated correctness (all ranks complete successfully) - -**Performance:** -- **Throughput:** 115-302 Mnodes/s (scales with mesh size) -- **Communication overhead:** 29-61% (MPI transfers dominate for small/medium meshes) -- **Compute performance:** GPU kernel is fast (0.16-0.56 ms), communication is bottleneck - ---- - -## Detailed Results - -### Benchmark Configuration -- **MPI ranks:** 2 -- **GPU per rank:** 1 (shared device for both ranks in this test) -- **Partitioning:** Slab decomposition (nodes split evenly) -- **Element type:** Hex8 (8-node hexahedron) -- **Kernel:** Mock stiffness (simplified matvec for validation) -- **Warmup:** 10 iterations -- **Measurement:** 100 iterations (timed) - -### Performance Table - -| Mesh Size | Total Nodes | Total DOFs | Owned/Rank | Ghost/Rank | Throughput | Comm % | -|-----------|-------------|------------|------------|------------|------------|--------| -| 30³ | 27,000 | 81,000 | 13,500 | 900 | 114.84 Mnodes/s | 29.1% | -| 50³ | 125,000 | 375,000 | 62,500 | 2,500 | 130.64 Mnodes/s | 60.7% | -| 70³ | 343,000 | 1,029,000 | 171,500 | 4,900 | 301.83 Mnodes/s | 50.9% | - -### Detailed Timing Breakdown - -**30×30×30 mesh:** -``` -Rank 0: Total 0.235 ms = Compute 0.159 ms + Comm 0.069 ms (29.4%) -Rank 1: Total 0.227 ms = Compute 0.159 ms + Comm 0.068 ms (29.9%) -Throughput: 114.84 Mnodes/s -``` - -**50×50×50 mesh:** -``` -Rank 0: Total 0.957 ms = Compute 0.373 ms + Comm 0.581 ms (60.7%) -Rank 1: Total 0.957 ms = Compute 0.375 ms + Comm 0.581 ms (60.7%) -Throughput: 130.64 Mnodes/s -``` - -**70×70×70 mesh:** -``` -Rank 0: Total 1.136 ms = Compute 0.557 ms + Comm 0.579 ms (50.9%) -Rank 1: Total 1.136 ms = Compute 0.557 ms + Comm 0.579 ms (50.9%) -Throughput: 301.83 Mnodes/s -``` - ---- - -## Comparison with CPU Baseline - -**From `nodal_assembly_scalability.jl` (validated Nov 9, 2025):** - -### CPU Multi-Threading (8 threads, single node) - -| Mesh Size | Nodes | Single-Thread | 8 Threads | Speedup | Efficiency | -|-----------|---------|---------------|-----------|---------|------------| -| 20³ | 8,000 | 3.4 Mnodes/s | 49.6 Mnodes/s | 14.6× | 182% | -| 40³ | 64,000 | 3.6 Mnodes/s | 54.8 Mnodes/s | 15.2× | 189% | -| 60³ | 216,000 | 3.6 Mnodes/s | 47.9 Mnodes/s | 13.3× | 166% | - -### CPU Partitioned (4 partitions, sequential) - -| Mesh Size | Nodes | Throughput | Speedup vs Single-Thread | Interface Overhead | -|-----------|---------|------------|--------------------------|-------------------| -| 20³ | 8,000 | 29.7 Mnodes/s | 8.5× | 40.1% | -| 40³ | 64,000 | 28.8 Mnodes/s | 8.0× | 22.4% | -| 60³ | 216,000 | 27.3 Mnodes/s | 7.6× | 10.3% | - -### GPU vs CPU Comparison - -**Throughput Comparison (approximate mesh sizes):** - -| Mesh | CPU Single-Thread | CPU 8-Thread | CPU 4-Partition | GPU 2-Rank (MPI) | GPU Speedup vs 8-Thread | -|------|-------------------|--------------|-----------------|------------------|-------------------------| -| ~30³ | 3.5 Mnodes/s | ~50 Mnodes/s | ~29 Mnodes/s | 114.84 Mnodes/s | **2.3×** | -| ~60³ | 3.6 Mnodes/s | 47.9 Mnodes/s | 27.3 Mnodes/s | ~200 Mnodes/s (interpolated) | **4.2×** | -| 70³ | 3.6 Mnodes/s | ~48 Mnodes/s (est) | ~28 Mnodes/s (est) | 301.83 Mnodes/s | **6.3×** | - -**Key Observations:** -1. ✅ GPU is **2-6× faster** than CPU 8-thread for same mesh size -2. ✅ GPU throughput scales better with mesh size (114 → 302 Mnodes/s) -3. ⚠️ GPU communication overhead (29-61%) higher than CPU partitioned (10-40%) -4. 🎯 GPU shines on larger meshes (70³: 6.3× faster than CPU) - ---- - -## Analysis & Insights - -### What Worked Well ✅ - -1. **Nodal assembly pattern on GPU:** - - Each thread processes one node (no atomics!) - - Gathers contributions from connected elements - - Direct write to owned DOFs (no race conditions) - -2. **CSR-format node-to-elements:** - - `offsets[node_id]` → start of element list - - `data[offsets[i]:offsets[i+1]]` → element IDs - - Zero allocation, type-stable, GPU-friendly - -3. **Global-to-local index remapping:** - - Element IDs: Global mesh → Local partition indices - - Node IDs in connectivity: Global mesh → Local partition indices - - Critical for correctness with sliced arrays - -4. **MPI ghost exchange:** - - Interface nodes identified correctly - - Ghost values exchanged between ranks - - Enables domain decomposition - -### Performance Bottlenecks ⚠️ - -1. **Communication overhead dominates small/medium meshes:** - - 30³ mesh: 29% communication - - 50³ mesh: **61% communication** (worst case!) - - 70³ mesh: 51% communication - - **Root cause:** MPI transfers CPU ↔ GPU for every iteration - -2. **Single GPU shared between 2 MPI ranks:** - - Both ranks compete for same GPU - - No true parallelism in this test configuration - - Need multiple GPUs for real multi-GPU scaling - -3. **Mock kernel (simplified stiffness):** - - Real FEM kernel would be more compute-intensive - - Would reduce communication percentage - - Current kernel is memory-bound - -### Opportunities for Improvement 🎯 - -1. **CUDA-aware MPI:** - - Direct GPU-to-GPU transfers (no CPU staging) - - Can reduce communication time by 50-80% - - Requires recompilation of MPI with CUDA support - -2. **Multiple physical GPUs:** - - Current test uses 1 GPU for 2 ranks (shared) - - True multi-GPU: Each rank gets own GPU - - Would enable concurrent execution - -3. **Larger elements (higher-order):** - - Tet10, Hex20, Hex27 have more work per element - - More compute per node → reduces communication % - - Better compute/communication ratio - -4. **Full element stiffness:** - - Real FEM: Integration loops, material models, plasticity - - 10-100× more work per element - - Communication becomes negligible (<5%) - -5. **Batched assembly:** - - Assemble multiple timesteps before MPI sync - - Amortize communication cost - - Useful for explicit dynamics - ---- - -## Technical Details - -### Data Structures - -**Node (immutable, 32 bytes):** -```julia -struct Node - id::Int32 - x::Float32 - y::Float32 - z::Float32 -end -``` - -**Element (immutable, 36 bytes):** -```julia -struct Element - id::Int32 - connectivity::NTuple{8, Int32} # Hex8 -end -``` - -**Partition:** -- `owned_nodes`: Nodes owned by this rank -- `ghost_nodes`: Nodes owned by neighbors (interface) -- `local_elements`: Elements touching owned nodes -- `node_to_elements`: Inverse connectivity (node → elements) -- `interface_send/recv`: MPI communication patterns - -### GPU Kernel (Simplified) - -```julia -function gpu_matvec_kernel!(y, x, nodes, elements, offsets, data, n_owned) - idx = (blockIdx().x - 1) * blockDim().x + threadIdx().x - - if idx > n_owned - return # Thread beyond owned nodes - end - - # Initialize accumulator - fx = fy = fz = 0.0f0 - - # Loop over connected elements (CSR access) - elem_start = offsets[idx] + 1 - elem_end = offsets[idx + 1] - for i in elem_start:elem_end - elem_id = data[i] - element = elements[elem_id] - - # Gather from element nodes - for j in 1:8 - nid = element.connectivity[j] - dof_base = (nid - 1) * 3 + 1 - fx += 0.1f0 * x[dof_base] - fy += 0.1f0 * x[dof_base + 1] - fz += 0.1f0 * x[dof_base + 2] - end - end - - # Write result (owned DOFs only) - dof_base = (idx - 1) * 3 + 1 - y[dof_base] = fx - y[dof_base + 1] = fy - y[dof_base + 2] = fz -end -``` - -### Key Implementation Challenges & Solutions - -**Challenge 1:** CuArray{CuArray} not supported -**Solution:** Flatten to CSR format (offsets + data arrays) - -**Challenge 2:** Global element IDs in CSR data, but local array -**Solution:** Create `global_to_local_elem` mapping, remap before GPU transfer - -**Challenge 3:** Global node IDs in element connectivity -**Solution:** Create `global_to_local_node` mapping, rebuild elements with local indices - -**Challenge 4:** BoundsError during kernel execution -**Solution:** All three index spaces must be consistent (nodes, elements, DOFs) - ---- - -## Conclusions - -### Claims We Can Now Make ✅ - -1. ✅ **Nodal assembly works on GPU** - Validated with working implementation -2. ✅ **2-6× faster than CPU multi-threading** - Real measurements on same mesh -3. ✅ **Scales to 343K nodes / 1M DOFs** - Successfully ran 70³ mesh -4. ✅ **Communication overhead acceptable** - 29-61% (will improve with CUDA-aware MPI) -5. ✅ **CSR format enables zero-allocation** - No dynamic memory in kernel - -### Claims We CANNOT Yet Make ⚠️ - -1. ⚠️ **Multi-GPU strong scaling** - Only tested 1 GPU with 2 ranks (not true multi-GPU) -2. ⚠️ **Production-ready performance** - Mock kernel, needs real FEM stiffness -3. ⚠️ **Weak scaling to N GPUs** - Need cluster with multiple GPUs -4. ⚠️ **Better than Gridap/Ferrite** - Haven't compared with other libraries -5. ⚠️ **Contact mechanics on GPU** - Not yet implemented - -### Next Steps 🎯 - -**Immediate (validate architecture):** -1. Test with multiple physical GPUs (2-4 GPUs on cluster) -2. Implement real element stiffness (not mock) -3. Measure CUDA-aware MPI improvement -4. Add higher-order elements (Tet10, Hex20) - -**Short-term (production features):** -1. Material state updates on GPU (plasticity, damage) -2. Contact detection and assembly on GPU -3. Preconditioned GMRES on GPU (full solver) -4. Integration with JuliaFEM element library - -**Long-term (scale-up):** -1. Weak scaling study (1-64 GPUs) -2. Strong scaling study (fixed problem, varying GPUs) -3. Comparison with Gridap.jl + PETSc -4. Real-world contact mechanics problem (1M+ DOFs) - ---- - -## Files & Artifacts - -**Benchmark code:** -- `benchmarks/multigpu_mpi_benchmark.jl` (555 lines, working) - -**CPU baseline (validated):** -- `benchmarks/nodal_assembly_scalability.jl` (500 lines) - -**Documentation:** -- `docs/book/multigpu_nodal_assembly.md` (design, needs update with real data) -- `docs/book/nodal_assembly_gpu_pattern.md` (architecture) -- `demos/gpu_nodal_assembly_demo.jl` (educational demo) - -**This report:** -- `benchmarks/multigpu_results_2025-11-09.md` - ---- - -## Acknowledgments - -**User (Jukka):** Demanded real measurements, not designs. Caught AI making unvalidated claims. Insisted on "Just run" - forcing validation before documentation. - -**Key Insight:** "Did you actually run that code?" - Best engineering feedback possible. No more design documents without validation! - ---- - -**Status:** ✅ Multi-GPU architecture VALIDATED -**Verdict:** Nodal assembly on GPU is **feasible and fast**. Communication overhead acceptable. Ready for production implementation. diff --git a/benchmarks/neo_hookean_analysis.jl b/benchmarks/neo_hookean_analysis.jl deleted file mode 100644 index 3eae963..0000000 --- a/benchmarks/neo_hookean_analysis.jl +++ /dev/null @@ -1,260 +0,0 @@ -""" -Performance Analysis: NeoHookean Hyperelastic Material - -Benchmarks automatic differentiation overhead in hyperelastic stress computation. - -Key Questions: -1. What is the cost of AD compared to LinearElastic? -2. Is the implementation allocation-free? -3. How does performance scale with problem size? -4. What is the breakdown of strain energy vs stress vs tangent? - -Results inform whether AD is suitable for production FEM assembly loops. -""" - -using BenchmarkTools -using Tensors -using LinearAlgebra -using Printf - -# Load implementations -include("../src/materials/abstract_material.jl") -include("../src/materials/linear_elastic.jl") -include("../src/materials/neo_hookean.jl") - -println("="^80) -println("NeoHookean Hyperelastic Material - Performance Analysis") -println("="^80) -println() - -# ============================================================================= -# Test 1: Single Stress Evaluation (typical FEM use case) -# ============================================================================= -println("Test 1: Single Stress Evaluation") -println("-"^80) - -# Create materials -neo = NeoHookean(E_mod=200e9, nu=0.3) # Steel-like properties -linear = LinearElastic(E=200e9, ν=0.3) - -# Test strain (moderate deformation) -E_strain = SymmetricTensor{2,3}((0.01, 0.005, 0.003, -0.002, 0.004, 0.006)) - -# Compile first -compute_stress(neo, E_strain, nothing, 0.0) -compute_stress(linear, E_strain, nothing, 0.0) - -# Benchmark -println("\nLinearElastic (manual derivatives):") -t_linear = @benchmark compute_stress($linear, $E_strain, nothing, 0.0) -display(t_linear) -println() - -println("\nNeoHookean (automatic differentiation):") -t_neo = @benchmark compute_stress($neo, $E_strain, nothing, 0.0) -display(t_neo) -println() - -# Compute overhead -overhead = median(t_neo).time / median(t_linear).time -println("\nAD Overhead: $(round(overhead, digits=1))x") -println("Absolute difference: $(round((median(t_neo).time - median(t_linear).time)/1e3, digits=1)) μs") - -# ============================================================================= -# Test 2: Allocation Check -# ============================================================================= -println("\n" * "="^80) -println("Test 2: Allocation Analysis") -println("-"^80) - -allocs_neo = @allocated compute_stress(neo, E_strain, nothing, 0.0) -allocs_linear = @allocated compute_stress(linear, E_strain, nothing, 0.0) - -println("LinearElastic allocations: $allocs_linear bytes") -println("NeoHookean allocations: $allocs_neo bytes") - -if allocs_neo == 0 - println("✅ Zero-allocation achieved!") -else - println("⚠️ Allocations detected - investigate") -end - -# ============================================================================= -# Test 3: Component Breakdown (where does time go?) -# ============================================================================= -println("\n" * "="^80) -println("Test 3: Component Breakdown") -println("-"^80) - -# Test strain energy only -C = 2E_strain + one(E_strain) -println("\nStrain energy computation:") -t_energy = @benchmark strain_energy($neo, $C) -display(t_energy) -println() - -# Stress only (includes strain energy + gradient) -println("\nComplete stress computation (energy + gradient + hessian):") -display(t_neo) -println() - -energy_fraction = median(t_energy).time / median(t_neo).time -println("Strain energy is $(round(energy_fraction*100, digits=1))% of total time") -println("AD overhead (gradient + hessian) is $(round((1-energy_fraction)*100, digits=1))% of total time") - -# ============================================================================= -# Test 4: Deformation Magnitude Sensitivity -# ============================================================================= -println("\n" * "="^80) -println("Test 4: Performance vs Deformation Magnitude") -println("-"^80) - -strain_levels = [1e-6, 1e-4, 1e-2, 0.1, 0.5] -times = Float64[] - -for ε_mag in strain_levels - E_test = SymmetricTensor{2,3}((ε_mag, 0.0, 0.0, 0.0, 0.0, 0.0)) - compute_stress(neo, E_test, nothing, 0.0) # Compile - t = @benchmark compute_stress($neo, $E_test, nothing, 0.0) samples = 1000 - push!(times, median(t).time) - @printf("Strain magnitude: %.1e → Time: %.1f ns\n", ε_mag, median(t).time) -end - -time_variation = (maximum(times) - minimum(times)) / minimum(times) * 100 -println("\nTime variation across strain levels: $(round(time_variation, digits=1))%") -if time_variation < 10 - println("✅ Performance is strain-independent (good for Newton solvers)") -else - println("⚠️ Performance varies with strain (may impact Newton convergence)") -end - -# ============================================================================= -# Test 5: Tangent Accuracy vs Finite Difference -# ============================================================================= -println("\n" * "="^80) -println("Test 5: Tangent Accuracy (AD vs Finite Difference)") -println("-"^80) - -E_base = SymmetricTensor{2,3}((0.01, 0.005, 0.003, -0.002, 0.004, 0.006)) -S_ad, 𝔻_ad, _ = compute_stress(neo, E_base, nothing, 0.0) - -# Finite difference tangent -ε_fd = 1e-8 -errors = Float64[] -for i in 1:6 - E_pert_data = collect(E_base.data) - E_pert_data[i] += ε_fd - E_pert = SymmetricTensor{2,3}(tuple(E_pert_data...)) - - S_pert, _, _ = compute_stress(neo, E_pert, nothing, 0.0) - - ∂S∂E_fd = (S_pert - S_ad) / ε_fd - error = norm(∂S∂E_fd) # Simplified error metric - push!(errors, error) -end - -println("Tangent norm comparison:") -println(" AD tangent: $(round(norm(𝔻_ad), sigdigits=6))") -println(" FD derivative: $(round(mean(errors), sigdigits=6))") -println(" Relative diff: $(round((norm(𝔻_ad) - mean(errors))/norm(𝔻_ad)*100, digits=2))%") -println("\n✅ AD provides exact derivatives (limited only by machine precision)") - -# ============================================================================= -# Test 6: Memory Footprint -# ============================================================================= -println("\n" * "="^80) -println("Test 6: Memory Footprint") -println("-"^80) - -println("Struct sizes:") -println(" NeoHookean: $(sizeof(neo)) bytes") -println(" LinearElastic: $(sizeof(linear)) bytes") -println("\nReturn value sizes:") -println(" SymmetricTensor{2,3}: $(sizeof(S_ad)) bytes") -println(" SymmetricTensor{4,3}: $(sizeof(𝔻_ad)) bytes") -println(" Total per evaluation: $(sizeof(S_ad) + sizeof(𝔻_ad)) bytes") - -# ============================================================================= -# Test 7: Scaling with Multiple Evaluations -# ============================================================================= -println("\n" * "="^80) -println("Test 7: Assembly Loop Simulation (1000 evaluations)") -println("-"^80) - -n_evals = 1000 -strain_samples = [SymmetricTensor{2,3}((rand(), rand(), rand(), rand(), rand(), rand())) * 0.01 - for _ in 1:n_evals] - -# Compile -for E in strain_samples[1:10] - compute_stress(neo, E, nothing, 0.0) - compute_stress(linear, E, nothing, 0.0) -end - -println("\nLinearElastic ($n_evals evaluations):") -t_linear_loop = @benchmark begin - for E in $strain_samples - compute_stress($linear, E, nothing, 0.0) - end -end -display(t_linear_loop) -println() - -println("\nNeoHookean ($n_evals evaluations):") -t_neo_loop = @benchmark begin - for E in $strain_samples - compute_stress($neo, E, nothing, 0.0) - end -end -display(t_neo_loop) -println() - -overhead_loop = median(t_neo_loop).time / median(t_linear_loop).time -per_eval_neo = median(t_neo_loop).time / n_evals -per_eval_linear = median(t_linear_loop).time / n_evals - -println("Per-evaluation time:") -println(" LinearElastic: $(round(per_eval_linear, digits=1)) ns") -println(" NeoHookean: $(round(per_eval_neo, digits=1)) ns") -println(" Overhead: $(round(overhead_loop, digits=1))x") - -# ============================================================================= -# Summary and Recommendations -# ============================================================================= -println("\n" * "="^80) -println("SUMMARY AND RECOMMENDATIONS") -println("="^80) - -total_overhead = median(t_neo).time / median(t_linear).time - -println("\n📊 Performance Metrics:") -println(" Single evaluation: $(round(median(t_neo).time, digits=1)) ns") -println(" AD overhead: $(round(total_overhead, digits=1))x") -println(" Allocations: $(allocs_neo) bytes") -println(" Strain-independent: $(time_variation < 10 ? "Yes ✅" : "No ⚠️")") - -println("\n🎯 Recommendations:") - -if total_overhead < 5 - println(" ✅ EXCELLENT: AD overhead < 5x, suitable for production FEM") -elseif total_overhead < 10 - println(" ✅ GOOD: AD overhead < 10x, acceptable for most applications") -elseif total_overhead < 20 - println(" ⚠️ MODERATE: AD overhead < 20x, consider for prototyping only") -else - println(" ❌ HIGH: AD overhead > 20x, manual derivatives recommended for production") -end - -if allocs_neo == 0 - println(" ✅ Zero allocations achieved - suitable for tight loops") -else - println(" ⚠️ Allocations detected - profile and optimize") -end - -println("\n💡 Use Cases:") -println(" • Research code: Strongly recommended (correctness > speed)") -println(" • Prototyping: Excellent (rapid implementation)") -println(" • Production: $(total_overhead < 10 ? "Acceptable" : "Profile first") ($(round(total_overhead, digits=1))x overhead)") -println(" • Contact: Excellent (unsymmetric tangent, complex derivatives)") - -println("\n" * "="^80) diff --git a/benchmarks/nodal_assembly_scalability.jl b/benchmarks/nodal_assembly_scalability.jl deleted file mode 100755 index 6d073c2..0000000 --- a/benchmarks/nodal_assembly_scalability.jl +++ /dev/null @@ -1,438 +0,0 @@ -#!/usr/bin/env julia -# -# Nodal Assembly Scalability Benchmark -# -# Tests: -# 1. Single-threaded baseline -# 2. Multi-threaded scaling (2, 4, 8 threads) -# 3. Actual speedup measurements -# 4. Interface communication overhead -# -# Run with: julia --project=. -t 8 benchmarks/nodal_assembly_scalability.jl - -using LinearAlgebra -using Printf -using Base.Threads - -println("="^70) -println("Nodal Assembly Scalability Benchmark") -println("="^70) -println() -println("Julia threads available: ", nthreads()) -println() - -# ============================================================================ -# Data Structures -# ============================================================================ - -struct Node - id::Int - x::Float64 - y::Float64 - z::Float64 -end - -struct Element - id::Int - connectivity::NTuple{8,Int} # Hex8 -end - -struct Partition - rank::Int - owned_nodes::UnitRange{Int} - ghost_nodes::Vector{Int} - local_elements::Vector{Int} - node_to_elements::Vector{Vector{Int}} - interface_nodes::Dict{Int,Vector{Int}} -end - -# ============================================================================ -# Mesh Generation -# ============================================================================ - -function create_hex_mesh(nx, ny, nz) - """Create structured hexahedral mesh""" - n_nodes = nx * ny * nz - n_elements = (nx - 1) * (ny - 1) * (nz - 1) - - # Create nodes - nodes = Node[] - for k in 1:nz, j in 1:ny, i in 1:nx - node_id = (k - 1) * nx * ny + (j - 1) * nx + i - push!(nodes, Node(node_id, Float64(i), Float64(j), Float64(k))) - end - - # Create elements (Hex8) - elements = Element[] - for k in 1:(nz-1), j in 1:(ny-1), i in 1:(nx-1) - n1 = (k - 1) * nx * ny + (j - 1) * nx + i - n2 = n1 + 1 - n3 = n2 + nx - n4 = n1 + nx - n5 = n1 + nx * ny - n6 = n2 + nx * ny - n7 = n3 + nx * ny - n8 = n4 + nx * ny - - elem_id = length(elements) + 1 - push!(elements, Element(elem_id, (n1, n2, n3, n4, n5, n6, n7, n8))) - end - - return nodes, elements -end - -function build_node_to_elements(nodes, elements) - """Build inverse connectivity""" - node_to_elems = [Int[] for _ in 1:length(nodes)] - - for (elem_id, element) in enumerate(elements) - for node_id in element.connectivity - push!(node_to_elems[node_id], elem_id) - end - end - - return node_to_elems -end - -# ============================================================================ -# Partitioning -# ============================================================================ - -function partition_mesh(nodes, elements, n_partitions) - """Partition mesh by nodes""" - n_nodes = length(nodes) - nodes_per_partition = ceil(Int, n_nodes / n_partitions) - - node_to_elems = build_node_to_elements(nodes, elements) - - partitions = Partition[] - - for rank in 0:(n_partitions-1) - # Owned nodes - start_node = rank * nodes_per_partition + 1 - end_node = min((rank + 1) * nodes_per_partition, n_nodes) - owned_nodes = start_node:end_node - - # Find elements touching owned nodes - local_elements = Int[] - ghost_nodes = Set{Int}() - - for (elem_id, element) in enumerate(elements) - if any(nid in owned_nodes for nid in element.connectivity) - push!(local_elements, elem_id) - - # Mark ghost nodes - for nid in element.connectivity - if !(nid in owned_nodes) - push!(ghost_nodes, nid) - end - end - end - end - - # Build local node_to_elements (owned nodes only) - local_node_to_elems = [node_to_elems[nid] for nid in owned_nodes] - - # Find interface nodes (owned nodes that couple to other partitions) - interface = Dict{Int,Vector{Int}}() - for neighbor_rank in 0:(n_partitions-1) - if neighbor_rank == rank - continue - end - - neighbor_start = neighbor_rank * nodes_per_partition + 1 - neighbor_end = min((neighbor_rank + 1) * nodes_per_partition, n_nodes) - neighbor_owned = neighbor_start:neighbor_end - - interface_with_neighbor = Int[] - for elem_id in local_elements - element = elements[elem_id] - has_owned = any(nid in owned_nodes for nid in element.connectivity) - has_neighbor = any(nid in neighbor_owned for nid in element.connectivity) - - if has_owned && has_neighbor - for nid in element.connectivity - if nid in owned_nodes && !(nid in interface_with_neighbor) - push!(interface_with_neighbor, nid) - end - end - end - end - - if !isempty(interface_with_neighbor) - interface[neighbor_rank] = interface_with_neighbor - end - end - - partition = Partition( - rank, - owned_nodes, - collect(ghost_nodes), - local_elements, - local_node_to_elems, - interface - ) - - push!(partitions, partition) - end - - return partitions -end - -# ============================================================================ -# Nodal Assembly (Matrix-Vector Product) -# ============================================================================ - -function matvec_single_threaded!(y, x, nodes, elements, node_to_elements) - """Single-threaded nodal assembly""" - fill!(y, 0.0) - - for node_id in 1:length(nodes) - node = nodes[node_id] - - # Get node DOFs - dof_start = (node_id - 1) * 3 + 1 - y_nodal = zeros(3) - - # Gather from connected elements - for elem_id in node_to_elements[node_id] - element = elements[elem_id] - - # Mock element contribution (just for timing) - for nid in element.connectivity - x_dof_start = (nid - 1) * 3 + 1 - for d in 1:3 - y_nodal[d] += 0.1 * x[x_dof_start+d-1] - end - end - end - - # Write to global - for d in 1:3 - y[dof_start+d-1] = y_nodal[d] - end - end -end - -function matvec_multi_threaded!(y, x, nodes, elements, node_to_elements) - """Multi-threaded nodal assembly (direct, no partitioning)""" - fill!(y, 0.0) - - @threads for node_id in 1:length(nodes) - node = nodes[node_id] - - dof_start = (node_id - 1) * 3 + 1 - y_nodal = zeros(3) - - for elem_id in node_to_elements[node_id] - element = elements[elem_id] - - for nid in element.connectivity - x_dof_start = (nid - 1) * 3 + 1 - for d in 1:3 - y_nodal[d] += 0.1 * x[x_dof_start+d-1] - end - end - end - - for d in 1:3 - y[dof_start+d-1] = y_nodal[d] - end - end -end - -function matvec_partitioned!(y, x, nodes, elements, partitions) - """Multi-threaded with explicit partitioning (simulates multi-GPU)""" - fill!(y, 0.0) - - # Each partition processed by one thread - @threads for partition in partitions - # Process owned nodes - for (local_idx, node_id) in enumerate(partition.owned_nodes) - node = nodes[node_id] - - dof_start = (node_id - 1) * 3 + 1 - y_nodal = zeros(3) - - for elem_id in partition.node_to_elements[local_idx] - element = elements[elem_id] - - for nid in element.connectivity - x_dof_start = (nid - 1) * 3 + 1 - for d in 1:3 - y_nodal[d] += 0.1 * x[x_dof_start+d-1] - end - end - end - - for d in 1:3 - y[dof_start+d-1] = y_nodal[d] - end - end - end -end - -# ============================================================================ -# Benchmarks -# ============================================================================ - -function run_benchmark(name, nx, ny, nz, n_warmup=2, n_runs=10) - println("\n" * "="^70) - println("Benchmark: $name") - println(" Mesh: $nx × $ny × $nz = $(nx*ny*nz) nodes, $((nx-1)*(ny-1)*(nz-1)) elements") - println("="^70) - - # Create mesh - print("Creating mesh... ") - nodes, elements = create_hex_mesh(nx, ny, nz) - node_to_elements = build_node_to_elements(nodes, elements) - n_dofs = 3 * length(nodes) - println("✓") - - println(" Nodes: ", length(nodes)) - println(" Elements: ", length(elements)) - println(" DOFs: ", n_dofs) - println(" Avg elements/node: ", sum(length.(node_to_elements)) / length(nodes)) - - # Test vectors - x = randn(n_dofs) - y_ref = zeros(n_dofs) - y_test = zeros(n_dofs) - - # ======================================================================== - # 1. Single-threaded baseline - # ======================================================================== - println("\n1. Single-threaded baseline:") - - # Warmup - for _ in 1:n_warmup - matvec_single_threaded!(y_ref, x, nodes, elements, node_to_elements) - end - - # Benchmark - times = Float64[] - for _ in 1:n_runs - t_start = time_ns() - matvec_single_threaded!(y_ref, x, nodes, elements, node_to_elements) - t_end = time_ns() - push!(times, (t_end - t_start) / 1e9) - end - - t_single = minimum(times) - println(" Time: ", @sprintf("%.3f ms", t_single * 1000)) - println(" Throughput: ", @sprintf("%.2f Mnodes/s", length(nodes) / t_single / 1e6)) - - # ======================================================================== - # 2. Multi-threaded (all available threads) - # ======================================================================== - if nthreads() > 1 - println("\n2. Multi-threaded ($(nthreads()) threads):") - - # Warmup - for _ in 1:n_warmup - matvec_multi_threaded!(y_test, x, nodes, elements, node_to_elements) - end - - # Verify correctness - error = norm(y_test - y_ref) / norm(y_ref) - println(" Verification: ", error < 1e-10 ? "✓ PASS" : "✗ FAIL (error=$error)") - - # Benchmark - times = Float64[] - for _ in 1:n_runs - t_start = time_ns() - matvec_multi_threaded!(y_test, x, nodes, elements, node_to_elements) - t_end = time_ns() - push!(times, (t_end - t_start) / 1e9) - end - - t_multi = minimum(times) - speedup = t_single / t_multi - efficiency = speedup / nthreads() * 100 - - println(" Time: ", @sprintf("%.3f ms", t_multi * 1000)) - println(" Speedup: ", @sprintf("%.2fx", speedup)) - println(" Efficiency: ", @sprintf("%.1f%%", efficiency)) - println(" Throughput: ", @sprintf("%.2f Mnodes/s", length(nodes) / t_multi / 1e6)) - end - - # ======================================================================== - # 3. Partitioned (simulates multi-GPU) - # ======================================================================== - if nthreads() >= 4 - n_partitions = 4 - println("\n3. Partitioned ($n_partitions partitions, 4 threads):") - - # Create partitions - print(" Creating partitions... ") - partitions = partition_mesh(nodes, elements, n_partitions) - println("✓") - - # Print partition info - for partition in partitions - n_owned = length(partition.owned_nodes) - n_ghost = length(partition.ghost_nodes) - n_interface = sum(length.(values(partition.interface_nodes))) - interface_pct = n_interface / n_owned * 100 - - println(" Partition $(partition.rank): $n_owned owned, $n_ghost ghost, " * - "$n_interface interface ($(round(interface_pct, digits=1))%)") - end - - # Warmup - for _ in 1:n_warmup - matvec_partitioned!(y_test, x, nodes, elements, partitions) - end - - # Verify correctness - error = norm(y_test - y_ref) / norm(y_ref) - println(" Verification: ", error < 1e-10 ? "✓ PASS" : "✗ FAIL (error=$error)") - - # Benchmark - times = Float64[] - for _ in 1:n_runs - t_start = time_ns() - matvec_partitioned!(y_test, x, nodes, elements, partitions) - t_end = time_ns() - push!(times, (t_end - t_start) / 1e9) - end - - t_partitioned = minimum(times) - speedup = t_single / t_partitioned - efficiency = speedup / n_partitions * 100 - - println(" Time: ", @sprintf("%.3f ms", t_partitioned * 1000)) - println(" Speedup: ", @sprintf("%.2fx", speedup)) - println(" Efficiency: ", @sprintf("%.1f%%", efficiency)) - println(" Throughput: ", @sprintf("%.2f Mnodes/s", length(nodes) / t_partitioned / 1e6)) - end - - println() -end - -# ============================================================================ -# Run Benchmarks -# ============================================================================ - -# Small mesh -run_benchmark("Small Mesh", 20, 20, 20) - -# Medium mesh -run_benchmark("Medium Mesh", 40, 40, 40) - -# Large mesh (if enough threads) -if nthreads() >= 4 - run_benchmark("Large Mesh", 60, 60, 60) -end - -println("="^70) -println("✓ Benchmark Complete") -println("="^70) -println() -println("Notes:") -println(" - Single-threaded: Baseline performance") -println(" - Multi-threaded: All available threads, direct parallelization") -println(" - Partitioned: Simulates multi-GPU with explicit partitions") -println(" - Efficiency = Speedup / N_threads × 100%") -println(" - Near 100% efficiency = perfect scaling") -println() diff --git a/benchmarks/perfect_plasticity_analysis.jl b/benchmarks/perfect_plasticity_analysis.jl deleted file mode 100644 index e06ea4d..0000000 --- a/benchmarks/perfect_plasticity_analysis.jl +++ /dev/null @@ -1,320 +0,0 @@ -""" -Performance Analysis: PerfectPlasticity Material Model - -Comprehensive benchmarking of J2 plasticity implementation with radial return mapping. - -Tests: -1. Single evaluation performance (elastic vs plastic) -2. Zero-allocation verification -3. Type stability verification -4. State overhead measurement -5. Hardening parameter sensitivity -6. Assembly loop simulation -7. Comparison to LinearElastic and NeoHookean -8. Strain level scalability - -Run with: - julia --project=. benchmarks/perfect_plasticity_analysis.jl -""" - -using BenchmarkTools -using Tensors -using Statistics -using Printf -using Dates - -# Load implementations -include("../src/materials/abstract_material.jl") -include("../src/materials/linear_elastic.jl") -include("../src/materials/neo_hookean.jl") -include("../src/materials/perfect_plasticity.jl") - -println("="^80) -println("PERFECT PLASTICITY MATERIAL - PERFORMANCE ANALYSIS") -println("="^80) -println() - -# ============================================================================== -# TEST 1: Single Evaluation - Elastic Path -# ============================================================================== -println("TEST 1: Single Evaluation - Elastic Path") -println("-"^80) - -steel = PerfectPlasticity(E=200e9, ν=0.3, σ_y=250e6, H=1e9) -ε_elastic = SymmetricTensor{2,3}((1e-5, 0.0, 0.0, 0.0, 0.0, 0.0)) # Below yield -state = PlasticityState() - -# Benchmark elastic path -bench_elastic = @benchmark compute_stress($steel, $ε_elastic, $state, 0.0) -t_elastic = median(bench_elastic.times) -allocs_elastic = bench_elastic.allocs - -println("Elastic path (no yielding):") -println(" Time: ", @sprintf("%.2f ns", t_elastic)) -println(" Allocations: ", allocs_elastic) -println(" Memory: ", bench_elastic.memory, " bytes") -println() - -# ============================================================================== -# TEST 2: Single Evaluation - Plastic Path -# ============================================================================== -println("TEST 2: Single Evaluation - Plastic Path") -println("-"^80) - -ε_plastic = SymmetricTensor{2,3}((0.003, 0.0, 0.0, 0.0, 0.0, 0.0)) # Beyond yield - -# Benchmark plastic path -bench_plastic = @benchmark compute_stress($steel, $ε_plastic, $state, 0.0) -t_plastic = median(bench_plastic.times) -allocs_plastic = bench_plastic.allocs - -println("Plastic path (radial return):") -println(" Time: ", @sprintf("%.2f ns", t_plastic)) -println(" Allocations: ", allocs_plastic) -println(" Memory: ", bench_plastic.memory, " bytes") -println() - -println("Plastic overhead:") -println(" Ratio: ", @sprintf("%.2fx", t_plastic / t_elastic)) -println() - -# ============================================================================== -# TEST 3: State Management Overhead -# ============================================================================== -println("TEST 3: State Management Overhead") -println("-"^80) - -# Compare with and without state history -σ1, 𝔻1, state1 = compute_stress(steel, ε_plastic, nothing, 0.0) # Fresh state -σ2, 𝔻2, state2 = compute_stress(steel, ε_plastic, state1, 0.0) # With history - -bench_fresh = @benchmark compute_stress($steel, $ε_plastic, nothing, 0.0) -bench_history = @benchmark compute_stress($steel, $ε_plastic, $state1, 0.0) - -println("Fresh state (ε_p = 0, α = 0):") -println(" Time: ", @sprintf("%.2f ns", median(bench_fresh.times))) -println() -println("With history (ε_p ≠ 0, α ≠ 0):") -println(" Time: ", @sprintf("%.2f ns", median(bench_history.times))) -println() -println("State overhead: ", @sprintf("%.1f%%", - (median(bench_history.times) - median(bench_fresh.times)) / median(bench_fresh.times) * 100)) -println() - -# ============================================================================== -# TEST 4: Hardening Parameter Sensitivity -# ============================================================================== -println("TEST 4: Hardening Parameter Sensitivity") -println("-"^80) - -hardening_values = [0.0, 1e8, 1e9, 10e9, 100e9] # Perfect to strong hardening -times_H = Float64[] - -for H in hardening_values - mat = PerfectPlasticity(E=200e9, ν=0.3, σ_y=250e6, H=H) - bench = @benchmark compute_stress($mat, $ε_plastic, $state, 0.0) - push!(times_H, median(bench.times)) -end - -println("H (Pa) Time (ns) Overhead") -println(repeat("-", 45)) -for (H, t) in zip(hardening_values, times_H) - overhead = (t - times_H[1]) / times_H[1] * 100 - println(@sprintf("%-15.1e %8.2f %+6.1f%%", H, t, overhead)) -end -println() - -# ============================================================================== -# TEST 5: Comparison to Other Materials -# ============================================================================== -println("TEST 5: Comparison to Other Materials") -println("-"^80) - -# LinearElastic -linear = LinearElastic(E=200e9, ν=0.3) -bench_linear = @benchmark compute_stress($linear, $ε_plastic) -t_linear = median(bench_linear.times) - -# NeoHookean (uses Green-Lagrange strain for small deformation) -μ_neo = 200e9 / (2 * (1 + 0.3)) -λ_neo = 200e9 * 0.3 / ((1 + 0.3) * (1 - 2 * 0.3)) -neo = NeoHookean(μ=μ_neo, λ=λ_neo) -E_gl = ε_plastic # For small strains, E_GL ≈ ε -bench_neo = @benchmark compute_stress($neo, $E_gl) -t_neo = median(bench_neo.times) - -println("Material Time (ns) Ratio vs Linear") -println(repeat("-", 50)) -println(@sprintf("LinearElastic %8.2f 1.00x (baseline)", t_linear)) -println(@sprintf("PerfectPlasticity %8.2f %.2fx", t_plastic, t_plastic / t_linear)) -println(@sprintf("NeoHookean %8.2f %.2fx", t_neo, t_neo / t_linear)) -println() - -println("Performance ranking:") -println(" 1. LinearElastic (fastest, no state, manual derivatives)") -println(" 2. PerfectPlasticity (", @sprintf("%.1fx", t_plastic / t_linear), - " - state management + radial return)") -println(" 3. NeoHookean (", @sprintf("%.1fx", t_neo / t_linear), - " - automatic differentiation overhead)") -println() - -# ============================================================================== -# TEST 6: Assembly Loop Simulation -# ============================================================================== -println("TEST 6: Assembly Loop Simulation (1000 Gauss points)") -println("-"^80) - -n_gauss = 1000 -strains = [SymmetricTensor{2,3}((0.001 + 0.002 * rand(), 0.0, 0.0, 0.0, 0.0, 0.0)) - for _ in 1:n_gauss] - -# Elastic assembly -function assembly_elastic(material, strains) - total = zero(SymmetricTensor{2,3}) - for ε in strains - σ, _, _ = compute_stress(material, ε) - total += σ - end - return total -end - -# Plastic assembly (stateful) -function assembly_plastic(material, strains, state) - total = zero(SymmetricTensor{2,3}) - for ε in strains - σ, _, state = compute_stress(material, ε, state, 0.0) - total += σ - end - return total, state -end - -bench_asm_linear = @benchmark assembly_elastic($linear, $strains) -bench_asm_plastic = @benchmark assembly_plastic($steel, $strains, $state) - -t_asm_linear = median(bench_asm_linear.times) / 1e6 # Convert to ms -t_asm_plastic = median(bench_asm_plastic.times) / 1e6 - -println("LinearElastic assembly: ", @sprintf("%.3f ms", t_asm_linear)) -println("PerfectPlasticity assembly: ", @sprintf("%.3f ms", t_asm_plastic)) -println("Overhead: ", @sprintf("%.2fx", t_asm_plastic / t_asm_linear)) -println() - -# ============================================================================== -# TEST 7: Strain Level Scalability -# ============================================================================== -println("TEST 7: Strain Level Scalability") -println("-"^80) - -strain_magnitudes = [0.0005, 0.001, 0.002, 0.005, 0.01, 0.02] -times_strain = Float64[] -yields = Bool[] - -for ε_mag in strain_magnitudes - ε_test = SymmetricTensor{2,3}((ε_mag, 0.0, 0.0, 0.0, 0.0, 0.0)) - σ_test, _, state_test = compute_stress(steel, ε_test, nothing, 0.0) - - bench = @benchmark compute_stress($steel, $ε_test, nothing, 0.0) - push!(times_strain, median(bench.times)) - push!(yields, state_test.κ > 0.0) -end - -println("ε_magnitude Time (ns) Yielded?") -println(repeat("-", 40)) -for (ε_mag, t, y) in zip(strain_magnitudes, times_strain, yields) - status = y ? "YES" : "no" - println(@sprintf("%.4f %8.2f %s", ε_mag, t, status)) -end -println() - -# ============================================================================== -# TEST 8: Cyclic Loading Performance -# ============================================================================== -println("TEST 8: Cyclic Loading (Bauschinger Effect)") -println("-"^80) - -# Simulate cyclic loading path -ε_cycle = [ - SymmetricTensor{2,3}((0.003, 0.0, 0.0, 0.0, 0.0, 0.0)), # Tension - SymmetricTensor{2,3}((0.0, 0.0, 0.0, 0.0, 0.0, 0.0)), # Unload - SymmetricTensor{2,3}((-0.002, 0.0, 0.0, 0.0, 0.0, 0.0)), # Compression - SymmetricTensor{2,3}((0.0, 0.0, 0.0, 0.0, 0.0, 0.0)), # Unload -] - -function cyclic_loading(material, strains, state) - for ε in strains - σ, 𝔻, state = compute_stress(material, ε, state, 0.0) - end - return state -end - -bench_cyclic = @benchmark cyclic_loading($steel, $ε_cycle, $state) -t_cyclic = median(bench_cyclic.times) - -println("Cyclic loading (4 load steps):") -println(" Total time: ", @sprintf("%.2f ns", t_cyclic)) -println(" Per load step: ", @sprintf("%.2f ns", t_cyclic / 4)) -println() - -# ============================================================================== -# TEST 9: Type Stability Verification -# ============================================================================== -println("TEST 9: Type Stability") -println("-"^80) - -using InteractiveUtils - -println("Return type inference:") -result_type = @code_typed compute_stress(steel, ε_plastic, state, 0.0) -println(" ✓ Type stable: ", result_type[2]) -println() - -# ============================================================================== -# SUMMARY -# ============================================================================== -println("="^80) -println("SUMMARY") -println("="^80) -println() - -println("Performance Characteristics:") -println(" • Elastic path: ", @sprintf("%.0f ns", t_elastic), " (no allocations)") -println(" • Plastic path: ", @sprintf("%.0f ns", t_plastic), " (~128 bytes for state)") -println(" • Plastic overhead:", @sprintf("%.2fx", t_plastic / t_elastic)) -println() - -println("Comparison to other materials:") -println(" • ", @sprintf("%.2fx", t_plastic / t_linear), " slower than LinearElastic (baseline)") -println(" • ", @sprintf("%.2fx", t_neo / t_plastic), " faster than NeoHookean (AD)") -println() - -println("Key findings:") -println(" ✓ Zero allocations on elastic path") -println(" ✓ Minimal allocations on plastic path (state struct only)") -println(" ✓ Type stable") -println(" ✓ Hardening parameter has negligible performance impact") -println(" ✓ Performance independent of strain level") -println(" ✓ Suitable for production FEM with ~", - @sprintf("%.0f", 1e9 / t_plastic), " evaluations/second") -println() - -println("Recommendations:") -if t_plastic < 500 - println(" ✓ Excellent performance - suitable for all applications") -elseif t_plastic < 1000 - println(" ✓ Good performance - suitable for most applications") - println(" • Consider caching for problems with >10M DOF") -else - println(" ⚠ Acceptable performance - profile before using with >1M DOF") - println(" • Consider precomputation for repeated analyses") -end -println() - -println("Expected performance in FEM assembly:") -println(" • Small problems (<10K DOF): Negligible overhead") -println(" • Medium problems (10K-1M DOF): ", @sprintf("<%.1f seconds", 1e6 * t_plastic / 1e9)) -println(" • Large problems (>1M DOF): ", @sprintf("<%.1f seconds", 1e7 * t_plastic / 1e9)) -println() - -println("="^80) -println("Analysis complete: ", now()) -println("="^80) diff --git a/benchmarks/results/benchmark_output.txt b/benchmarks/results/benchmark_output.txt deleted file mode 100644 index b7ba028..0000000 --- a/benchmarks/results/benchmark_output.txt +++ /dev/null @@ -1,348 +0,0 @@ -Precompiling packages... - 669.3 ms ✓ EpollShim_jll - 725.5 ms ✓ libfdk_aac_jll - 760.9 ms ✓ Graphite2_jll - 752.3 ms ✓ fzf_jll - 756.4 ms ✓ LERC_jll - 822.1 ms ✓ Xorg_libICE_jll - 815.2 ms ✓ LAME_jll - 797.9 ms ✓ Ogg_jll - 788.9 ms ✓ x265_jll - 829.1 ms ✓ libaom_jll - 849.2 ms ✓ mtdev_jll - 843.9 ms ✓ MbedTLS_jll - 840.0 ms ✓ x264_jll - 860.8 ms ✓ XZ_jll - 674.8 ms ✓ libevdev_jll - 769.9 ms ✓ Opus_jll - 743.6 ms ✓ eudev_jll - 738.4 ms ✓ FriBidi_jll - 707.7 ms ✓ Dbus_jll - 730.8 ms ✓ Xorg_libxkbfile_jll - 779.8 ms ✓ Xorg_xcb_util_jll - 770.5 ms ✓ Xorg_libXi_jll - 772.1 ms ✓ Xorg_libXrandr_jll - 793.7 ms ✓ Xorg_libXcursor_jll - 892.3 ms ✓ Wayland_jll - 2016.1 ms ✓ ColorVectorSpace - 1008.1 ms ✓ HarfBuzz_jll - 711.6 ms ✓ JLFzf - 1304.3 ms ✓ Ghostscript_jll - 737.4 ms ✓ Xorg_libSM_jll - 1455.9 ms ✓ RecipesBase - 803.4 ms ✓ libvorbis_jll - 731.4 ms ✓ libinput_jll - 785.6 ms ✓ Libtiff_jll - 742.5 ms ✓ Xorg_xkbcomp_jll - 731.2 ms ✓ Xorg_xcb_util_image_jll - 733.0 ms ✓ Xorg_xcb_util_keysyms_jll - 741.3 ms ✓ Xorg_xcb_util_renderutil_jll - 2530.6 ms ✓ StatsBase - 764.4 ms ✓ Xorg_xcb_util_wm_jll - 1120.5 ms ✓ MbedTLS - 879.4 ms ✓ libass_jll - 653.5 ms ✓ Xorg_xkeyboard_config_jll - 962.5 ms ✓ Pango_jll - 711.2 ms ✓ Xorg_xcb_util_cursor_jll - 758.4 ms ✓ xkbcommon_jll - 1332.1 ms ✓ FFMPEG_jll - 999.6 ms ✓ Vulkan_Loader_jll - 1149.2 ms ✓ libdecor_jll - 839.5 ms ✓ FFMPEG - 2960.5 ms ✓ Latexify - 974.9 ms ✓ GLFW_jll - 4035.5 ms ✓ ColorSchemes - 852.9 ms ✓ Latexify → SparseArraysExt - 1490.1 ms ✓ Qt6Base_jll - 918.9 ms ✓ Qt6ShaderTools_jll - 1397.3 ms ✓ GR_jll - 2814.7 ms ✓ Qt6Declarative_jll - 1317.4 ms ✓ Qt6Wayland_jll - 7309.2 ms ✓ PlotUtils - 12361.6 ms ✓ HTTP - 2980.3 ms ✓ PlotThemes - 3588.3 ms ✓ RecipesPipeline - 5614.7 ms ✓ GR - 64840.3 ms ✓ Plots - 65 dependencies successfully precompiled in 87 seconds. 112 already precompiled. -================================================================================ -SYSTEM INFORMATION -================================================================================ - -CPU Model: Intel(R) Xeon(R) Gold 6326 CPU @ 2.90GHz -CPU Cores: 32 threads (32 physical cores) -CPU Speed: 3300 MHz - -Julia Version: 1.12.1 -OS: Linux x86_64-linux-gnu -Word Size: 64 bits - -Approximate CPU Cache Sizes: - L1 Cache: ~32-64 KB per core (typical) - L2 Cache: ~256-512 KB per core (typical) - L3 Cache: ~8-32 MB shared (typical) - -Note: Testing up to 8KB structs to exceed L1 cache - -================================================================================ -SYSTEM INFORMATION -================================================================================ -Julia Version: 1.12.1 -CPU Model: Intel(R) Xeon(R) Gold 6326 CPU @ 2.90GHz -CPU Cores: 32 -Total Memory: 503.35 GB -L1 Cache: 48K -L2 Cache: 1280K -L3 Cache: 24576K - -================================================================================ -STRUCT SIZE SCALING BENCHMARK -================================================================================ - -Testing hypothesis: Immutable slows down with struct size, mutable stays constant - -Testing struct with 1 Float64 fields (8 bytes)... - Access: Mut=4.62ns Imm=2.02ns Speedup=2.3x - Update: Mut=7.2ns Imm=2.32ns Speedup=3.1x - Iterate: Mut=11.77ns Imm=2.02ns Speedup=5.8x - Copy: 2.02ns - -Testing struct with 2 Float64 fields (16 bytes)... - Access: Mut=4.9ns Imm=2.02ns Speedup=2.4x - Update: Mut=7.21ns Imm=2.31ns Speedup=3.1x - Iterate: Mut=14.49ns Imm=2.02ns Speedup=7.2x - Copy: 2.03ns - -Testing struct with 5 Float64 fields (40 bytes)... - Access: Mut=4.9ns Imm=2.37ns Speedup=2.1x - Update: Mut=7.2ns Imm=2.6ns Speedup=2.8x - Iterate: Mut=16.71ns Imm=2.31ns Speedup=7.2x - Copy: 2.38ns - -Testing struct with 10 Float64 fields (80 bytes)... - Access: Mut=4.62ns Imm=2.03ns Speedup=2.3x - Update: Mut=7.2ns Imm=2.6ns Speedup=2.8x - Iterate: Mut=23.13ns Imm=2.6ns Speedup=8.9x - Copy: 3.31ns - -Testing struct with 20 Float64 fields (160 bytes)... - Access: Mut=4.62ns Imm=2.02ns Speedup=2.3x - Update: Mut=7.2ns Imm=3.16ns Speedup=2.3x - Iterate: Mut=63.13ns Imm=5.52ns Speedup=11.4x - Copy: 3.18ns - -Testing struct with 50 Float64 fields (400 bytes)... - Access: Mut=4.62ns Imm=2.03ns Speedup=2.3x - Update: Mut=7.2ns Imm=6.02ns Speedup=1.2x - Iterate: Mut=196.85ns Imm=24.78ns Speedup=7.9x - Copy: 6.9ns - -Testing struct with 100 Float64 fields (800 bytes)... - Access: Mut=4.62ns Imm=2.02ns Speedup=2.3x - Update: Mut=7.2ns Imm=11.75ns Speedup=0.6x - Iterate: Mut=260.56ns Imm=68.5ns Speedup=3.8x - Copy: 9.48ns - -Testing struct with 200 Float64 fields (1600 bytes)... - Access: Mut=4.62ns Imm=2.37ns Speedup=1.9x - Update: Mut=7.41ns Imm=24.63ns Speedup=0.3x - Iterate: Mut=777.91ns Imm=155.16ns Speedup=5.0x - Copy: 20.95ns - -Testing struct with 500 Float64 fields (4000 bytes)... - Access: Mut=4.62ns Imm=2.03ns Speedup=2.3x - Update: Mut=7.2ns Imm=80.84ns Speedup=0.1x - Iterate: Mut=1156.3ns Imm=499.36ns Speedup=2.3x - Copy: 58.68ns - -Testing struct with 1000 Float64 fields (8000 bytes)... - Access: Mut=4.67ns Imm=2.08ns Speedup=2.2x - Update: Mut=7.2ns Imm=193.26ns Speedup=0.0x - Iterate: Mut=3465.75ns Imm=1100.2ns Speedup=3.2x - Copy: 45.7ns - -Testing struct with 2000 Float64 fields (16000 bytes)... - Access: Mut=4.62ns Imm=2.02ns Speedup=2.3x - Update: Mut=7.2ns Imm=410.85ns Speedup=0.0x - Iterate: Mut=4811.71ns Imm=2226.22ns Speedup=2.2x - Copy: 82.95ns - -Testing struct with 5000 Float64 fields (40000 bytes)... - Access: Mut=4.62ns Imm=2.08ns Speedup=2.2x - Update: Mut=7.2ns Imm=1936.1ns Speedup=0.0x - Iterate: Mut=33969.0ns Imm=5667.17ns Speedup=6.0x - Copy: 867.04ns - -================================================================================ -RESULTS SUMMARY -================================================================================ - -Field Access Performance: -Size (fields) | Bytes | Mutable (ns) | Immutable (ns) | Speedup ----------------------------------------------------------------------- - 1 | 8 | 4.62 | 2.02 | 2.3x - 2 | 16 | 4.90 | 2.02 | 2.4x - 5 | 40 | 4.90 | 2.37 | 2.1x - 10 | 80 | 4.62 | 2.03 | 2.3x - 20 | 160 | 4.62 | 2.02 | 2.3x - 50 | 400 | 4.62 | 2.03 | 2.3x - 100 | 800 | 4.62 | 2.02 | 2.3x - 200 | 1600 | 4.62 | 2.37 | 1.9x - 500 | 4000 | 4.62 | 2.03 | 2.3x - 1000 | 8000 | 4.67 | 2.08 | 2.2x - 2000 | 16000 | 4.62 | 2.02 | 2.3x - 5000 | 40000 | 4.62 | 2.08 | 2.2x - -Field Update Performance: -Size (fields) | Bytes | Mutable (ns) | Immutable (ns) | Speedup ----------------------------------------------------------------------- - 1 | 8 | 7.20 | 2.31 | 3.1x - 2 | 16 | 7.21 | 2.31 | 3.1x - 5 | 40 | 7.20 | 2.60 | 2.8x - 10 | 80 | 7.20 | 2.60 | 2.8x - 20 | 160 | 7.20 | 3.16 | 2.3x - 50 | 400 | 7.20 | 6.02 | 1.2x - 100 | 800 | 7.20 | 11.75 | 0.6x - 200 | 1600 | 7.41 | 24.63 | 0.3x - 500 | 4000 | 7.20 | 80.84 | 0.1x - 1000 | 8000 | 7.20 | 193.26 | 0.0x - 2000 | 16000 | 7.20 | 410.85 | 0.0x - 5000 | 40000 | 7.20 | 1936.10 | 0.0x - -Iteration Performance: -Size (fields) | Bytes | Mutable (ns) | Immutable (ns) | Speedup ----------------------------------------------------------------------- - 1 | 8 | 11.77 | 2.02 | 5.8x - 2 | 16 | 14.49 | 2.02 | 7.2x - 5 | 40 | 16.71 | 2.31 | 7.2x - 10 | 80 | 23.13 | 2.60 | 8.9x - 20 | 160 | 63.13 | 5.52 | 11.4x - 50 | 400 | 196.85 | 24.78 | 7.9x - 100 | 800 | 260.56 | 68.50 | 3.8x - 200 | 1600 | 777.91 | 155.16 | 5.0x - 500 | 4000 | 1156.30 | 499.36 | 2.3x - 1000 | 8000 | 3465.75 | 1100.20 | 3.2x - 2000 | 16000 | 4811.71 | 2226.22 | 2.2x - 5000 | 40000 | 33969.00 | 5667.17 | 6.0x - -Immutable Copy Cost (ns): -Size (fields) | Bytes | Copy Time (ns) ----------------------------------------- - 1 | 8 | 2.02 - 2 | 16 | 2.03 - 5 | 40 | 2.38 - 10 | 80 | 3.31 - 20 | 160 | 3.18 - 50 | 400 | 6.90 - 100 | 800 | 9.48 - 200 | 1600 | 20.95 - 500 | 4000 | 58.68 - 1000 | 8000 | 45.70 - 2000 | 16000 | 82.95 - 5000 | 40000 | 867.04 - -================================================================================ -ANALYSIS -================================================================================ - -✓ Immutable ALWAYS faster for field access (even at 1000 fields = 8KB) - Minimum speedup: 1.9x at 5000 fields - -⚠ Mutable wins for field update at 100 fields - -✓ Immutable ALWAYS faster for iteration (even at 1000 fields = 8KB) - Minimum speedup: 2.2x at 5000 fields - -Scaling Analysis: - -Copy time scaling: - Linear fit: time(ns) = -25.47 + 0.1587 * nfields - Per-field cost: 0.1587 ns/field - Base overhead: -25.47 ns - -Is copy time linear? (checking R²) - R² = 0.9006 - ⚠ Copy time not perfectly linear (compiler optimizations?) - -KEY INSIGHT: --------------------------------------------------------------------------------- - -Even at 1000 fields (8KB struct), immutable is STILL faster because: - 1. Dict lookup cost (~40-50ns) >> copy cost per field (~0.1587ns) - 2. Type stability enables compiler optimizations (inlining, SIMD) - 3. Stack allocation has better cache locality than heap pointers - -Theoretical crossover point (if it exists): - Would occur at ~413 fields (3KB) - -================================================================================ -CONCLUSION -================================================================================ - -Your intuition about O(n) scaling is CORRECT, BUT: - - • Dict lookup base cost is SO high (~40-50ns) - • Copy cost per field is SO low (~0.1587ns) - • Compiler optimizations are SO good (inlining, SIMD, escape analysis) - -That immutable wins even for unrealistically large structs (8KB+)! - -For typical FEM elements: - • Material properties: 3-10 fields (24-80 bytes) - • State variables: 10-50 fields (80-400 bytes) - • Even with 100 fields (800 bytes), immutable is >10x faster - -Type stability > Everything else. - -================================================================================ -SAVING DATA -================================================================================ - -✓ Data saved to: /home/juajukka/dev/JuliaFEM.jl/benchmarks/results/struct_size_scaling.json -✓ CSV saved to: /home/juajukka/dev/JuliaFEM.jl/benchmarks/results/struct_size_scaling.csv - -================================================================================ -GENERATING PLOTS -================================================================================ - -]1337;ReportCellSizeP+q544e\GKS: cannot open display - headless operation mode active -✓ Plot saved: /home/juajukka/dev/JuliaFEM.jl/benchmarks/results/field_access_scaling.png -✓ Plot saved: /home/juajukka/dev/JuliaFEM.jl/benchmarks/results/field_update_scaling.png -✓ Plot saved: /home/juajukka/dev/JuliaFEM.jl/benchmarks/results/iteration_scaling.png -✓ Plot saved: /home/juajukka/dev/JuliaFEM.jl/benchmarks/results/copy_cost_linear.png -✓ Plot saved: /home/juajukka/dev/JuliaFEM.jl/benchmarks/results/speedup_ratios.png - -All plots saved to: /home/juajukka/dev/JuliaFEM.jl/benchmarks/results - -================================================================================ -SAVING DATA -================================================================================ - -✓ Data saved to: benchmarks/results/struct_scaling_20251109_201256.json - -✓ CSV saved to: benchmarks/results/struct_scaling_20251109_201256.csv - -================================================================================ -GENERATING PLOTS -================================================================================ - -┌ Warning: Assignment to `p1` in soft scope is ambiguous because a global variable by the same name exists: `p1` will be treated as a new local. Disambiguate by using `local p1` to suppress this warning or `global p1` to assign to the existing global variable. -└ @ ~/dev/JuliaFEM.jl/benchmarks/struct_size_scaling.jl:633 -┌ Warning: Assignment to `p2` in soft scope is ambiguous because a global variable by the same name exists: `p2` will be treated as a new local. Disambiguate by using `local p2` to suppress this warning or `global p2` to assign to the existing global variable. -└ @ ~/dev/JuliaFEM.jl/benchmarks/struct_size_scaling.jl:649 -┌ Warning: Assignment to `p3` in soft scope is ambiguous because a global variable by the same name exists: `p3` will be treated as a new local. Disambiguate by using `local p3` to suppress this warning or `global p3` to assign to the existing global variable. -└ @ ~/dev/JuliaFEM.jl/benchmarks/struct_size_scaling.jl:664 -┌ Warning: Assignment to `p4` in soft scope is ambiguous because a global variable by the same name exists: `p4` will be treated as a new local. Disambiguate by using `local p4` to suppress this warning or `global p4` to assign to the existing global variable. -└ @ ~/dev/JuliaFEM.jl/benchmarks/struct_size_scaling.jl:679 -┌ Warning: Assignment to `p5` in soft scope is ambiguous because a global variable by the same name exists: `p5` will be treated as a new local. Disambiguate by using `local p5` to suppress this warning or `global p5` to assign to the existing global variable. -└ @ ~/dev/JuliaFEM.jl/benchmarks/struct_size_scaling.jl:697 -✓ Saved: field_access_20251109_201256.png -✓ Saved: field_update_20251109_201256.png -✓ Saved: iteration_20251109_201256.png -✓ Saved: speedup_factors_20251109_201256.png -✓ Saved: copy_cost_20251109_201256.png -✓ Saved: combined_20251109_201256.png - -All plots saved successfully! - -================================================================================ diff --git a/benchmarks/results/combined_20251109_201256.png b/benchmarks/results/combined_20251109_201256.png deleted file mode 100644 index ca0f87a8033fc99d56ccd68283cc6ef5e5b9833b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 156594 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

z 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 iJQTw0a=!;)4JN)@AC52qDk{;z|P20K0p4Y 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 diff --git a/benchmarks/results/copy_cost_20251109_201256.png b/benchmarks/results/copy_cost_20251109_201256.png deleted file mode 100644 index 004a6c9aa485fc33c9eeeda1aadbc36ddee1d198..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 36593 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 z%0D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 epk1hcd&g3FL*aV3!36?ds>w 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≷AA&@MN8JcTS_l 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 Kd?9aj=f42PE#r>> diff --git a/benchmarks/results/field_access_20251109_201256.png b/benchmarks/results/field_access_20251109_201256.png deleted file mode 100644 index 5bfd2fef985df252317848f444daebe8121a1188..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 34661 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 diff --git a/benchmarks/results/field_access_scaling.png b/benchmarks/results/field_access_scaling.png deleted file mode 100644 index 5c053a5b1bce65875b4ef47e9bebf504dd89c802..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 47022 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�HZ@9Hvjc0%09s(v3y^CozLjnXw!~L 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 z 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 diff --git a/benchmarks/results/field_update_scaling.png b/benchmarks/results/field_update_scaling.png deleted file mode 100644 index 677e9c4bf1797e87911cf8e36610f8ad4a47090d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 56813 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%0A3@XuB{@b;iouC}pjJzm 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

CA5O?%i)I~D?DCypIkTv^eG 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
jxC?3{MktbuGpduT1qZO^B z!%6W-N-~`$FE=;z@sK2|YiRb~OZlkxG30gDLO1I+o#e3dggnWVNGAsUBRx#Sqa^T5 zUSOKMcOrT%GyK9Nzv$OZH%Dvh1<*;rD#rsj!cU*NhlguGdjSE~H}i)-jRDL7_2CIp zTU$5}%`GfgSByKHxva33JXTNE*7U1#vy#2v`k>p!yWB@E<1lqw`^?v<))N1Nb~L&a zZk&C~XqhpYkTCx$N;$>~&dhr|8cboOkdK0F3KW*gjgelj%@g3@Y*_w?#@gemi2d0L z`kCWiLCjdI@zXOG$@a;Vps1L;Sr6j#pBm%wNTU=*SFXx@0BK>|oreRIr)m_)3|gyA zA|;XzJ%Mh=>DbkCoLThhm?>l&5Kh(XD z=K|pChr=E?sY@_L#Z|dglcmTEkDT0q751M47JrQ{#HMIc<)#^OKk2VsZDlq#i)&;f zwoLXb)GsJiF5X$wBC=Vkn$M?OxFC<}{c1Bs@uvL+OnDk%`e0CBz*!O8PWEG;1t;7? zqL%u4998z&4t2H@U1@IJJM7?|QMe?YSwuj#bf;pANs00D>+p3i@n>1J;}T4g@83$T zrwZn(5R`vby6?7D=z8p2YotN6s^qIY8_4A4aWUu2i*nxb$h)O)rTDbv+x-ws&ywS35as`&Kepq zdir#uKtMWHKJiLWyCPlg&nb8hiZL3C${@4`#3V=G0 zCTUR&KvmAx!_J~aJJ>cD?6%!2r$5u=qW2!O%x;9q$|j}w)!r)krpW!M68o&3Wqn!%3^hTHkNn4FQUK8Iz1^)L1i{) zrfawJQ_ZG1+2!gNcAbXe-Cau`ha0d;e=1&Gl{KSI^Z&FnWn)BY3L72e@0$x_itRs7 zdCbL#Y6YoUZ>^ZipHX>N{9YP)_vcx%A#a5G$j=AZ?e=?(=4CwTMp6krzI z1V`B;D=R%Z3)l%9DowBE^MV`>B#b_dFZTIG2(F6Us8#Uyjq4r3d!796eX76aMGE(W>7Bh$Gv`k$6zK^O0==X|QrJ=|CY^6}`N@XWx(7*W7Op*U0X@LxW>x zBv9k{ZP#18jt^5Q+xbRB{Rn@0_NMj~wcs@95_CBYtjckOa<2 zxGrz}agq4)C04)PllZ(+JzZ7?lAK^JhS=@j{(c&$*}*3Wi@${Z9|71lqaJE4nQ3rW zPMIcIFm*@YA5H7axO@Sjo-C%0p#f)VLWMKosOX?nh&hYTSd*QxuSVa?td4ZPvGTmx zjhk~Wbm>Qh{UL>kflPXNdazO+gFJDUqnn$X8^Hu1511*&wXQTWUKLuLBJPRE#Bvw> zpNm$@Z^EZz1SJ0p6JHYC?3$6PtJ8d-;po(=`wa(|D)TB4zIq=BA`w%ot42CHnNK4_ zL$$c$;QfVd1CfcsS(hK3s4Fpck4FodYkWJL7|SXW#3z}m?f*idXfKEghM5jxSBT6I zC~9iY<$kVH87ya%08x71e(nmHknWE|wOHd?#^OG<3#MYsCA;%gjxEX4C?Uy%YHc$&PJ2 za5Rx6suF$QVO3F^vc`?d|Gg=h1o_3zd1r*9v$GVO9U=J$YI*_^+PC2OojSOTZ0Mi1 z|NDW0yDp`adAhvS%7U%!;>HV0>iIZw_9lJ{?h5<(AT76$=iOjXc1E5+Zlc};l`*89 z|AL=ix1i4k@pxXNFzm0$m02p4-1c8@pFGI+^-MP4et7j~1o~O!Rp+a=h!g~U>BW+f z2l3zn0Kn5H;};J{(v>tNROI-q?P~<;xE6LCV|KilRsRbZ?P{dl1+HSgzjAopbY{iL z(AFcFJK4rOCQ~QU6=8WJnDdkN;v+Hg6CwDf}yJ$lBj5Vj^2y@#}S zMMXcbO*+5p6}w4<88~q2eOZXlm0RyabPqm1;7#h&+I>V21P0^W)ZRZIHID0C+q$G~ zUqA)>CZ}b}TB-()^Ka~#*YO4C3#Z7yW^}SQWJ(AA;au$x7=|}?KNAE&UF+fEQU_%h zFy8WR&ms(~))IHy7IG~_|2R#rWT)_dz1_Rq6{6#uXMc{FpX5L^w@NqLG+U*a3XCc- z@1no$%${jeuu~9$*!iGOQ*OY0h&oSid2S950Z|te*Nw`)ZBEfb=3@fv5D<+OQ9f4- zL}%oRQC0ltxc3DvHBM@SSX{qVXxp|$j`6)Hq;~kCrOemPrZQ$~i=mG6OJKQO98H6% zT4EsUSI7%gVotvhFj8kc&y~f$0N#h@zP|D*kC)(Q7zCi7x3_oIB|+~|;iWCEw~Dwd z*G~S#2#X!8MdH$gKow9Es#>;XvSNC3kM30S4#=tzAQT?I)jfYjZ%scynRo$LVG(>g zYip+awG=*&>anHZsFquzV09+jqqP~zyo9|`wZwSLw`1haPj=4F{xvB_(9)RKzJhv< zW|9b&M|ZFku11gh>3Ztf$(5{J1c$88WRNf)wdPTv)LI!l)wdr%+2&BgLcK^ry?$c{ z__Pp>f&ByPY5{|hK2^h`k{&prf#z6(m30;#M3I#BcR2f4|4L$yR;~5e07}C$9Ad&D z!wu8Fu~C9hRLF*u;^&|8{vSpc5ncoA29>j(!-mIK74Gmwubr9D%!lB^NdcK9q*Mo|Dy%S zK1tUhiTn4-r>mrPwGLrMJ~r0abzf&STTjj-`DVUv_P(E>!p&4O?kvU~A(xrd|-zNn=30zXOR0btFGTX^pQ)>sb(Y!G!%H2Qi2?g|PQVCsitZmI@| z-JIbJ29^)bxDN2MjlpiRCiM(-&zmsVkJ?~c2CoV@*m4tx0!_N$;O<2KI{q`CxpG>D z&Ry#NVgaVtvs%aH+2nYFHoYG)lfOovW$T7FmmU-R;ksy;W?f8H1==u4$wQEFK-vQY z>r$SZOiJ1aCKM7=@p$Vyl_?Nyd)wL;+^UYjY;^i~1oBrfz4D=dLs(d6C>d*#n1dJi zM4$!=1q%{r$l&JH@t$^Wak1T<@cJF~FaL(}Hi~<$ir`70>^?{`tWa-s8?s%YaWE|@ znQ;nnT@i8^Way5hh!uFn>TU^>&o(Xaw-8YJ;Rj28fh=Ttmw%XMm)zNbCFJ|C=3n>$ z|85GP$N_->CpfsO5lb}0!afj5Y@FesygaCg5_^91ujz!aVI!j4t8EjtIcW0=+QT08 z*SgXH*^(?&PA7Py*Ri+Z2H3lkndn_dy{Mm|%;PTq{it*4EQ{+RIyH21#3ggA=#)19 zXi#bCjVY%Ma}>RKoyi}r*0;Pz$uU*@e@T80n9NeE4YV_jG!Z}J>B zFF31`HF?R2@+#rpvdp2jdLe*}W|vNmDYN>;NLpS-J4kwByRG6zCzj@yh>6t@Mv(D+ zt9`7YOJ&!@a{p>u&v&;~ddQHs|AD{v{m8YoFye40`>MVh=;{qTo+6D*rd6f-rwIAr{>WZg+ z=BBA8*kxUk_}qzmxj8a5scGmqQdahh#ZUR*Zt{?li1ztyIHUt<0n%NM!S#C73Qr&S zakQmk!4ng|PhYY}x4d8WbT+zHt#p!ORaTk9q~1fD!iAHO195dQAvs*;e<5WZt<1MV zQY2lnf%193l6iOk`>7Ch9nbK?QVjfW!e<&yGkZhn<{ zIyyTew5jqLfR$lYhi9q!+xoA|kyai9^JiAPszLFXq5VTn;k_MnGR1${UrgPxSwh)d z1H=_HcdRD`W)xa!KZ&9ma4xvNQy6TQM}ZZ1^Y8@pR~HEg?m5VLtkd6)*;^hEHgG^T z2wpTBRzC{s)Wdh66ZNKRI4oCDxf&iOH#cc>bzx5fB%jO8xjO?N*F zfRul|?zN13NJYsr0gEG9&{7#3wDUQwuu12rCH4XVbyf7W+PiujWp-U@VH+GXXU-Y{ z?kH*J5=E9Dz{N)*KmAS=cf}w6zH$XpoFAKiFFo&aXc_%vOoqPRwz)vA81oIe<2nWhg$T4Xc+Ib9Xwf(;e|rwu|I?Fn`dtaAWlJX? zWtCu#{IyMF*vD(%_T)R%;~Fdfo~s?q+cIXdxIQ$rG7BV z31BLX!+x))^L?dITvRggyw#AJIu6-P@P*m`wAFI>V3U+>Q&o_A+m=XHQnRSx7H;#W zqq%%{`Uk@Fe7AG@2BrpcduL+yCT$rfu74KGocQo!W3T8-1gq7vpPRzC8Ruhhf`lBo z{_NqrRDMrdXs%jm5Cl~!x}2N-2swOl`=Zg%?^va5zwRIcRTMF-!v}mlnnG?Wkj#qErNODo_l%jyDs8?T( zR+bX`N)rU&9QK~D_3syX_bZzt4RJFuIa&>1%LV-y3+%y%N*9Mp9RBa=Az{lSB3vy) zOE3BL^c}%i#~>)d8R37*W35iEe)w?eLC;v7NtwBf2?7e>xE)<3#3zPhd zywtKR;n>;H0g7`lYNOJ-#GTcV`j}J=f9JH`-tMmvey+K^l$!43oBIe!iwJ6876XQ=-GQW@vJScLZ|i1MZd7WZ2iJ}L6ca0G0=2O z8a0$Olz(jOxU&EEv_J9Ve~E6*BVi7+p4w63q__+MI1tRXmRuaD0)&=h*Cc8H-_aKBTREtE&Cjx7^hxh0NQRhTM zUCx4VHJ0B(oh4uuAwr>c{_=Q5h6oWdz~S%ER$Bh1$I-NxAfiYA?K;NL?+i61a|JnY zxrI>}H1)ikoD#qjgW^a5uSM1nXW2!=hiYF5mz~M(H3z~mUY4YM6ZgxILL6mWVRFu| z+FO4%znyZ7R4@N_S3(*xwJ3Nl^|HUt3^yVwdqhx{Fm3IsLvk=8C|x&F;n*U}^j_om zLD`lHCU2_Nt0p|@;jDh-+G|vqTFCRwxS=(&d@c&yS9y9C-0H$0q&}%h>=^pKta-Y$ z5iPL^nIk5Vj%V#uo{@Oy$~LLuW+q+z+#u#0w>Cy$qbrX@*{#KA-*Oe6j!7Hy#oM1~ z`yLZTeYE_?{AT>+f8Q`aN8vM+BB1L1D?xB}Hd5|Qc=!!&ZlQxu3yzqQl9rqLwkinITd1hsHw|AoF*#WV_E&2_r z49q_fB#1CT%x>J|=ZjbzEJ`-wvo7Jbnx+nTD89tcr+ZbT{@%-ja`K_RBjs+&s81!< ztOzsYj7QL)$-3x9nfILxfi{{9k!k}gzXRFr2LYAFjIcD_@dn1!U*F$p8bR%lcWo;1kngtA|VwD+@-9a zH8_6{EdMI<@%G#_)@!?*>E$XT>DE}^;)$xgX;Y*nX>25Xki1r?XGcU`go)X0SpKV| znc-04TdjI6o$1E}A@4DLO^N9j{`Y5{)d8q{!-40Y;Knoh?vn9jF}Y;OZQuQ2{rjpw zn~RN2+~y@mrV%gH+Rax_CW4!AFHHILyMK8Z>-|oI4l_;jmCJ9+`Pq1+uw2Q5YCiAz z@>el;?+*SPd^E|vM!(N+8pa4|ArT{Z0(x@hj7v zFt?cz#OU2CQnbZ)6J{e5-{KtE%d!en_zB7?jxT&Ip)I|C^T}$$NYTKY&AKsN@W@BSxp;Y0sC)=AVGwKz4 zOj2HCjr*QEEp%EYXLoDexNa>$!AXkr_=@$QQv1i7MyW|ht-ecI~J5|(^-lZgo zeX`EUIDepcRc5T-%h0Nv16S%ENz>d-Ws|MlSJZwRug&LF#D7FS!3iN#ZhLhc(U!-C z5)$>a?dRU5l{i6wqg3T|{{BMM@Itg8Lb z5fobA9F-(Pg8$t%`#vr{H>Zx^cU#UuffwAIdJ3f>Aby#>R86t2zQ-dsHRQc5a<2Na zu1)!6^wFkE@m|KG!mwhtCeOX0+MpY6K9qO$19K=2sYczyQpq&`Fj$E1v!9oZ^U42v(!LXijqfsB`&S<>(!+>*$x8 z$GvUhgM1?umXl{ixB30w-67E7WgU=DyW$)du7|6`yR*CHFV}dPL$<$^J>_8P8v(y` z<{Jiyt#3lYZbNJ`s}|LlteB!uQNCCV{;np zCFqg>g@apU*sc2G#8oJeBFNgYgTMSZse0&wQZik;jHDnu*dcK&;VSG z6KkEdLq-_~!tJXv7(6@5E=HLmYp1rQ@%mRTcc@IZ^RGpW)q1QLkT)TA^@bB=vb^u% zs*kF;k&`BzB=r#q5d)zo4P@y9?$03V5t~+AC8I>L>uaFae zb13anG0xW($}zktZYCrADf7E}vhLOR9v7G^@)D)I|E)%ku${bo$n(rO^(9)=Vm(-c zLAw8EcJwvuI;H=vo(lO@@KLVi&twLN$*yYL$)p+66#V7GX)prx6v(zwLu_i>M zozzFpl8))pr{AfFmbP2hg9`#2$ph+mKNBMFU4VayKX9Z9NDlZUy1p?nI_f=4x?@mm zBneCJf{R5Ac_R1fk$GuU6`R!fYhB9|KxSE|R z$b?Makq1MMsLg~|{&JjJ$CWcYVV`*8BzUV2wj-D%FdkKVq@@2D_c-YwBG`}=>4yT8gXsEqRe zaf4|V5mi%`!n7*xx@)-McXj*mRjIel-;1b5)1)@UTV~Gk!%Fd8(t@J`B3s6Ek(SrS17?AEj+)=gU2;WF>_(p z&Cn&!GhBP+;yKS{P0D87aF(WMiQzBZ%72N&6bAdc4JHxysWWVgC)?33$ zZCCfhkLIGne{;S=Bgk*8U#mX=V3hY%&1FNXt!H>GHratmu8=pZS@jKggzoMko9DkQ;$wD^Grm48Nss>rrmUF;qq2$U|7X zagti^HGbrBZ)m{DgQ04!AU8MZ*O%A6m-{;W+g_DhBt`O!TBFL z=fB%6JQjW$?T(JuE_b4L2);pgks009eRYAdBq@u>>!;h6D?@zf>|<7|EqF7pJIT)q zJlE)Jjox7YNCx{y=(waC!}V~Lk&tSqYe(!~X3ODp*iH?v;UGAr%X3`2xan2sn0c_7 zH>@g;aGB9xUzoP*@L|` z9z4w{A&U^EinuL1CZq)r%33G+(W($Q8S>vT(5d$__&!TOuVNiv>f+f*@(1SoHymQL zRTlR(IZl%uQGJa4?9zf1U-WD^_A_S*AdAB8WR+D`ViSP+G%C_xANJoEZ0l(~rwYDr z#k@n7IRvGe;1{YR^@U(yiQEH5X9=rq}Gnco|`cKJY(|?EiCJ*>)G!EXF%8ebcGY6}Yo3JVlzMyz52yrcJ&MYfvM+p#0 zqJ(%skIEceNPCgFO#UtUj@?_(U!sIa{}{H`9vmHw3=fwYEAd)z8b9l}a5ycOKtwWH zSsw_g@2b1qdDz?KlBMQ&vCH>LO7A*)$)9_K5Hlh-pPa zp=erW0-x~8B2n4he}x8#{SAvmz%!vFVqJrzTLh8Hh7Eu{@CKZr*Jto-GDecCOSDKo0nz!M@^!*>&DND2c) z?6H30%t2LIUmf)wl@TI_T4Ge=P~_CeCu1sfdp&#|mu7(u9*#TzinBWJ1Tw8+Tq*Zx zTAUm$>TU!_u|vi(Tf=1ov$U>%wI&hKT*An=Eh?mXyif(|+g$s!Sg4`8R>^ z?Ty*8(LP5CYUZ0*Hu~=7L4EGE;s#|D6mwS4NW)9S=x^6Tw2d{n#_K|2 zC~*}2C|6MZqbaB;{Z63>$27PYKp^2jHNA7#Iy~;-()hbR_ROcQePFGwOOAK^v_MRl z1mW4JFmn*;xre@hpdgyVLyXdSvM#UYDifh_-*3uoM zWnb*TS%cNtX;t=HqZ{c;4gJB*5Oc^GhbV-i9ta?{d0B2}mevJo)GoUzT zPD3goOJO3G225?=Jg;i;KpZ<)p8~*($jMO5&N#!0#oF3NQtDo zX2UZVYNV=S-K){=uq&oB9;V9XszUbN}J3 zMjuwZ$#=7hYDC}Z3CP-dU}2HCtnu*!xeDiXh7^f68hDzUUl*JuHjZdf=-hX(k6rhZ zarw9J&{e(T;P8(@W*v~A-gRjbkNRJ{Ex+jQz^_*0^FmkpwtReN`iX8wo-@Ajb=Xo| zYr^^Nbm%ltfLxV{QU@0VAs6wz;f(-Y))B<^Gc z1)kTX9Q$}jXPy*Qze1r!JJCU#f93AIQ+jq3ww7P1sqrJ`hgBtQaNa$>jYUjkTb`fM z-r2OYROwT6`hUH@$zTH zJ*+4TH;B`3pjF}N%oU=kH`%yy`cSf0LshEQQStZ3qNoZN&)&?ZuCCYNaxM6X^^DvO z4zCp}O>uNgrA$lue?O*0>tBUHH4(6O1(sFHYr$$Sl=r%pw!hX@R$szAJh;JF#LbU6 z!@sAi5#MPuzd}N%jQZFx8|&}>hJM;Va!u)6#&PP8bd38O`No;A*U-p}A**Qx)q=RP zTeo``G!h5r_&V;8@W1BJI~x?zMI>a2PY=d8l1J9plsYxCc|zwDA==eq2hJv_Xf`v6h=T9{*3nR2&pblr;3)m_C|2|Xf}YQ@Lx*F_ z{2T>}2BYy`R_PVD2%gIM_mqpQKy94-@+HB<>FGK&eZs=y^%^Z6u~PzB^!VrqGe819W<>yy*Ly~>$4a78WY1Hz$S34bz~D9e^UQ zv_;)o)Ta*Nx^V-l%w~0{SWt_sEiD~xyZvNElEK5p1Wne#e+@+;RQvyY*6R*{_=Q3Z z{D4f(vW_zNX8@)s?dCI}s|t<2NFcvNV?kp0iT&=oMb}!$D;*sl1C=>u%^4+RGwHMQ z3;JZBfeHBG!*KC;dovVE?EX?t`Z6PYDH+dA#B)?eJYZYnRN0{Yl2si__<4?UG|gwo z^#T_R?2>=@@R<9u25fWGllR9xCQFS&55K@($OoW0W@c>5TvXV1`=OwyrK7Xs4bZi< zPdkG8Qzx(gv55UWE3r)=9H@Zz67py8aY1pR>o)cho}a^T89qYxVCp$I)j*F97@AWs zP z#qc9M?2>Oz4NWhut0!UYm>C%vaSEPbN&zJR$jlM)Fgmb14>0Ti^@gR>0XG_yFix|r zg_Lhlmmo9Z1Ad(sXhGM-?hdApHnngaXv+evV?HQHV2LvT1t>5T0XKDXv%K1up_dAw z(0wh~h!6Z5AeYl0jlW?C5WpzNrQ!w{@nI8>&~xE_J{hxaDYSj&i$9)z$!OpJfLcdr zGXf9hv(Xb()pjVND#KWvc0{}fjWc9lSNm<#U}u*8Y=rCY4T6R<_-nh7(2OG+ehnfX z>T14VwclYAH$dFi!w+VVPLLNe-t_E#uYikyM5mDZt4EimJDJF1;^P}DZot}

mftLvyzu3WG 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 diff --git a/benchmarks/results/iteration_scaling.png b/benchmarks/results/iteration_scaling.png deleted file mode 100644 index e0997e9d5a2514b68205afb48e66ac6d1bbf8153..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 45409 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 bZ;h7e+2MdA)3XqaAd!?6G*0D^&2IcJ3v7%B diff --git a/benchmarks/results/struct_scaling_20251109_201256.csv b/benchmarks/results/struct_scaling_20251109_201256.csv deleted file mode 100644 index 8c5fdfd..0000000 --- a/benchmarks/results/struct_scaling_20251109_201256.csv +++ /dev/null @@ -1,13 +0,0 @@ -nfields,bytes,mut_access_ns,imm_access_ns,speedup_access,mut_update_ns,imm_update_ns,speedup_update,mut_iter_ns,imm_iter_ns,speedup_iter,imm_copy_ns -1,8,4.615,2.023,2.2812654473554126,7.1991991991991995,2.315,3.1098052696324836,11.773773773773774,2.024,5.817081904038426,2.023 -2,16,4.904,2.023,2.4241225902125554,7.207207207207207,2.314,3.11460985618289,14.48997995991984,2.025,7.1555456592196744,2.029 -5,40,4.897,2.373,2.063632532659081,7.201201201201201,2.604,2.765438249309217,16.70941883767535,2.312,7.227257282731553,2.383 -10,80,4.621,2.029,2.2774765894529327,7.1991991991991995,2.602,2.7667944654877785,23.13152610441767,2.602,8.889902422912249,3.311 -20,160,4.618,2.023,2.282748393475037,7.201201201201201,3.1633266533066133,2.2764646179280326,63.131632653061224,5.523,11.43067764857165,3.177 -50,400,4.617,2.029,2.275505174963036,7.197197197197197,6.022,1.1951506471599465,196.84902597402598,24.783132530120483,7.942862982909166,6.895895895895896 -100,800,4.618,2.025,2.280493827160494,7.198198198198198,11.745745745745745,0.612834498039884,260.55786350148367,68.50307377049181,3.803593753682347,9.476476476476476 -200,1600,4.621,2.373,1.9473240623683101,7.41041041041041,24.633534136546185,0.30082611651798524,777.9142857142857,155.1606475716065,5.013605562294905,20.948897795591183 -500,4000,4.624,2.027,2.281203749383325,7.198198198198198,80.83854166666667,0.08904413723690831,1156.3,499.35567010309273,2.3155840000000003,58.68463886063072 -1000,8000,4.671,2.084,2.241362763915547,7.2002002002002,193.26257861635222,0.03725604952469045,3465.75,1100.2,3.1501090710779853,45.70171890798787 -2000,16000,4.625,2.024,2.2850790513833994,7.197197197197197,410.8542713567839,0.0175176399491468,4811.714285714285,2226.222222222222,2.1613809428742545,82.94813278008299 -5000,40000,4.623,2.083,2.2193951032165145,7.2042042042042045,1936.1,0.0037209876577677828,33969.0,5667.166666666667,5.994000529365056,867.0408163265306 diff --git a/benchmarks/results/struct_scaling_20251109_201256.json b/benchmarks/results/struct_scaling_20251109_201256.json deleted file mode 100644 index 3cdf65f..0000000 --- a/benchmarks/results/struct_scaling_20251109_201256.json +++ /dev/null @@ -1,187 +0,0 @@ -{ - "julia_version": "1.12.1", - "analysis": { - "copy_intercept_ns": -25.467962218157183, - "copy_slope_ns_per_field": 0.1586673181436861, - "r_squared": 0.9005527188987935 - }, - "results": [ - { - "mutable_update_ns": 7.1991991991991995, - "immutable_copy_ns": 2.023, - "mutable_access_ns": 4.615, - "bytes": 8, - "speedup_update": 3.1098052696324836, - "speedup_iter": 5.817081904038426, - "immutable_access_ns": 2.023, - "speedup_access": 2.2812654473554126, - "nfields": 1, - "immutable_update_ns": 2.315, - "mutable_iter_ns": 11.773773773773774, - "immutable_iter_ns": 2.024 - }, - { - "mutable_update_ns": 7.207207207207207, - "immutable_copy_ns": 2.029, - "mutable_access_ns": 4.904, - "bytes": 16, - "speedup_update": 3.11460985618289, - "speedup_iter": 7.1555456592196744, - "immutable_access_ns": 2.023, - "speedup_access": 2.4241225902125554, - "nfields": 2, - "immutable_update_ns": 2.314, - "mutable_iter_ns": 14.48997995991984, - "immutable_iter_ns": 2.025 - }, - { - "mutable_update_ns": 7.201201201201201, - "immutable_copy_ns": 2.383, - "mutable_access_ns": 4.897, - "bytes": 40, - "speedup_update": 2.765438249309217, - "speedup_iter": 7.227257282731553, - "immutable_access_ns": 2.373, - "speedup_access": 2.063632532659081, - "nfields": 5, - "immutable_update_ns": 2.604, - "mutable_iter_ns": 16.70941883767535, - "immutable_iter_ns": 2.312 - }, - { - "mutable_update_ns": 7.1991991991991995, - "immutable_copy_ns": 3.311, - "mutable_access_ns": 4.621, - "bytes": 80, - "speedup_update": 2.7667944654877785, - "speedup_iter": 8.889902422912249, - "immutable_access_ns": 2.029, - "speedup_access": 2.2774765894529327, - "nfields": 10, - "immutable_update_ns": 2.602, - "mutable_iter_ns": 23.13152610441767, - "immutable_iter_ns": 2.602 - }, - { - "mutable_update_ns": 7.201201201201201, - "immutable_copy_ns": 3.177, - "mutable_access_ns": 4.618, - "bytes": 160, - "speedup_update": 2.2764646179280326, - "speedup_iter": 11.43067764857165, - "immutable_access_ns": 2.023, - "speedup_access": 2.282748393475037, - "nfields": 20, - "immutable_update_ns": 3.1633266533066133, - "mutable_iter_ns": 63.131632653061224, - "immutable_iter_ns": 5.523 - }, - { - "mutable_update_ns": 7.197197197197197, - "immutable_copy_ns": 6.895895895895896, - "mutable_access_ns": 4.617, - "bytes": 400, - "speedup_update": 1.1951506471599465, - "speedup_iter": 7.942862982909166, - "immutable_access_ns": 2.029, - "speedup_access": 2.275505174963036, - "nfields": 50, - "immutable_update_ns": 6.022, - "mutable_iter_ns": 196.84902597402598, - "immutable_iter_ns": 24.783132530120483 - }, - { - "mutable_update_ns": 7.198198198198198, - "immutable_copy_ns": 9.476476476476476, - "mutable_access_ns": 4.618, - "bytes": 800, - "speedup_update": 0.612834498039884, - "speedup_iter": 3.803593753682347, - "immutable_access_ns": 2.025, - "speedup_access": 2.280493827160494, - "nfields": 100, - "immutable_update_ns": 11.745745745745745, - "mutable_iter_ns": 260.55786350148367, - "immutable_iter_ns": 68.50307377049181 - }, - { - "mutable_update_ns": 7.41041041041041, - "immutable_copy_ns": 20.948897795591183, - "mutable_access_ns": 4.621, - "bytes": 1600, - "speedup_update": 0.30082611651798524, - "speedup_iter": 5.013605562294905, - "immutable_access_ns": 2.373, - "speedup_access": 1.9473240623683101, - "nfields": 200, - "immutable_update_ns": 24.633534136546185, - "mutable_iter_ns": 777.9142857142857, - "immutable_iter_ns": 155.1606475716065 - }, - { - "mutable_update_ns": 7.198198198198198, - "immutable_copy_ns": 58.68463886063072, - "mutable_access_ns": 4.624, - "bytes": 4000, - "speedup_update": 0.08904413723690831, - "speedup_iter": 2.3155840000000003, - "immutable_access_ns": 2.027, - "speedup_access": 2.281203749383325, - "nfields": 500, - "immutable_update_ns": 80.83854166666667, - "mutable_iter_ns": 1156.3, - "immutable_iter_ns": 499.35567010309273 - }, - { - "mutable_update_ns": 7.2002002002002, - "immutable_copy_ns": 45.70171890798787, - "mutable_access_ns": 4.671, - "bytes": 8000, - "speedup_update": 0.03725604952469045, - "speedup_iter": 3.1501090710779853, - "immutable_access_ns": 2.084, - "speedup_access": 2.241362763915547, - "nfields": 1000, - "immutable_update_ns": 193.26257861635222, - "mutable_iter_ns": 3465.75, - "immutable_iter_ns": 1100.2 - }, - { - "mutable_update_ns": 7.197197197197197, - "immutable_copy_ns": 82.94813278008299, - "mutable_access_ns": 4.625, - "bytes": 16000, - "speedup_update": 0.0175176399491468, - "speedup_iter": 2.1613809428742545, - "immutable_access_ns": 2.024, - "speedup_access": 2.2850790513833994, - "nfields": 2000, - "immutable_update_ns": 410.8542713567839, - "mutable_iter_ns": 4811.714285714285, - "immutable_iter_ns": 2226.222222222222 - }, - { - "mutable_update_ns": 7.2042042042042045, - "immutable_copy_ns": 867.0408163265306, - "mutable_access_ns": 4.623, - "bytes": 40000, - "speedup_update": 0.0037209876577677828, - "speedup_iter": 5.994000529365056, - "immutable_access_ns": 2.083, - "speedup_access": 2.2193951032165145, - "nfields": 5000, - "immutable_update_ns": 1936.1, - "mutable_iter_ns": 33969.0, - "immutable_iter_ns": 5667.166666666667 - } - ], - "timestamp": "20251109_201256", - "system": { - "cpu_model": "Intel(R) Xeon(R) Gold 6326 CPU @ 2.90GHz", - "cpu_speed_mhz": 3300, - "machine": "x86_64-linux-gnu", - "word_size": 64, - "cpu_cores": 32, - "os": "Linux" - } -} \ No newline at end of file diff --git a/benchmarks/results/struct_size_scaling.csv b/benchmarks/results/struct_size_scaling.csv deleted file mode 100644 index fc8e7c4..0000000 --- a/benchmarks/results/struct_size_scaling.csv +++ /dev/null @@ -1,13 +0,0 @@ -nfields,bytes,mut_access_ns,imm_access_ns,speedup_access,mut_update_ns,imm_update_ns,speedup_update,imm_copy_ns,mut_iter_ns,imm_iter_ns,speedup_iter -1,8,4.615,2.023,2.2812654473554126,7.1991991991991995,2.315,3.1098052696324836,2.023,11.773773773773774,2.024,5.817081904038426 -2,16,4.904,2.023,2.4241225902125554,7.207207207207207,2.314,3.11460985618289,2.029,14.48997995991984,2.025,7.1555456592196744 -5,40,4.897,2.373,2.063632532659081,7.201201201201201,2.604,2.765438249309217,2.383,16.70941883767535,2.312,7.227257282731553 -10,80,4.621,2.029,2.2774765894529327,7.1991991991991995,2.602,2.7667944654877785,3.311,23.13152610441767,2.602,8.889902422912249 -20,160,4.618,2.023,2.282748393475037,7.201201201201201,3.1633266533066133,2.2764646179280326,3.177,63.131632653061224,5.523,11.43067764857165 -50,400,4.617,2.029,2.275505174963036,7.197197197197197,6.022,1.1951506471599465,6.895895895895896,196.84902597402598,24.783132530120483,7.942862982909166 -100,800,4.618,2.025,2.280493827160494,7.198198198198198,11.745745745745745,0.612834498039884,9.476476476476476,260.55786350148367,68.50307377049181,3.803593753682347 -200,1600,4.621,2.373,1.9473240623683101,7.41041041041041,24.633534136546185,0.30082611651798524,20.948897795591183,777.9142857142857,155.1606475716065,5.013605562294905 -500,4000,4.624,2.027,2.281203749383325,7.198198198198198,80.83854166666667,0.08904413723690831,58.68463886063072,1156.3,499.35567010309273,2.3155840000000003 -1000,8000,4.671,2.084,2.241362763915547,7.2002002002002,193.26257861635222,0.03725604952469045,45.70171890798787,3465.75,1100.2,3.1501090710779853 -2000,16000,4.625,2.024,2.2850790513833994,7.197197197197197,410.8542713567839,0.0175176399491468,82.94813278008299,4811.714285714285,2226.222222222222,2.1613809428742545 -5000,40000,4.623,2.083,2.2193951032165145,7.2042042042042045,1936.1,0.0037209876577677828,867.0408163265306,33969.0,5667.166666666667,5.994000529365056 diff --git a/benchmarks/results/struct_size_scaling.json b/benchmarks/results/struct_size_scaling.json deleted file mode 100644 index a391a4b..0000000 --- a/benchmarks/results/struct_size_scaling.json +++ /dev/null @@ -1,196 +0,0 @@ -{ - "struct_sizes": [ - 1, - 2, - 5, - 10, - 20, - 50, - 100, - 200, - 500, - 1000, - 2000, - 5000 - ], - "system_info": { - "cpu_model": "Intel(R) Xeon(R) Gold 6326 CPU @ 2.90GHz", - "julia_version": "1.12.1", - "total_memory_gb": 503.35, - "l1_cache": "48K", - "l2_cache": "1280K", - "l3_cache": "24576K", - "cpu_cores": 32 - }, - "results": [ - { - "mutable_update_ns": 7.1991991991991995, - "immutable_copy_ns": 2.023, - "mutable_access_ns": 4.615, - "bytes": 8, - "speedup_update": 3.1098052696324836, - "speedup_iter": 5.817081904038426, - "immutable_access_ns": 2.023, - "speedup_access": 2.2812654473554126, - "nfields": 1, - "immutable_update_ns": 2.315, - "mutable_iter_ns": 11.773773773773774, - "immutable_iter_ns": 2.024 - }, - { - "mutable_update_ns": 7.207207207207207, - "immutable_copy_ns": 2.029, - "mutable_access_ns": 4.904, - "bytes": 16, - "speedup_update": 3.11460985618289, - "speedup_iter": 7.1555456592196744, - "immutable_access_ns": 2.023, - "speedup_access": 2.4241225902125554, - "nfields": 2, - "immutable_update_ns": 2.314, - "mutable_iter_ns": 14.48997995991984, - "immutable_iter_ns": 2.025 - }, - { - "mutable_update_ns": 7.201201201201201, - "immutable_copy_ns": 2.383, - "mutable_access_ns": 4.897, - "bytes": 40, - "speedup_update": 2.765438249309217, - "speedup_iter": 7.227257282731553, - "immutable_access_ns": 2.373, - "speedup_access": 2.063632532659081, - "nfields": 5, - "immutable_update_ns": 2.604, - "mutable_iter_ns": 16.70941883767535, - "immutable_iter_ns": 2.312 - }, - { - "mutable_update_ns": 7.1991991991991995, - "immutable_copy_ns": 3.311, - "mutable_access_ns": 4.621, - "bytes": 80, - "speedup_update": 2.7667944654877785, - "speedup_iter": 8.889902422912249, - "immutable_access_ns": 2.029, - "speedup_access": 2.2774765894529327, - "nfields": 10, - "immutable_update_ns": 2.602, - "mutable_iter_ns": 23.13152610441767, - "immutable_iter_ns": 2.602 - }, - { - "mutable_update_ns": 7.201201201201201, - "immutable_copy_ns": 3.177, - "mutable_access_ns": 4.618, - "bytes": 160, - "speedup_update": 2.2764646179280326, - "speedup_iter": 11.43067764857165, - "immutable_access_ns": 2.023, - "speedup_access": 2.282748393475037, - "nfields": 20, - "immutable_update_ns": 3.1633266533066133, - "mutable_iter_ns": 63.131632653061224, - "immutable_iter_ns": 5.523 - }, - { - "mutable_update_ns": 7.197197197197197, - "immutable_copy_ns": 6.895895895895896, - "mutable_access_ns": 4.617, - "bytes": 400, - "speedup_update": 1.1951506471599465, - "speedup_iter": 7.942862982909166, - "immutable_access_ns": 2.029, - "speedup_access": 2.275505174963036, - "nfields": 50, - "immutable_update_ns": 6.022, - "mutable_iter_ns": 196.84902597402598, - "immutable_iter_ns": 24.783132530120483 - }, - { - "mutable_update_ns": 7.198198198198198, - "immutable_copy_ns": 9.476476476476476, - "mutable_access_ns": 4.618, - "bytes": 800, - "speedup_update": 0.612834498039884, - "speedup_iter": 3.803593753682347, - "immutable_access_ns": 2.025, - "speedup_access": 2.280493827160494, - "nfields": 100, - "immutable_update_ns": 11.745745745745745, - "mutable_iter_ns": 260.55786350148367, - "immutable_iter_ns": 68.50307377049181 - }, - { - "mutable_update_ns": 7.41041041041041, - "immutable_copy_ns": 20.948897795591183, - "mutable_access_ns": 4.621, - "bytes": 1600, - "speedup_update": 0.30082611651798524, - "speedup_iter": 5.013605562294905, - "immutable_access_ns": 2.373, - "speedup_access": 1.9473240623683101, - "nfields": 200, - "immutable_update_ns": 24.633534136546185, - "mutable_iter_ns": 777.9142857142857, - "immutable_iter_ns": 155.1606475716065 - }, - { - "mutable_update_ns": 7.198198198198198, - "immutable_copy_ns": 58.68463886063072, - "mutable_access_ns": 4.624, - "bytes": 4000, - "speedup_update": 0.08904413723690831, - "speedup_iter": 2.3155840000000003, - "immutable_access_ns": 2.027, - "speedup_access": 2.281203749383325, - "nfields": 500, - "immutable_update_ns": 80.83854166666667, - "mutable_iter_ns": 1156.3, - "immutable_iter_ns": 499.35567010309273 - }, - { - "mutable_update_ns": 7.2002002002002, - "immutable_copy_ns": 45.70171890798787, - "mutable_access_ns": 4.671, - "bytes": 8000, - "speedup_update": 0.03725604952469045, - "speedup_iter": 3.1501090710779853, - "immutable_access_ns": 2.084, - "speedup_access": 2.241362763915547, - "nfields": 1000, - "immutable_update_ns": 193.26257861635222, - "mutable_iter_ns": 3465.75, - "immutable_iter_ns": 1100.2 - }, - { - "mutable_update_ns": 7.197197197197197, - "immutable_copy_ns": 82.94813278008299, - "mutable_access_ns": 4.625, - "bytes": 16000, - "speedup_update": 0.0175176399491468, - "speedup_iter": 2.1613809428742545, - "immutable_access_ns": 2.024, - "speedup_access": 2.2850790513833994, - "nfields": 2000, - "immutable_update_ns": 410.8542713567839, - "mutable_iter_ns": 4811.714285714285, - "immutable_iter_ns": 2226.222222222222 - }, - { - "mutable_update_ns": 7.2042042042042045, - "immutable_copy_ns": 867.0408163265306, - "mutable_access_ns": 4.623, - "bytes": 40000, - "speedup_update": 0.0037209876577677828, - "speedup_iter": 5.994000529365056, - "immutable_access_ns": 2.083, - "speedup_access": 2.2193951032165145, - "nfields": 5000, - "immutable_update_ns": 1936.1, - "mutable_iter_ns": 33969.0, - "immutable_iter_ns": 5667.166666666667 - } - ], - "timestamp": "2025-11-09T20:12:52.503" -} \ No newline at end of file diff --git a/benchmarks/struct_size_scaling.jl b/benchmarks/struct_size_scaling.jl deleted file mode 100644 index a84412d..0000000 --- a/benchmarks/struct_size_scaling.jl +++ /dev/null @@ -1,731 +0,0 @@ -# Benchmark: Does immutable performance degrade with struct size? -# Theory: Stack copying is O(n), heap pointers are O(1) -# Question: At what size does mutable win? - -# NOTE: Using packages from global environment (not project) -using BenchmarkTools -using Printf -using JSON -using Plots -using JSON -using Dates - -# Get system information -println("="^80) -println("SYSTEM INFORMATION") -println("="^80) -println() - -# CPU info -cpu_info = Sys.cpu_info() -println("CPU Model: ", cpu_info[1].model) -println("CPU Cores: ", Sys.CPU_THREADS, " threads (", length(cpu_info), " physical cores)") -println("CPU Speed: ", cpu_info[1].speed, " MHz") -println() - -# Julia and system info -println("Julia Version: ", VERSION) -println("OS: ", Sys.KERNEL, " ", Sys.MACHINE) -println("Word Size: ", Sys.WORD_SIZE, " bits") -println() - -# Memory and cache info (approximate) -println("Approximate CPU Cache Sizes:") -println(" L1 Cache: ~32-64 KB per core (typical)") -println(" L2 Cache: ~256-512 KB per core (typical)") -println(" L3 Cache: ~8-32 MB shared (typical)") -println() -println("Note: Testing up to 8KB structs to exceed L1 cache") -println() - -using BenchmarkTools -using Printf -using JSON -using Plots - -# Collect system information -function get_system_info() - info = Dict{String,Any}() - info["julia_version"] = string(VERSION) - info["cpu_model"] = Sys.cpu_info()[1].model - info["cpu_cores"] = Sys.CPU_THREADS - info["total_memory_gb"] = round(Sys.total_memory() / 1024^3, digits=2) - - # Try to get CPU cache info (Linux) - try - if Sys.islinux() - l1_cache = read("/sys/devices/system/cpu/cpu0/cache/index0/size", String) |> strip - l2_cache = read("/sys/devices/system/cpu/cpu0/cache/index2/size", String) |> strip - l3_cache = read("/sys/devices/system/cpu/cpu0/cache/index3/size", String) |> strip - info["l1_cache"] = l1_cache - info["l2_cache"] = l2_cache - info["l3_cache"] = l3_cache - end - catch - info["cache_info"] = "Not available" - end - - return info -end - -system_info = get_system_info() - -println("="^80) -println("SYSTEM INFORMATION") -println("="^80) -println("Julia Version: $(system_info["julia_version"])") -println("CPU Model: $(system_info["cpu_model"])") -println("CPU Cores: $(system_info["cpu_cores"])") -println("Total Memory: $(system_info["total_memory_gb"]) GB") -if haskey(system_info, "l1_cache") - println("L1 Cache: $(system_info["l1_cache"])") - println("L2 Cache: $(system_info["l2_cache"])") - println("L3 Cache: $(system_info["l3_cache"])") -end -println() - -println("="^80) -println("STRUCT SIZE SCALING BENCHMARK") -println("="^80) -println() -println("Testing hypothesis: Immutable slows down with struct size, mutable stays constant") -println() - -# Test different struct sizes (number of Float64 fields) -# Extended range to go well beyond register file and L1 cache -STRUCT_SIZES = [1, 2, 5, 10, 20, 50, 100, 200, 500, 1000, 2000, 5000] - -results = [] - -for nfields in STRUCT_SIZES - println("Testing struct with $nfields Float64 fields ($(nfields * 8) bytes)...") - - # Generate mutable version - field_names_mut = [Symbol("field$i") for i in 1:nfields] - - # Mutable: Dict-based - mutable_data = Dict{Symbol,Float64}() - for fname in field_names_mut - mutable_data[fname] = rand() - end - - # Immutable: NamedTuple-based - immutable_data = NamedTuple{Tuple(field_names_mut)}(Tuple(rand() for _ in 1:nfields)) - - # Benchmark 1: Field Access (read first field) - first_field = field_names_mut[1] - - time_mut_access = @belapsed $mutable_data[$first_field] - time_imm_access = @belapsed $immutable_data.$first_field - - # Benchmark 2: Field Update (change first field) - time_mut_update = @belapsed begin - $mutable_data[$first_field] = 42.0 - end - - time_imm_update = @belapsed begin - $immutable_data = (; $immutable_data..., $first_field=42.0) - end - - # Benchmark 3: Struct Copy (merge with empty to force copy) - time_imm_copy = @belapsed merge($immutable_data, NamedTuple()) - - # Benchmark 4: Iteration over all fields - time_mut_iter = @belapsed begin - sum = 0.0 - for (k, v) in $mutable_data - sum += v - end - sum - end - - time_imm_iter = @belapsed begin - sum = 0.0 - for v in $immutable_data - sum += v - end - sum - end - - speedup_access = time_mut_access / time_imm_access - speedup_update = time_mut_update / time_imm_update - speedup_iter = time_mut_iter / time_imm_iter - - push!(results, ( - nfields=nfields, - bytes=nfields * 8, - # Access times - mut_access=time_mut_access, - imm_access=time_imm_access, - speedup_access=speedup_access, - # Update times - mut_update=time_mut_update, - imm_update=time_imm_update, - speedup_update=speedup_update, - # Copy time - imm_copy=time_imm_copy, - # Iteration times - mut_iter=time_mut_iter, - imm_iter=time_imm_iter, - speedup_iter=speedup_iter - )) - - println(" Access: Mut=$(round(time_mut_access*1e9, digits=2))ns Imm=$(round(time_imm_access*1e9, digits=2))ns Speedup=$(round(speedup_access, digits=1))x") - println(" Update: Mut=$(round(time_mut_update*1e9, digits=2))ns Imm=$(round(time_imm_update*1e9, digits=2))ns Speedup=$(round(speedup_update, digits=1))x") - println(" Iterate: Mut=$(round(time_mut_iter*1e9, digits=2))ns Imm=$(round(time_imm_iter*1e9, digits=2))ns Speedup=$(round(speedup_iter, digits=1))x") - println(" Copy: $(round(time_imm_copy*1e9, digits=2))ns") - println() -end - -println("="^80) -println("RESULTS SUMMARY") -println("="^80) -println() - -println("Field Access Performance:") -println("Size (fields) | Bytes | Mutable (ns) | Immutable (ns) | Speedup") -println("-"^70) -for r in results - @printf("%13d | %5d | %12.2f | %14.2f | %6.1fx\n", - r.nfields, r.bytes, r.mut_access * 1e9, r.imm_access * 1e9, r.speedup_access) -end -println() - -println("Field Update Performance:") -println("Size (fields) | Bytes | Mutable (ns) | Immutable (ns) | Speedup") -println("-"^70) -for r in results - @printf("%13d | %5d | %12.2f | %14.2f | %6.1fx\n", - r.nfields, r.bytes, r.mut_update * 1e9, r.imm_update * 1e9, r.speedup_update) -end -println() - -println("Iteration Performance:") -println("Size (fields) | Bytes | Mutable (ns) | Immutable (ns) | Speedup") -println("-"^70) -for r in results - @printf("%13d | %5d | %12.2f | %14.2f | %6.1fx\n", - r.nfields, r.bytes, r.mut_iter * 1e9, r.imm_iter * 1e9, r.speedup_iter) -end -println() - -println("Immutable Copy Cost (ns):") -println("Size (fields) | Bytes | Copy Time (ns)") -println("-"^40) -for r in results - @printf("%13d | %5d | %13.2f\n", r.nfields, r.bytes, r.imm_copy * 1e9) -end -println() - -# Analysis -println("="^80) -println("ANALYSIS") -println("="^80) -println() - -# Check if mutable ever wins -access_wins = [r for r in results if r.speedup_access < 1.0] -update_wins = [r for r in results if r.speedup_update < 1.0] -iter_wins = [r for r in results if r.speedup_iter < 1.0] - -if isempty(access_wins) - println("✓ Immutable ALWAYS faster for field access (even at 1000 fields = 8KB)") - min_speedup = minimum(r.speedup_access for r in results) - println(" Minimum speedup: $(round(min_speedup, digits=1))x at $(results[end].nfields) fields") -else - println("⚠ Mutable wins for field access at $(access_wins[1].nfields) fields") -end -println() - -if isempty(update_wins) - println("✓ Immutable ALWAYS faster for field update (even at 1000 fields = 8KB)") - min_speedup = minimum(r.speedup_update for r in results) - println(" Minimum speedup: $(round(min_speedup, digits=1))x at $(results[end].nfields) fields") -else - println("⚠ Mutable wins for field update at $(update_wins[1].nfields) fields") -end -println() - -if isempty(iter_wins) - println("✓ Immutable ALWAYS faster for iteration (even at 1000 fields = 8KB)") - min_speedup = minimum(r.speedup_iter for r in results) - println(" Minimum speedup: $(round(min_speedup, digits=1))x at $(results[end].nfields) fields") -else - println("⚠ Mutable wins for iteration at $(iter_wins[1].nfields) fields") -end -println() - -# Check scaling behavior -println("Scaling Analysis:") -println() - -# Linear regression on copy time vs size -sizes = [r.nfields for r in results] -copy_times = [r.imm_copy * 1e9 for r in results] # Convert to ns - -# Simple linear fit: time = a + b*size -n = length(sizes) -mean_size = sum(sizes) / n -mean_time = sum(copy_times) / n -cov = sum((sizes[i] - mean_size) * (copy_times[i] - mean_time) for i in 1:n) / n -var_size = sum((s - mean_size)^2 for s in sizes) / n -slope = cov / var_size -intercept = mean_time - slope * mean_size - -println("Copy time scaling:") -println(" Linear fit: time(ns) = $(round(intercept, digits=2)) + $(round(slope, digits=4)) * nfields") -println(" Per-field cost: $(round(slope, digits=4)) ns/field") -println(" Base overhead: $(round(intercept, digits=2)) ns") -println() - -# Check if copy time grows linearly -println("Is copy time linear? (checking R²)") -ss_tot = sum((t - mean_time)^2 for t in copy_times) -ss_res = sum((copy_times[i] - (intercept + slope * sizes[i]))^2 for i in 1:n) -r_squared = 1 - ss_res / ss_tot -println(" R² = $(round(r_squared, digits=4))") -if r_squared > 0.95 - println(" ✓ Copy time is linear in struct size (as expected)") -else - println(" ⚠ Copy time not perfectly linear (compiler optimizations?)") -end -println() - -# Key insight -println("KEY INSIGHT:") -println("-"^80) -println() -println("Even at 1000 fields (8KB struct), immutable is STILL faster because:") -println(" 1. Dict lookup cost (~40-50ns) >> copy cost per field (~$(round(slope, digits=4))ns)") -println(" 2. Type stability enables compiler optimizations (inlining, SIMD)") -println(" 3. Stack allocation has better cache locality than heap pointers") -println() -println("Theoretical crossover point (if it exists):") -crossover_fields = (40.0 - intercept) / slope # When copy cost = Dict lookup -println(" Would occur at ~$(round(Int, crossover_fields)) fields ($(round(Int, crossover_fields*8/1024))KB)") -if crossover_fields > 1000 - println(" But this is beyond any realistic FEM element!") -end -println() - -println("="^80) -println("CONCLUSION") -println("="^80) -println() -println("Your intuition about O(n) scaling is CORRECT, BUT:") -println() -println(" • Dict lookup base cost is SO high (~40-50ns)") -println(" • Copy cost per field is SO low (~$(round(slope, digits=4))ns)") -println(" • Compiler optimizations are SO good (inlining, SIMD, escape analysis)") -println() -println("That immutable wins even for unrealistically large structs (8KB+)!") -println() -println("For typical FEM elements:") -println(" • Material properties: 3-10 fields (24-80 bytes)") -println(" • State variables: 10-50 fields (80-400 bytes)") -println(" • Even with 100 fields (800 bytes), immutable is >10x faster") -println() -println("Type stability > Everything else.") -println() - -# ============================================================================ -# SAVE DATA TO DISK -# ============================================================================ - -println("="^80) -println("SAVING DATA") -println("="^80) -println() - -# Create results directory -results_dir = joinpath(@__DIR__, "results") -mkpath(results_dir) - -# Prepare data for JSON -data_to_save = Dict( - "system_info" => system_info, - "timestamp" => string(now()), - "struct_sizes" => STRUCT_SIZES, - "results" => [ - Dict( - "nfields" => r.nfields, - "bytes" => r.bytes, - "mutable_access_ns" => r.mut_access * 1e9, - "immutable_access_ns" => r.imm_access * 1e9, - "speedup_access" => r.speedup_access, - "mutable_update_ns" => r.mut_update * 1e9, - "immutable_update_ns" => r.imm_update * 1e9, - "speedup_update" => r.speedup_update, - "immutable_copy_ns" => r.imm_copy * 1e9, - "mutable_iter_ns" => r.mut_iter * 1e9, - "immutable_iter_ns" => r.imm_iter * 1e9, - "speedup_iter" => r.speedup_iter - ) - for r in results - ] -) - -# Save as JSON -json_file = joinpath(results_dir, "struct_size_scaling.json") -open(json_file, "w") do f - JSON.print(f, data_to_save, 2) -end -println("✓ Data saved to: $json_file") - -# Save as CSV for easy plotting in other tools -csv_file = joinpath(results_dir, "struct_size_scaling.csv") -open(csv_file, "w") do f - println(f, "nfields,bytes,mut_access_ns,imm_access_ns,speedup_access,mut_update_ns,imm_update_ns,speedup_update,imm_copy_ns,mut_iter_ns,imm_iter_ns,speedup_iter") - for r in results - println(f, "$(r.nfields),$(r.bytes),$(r.mut_access*1e9),$(r.imm_access*1e9),$(r.speedup_access),$(r.mut_update*1e9),$(r.imm_update*1e9),$(r.speedup_update),$(r.imm_copy*1e9),$(r.mut_iter*1e9),$(r.imm_iter*1e9),$(r.speedup_iter)") - end -end -println("✓ CSV saved to: $csv_file") -println() - -# ============================================================================ -# GENERATE PLOTS -# ============================================================================ - -println("="^80) -println("GENERATING PLOTS") -println("="^80) -println() - -# Extract data for plotting -bytes_vals = [r.bytes for r in results] -mut_access = [r.mut_access * 1e9 for r in results] -imm_access = [r.imm_access * 1e9 for r in results] -mut_update = [r.mut_update * 1e9 for r in results] -imm_update = [r.imm_update * 1e9 for r in results] -mut_iter = [r.mut_iter * 1e9 for r in results] -imm_iter = [r.imm_iter * 1e9 for r in results] -imm_copy = [r.imm_copy * 1e9 for r in results] - -# Typical FEM element sizes -fem_small = 40 # 5 fields (E, ν, ρ, etc.) -fem_medium = 160 # 20 fields (material + state) -fem_large = 400 # 50 fields (complex plasticity) - -# Plot 1: Field Access Performance -p1 = plot(bytes_vals, mut_access, - label="Mutable (Dict)", - xlabel="Struct Size (bytes)", - ylabel="Time (nanoseconds)", - title="Field Access Performance vs Struct Size", - linewidth=2, - marker=:circle, - legend=:topleft, - size=(800, 600)) -plot!(p1, bytes_vals, imm_access, - label="Immutable (NamedTuple)", - linewidth=2, - marker=:square) -vline!(p1, [fem_small, fem_medium, fem_large], - label="Typical FEM sizes", - linestyle=:dash, - linecolor=:gray, - linewidth=1) -annotate!(p1, fem_small, maximum(mut_access) * 0.9, text("Small\n(5 fields)", 8, :left)) -annotate!(p1, fem_medium, maximum(mut_access) * 0.8, text("Medium\n(20 fields)", 8, :left)) -annotate!(p1, fem_large, maximum(mut_access) * 0.7, text("Large\n(50 fields)", 8, :left)) - -plot_file1 = joinpath(results_dir, "field_access_scaling.png") -savefig(p1, plot_file1) -println("✓ Plot saved: $plot_file1") - -# Plot 2: Field Update Performance (showing crossover) -p2 = plot(bytes_vals, mut_update, - label="Mutable (Dict)", - xlabel="Struct Size (bytes)", - ylabel="Time (nanoseconds)", - title="Field Update Performance vs Struct Size (Crossover at ~800 bytes)", - linewidth=2, - marker=:circle, - legend=:topleft, - size=(800, 600)) -plot!(p2, bytes_vals, imm_update, - label="Immutable (NamedTuple)", - linewidth=2, - marker=:square) -vline!(p2, [fem_small, fem_medium, fem_large, 800], - label=["", "", "", "Crossover (~100 fields)"], - linestyle=[:dash, :dash, :dash, :dot], - linecolor=[:gray, :gray, :gray, :red], - linewidth=[1, 1, 1, 2]) -annotate!(p2, fem_small, maximum(imm_update) * 0.2, text("Small", 8, :left)) -annotate!(p2, fem_medium, maximum(imm_update) * 0.3, text("Medium", 8, :left)) -annotate!(p2, fem_large, maximum(imm_update) * 0.4, text("Large", 8, :left)) - -plot_file2 = joinpath(results_dir, "field_update_scaling.png") -savefig(p2, plot_file2) -println("✓ Plot saved: $plot_file2") - -# Plot 3: Iteration Performance -p3 = plot(bytes_vals, mut_iter, - label="Mutable (Dict)", - xlabel="Struct Size (bytes)", - ylabel="Time (nanoseconds)", - title="Iteration Performance vs Struct Size", - linewidth=2, - marker=:circle, - legend=:topleft, - size=(800, 600), - yscale=:log10) -plot!(p3, bytes_vals, imm_iter, - label="Immutable (NamedTuple)", - linewidth=2, - marker=:square) -vline!(p3, [fem_small, fem_medium, fem_large], - label="Typical FEM sizes", - linestyle=:dash, - linecolor=:gray, - linewidth=1) - -plot_file3 = joinpath(results_dir, "iteration_scaling.png") -savefig(p3, plot_file3) -println("✓ Plot saved: $plot_file3") - -# Plot 4: Copy Cost (linear scaling) -p4 = plot(bytes_vals, imm_copy, - label="Measured", - xlabel="Struct Size (bytes)", - ylabel="Copy Time (nanoseconds)", - title="Immutable Struct Copy Cost (Linear Scaling)", - linewidth=2, - marker=:circle, - legend=:topright, - size=(800, 600)) -# Add linear fit line -plot!(p4, bytes_vals, [intercept + slope * (b / 8) for b in bytes_vals], - label="Linear fit: $(round(intercept, digits=1)) + $(round(slope, digits=3)) × nfields", - linestyle=:dash, - linewidth=2) -vline!(p4, [fem_small, fem_medium, fem_large], - label="Typical FEM sizes", - linestyle=:dash, - linecolor=:gray, - linewidth=1) - -plot_file4 = joinpath(results_dir, "copy_cost_linear.png") -savefig(p4, plot_file4) -println("✓ Plot saved: $plot_file4") - -# Plot 5: Speedup ratios (showing where immutable wins) -p5 = plot(bytes_vals, [r.speedup_access for r in results], - label="Field Access", - xlabel="Struct Size (bytes)", - ylabel="Speedup (Immutable / Mutable)", - title="Performance Speedup: Immutable vs Mutable", - linewidth=2, - marker=:circle, - legend=:right, - size=(800, 600)) -plot!(p5, bytes_vals, [r.speedup_update for r in results], - label="Field Update", - linewidth=2, - marker=:square) -plot!(p5, bytes_vals, [r.speedup_iter for r in results], - label="Iteration", - linewidth=2, - marker=:diamond) -hline!(p5, [1.0], - label="Break-even", - linestyle=:dot, - linecolor=:black, - linewidth=2) -vline!(p5, [fem_small, fem_medium, fem_large], - label="", - linestyle=:dash, - linecolor=:gray, - linewidth=1) -annotate!(p5, fem_large, 0.5, text("Typical FEM range →", 8, :left)) - -plot_file5 = joinpath(results_dir, "speedup_ratios.png") -savefig(p5, plot_file5) -println("✓ Plot saved: $plot_file5") - -println() -println("All plots saved to: $results_dir") -println() - -# Save results to JSON -println("="^80) -println("SAVING DATA") -println("="^80) -println() - -timestamp = Dates.format(now(), "yyyymmdd_HHMMSS") -output_dir = "benchmarks/results" -mkpath(output_dir) - -# Prepare data for saving -benchmark_data = Dict( - "timestamp" => timestamp, - "julia_version" => string(VERSION), - "system" => Dict( - "cpu_model" => cpu_info[1].model, - "cpu_cores" => Sys.CPU_THREADS, - "cpu_speed_mhz" => cpu_info[1].speed, - "os" => string(Sys.KERNEL), - "machine" => string(Sys.MACHINE), - "word_size" => Sys.WORD_SIZE - ), - "results" => [ - Dict( - "nfields" => r.nfields, - "bytes" => r.bytes, - "mutable_access_ns" => r.mut_access * 1e9, - "immutable_access_ns" => r.imm_access * 1e9, - "speedup_access" => r.speedup_access, - "mutable_update_ns" => r.mut_update * 1e9, - "immutable_update_ns" => r.imm_update * 1e9, - "speedup_update" => r.speedup_update, - "mutable_iter_ns" => r.mut_iter * 1e9, - "immutable_iter_ns" => r.imm_iter * 1e9, - "speedup_iter" => r.speedup_iter, - "immutable_copy_ns" => r.imm_copy * 1e9 - ) for r in results - ], - "analysis" => Dict( - "copy_slope_ns_per_field" => slope, - "copy_intercept_ns" => intercept, - "r_squared" => r_squared - ) -) - -json_file = joinpath(output_dir, "struct_scaling_$(timestamp).json") -open(json_file, "w") do f - JSON.print(f, benchmark_data, 2) -end -println("✓ Data saved to: $json_file") -println() - -# Also save as CSV for easy plotting -csv_file = joinpath(output_dir, "struct_scaling_$(timestamp).csv") -open(csv_file, "w") do f - println(f, "nfields,bytes,mut_access_ns,imm_access_ns,speedup_access,mut_update_ns,imm_update_ns,speedup_update,mut_iter_ns,imm_iter_ns,speedup_iter,imm_copy_ns") - for r in results - println(f, "$(r.nfields),$(r.bytes),$(r.mut_access*1e9),$(r.imm_access*1e9),$(r.speedup_access),$(r.mut_update*1e9),$(r.imm_update*1e9),$(r.speedup_update),$(r.mut_iter*1e9),$(r.imm_iter*1e9),$(r.speedup_iter),$(r.imm_copy*1e9)") - end -end -println("✓ CSV saved to: $csv_file") -println() - -println("="^80) -println("GENERATING PLOTS") -println("="^80) -println() - -# Note: Using Plots from global environment -try - # Import from global environment - pushfirst!(LOAD_PATH, "@stdlib") - import Plots - - # Set backend - Plots.gr() - - # Extract data for plotting - bytes_sizes = [r.bytes for r in results] - - # Plot 1: Field Access Performance - p1 = Plots.plot(bytes_sizes, [r.mut_access * 1e9 for r in results], - label="Mutable (Dict)", linewidth=2, marker=:circle, - xlabel="Struct Size (bytes)", ylabel="Time (nanoseconds)", - title="Field Access Performance", - legend=:topleft, xscale=:log10, grid=true) - Plots.plot!(p1, bytes_sizes, [r.imm_access * 1e9 for r in results], - label="Immutable (NamedTuple)", linewidth=2, marker=:square) - - # Add typical FEM element size markers - Plots.vline!(p1, [40, 400], label="Typical FEM (5-50 fields)", - linestyle=:dash, linewidth=1, color=:gray) - - Plots.savefig(p1, joinpath(output_dir, "field_access_$(timestamp).png")) - println("✓ Saved: field_access_$(timestamp).png") - - # Plot 2: Field Update Performance - p2 = Plots.plot(bytes_sizes, [r.mut_update * 1e9 for r in results], - label="Mutable (Dict)", linewidth=2, marker=:circle, - xlabel="Struct Size (bytes)", ylabel="Time (nanoseconds)", - title="Field Update Performance", - legend=:topleft, xscale=:log10, grid=true) - Plots.plot!(p2, bytes_sizes, [r.imm_update * 1e9 for r in results], - label="Immutable (NamedTuple)", linewidth=2, marker=:square) - - Plots.vline!(p2, [40, 400], label="Typical FEM (5-50 fields)", - linestyle=:dash, linewidth=1, color=:gray) - - Plots.savefig(p2, joinpath(output_dir, "field_update_$(timestamp).png")) - println("✓ Saved: field_update_$(timestamp).png") - - # Plot 3: Iteration Performance - p3 = Plots.plot(bytes_sizes, [r.mut_iter * 1e9 for r in results], - label="Mutable (Dict)", linewidth=2, marker=:circle, - xlabel="Struct Size (bytes)", ylabel="Time (nanoseconds)", - title="Field Iteration Performance", - legend=:topleft, xscale=:log10, yscale=:log10, grid=true) - Plots.plot!(p3, bytes_sizes, [r.imm_iter * 1e9 for r in results], - label="Immutable (NamedTuple)", linewidth=2, marker=:square) - - Plots.vline!(p3, [40, 400], label="Typical FEM (5-50 fields)", - linestyle=:dash, linewidth=1, color=:gray) - - Plots.savefig(p3, joinpath(output_dir, "iteration_$(timestamp).png")) - println("✓ Saved: iteration_$(timestamp).png") - - # Plot 4: Speedup Factors - p4 = Plots.plot(bytes_sizes, [r.speedup_access for r in results], - label="Access Speedup", linewidth=2, marker=:circle, - xlabel="Struct Size (bytes)", ylabel="Speedup Factor (Immutable/Mutable)", - title="Performance Advantage of Immutable Elements", - legend=:right, xscale=:log10, grid=true) - Plots.plot!(p4, bytes_sizes, [r.speedup_update for r in results], - label="Update Speedup", linewidth=2, marker=:square) - Plots.plot!(p4, bytes_sizes, [r.speedup_iter for r in results], - label="Iteration Speedup", linewidth=2, marker=:diamond) - - Plots.hline!(p4, [1.0], label="Break-even", linestyle=:dash, color=:black, linewidth=1) - Plots.vline!(p4, [40, 400], label="Typical FEM", - linestyle=:dash, linewidth=1, color=:gray) - - Plots.savefig(p4, joinpath(output_dir, "speedup_factors_$(timestamp).png")) - println("✓ Saved: speedup_factors_$(timestamp).png") - - # Plot 5: Copy Cost Scaling - p5 = Plots.plot(bytes_sizes, [r.imm_copy * 1e9 for r in results], - label="Measured", linewidth=2, marker=:circle, - xlabel="Struct Size (bytes)", ylabel="Copy Time (nanoseconds)", - title="Immutable Struct Copy Cost", - legend=:topleft, xscale=:log10, grid=true) - - # Add linear fit - fitted = [intercept + slope * r.nfields for r in results] - Plots.plot!(p5, bytes_sizes, fitted, - label="Linear Fit ($(round(slope, digits=4)) ns/field)", - linewidth=2, linestyle=:dash) - - Plots.vline!(p5, [40, 400], label="Typical FEM", - linestyle=:dash, linewidth=1, color=:gray) - - Plots.savefig(p5, joinpath(output_dir, "copy_cost_$(timestamp).png")) - println("✓ Saved: copy_cost_$(timestamp).png") - - # Combined plot - layout = Plots.@layout [a b; c d] - p_combined = Plots.plot(p1, p2, p3, p4, layout=layout, size=(1200, 900)) - Plots.savefig(p_combined, joinpath(output_dir, "combined_$(timestamp).png")) - println("✓ Saved: combined_$(timestamp).png") - - println() - println("All plots saved successfully!") - -catch e - println("⚠ Could not generate plots (Plots.jl not available in global environment)") - println(" Error: $e") - println(" Install with: julia -e 'using Pkg; Pkg.add(\"Plots\")'") -end - -println() -println("="^80) diff --git a/benchmarks/test_gpu_benchmarks.sh b/benchmarks/test_gpu_benchmarks.sh deleted file mode 100755 index ffe9d84..0000000 --- a/benchmarks/test_gpu_benchmarks.sh +++ /dev/null @@ -1,159 +0,0 @@ -#!/bin/bash -# Quick test script for GPU benchmarks - -echo "======================================================================" -echo "GPU Benchmark Quick Test" -echo "======================================================================" -echo "" - -# Check Julia -echo "Checking Julia installation..." -if ! command -v julia &> /dev/null; then - echo "❌ Julia not found! Please install Julia 1.9+" - exit 1 -fi - -julia_version=$(julia --version) -echo "✅ Found: $julia_version" -echo "" - -# Check GPU -echo "Checking GPU availability..." -if command -v nvidia-smi &> /dev/null; then - echo "✅ NVIDIA GPU detected:" - nvidia-smi --query-gpu=name,memory.total --format=csv,noheader - echo "" -else - echo "⚠️ No NVIDIA GPU detected. Benchmarks will run CPU-only." - echo "" -fi - -# Check CUDA.jl -echo "Checking CUDA.jl..." -julia --project=. -e ' -using Pkg -try - using CUDA - if CUDA.functional() - println("✅ CUDA.jl functional: ", CUDA.name(CUDA.device())) - else - println("⚠️ CUDA.jl installed but GPU not functional") - end -catch - println("⚠️ CUDA.jl not installed. Run: Pkg.add(\"CUDA\")") -end -' 2>/dev/null -echo "" - -# Run quick state management test (small size) -echo "======================================================================" -echo "Test 1: State Management (10K elements)" -echo "======================================================================" -julia --project=. -e ' -n = 10_000 -println("Running state management benchmark with $n elements...") -include("benchmarks/gpu_state_management_benchmark.jl") - -# Override main() to run smaller test -Δε_p, Δα, elements_s1, geometry_s2, state_s2 = setup_benchmark(n) - -# CPU test -println("\n📊 CPU Test:") -state_s2_copy = deepcopy(state_s2) -t = @elapsed update_state_strategy2_cpu!(state_s2_copy, Δε_p, Δα) -println("Time: $(round(t * 1000, digits=2)) ms") -println("✅ CPU benchmark works!") - -# GPU test (if available) -if USE_GPU - println("\n📊 GPU Test:") - try - T = Float64 - state_gpu = AssemblyState{T}( - CUDA.zeros(T, n * 6), - CUDA.zeros(T, n), - n - ) - Δε_p_flat = zeros(T, n * 6) - Δα_gpu = CuArray(Δα) - for i in 1:n - offset = (i - 1) * 6 - ε = Δε_p[i] - Δε_p_flat[offset + 1] = ε[1, 1] - Δε_p_flat[offset + 2] = ε[2, 2] - Δε_p_flat[offset + 3] = ε[3, 3] - Δε_p_flat[offset + 4] = ε[1, 2] - Δε_p_flat[offset + 5] = ε[1, 3] - Δε_p_flat[offset + 6] = ε[2, 3] - end - Δε_p_flat_gpu = CuArray(Δε_p_flat) - - update_state_strategy2_gpu!(state_gpu, Δε_p_flat_gpu, Δα_gpu) - println("✅ GPU benchmark works!") - catch e - println("⚠️ GPU test failed: $e") - end -end -' -echo "" - -# Run quick matrix-free test (small size) -echo "======================================================================" -echo "Test 2: Matrix-Free Newton-Krylov (1K DOFs)" -echo "======================================================================" -julia --project=. -e ' -n = 1000 -println("Running matrix-free benchmark with $n DOFs...") -include("benchmarks/matrix_free_gpu_benchmark.jl") - -# Override to run small test -T = Float64 -K = Matrix(Tridiagonal(-ones(T, n-1), 2ones(T, n), -ones(T, n-1))) -f = ones(T, n) * 0.1 -β = T(1e-3) -prob = NonlinearProblem(K, f, β, n) - -println("\n📊 CPU Test:") -u = zeros(T, n) -r = zeros(T, n) -du = zeros(T, n) -temp = zeros(T, n) -Jv = zeros(T, n) - -t = @elapsed iters = newton_matrix_free!(u, prob, r, du, temp, Jv; - max_iter=10, verbose=false) -println("Time: $(round(t * 1000, digits=2)) ms") -println("Iterations: $iters") -println("✅ CPU benchmark works!") - -if USE_GPU - println("\n📊 GPU Test:") - try - K_gpu = CuArray(K) - f_gpu = CuArray(f) - u_gpu = CUDA.zeros(T, n) - - t_gpu = CUDA.@elapsed begin - iters_gpu = newton_matrix_free_gpu!(u_gpu, K_gpu, f_gpu, β; - max_iter=10, verbose=false) - CUDA.synchronize() - end - - println("Time: $(round(t_gpu * 1000, digits=2)) ms") - println("Iterations: $iters_gpu") - println("✅ GPU benchmark works!") - catch e - println("⚠️ GPU test failed: $e") - end -end -' -echo "" - -echo "======================================================================" -echo "Quick Test Complete!" -echo "======================================================================" -echo "" -echo "To run full benchmarks:" -echo " julia --project=. benchmarks/gpu_state_management_benchmark.jl" -echo " julia --project=. benchmarks/matrix_free_gpu_benchmark.jl" -echo "" diff --git a/benchmarks/tet10_derivatives_benchmark.jl b/benchmarks/tet10_derivatives_benchmark.jl deleted file mode 100644 index 80d7207..0000000 --- a/benchmarks/tet10_derivatives_benchmark.jl +++ /dev/null @@ -1,284 +0,0 @@ -# Benchmark: Tet10 Shape Function Derivatives - Manual vs AD -# -# Compares two approaches: -# 1. Manual: Hand-calculated derivatives (traditional FEM) -# 2. AD: Automatic differentiation using Tensors.jl gradient() -# -# Run with: julia --project=. benchmarks/tet10_derivatives_benchmark.jl - -using BenchmarkTools -using Tensors -using Printf - -println("="^70) -println("Tet10 Shape Function Derivatives: Manual vs AD Benchmark") -println("="^70) -println() - -# ============================================================================ -# METHOD 1: MANUAL (Hand-Calculated Derivatives) -# ============================================================================ - -""" -Tet10 shape functions (manual implementation). -Reference element: ξ ∈ [0,1], η ∈ [0,1], ζ ∈ [0,1], ξ+η+ζ ≤ 1 -""" -module ManualTet10 -using Tensors - -# Shape functions -@inline N1(ξ, η, ζ) = (1 - ξ - η - ζ) * (2 * (1 - ξ - η - ζ) - 1) -@inline N2(ξ, η, ζ) = ξ * (2 * ξ - 1) -@inline N3(ξ, η, ζ) = η * (2 * η - 1) -@inline N4(ξ, η, ζ) = ζ * (2 * ζ - 1) -@inline N5(ξ, η, ζ) = 4 * ξ * (1 - ξ - η - ζ) -@inline N6(ξ, η, ζ) = 4 * ξ * η -@inline N7(ξ, η, ζ) = 4 * η * (1 - ξ - η - ζ) -@inline N8(ξ, η, ζ) = 4 * ζ * (1 - ξ - η - ζ) -@inline N9(ξ, η, ζ) = 4 * ξ * ζ -@inline N10(ξ, η, ζ) = 4 * η * ζ - -# Derivatives (calculated by hand - error-prone!) -@inline dN1_dξ(ξ, η, ζ) = 4 * ξ + 4 * η + 4 * ζ - 3 -@inline dN1_dη(ξ, η, ζ) = 4 * ξ + 4 * η + 4 * ζ - 3 -@inline dN1_dζ(ξ, η, ζ) = 4 * ξ + 4 * η + 4 * ζ - 3 - -@inline dN2_dξ(ξ, η, ζ) = 4 * ξ - 1 -@inline dN2_dη(ξ, η, ζ) = 0.0 -@inline dN2_dζ(ξ, η, ζ) = 0.0 - -@inline dN3_dξ(ξ, η, ζ) = 0.0 -@inline dN3_dη(ξ, η, ζ) = 4 * η - 1 -@inline dN3_dζ(ξ, η, ζ) = 0.0 - -@inline dN4_dξ(ξ, η, ζ) = 0.0 -@inline dN4_dη(ξ, η, ζ) = 0.0 -@inline dN4_dζ(ξ, η, ζ) = 4 * ζ - 1 - -@inline dN5_dξ(ξ, η, ζ) = 4 * (1 - 2 * ξ - η - ζ) -@inline dN5_dη(ξ, η, ζ) = -4 * ξ -@inline dN5_dζ(ξ, η, ζ) = -4 * ξ - -@inline dN6_dξ(ξ, η, ζ) = 4 * η -@inline dN6_dη(ξ, η, ζ) = 4 * ξ -@inline dN6_dζ(ξ, η, ζ) = 0.0 - -@inline dN7_dξ(ξ, η, ζ) = -4 * η -@inline dN7_dη(ξ, η, ζ) = 4 * (1 - ξ - 2 * η - ζ) -@inline dN7_dζ(ξ, η, ζ) = -4 * η - -@inline dN8_dξ(ξ, η, ζ) = -4 * ζ -@inline dN8_dη(ξ, η, ζ) = -4 * ζ -@inline dN8_dζ(ξ, η, ζ) = 4 * (1 - ξ - η - 2 * ζ) - -@inline dN9_dξ(ξ, η, ζ) = 4 * ζ -@inline dN9_dη(ξ, η, ζ) = 0.0 -@inline dN9_dζ(ξ, η, ζ) = 4 * ξ - -@inline dN10_dξ(ξ, η, ζ) = 0.0 -@inline dN10_dη(ξ, η, ζ) = 4 * ζ -@inline dN10_dζ(ξ, η, ζ) = 4 * η - -# Evaluation function (returns tuple - zero allocation) -@inline function eval_basis_and_grad(xi::Vec{3}) - ξ, η, ζ = xi[1], xi[2], xi[3] - - N = (N1(ξ, η, ζ), N2(ξ, η, ζ), N3(ξ, η, ζ), N4(ξ, η, ζ), N5(ξ, η, ζ), - N6(ξ, η, ζ), N7(ξ, η, ζ), N8(ξ, η, ζ), N9(ξ, η, ζ), N10(ξ, η, ζ)) - - dN = (Vec(dN1_dξ(ξ, η, ζ), dN1_dη(ξ, η, ζ), dN1_dζ(ξ, η, ζ)), - Vec(dN2_dξ(ξ, η, ζ), dN2_dη(ξ, η, ζ), dN2_dζ(ξ, η, ζ)), - Vec(dN3_dξ(ξ, η, ζ), dN3_dη(ξ, η, ζ), dN3_dζ(ξ, η, ζ)), - Vec(dN4_dξ(ξ, η, ζ), dN4_dη(ξ, η, ζ), dN4_dζ(ξ, η, ζ)), - Vec(dN5_dξ(ξ, η, ζ), dN5_dη(ξ, η, ζ), dN5_dζ(ξ, η, ζ)), - Vec(dN6_dξ(ξ, η, ζ), dN6_dη(ξ, η, ζ), dN6_dζ(ξ, η, ζ)), - Vec(dN7_dξ(ξ, η, ζ), dN7_dη(ξ, η, ζ), dN7_dζ(ξ, η, ζ)), - Vec(dN8_dξ(ξ, η, ζ), dN8_dη(ξ, η, ζ), dN8_dζ(ξ, η, ζ)), - Vec(dN9_dξ(ξ, η, ζ), dN9_dη(ξ, η, ζ), dN9_dζ(ξ, η, ζ)), - Vec(dN10_dξ(ξ, η, ζ), dN10_dη(ξ, η, ζ), dN10_dζ(ξ, η, ζ))) - - return N, dN -end -end - -# ============================================================================ -# ============================================================================ -# METHOD 2: AD (Tensors.jl gradient) -# ============================================================================ - -module ADTet10 -using Tensors - -# Just shape functions (no manual derivatives!) -@inline N1(xi) = (1 - xi[1] - xi[2] - xi[3]) * (2 * (1 - xi[1] - xi[2] - xi[3]) - 1) -@inline N2(xi) = xi[1] * (2 * xi[1] - 1) -@inline N3(xi) = xi[2] * (2 * xi[2] - 1) -@inline N4(xi) = xi[3] * (2 * xi[3] - 1) -@inline N5(xi) = 4 * xi[1] * (1 - xi[1] - xi[2] - xi[3]) -@inline N6(xi) = 4 * xi[1] * xi[2] -@inline N7(xi) = 4 * xi[2] * (1 - xi[1] - xi[2] - xi[3]) -@inline N8(xi) = 4 * xi[3] * (1 - xi[1] - xi[2] - xi[3]) -@inline N9(xi) = 4 * xi[1] * xi[3] -@inline N10(xi) = 4 * xi[2] * xi[3] - -const shape_fns = (N1, N2, N3, N4, N5, N6, N7, N8, N9, N10) - -@inline function eval_basis_and_grad(xi::Vec{3}) - # Evaluate basis functions - N = ntuple(i -> shape_fns[i](xi), 10) - - # Compute gradients with Tensors.jl gradient() - dN = ntuple(i -> gradient(shape_fns[i], xi), 10) - - return N, dN -end -end - -# ============================================================================ -# BENCHMARKING -# ============================================================================ - -println("Setting up benchmark...") -println() - -# Test point (typical integration point) -const ξ_test = Vec(0.25, 0.25, 0.25) - -# Verification: Both methods should give same results -println("Verifying correctness...") -N_manual, dN_manual = ManualTet10.eval_basis_and_grad(ξ_test) -N_ad, dN_ad = ADTet10.eval_basis_and_grad(ξ_test) - -println(" Manual basis: ", N_manual) -println(" AD basis: ", N_ad) -println() - -# Check agreement -rtol = 1e-10 -if !all(isapprox.(N_manual, N_ad, rtol=rtol)) - @warn "Manual and AD basis functions disagree!" -end - -# Check derivatives -for i in 1:10 - if !isapprox(dN_manual[i], dN_ad[i], rtol=rtol) - @warn "Manual and AD derivative $i disagree!" dN_manual[i] dN_ad[i] - end -end - -println("✓ Both methods agree (within tolerance)") -println() - -# ============================================================================ -# RUN BENCHMARKS -# ============================================================================ - -println("Running benchmarks (this may take a minute)...") -println() - -# Warm-up -for _ in 1:1000 - ManualTet10.eval_basis_and_grad(ξ_test) - ADTet10.eval_basis_and_grad(ξ_test) -end - -# Benchmark each method -b_manual = @benchmark ManualTet10.eval_basis_and_grad($ξ_test) -b_ad = @benchmark ADTet10.eval_basis_and_grad($ξ_test) - -# ============================================================================ -# RESULTS -# ============================================================================ - -# ============================================================================ -# RESULTS -# ============================================================================ - -println("="^70) -println("RESULTS") -println("="^70) -println() - -# Extract median times -t_manual = median(b_manual.times) -t_ad = median(b_ad.times) - -# Extract allocations -alloc_manual = b_manual.allocs -alloc_ad = b_ad.allocs - -# Calculate relative speed -rel_ad = t_ad / t_manual - -println("Method | Time (ns) | Allocations | Relative Speed") -println("----------------|-----------|-------------|----------------") -@printf "Manual | %9.1f | %11d | %.2f× (baseline)\n" t_manual alloc_manual 1.0 -@printf "AD (Tensors.jl) | %9.1f | %11d | %.2f×\n" t_ad alloc_ad rel_ad -println() - -# Detailed stats -println("Detailed Statistics:") -println() -println("Manual (Hand-Calculated):") -display(b_manual) -println() -println() -println("AD (Tensors.jl gradient):") -display(b_ad) -println() -println() - -# ============================================================================ -# ANALYSIS -# ============================================================================ - -println("="^70) -println("ANALYSIS") -println("="^70) -println() - -if rel_ad < 2.0 - println("🎉 RECOMMENDATION: Use AD everywhere!") - println() - println("Tensors.jl AD is within 2× of manual, providing:") - println(" ✓ Zero maintenance burden") - println(" ✓ No manual derivative errors") - println(" ✓ Easy to add new elements") - println(" ✓ Supports any basis type") - println() - println("Small performance cost is acceptable for these benefits.") -elseif rel_ad < 5.0 - println("⚠️ RECOMMENDATION: Hybrid approach") - println() - println("AD is 2-5× slower than manual. Consider:") - println(" • Common elements (Tet10, Hex8, Quad4): Manual") - println(" • Rare elements: AD-generated") - println(" • Research/prototype elements: Always AD") - println() - println("This balances performance and maintainability.") -else - println("❌ RECOMMENDATION: Manual derivatives (with symbolic generation)") - println() - println("AD is >5× slower than manual. For performance-critical code:") - println(" • Generate derivatives with SymPy/Symbolics.jl") - println(" • Unit test against AD to verify correctness") - println(" • Accept the maintenance burden") - println() - println("Consider AD only for prototyping.") -end - -println() -println("Memory analysis:") -if alloc_manual == 0 && alloc_ad == 0 - println(" ✓ Both methods achieve zero allocations (excellent!)") -elseif alloc_manual == 0 && alloc_ad > 0 - println(" ⚠️ AD allocates (", alloc_ad, " allocs)") - println(" This will hurt performance in tight loops.") -else - println(" ⚠️ Unexpected allocation pattern - investigate!") -end - -println() -println("="^70) -println("Benchmark complete! Results saved to console.") -println("="^70)