Skip to content

Optimize AlgorithmBacktrack2Deluxe2_AsByte with struct wrapper for InnerMap#106

Draft
Copilot wants to merge 7 commits intomasterfrom
copilot/optimize-virtcall-inner-map
Draft

Optimize AlgorithmBacktrack2Deluxe2_AsByte with struct wrapper for InnerMap#106
Copilot wants to merge 7 commits intomasterfrom
copilot/optimize-virtcall-inner-map

Conversation

Copy link
Contributor

Copilot AI commented Feb 13, 2026

Virtual call overhead from class-based generic constraint where M : InnerMap prevents JIT method inlining in the hot path. Each map cell access incurs virtual dispatch.

Solution

Introduced struct wrapper pattern using value-type generic constraint to enable JIT devirtualization and inlining:

// New optimized path
public Maze GoGenerateOptimized<MAccessor, TAction>(...)
    where MAccessor : struct, IInnerMapAccessor  // JIT can inline
    where TAction : struct, IProgressAction
{
    var mapAccessor = mapAccessorFactory.Create(width, height);
    return GoGenerateInternalOptimized(mapAccessor, ...);
}

Implementation

  • IInnerMapAccessor - Interface defining Width, Height, indexer, and GetInnerMap()
  • InnerMapAccessor<T> - Readonly struct wrapping any InnerMap with AggressiveInlining
  • InnerMapAccessorFactory<T> - Factory creating struct accessors from existing IInnerMapFactory<T>
  • GoGenerateOptimized() - New algorithm entry point using struct constraint
  • MazeGenerator.GenerateOptimized<T>() - Convenience helpers

Usage

// Opt-in to optimized path
var maze = MazeGenerator.GenerateOptimized<BitArreintjeFastInnerMap>(1024, 1024, seed, callback);

Original GoGenerate() unchanged - fully backward compatible.

Benchmark

Added MazeBenchmarkJob.OptimizedStructAccessor() benchmark comparing baseline vs. struct accessor performance.

Notes

No type-specific checks (if (innerMap is ConcreteType)) - works generically with any InnerMap implementation via compile-time specialization.

Original prompt

This section details on the original issue you should resolve

<issue_title>Optimize virtcall by using struct instead of class for innermap</issue_title>
<issue_description>I want to performance optimize: AlgorithmBacktrack2Deluxe2_AsByte

So I did some testing and if we would do this:

  public Maze GoGenerate<M, TAction>(int width, int height, int seed, IInnerMapFactory<M> mapFactory, IRandomFactory randomFactory, TAction pixelChangedCallback)
            where M : InnerMap
            where TAction : struct, IProgressAction
        {
            var innerMap = mapFactory.Create(width, height);
            var random = randomFactory.Create(seed);

            // Special handling for our unsafe map
            if (innerMap is BitArreintjeFastInnerMapUnsafe unsafeMap)
            {
                return GoGenerateInternalUnsafe(unsafeMap, random, pixelChangedCallback);
            }
            
            // Fallback (or throw, but let's try to be nice)
            // But since I don't want to duplicate the safe logic here, I'll just throw 
            // because the user selected the "Unsafe" algorithm implying they want speed.
            throw new InvalidOperationException($"AlgorithmBacktrack3_Unsafe requires BitArreintjeFastInnerMapUnsafe, but got {innerMap.GetType().Name}");
        }
        private unsafe Maze GoGenerateInternalUnsafe<TAction>(BitArreintjeFastInnerMapUnsafe map, IRandom random, TAction pixelChangedCallback)
            where TAction : struct, IProgressAction
        {

Then purely the fact that we use BitArreintjeFastInnerMapUnsafe instead of a generic, optimizes the maze generation algorithm performance quite significantly.

The reason for that is is because we now use the actual class BitArreintjeFastInnerMapUnsafe (which is sealed) rather then the implementation.

The problem is though that the only way to get C# to inline methods in the old method like this:

        private Maze GoGenerateInternal<M, TAction>(M map, IRandom random, TAction pixelChangedCallback) where M : InnerMap where TAction : struct, IProgressAction
        {

Is to ensure that M is actually also a struct, just like IProgressAction.

So what I want you to do is somehow create a wrapper or whatever around the InnerMap with struct functionality so we can get that inlined WITHOUT modifying the AlgorithmBacktrack2Deluxe2_AsByte to have specific code for the maze.

SO DO NOT ADD SOMETHING LIKE THIS:
if (innerMap is BitArreintjeFastInnerMapUnsafe unsafeMap).

Think of other solutions</issue_description>

<agent_instructions>Read the instructions very carefully and DONT ADD SOMETHING LIKE THIS AS A QUICK EASY FIX: if (innerMap is BitArreintjeFastInnerMapUnsafe unsafeMap)</agent_instructions>

Comments on the Issue (you are @copilot in this section)


💬 We'd love your input! Share your thoughts on Copilot coding agent in our 2 minute survey.

Copilot AI and others added 6 commits February 13, 2026 17:04
Co-authored-by: devedse <2350015+devedse@users.noreply.github.com>
Co-authored-by: devedse <2350015+devedse@users.noreply.github.com>
Co-authored-by: devedse <2350015+devedse@users.noreply.github.com>
Co-authored-by: devedse <2350015+devedse@users.noreply.github.com>
Co-authored-by: devedse <2350015+devedse@users.noreply.github.com>
Co-authored-by: devedse <2350015+devedse@users.noreply.github.com>
Copilot AI changed the title [WIP] Optimize virtcall using struct for innermap Optimize AlgorithmBacktrack2Deluxe2_AsByte with struct wrapper for InnerMap Feb 13, 2026
Copilot AI requested a review from devedse February 13, 2026 17:13
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.

Optimize virtcall by using struct instead of class for innermap

2 participants