Skip to content

[Research / Candidate fix] HEAPMNG page-map size and 3-4 GiB coalescing boundary #163

Description

@zhanghm18GIT

Description

FAF's custom allocator identifies itself as HEAPMNG Ver.0.87.

Under severe late-game workloads, Heap Total can grow in approximately 31-32 MiB steps while Heap Committed remains substantially lower. Reverse engineering identified two allocator bounds that are relevant to complete 4 GiB page bookkeeping and high-address free-region coalescing.

Relevant locations in the tested executable:

  • initialization near 0x00957E00;
  • page-map allocation immediate at 0x00957E35;
  • right-neighbour coalescing bound at 0x00958107;
  • growth handling near 0x00958400;
  • page-to-region descriptor fill at 0x0095854F (REP STOS).

Game-level evidence and run comparison:

FAForever/fa#7195

Analysis

Page-map allocation

A 32-bit address space contains:

2^32 / 2^12 = 0x100000

4 KiB page indices.

At four bytes per page-map entry, the arithmetic full-range capacity is:

0x100000 * 4 = 0x400000 bytes

The tested code currently allocates 0x3FF000 bytes at 0x00957E35.

Right-neighbour coalescing bound

The tested free/coalescing path compares the end page against 0xC0000, which corresponds to 3 GiB.

The candidate changes the bound to 0x100000, the one-past-end page index for the full 4 GiB range. This permits right-neighbour lookup for pages in the 3-4 GiB range while still skipping lookup at exactly 0x100000.

The original design intent of the smaller values is not documented. They may encode a legacy 3 GiB address-space assumption, a sentinel, or an intentionally excluded top range. This must be verified before official integration.

Course of Action

Apply the following two immediate-value changes to the tested executable family.

Change 1

Virtual address:

0x00957E35

Instruction:

PUSH 0x003FF000
->
PUSH 0x00400000

Bytes:

68 00 F0 3F 00
->
68 00 00 40 00

Change 2

Virtual address:

0x00958107

Instruction:

CMP EAX,0x000C0000
->
CMP EAX,0x00100000

Bytes:

3D 00 00 0C 00
->
3D 00 00 10 00

Tested local baseline:

size:   12,541,952 bytes
SHA256: db05b62768fd1cea4cbc2fdce4bbf0778fe85353cd1fc3b1048db330914d08b6

Allocator candidate:

size:   12,541,952 bytes
SHA256: 22cb537385a402ad2ad1671c5cc63da8a1a29e9af9c91769d6dc0ddc27ac855e

The local baseline already contains unrelated tread-animation and particle-capacity changes. Those changes are not part of this proposal and did not alter the two HEAPMNG instruction locations.

Relative to that baseline, the candidate changes only the two immediate values above plus the PE checksum.

Evidence

The allocator candidate was used in the lowest-growth documented run together with a private same-unit/same-tick M28 queue-length cache:

Heap Total / Committed = 1.406 GiB / approximately 1.30-1.33 GiB

A separate cache-active run reached:

1.930 GiB / approximately 1.24-1.26 GiB

The candidate's independent contribution is not isolated, but it is part of a configuration that used approximately 0.524 GiB less Heap Total at the selected comparison point.

The candidate was also tested as a universal high-memory fix and did not make every path above roughly 2 GiB safe. It should therefore be treated as a bookkeeping/reuse candidate, not as a universal 4 GiB crash fix.

Validation already completed

  • PE entry point and section-table integrity;
  • Large Address Aware preservation;
  • independent PE checksum recalculation;
  • GNU and LLVM disassembly agreement;
  • full-file byte-difference audit;
  • preservation of the existing local baseline changes;
  • full-range page-map arithmetic;
  • 4 GiB one-past-end boundary checks;
  • randomized virtual-region stress-model coverage.

These are static/model checks. They do not replace Windows runtime instrumentation.

Test Plan

Instrument the following before deciding on integration:

Growth path

  • requested reserve and commit sizes;
  • VirtualAlloc return address;
  • first and last page indices;
  • current Heap Total and Committed;
  • largest reusable free region before growth.

Page-map fill around 0x0095854F

  • map base;
  • destination start and end;
  • descriptor value;
  • page count;
  • whether the write remains within the allocated map.

Free/coalescing path

  • freed region base and size;
  • left and right neighbour page indices;
  • whether a lookup is skipped by a bound;
  • neighbour free/allocated state;
  • merge result;
  • all events at or above page index 0xC0000.

Run the four-cell test matrix defined in fa#7195, at least three repetitions per cell.

Pass criteria:

  • consistently lower Heap Total without lower simulation correctness;
  • no out-of-range page-map access;
  • no free-list corruption;
  • no invalid merge;
  • no vanilla or modded long-game regression.

Out of scope

  • The M28 cache implementation is tracked separately in maudlin27/M28AI#394.
  • Failed Lua-GC experiments and external-module crash diagnostics are preserved in the technical archive and are not part of this allocator patch proposal.

Attachment

UPLOAD_2_BINARY_TECHNICAL_EVIDENCE.zip

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions