Created
September 12, 2025 11:48
-
-
Save aj-fleming/7e5ae49126339e79b2154b1a31b9423d to your computer and use it in GitHub Desktop.
Reproduction of the behavior described in https://discourse.julialang.org/t/identical-method-redefinition-suspiciously-optimizes-runtime-and-allocations/132156
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| module PlanePolygons_MWE | |
| using StaticArrays | |
| using LinearAlgebra | |
| export SClosedPolygon, edge_starts | |
| # Absolute tolerance threshold. | |
| const _HOW_CLOSE_IS_TOO_CLOSE = 1.0e-12 | |
| # Represents a Line in ℝ^2 | |
| struct Line{T} | |
| p::SVector{2,T} | |
| dir::SVector{2,T} | |
| end | |
| # get a point on a line | |
| point_on(ℓ) = ℓ.p | |
| # get the direction of a line | |
| direction_of(ℓ) = ℓ.dir | |
| # get a vector that points into the right half-plane defined by ℓ | |
| # from any point on ℓ | |
| function right_normal(ℓ) | |
| dir = direction_of(ℓ) | |
| return SVector(dir[2], -dir[1]) | |
| end | |
| # Helper method for testing if a point is to the right of ℓ | |
| function _right_half_plane(ℓ, pt) | |
| v1 = right_normal(ℓ) ⋅ pt | |
| v2 = right_normal(ℓ) ⋅ point_on(ℓ) | |
| return (v1, v2) | |
| end | |
| # Test if a point is to the right of ℓ but NOT on ℓ | |
| function point_in_right_half_plane_strict(ℓ, pt) | |
| (v1, v2) = _right_half_plane(ℓ, pt) | |
| return v1 > v2 && !isapprox(v1 - v2, 0; atol=_HOW_CLOSE_IS_TOO_CLOSE) | |
| end | |
| # Represents a closed, clockwise-oriented polygon as an SVector{NV} of SVectors{2, T} | |
| struct SClosedPolygon{NV,T} | |
| pts::SVector{NV,SVector{2,T}} | |
| end | |
| function SClosedPolygon(pts::Vararg{SVector{2,T}}) where {T} | |
| return SClosedPolygon(SVector(pts...)) | |
| end | |
| # get an iterator that traverses the first point on each edge in order | |
| function edge_starts(p::SClosedPolygon) | |
| return p.pts | |
| end | |
| # get an iterator that traverses the second point on each edge in order | |
| function edge_ends(p::SClosedPolygon{NV,T}) where {NV,T} | |
| return p.pts[SVector(ntuple(i -> i + 1, NV - 1)..., 1)] | |
| end | |
| # get an iterator of lines coincendent with each edge of `poly` in order | |
| function edge_lines(poly::SClosedPolygon{NV}) where {NV} | |
| return mapreduce(vcat, edge_starts(poly), edge_ends(poly)) do a, b | |
| return SVector(Line(a, b - a)) | |
| end | |
| end | |
| # Test if a point is strictly inside a polygon | |
| # Base.Fix2 might be the issue here? | |
| function point_inside_strict(poly, pt) | |
| return all(Base.Fix2(point_in_right_half_plane_strict, pt), edge_lines(poly)) | |
| end | |
| end | |
| # Stripped-down version of Euler2D to reproduce the compiler bug(?). | |
| module Euler2D_MWE | |
| using StaticArrays | |
| using ..PlanePolygons_MWE | |
| export TangentQuadCell_MWE, cell_boundary_polygon | |
| # Analagous to the original code in Euler2D. | |
| abstract type FVMCell{T} end | |
| # Analagous to the original code in Euler2D. | |
| struct TangentQuadCell_MWE{T,NSEEDS,NTANGENTS} <: FVMCell{T} | |
| id::Int | |
| u::SVector{4,T} | |
| udot::SMatrix{4,NSEEDS,T,NTANGENTS} | |
| center::SVector{2,T} | |
| extent::SVector{2,T} | |
| end | |
| # Given a cell, which has a center and extent, produce the clockwise-oriented polygon that is its boundary. | |
| function cell_boundary_polygon(cell::TangentQuadCell_MWE) | |
| dx, dy = cell.extent / 2 | |
| return SClosedPolygon( | |
| cell.center + SVector(dx, -dy), | |
| cell.center + SVector(-dx, -dy), | |
| cell.center + SVector(-dx, dy), | |
| cell.center + SVector(dx, dy) | |
| ) | |
| end | |
| # Test if a cell's boundary polygon is completely contained by some other polygon. | |
| function is_cell_contained_by(cell1, poly) | |
| cell_poly = cell_boundary_polygon(cell1) | |
| return all(edge_starts(cell_poly)) do pt | |
| return PlanePolygons_MWE.point_inside_strict(poly, pt) | |
| end | |
| end | |
| # behavior is reproducible with or without second method definition. | |
| # function is_cell_contained_by(cell1, cell2::FVMCell) | |
| # return is_cell_contained_by(cell1, cell_boundary_polygon(cell2)) | |
| # end | |
| end | |
| # METHOD TO REPRODUCE | |
| # (works at the REPL) | |
| # using StaticArrays, BenchmarkTools | |
| # using .PlanePolygons_MWE, .Euler2D_MWE | |
| # test_poly1 = SClosedPolygon(@SVector([SVector(-1.3, 1.5), SVector(-1.4, 1.5), SVector(-1.4, 1.75), SVector(-1.3, 1.75)])) | |
| # test_cell = TangentQuadCell_MWE(1, SVector(1.0, 4.0, 0.0, 9.87), SMatrix{4,3}(1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0), SVector(-1.49687, 0.03125), SVector(0.00625, 0.00625)) | |
| # should yield allocations (6 allocs / 544 bytes on my machine) | |
| # @benchmark Euler2D_MWE.is_cell_contained_by($test_cell, $test_poly1) | |
| # resident eval | |
| # @eval Euler2D_MWE function is_cell_contained_by(cell1, poly) | |
| # cell_poly = cell_boundary_polygon(cell1) | |
| # return all(edge_starts(cell_poly)) do pt | |
| # return PlanePolygons_MWE.point_inside_strict(poly, pt) | |
| # end | |
| # end | |
| # benchmark again, should yield no allocs and ~4x speedup | |
| # @benchmark Euler2D_MWE.is_cell_contained_by($test_cell, $test_poly1) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment