Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

data_limits dispatch for Mesh to omit slow iteration over buffer #2874

Closed
wants to merge 10 commits into from

Conversation

koehlerson
Copy link
Contributor

@koehlerson koehlerson commented Apr 20, 2023

Description

Fixes #2864

This PR changes the performance of the following:

julia> begin
           fig = GLMakie.Figure()
           ax = GLMakie.LScene(fig[1, 1])
           @time meshplot =GLMakie.mesh!(ax, plotter.mesh)
           fig
       end
  0.637678 seconds (15.12 k allocations: 566.358 MiB)

to

julia> begin
           fig = GLMakie.Figure()
           ax = GLMakie.LScene(fig[1, 1])
           @time meshplot =GLMakie.mesh!(ax, plotter.mesh)
           fig
       end
0.186790 seconds (15.13 k allocations: 1.111 MiB)

by using extrema in data_limits instead of computing the bbox by iterating over mesh.position which is still expensive. I'm not sure tho what extrema uses under the hood the avoid the performance issues. I looked up the source code at

julia> @which extrema(plotter.physical_coords_mesh)
extrema(a::AbstractArray; dims, kw...) in Base at reducedim.jl:994

but I don't understand anything that's going on there :D

I included a function bbox_mesh that iterates over the coordinates and checks the bbox manually. There is some weird issue that if I move the function's body inside data_limits the for loop never stops. Not sure what's going on there

The PR is also beneficial for GeometryBasics.Meshes without ShaderAbstractions.Buffer. From this:

julia> not_buffered_mesh = GeometryBasics.Mesh(plotter.physical_coords,plotter.vis_triangles);

julia> begin
           fig = GLMakie.Figure()
           ax = GLMakie.LScene(fig[1, 1])
           @time meshplot =GLMakie.mesh!(ax, fake_mesh)
           fig
       end
  0.431182 seconds (15.12 k allocations: 1.111 MiB)

to

julia> begin
           fig = GLMakie.Figure()
           ax = GLMakie.LScene(fig[1, 1])
           @time meshplot =GLMakie.mesh!(ax, fake_mesh)
           fig
       end
  0.209928 seconds (15.12 k allocations: 1.111 MiB)

Type of change

Delete options that do not apply:

  • Bug fix (non-breaking change which fixes an issue)

Checklist

  • Added an entry in NEWS.md (for new features and breaking changes)
  • Added or changed relevant sections in the documentation
  • Added unit tests for new algorithms, conversion methods, etc.
  • Added reference image tests for new plotting functions, recipes, visual options, etc.

@asinghvi17
Copy link
Member

some errors in tests:

