🐣 Mobile Eggbert

Performance Notes

Four performance issues were identified and documented in RAM.md. All are located in src/WindowsPhoneSpeedyBlupi/Decor.cpp. None are blocking — the game runs correctly — but they represent unnecessary CPU overhead per frame.

Performance Analysis Source

The analysis is documented in RAM.md at the repository root. All four issues are in Decor.cpp in the ByeByeObject particle system and the MoveObject sort routine.

No Heap Allocation Issues Found The analysis confirmed that make_shared / make_unique calls are all one-time initialization (LoadContent, constructors). There are no per-frame allocations.

Issue 1 — ByeByeDraw (Decor.cpp:9252)

Severity: Low — copies a 72-byte struct every iteration of the per-frame draw loop.

// Current — copies 72-byte ByeByeObject struct each iteration:
for (ByeByeObject obj : m_byeByeObjects) {
    DrawByeByeObject(obj);
}

// Fix — use const reference:
for (const ByeByeObject& obj : m_byeByeObjects) {
    DrawByeByeObject(obj);
}

Issue 2 — ByeByeAdd (Decor.cpp:9187–9206)

Severity: Low — double-copy before vector insertion (construct on stack, then copy into vector).

// Current — constructs on stack then copies:
ByeByeObject obj;
obj.x = x;
obj.y = y;
obj.icon = icon;
// ... more fields ...
m_byeByeObjects.push_back(obj);  // copy

// Fix — use emplace_back with direct field initialization:
m_byeByeObjects.emplace_back(x, y, icon, ...);

Issue 3 — ByeByeStep (Decor.cpp:9241)

Severity: Medium — vector::erase() called in the middle of a vector during iteration is O(n) per removal. With many short-lived particles, this becomes O(n²).

// Current — O(n) erase during iteration:
for (auto it = m_byeByeObjects.begin(); it != m_byeByeObjects.end(); ) {
    it->time--;
    if (it->time <= 0) {
        it = m_byeByeObjects.erase(it);  // O(n) shift
    } else {
        ++it;
    }
}

// Fix — erase-remove idiom (single O(n) pass):
for (auto& obj : m_byeByeObjects) obj.time--;
m_byeByeObjects.erase(
    std::remove_if(m_byeByeObjects.begin(), m_byeByeObjects.end(),
                   [](const ByeByeObject& o){ return o.time <= 0; }),
    m_byeByeObjects.end()
);

Issue 4 — MoveObjectSort (Decor.cpp:9074–9108)

Severity: Medium — manual bubble sort with manual struct copying for the MoveObject array. This is O(n²) with a very large constant factor (each swap copies the whole struct).

// Current — manual bubble sort:
for (int i = 0; i < m_moveObjectCount - 1; i++) {
    for (int j = i + 1; j < m_moveObjectCount; j++) {
        if (m_moveObject[i].z > m_moveObject[j].z) {
            MoveObject temp = m_moveObject[i];   // full struct copy
            m_moveObject[i] = m_moveObject[j];
            m_moveObject[j] = temp;
        }
    }
}

// Fix — std::sort with lambda comparator:
std::sort(m_moveObject, m_moveObject + m_moveObjectCount,
          [](const MoveObject& a, const MoveObject& b){
              return a.z < b.z;
          });

Heap Allocation Status

The analysis scanned for per-frame heap allocations and found none. All make_shared and make_unique calls are in one-time initialization paths:

  • Game1::LoadContent() — creates Pixmap and Sound instances once
  • Game1::Initialize() — other one-time allocations
  • Constructors — class member initialization

No heap allocations were found in Update() or Draw() hot paths.