running  59: space 2D autolimits
space 2D autolimits: Error During Test at /home/runner/work/Makie.jl/Makie.jl/ReferenceTests/src/database.jl:45
  Test threw exception
  Expression: save_result(joinpath(RECORDING_DIR[], "space 2D autolimits"), result)
  MethodError: Cannot `convert` an object of type Tuple{Float32, Float32} to an object of type Float32
  Closest candidates are:
    convert(::Type{T}, ::Base.TwicePrecision) where T<:Number at twiceprecision.jl:273
    convert(::Type{T}, ::AbstractChar) where T<:Number at char.jl:185
    convert(::Type{T}, ::CartesianIndex{1}) where T<:Number at multidimensional.jl:130
    ...
  Stacktrace:
    [1] (::GeometryBasics.var"#23#34"{Float32, Tuple{Float32, Float32}})(i::Int64)
      @ GeometryBasics ~/.julia/packages/GeometryBasics/kt2Em/src/fixed_arrays.jl:45
    [2] ntuple
      @ ./ntuple.jl:50 [inlined]
    [3] Vec{3, Float32}(x::Tuple{Float32, Float32})
      @ GeometryBasics ~/.julia/packages/GeometryBasics/kt2Em/src/fixed_arrays.jl:45
    [4] convert
      @ ~/.julia/packages/StaticArrays/4uslg/src/convert.jl:176 [inlined]
    [5] HyperRectangle{3, Float32}(origin::StaticArraysCore.SizedVector{2, Float32, Vector{Float32}}, widths::StaticArraysCore.SizedVector{2, Float32, Vector{Float32}})
      @ GeometryBasics ~/.julia/packages/GeometryBasics/kt2Em/src/primitives/rectangles.jl:10
    [6] data_limits(plot::MakieCore.Mesh{Tuple{GeometryBasics.Mesh{2, Float32, TriangleP{2, Float32, PointMeta{2, Float32, Point{2, Float32}, (:uv, :normals), Tuple{Vec{2, Float32}, Vec{3, Float32}}}}, FaceView{TriangleP{2, Float32, PointMeta{2, Float32, Point{2, Float32}, (:uv, :normals), Tuple{Vec{2, Float32}, Vec{3, Float32}}}}, PointMeta{2, Float32, Point{2, Float32}, (:uv, :normals), Tuple{Vec{2, Float32}, Vec{3, Float32}}}, NgonFace{3, OffsetInteger{-1, UInt32}}, StructArrays.StructVector{PointMeta{2, Float32, Point{2, Float32}, (:uv, :normals), Tuple{Vec{2, Float32}, Vec{3, Float32}}}, NamedTuple{(:position, :uv, :normals), Tuple{Vector{Point{2, Float32}}, Vector{Vec{2, Float32}}, Vector{Vec{3, Float32}}}}, Int64}, Vector{NgonFace{3, OffsetInteger{-1, UInt32}}}}}}})
      @ Makie ~/work/Makie.jl/Makie.jl/src/layouting/data_limits.jl:215
    [7] (::Makie.var"#775#777"{Makie.var"#exclude#1397"{Int64}, Base.RefValue{HyperRectangle{3, Float32}}})(plot::MakieCore.Mesh{Tuple{GeometryBasics.Mesh{2, Float32, TriangleP{2, Float32, PointMeta{2, Float32, Point{2, Float32}, (:uv, :normals), Tuple{Vec{2, Float32}, Vec{3, Float32}}}}, FaceView{TriangleP{2, Float32, PointMeta{2, Float32, Point{2, Float32}, (:uv, :normals), Tuple{Vec{2, Float32}, Vec{3, Float32}}}}, PointMeta{2, Float32, Point{2, Float32}, (:uv, :normals), Tuple{Vec{2, Float32}, Vec{3, Float32}}}, NgonFace{3, OffsetInteger{-1, UInt32}}, StructArrays.StructVector{PointMeta{2, Float32, Point{2, Float32}, (:uv, :normals), Tuple{Vec{2, Float32}, Vec{3, Float32}}}, NamedTuple{(:position, :uv, :normals), Tuple{Vector{Point{2, Float32}}, Vector{Vec{2, Float32}}, Vector{Vec{3, Float32}}}}, Int64}, Vector{NgonFace{3, OffsetInteger{-1, UInt32}}}}}}})
      @ Makie ~/work/Makie.jl/Makie.jl/src/layouting/data_limits.jl:198
    [8] foreach(f::Makie.var"#775#777"{Makie.var"#exclude#1397"{Int64}, Base.RefValue{HyperRectangle{3, Float32}}}, itr::Vector{AbstractPlot})
      @ Base ./abstractarray.jl:2774
    [9] foreach_plot
      @ ~/work/Makie.jl/Makie.jl/src/layouting/data_limits.jl:109 [inlined]
   [10] foreach_plot
      @ ~/work/Makie.jl/Makie.jl/src/layouting/data_limits.jl:103 [inlined]
   [11] data_limits
      @ ~/work/Makie.jl/Makie.jl/src/layouting/data_limits.jl:196 [inlined]
   [12] getlimits(la::Axis, dim::Int64)
      @ Makie ~/work/Makie.jl/Makie.jl/src/makielayout/blocks/axis.jl:881
   [13] autolimits(ax::Axis, dim::Int64)
      @ Makie ~/work/Makie.jl/Makie.jl/src/makielayout/blocks/axis.jl:951
   [14] xautolimits
      @ ~/work/Makie.jl/Makie.jl/src/makielayout/blocks/axis.jl:982 [inlined]
   [15] reset_limits!(ax::Axis; xauto::Bool, yauto::Bool, zauto::Bool)
      @ Makie ~/work/Makie.jl/Makie.jl/src/makielayout/blocks/axis.jl:582
   [16] reset_limits!
      @ ~/work/Makie.jl/Makie.jl/src/makielayout/blocks/axis.jl:[569](https://github.com/MakieOrg/Makie.jl/actions/runs/4752774705/jobs/8443537615?pr=2874#step:6:570) [inlined]
   [17] update_state_before_display!(ax::Axis)
      @ Makie ~/work/Makie.jl/Makie.jl/src/makielayout/blocks/axis.jl:1376
   [18] update_state_before_display!(f::Figure)
      @ Makie ~/work/Makie.jl/Makie.jl/src/figureplotting.jl:178
updating multiple meshes: Error During Test at /home/runner/work/Makie.jl/Makie.jl/ReferenceTests/src/database.jl:32
  Got exception outside of a @test
  type Array has no field position
  Stacktrace:
    [1] getproperty(x::Vector{GeometryBasics.Mesh{3, Float32, TriangleP{3, Float32, PointMeta{3, Float32, Point{3, Float32}, (:normals,), Tuple{Vec{3, Float32}}}}, FaceView{TriangleP{3, Float32, PointMeta{3, Float32, Point{3, Float32}, (:normals,), Tuple{Vec{3, Float32}}}}, PointMeta{3, Float32, Point{3, Float32}, (:normals,), Tuple{Vec{3, Float32}}}, NgonFace{3, OffsetInteger{-1, UInt32}}, StructArrays.StructVector{PointMeta{3, Float32, Point{3, Float32}, (:normals,), Tuple{Vec{3, Float32}}}, NamedTuple{(:position, :normals), Tuple{Vector{Point{3, Float32}}, Vector{Vec{3, Float32}}}}, Int64}, Vector{NgonFace{3, OffsetInteger{-1, UInt32}}}}}}, f::Symbol)
      @ Base ./Base.jl:38
    [2] data_limits(plot::MakieCore.Mesh{Tuple{Vector{GeometryBasics.Mesh{3, Float32, TriangleP{3, Float32, PointMeta{3, Float32, Point{3, Float32}, (:normals,), Tuple{Vec{3, Float32}}}}, FaceView{TriangleP{3, Float32, PointMeta{3, Float32, Point{3, Float32}, (:normals,), Tuple{Vec{3, Float32}}}}, PointMeta{3, Float32, Point{3, Float32}, (:normals,), Tuple{Vec{3, Float32}}}, NgonFace{3, OffsetInteger{-1, UInt32}}, StructArrays.StructVector{PointMeta{3, Float32, Point{3, Float32}, (:normals,), Tuple{Vec{3, Float32}}}, NamedTuple{(:position, :normals), Tuple{Vector{Point{3, Float32}}, Vector{Vec{3, Float32}}}}, Int64}, Vector{NgonFace{3, OffsetInteger{-1, UInt32}}}}}}}})
      @ Makie ~/work/Makie.jl/Makie.jl/src/layouting/data_limits.jl:213
    [3] (::Makie.var"#775#777"{Makie.var"#774#776", Base.RefValue{HyperRectangle{3, Float32}}})(plot::MakieCore.Mesh{Tuple{Vector{GeometryBasics.Mesh{3, Float32, TriangleP{3, Float32, PointMeta{3, Float32, Point{3, Float32}, (:normals,), Tuple{Vec{3, Float32}}}}, FaceView{TriangleP{3, Float32, PointMeta{3, Float32, Point{3, Float32}, (:normals,), Tuple{Vec{3, Float32}}}}, PointMeta{3, Float32, Point{3, Float32}, (:normals,), Tuple{Vec{3, Float32}}}, NgonFace{3, OffsetInteger{-1, UInt32}}, StructArrays.StructVector{PointMeta{3, Float32, Point{3, Float32}, (:normals,), Tuple{Vec{3, Float32}}}, NamedTuple{(:position, :normals), Tuple{Vector{Point{3, Float32}}, Vector{Vec{3, Float32}}}}, Int64}, Vector{NgonFace{3, OffsetInteger{-1, UInt32}}}}}}}})
      @ Makie ~/work/Makie.jl/Makie.jl/src/layouting/data_limits.jl:198
    [4] foreach(f::Makie.var"#775#777"{Makie.var"#774#776", Base.RefValue{HyperRectangle{3, Float32}}}, itr::Vector{AbstractPlot})
      @ Base ./abstractarray.jl:2774
    [5] foreach_plot
      @ ~/work/Makie.jl/Makie.jl/src/layouting/data_limits.jl:109 [inlined]
    [6] foreach_plot
      @ ~/work/Makie.jl/Makie.jl/src/layouting/data_limits.jl:103 [inlined]
    [7] data_limits
      @ ~/work/Makie.jl/Makie.jl/src/layouting/data_limits.jl:196 [inlined]
    [8] data_limits
      @ ~/work/Makie.jl/Makie.jl/src/layouting/data_limits.jl:195 [inlined]
    [9] is2d
      @ ~/work/Makie.jl/Makie.jl/src/scenes.jl:590 [inlined]
   [10] plot(P::Type{MakieCore.Mesh}, args::Vector{GeometryBasics.Mesh{3, Float32, TriangleP{3, Float32, PointMeta{3, Float32, Point{3, Float32}, (:normals,), Tuple{Vec{3, Float32}}}}, FaceView{TriangleP{3, Float32, PointMeta{3, Float32, Point{3, Float32}, (:normals,), Tuple{Vec{3, Float32}}}}, PointMeta{3, Float32, Point{3, Float32}, (:normals,), Tuple{Vec{3, Float32}}}, NgonFace{3, OffsetInteger{-1, UInt32}}, StructArrays.StructVector{PointMeta{3, Float32, Point{3, Float32}, (:normals,), Tuple{Vec{3, Float32}}}, NamedTuple{(:position, :normals), Tuple{Vector{Point{3, Float32}}, Vector{Vec{3, Float32}}}}, Int64}, Vector{NgonFace{3, OffsetInteger{-1, UInt32}}}}}}; axis::NamedTuple{(), Tuple{}}, figure::NamedTuple{(), Tuple{}}, kw_attributes::Base.Pairs{Symbol, Vector{RGB{Float32}}, Tuple{Symbol}, NamedTuple{(:color,), Tuple{Vector{RGB{Float32}}}}})
      @ Makie ~/work/Makie.jl/Makie.jl/src/figureplotting.jl:49
   [11] #mesh#39
      @ ~/work/Makie.jl/Makie.jl/MakieCore/src/recipes.jl:34 [inlined]

@koehlerson
Copy link
Contributor Author

Remaining CI fails seem unrelated, locally all tests pass for me

@ffreyer
Copy link
Collaborator

ffreyer commented Apr 21, 2023

The test case that fails has plot.mesh[] of type Vector{GeometryBasics.Mesh} not having a positions field which seems like a reasonable failure. There should probably be another method for that.

@koehlerson
Copy link
Contributor Author

Should I restrict to ::Mesh{<:Tuple{<:GeometryBasics.Mesh} again and in case of ::Mesh{<:Tuple{Vector{<:GeometryBasics.Mesh}} merge the bboxes?

@ffreyer
Copy link
Collaborator

ffreyer commented Apr 21, 2023

Sounds reasonable to me

@SimonDanisch
Copy link
Member

I still think we should just fix the iteration performance in Buffer, instead of special casing in Makie.

@koehlerson
Copy link
Contributor Author

would it be possible to dispatch data_limits on the args instead of the plottype ?

@termi-official
Copy link
Contributor

termi-official commented Apr 21, 2023

After peeking into this further, I do not think it is an iteration over the buffer.

function f(buf::T) where T
           sum = zero(eltype(T))
           for val  buf
                 sum += val^2
              end
           return sum
  end;

buf = GLMakie.Buffer(ones(100));

@btime f($buf); #  92.409 ns (0 allocations: 0 bytes)

I think most of the allocations come from

point_iterator(mesh::GeometryBasics.Mesh) = decompose(Point, mesh)

Benchmark:

brain = load(assetpath("brain.stl"))
brain_1 = GeometryBasics.Mesh(metafree(coordinates(brain)), faces(brain))
@btime mesh(
           $brain_1
       ) # 9.735 ms (36781 allocations: 2.28 MiB)
@btime decompose(Point, $brain_1) # 3.120 ns (0 allocations: 0 bytes)
brain_2 = GeometryBasics.Mesh(Buffer(metafree(coordinates(brain))), Buffer(faces(brain)))
@btime mesh(
           $brain_2
       ) # 10.634 ms (36789 allocations: 6.30 MiB)
@btime decompose(Point, $brain_2) # 131.912 μs (2 allocations: 1.34 MiB) | is called 3 times => 4.02MB

@koehlerson
Copy link
Contributor Author

koehlerson commented Apr 21, 2023

After peeking into this further, I do not think it is an iteration over the buffer.

I guess with iterator Simon means the point_iterator implementation

@koehlerson
Copy link
Contributor Author

would it be possible to dispatch data_limits on the args instead of the plottype ?

Because it would be very nice to squeeze out every bit of possible performance for this. At least I'd appreciate it very much :D

@SimonDanisch
Copy link
Member

Is this actually still needed after the data_limit changes I introduced?

@koehlerson
Copy link
Contributor Author

Which changes exactly? Just that I know what version to try

@SimonDanisch
Copy link
Member

Does #3076 solve your issue?

@SimonDanisch
Copy link
Member

I'm closing this for now, since we need a better undestanding of what this is supposed to solve!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Slow code path for mesh when ShaderAbstractions.Buffer used
5 participants