Skip to content

Refactor Memory Allocation to Dynamically Determine Page Size Instead of Hardcoding 4KB AssumptionΒ #6055

@calcitem

Description

@calcitem

Describe the issue

The Stockfish engine's memory management code, specifically in src/memory.cpp and src/memory.h, contains hardcoded assumptions of a 4KB (4096 bytes) system page size. This primarily affects the aligned_large_pages_alloc function for non-Windows/non-Linux (or generic Linux fallback) code paths and related helper functions like make_unique_large_page.

Specific Locations:

  1. aligned_large_pages_alloc (non-Windows/non-Linux path) in src/memory.cpp:

    C++

    #else // Not _WIN32
    void* aligned_large_pages_alloc(size_t allocSize) {
    #if defined(__linux__) && !defined(__ANDROID__)
        constexpr size_t alignment = 2 * 1024 * 1024;  // 2MB page size assumed
    #else
        constexpr size_t alignment = 4096;  // <--- HERE: small page size assumed
    #endif
    // ...
    }
    #endif
    

    This hardcoded 4096 is problematic when the actual system page size is different (e.g., 16KB).

  2. static_assert in make_unique_large_page in src/memory.h:

    C++

    template<typename T, typename... Args>
    std::enable_if_t<!std::is_array_v<T>, LargePagePtr<T>> make_unique_large_page(Args&&... args) {
        static_assert(alignof(T) <= 4096, // <--- HERE
                      "aligned_large_pages_alloc() may fail for such a big alignment requirement of T");
    // ...
    }
    // Similar assert for array version
    

    This reinforces the 4KB assumption.

Impact:

This hardcoding can lead to issues on systems with page sizes other than 4KB, particularly:

  • Suboptimal Alignment: Memory may not be aligned to the true system page boundary (e.g., 16KB), potentially negating performance benefits of such alignment.
  • Inefficiency: May lead to memory fragmentation or waste if the OS rounds allocations to the actual larger page size.
  • Compatibility: This is particularly relevant for newer Android devices and NDKs (e.g., NDK r27+) which promote 16KB page alignment. NDK guidelines also state that the PAGE_SIZE macro might become undefined in 16KB mode, emphasizing the need to dynamically query page sizes.
  • The function name aligned_large_pages_alloc implies alignment to a system-relevant page size, which may not be met with a hardcoded value.

Expected behavior

The memory allocation functions, especially aligned_large_pages_alloc, should dynamically determine the actual system page size at runtime and use this value for memory alignment when intending to align to "page size".

Specifically:

  1. The hardcoded 4096 value used as a presumed page size should be replaced with a value obtained by calling sysconf(_SC_PAGESIZE) (on POSIX-compliant systems like Linux, Android, macOS) or a similar platform-specific API.
  2. Memory allocations intended to be page-aligned should be aligned to this dynamically fetched, actual system page size (e.g., 4KB, 16KB, or other).
  3. The static_assert in make_unique_large_page should be re-evaluated. If it's meant to check against the page size, it needs to accommodate dynamic page sizes (perhaps by becoming a runtime check for unusually large alignof(T) or by using a more flexible compile-time constant if page size is constrained within a known set for targeted platforms).
  4. Comments should reflect the dynamic nature of page size determination.

Steps to reproduce

This is a code architecture issue identified through code review, rather than a runtime bug reproducible with specific UCI commands. The issue becomes relevant on systems where the actual page size differs from the hardcoded 4KB assumption.

To observe the relevant code:

  1. Review the

    aligned_large_pages_alloc
    

    function in

    src/memory.cpp
    

    , specifically the

    #else
    

    block for non-Windows, non-specific-Linux code paths.

    C++

    // memory.cpp, within aligned_large_pages_alloc
    #else // Not _WIN32
    // ...
    #if defined(__linux__) && !defined(__ANDROID__)
        // ...
    #else
        constexpr size_t alignment = 4096; // Problematic line
    #endif
    
  2. Review the

    static_assert
    

    conditions in

    make_unique_large_page
    

    in

    src/memory.h
    

    .

    C++

    // memory.h, within make_unique_large_page
    static_assert(alignof(T) <= 4096, ...);
    
  3. Consider the execution path and memory alignment results if this code runs on a system with a 16KB page size (e.g., an Android device compiled with NDK r27+ targeting 16KB-aligned executables). The memory will be aligned to 4KB, not the system's actual 16KB page size.

Anything else?

Proposed Solution Details:

  1. Use sysconf(_SC_PAGESIZE):

    In src/memory.cpp, for POSIX-like systems (including Linux, Android, macOS), replace the hardcoded 4096 with a call to sysconf(_SC_PAGESIZE).

    C++

    #include <unistd.h> // For sysconf
    
    // Helper (can be placed appropriately)
    static size_t get_system_page_size() {
        long page_size_val = sysconf(_SC_PAGESIZE);
        if (page_size_val == -1) {
            // Fallback or error handling
            // For robustness, might log an error and return a default like 4096,
            // or throw, depending on desired error handling strategy.
            return 4096; 
        }
        return static_cast<size_t>(page_size_val);
    }
    
    // In aligned_large_pages_alloc for the generic path:
    // ...
    #else // for the generic POSIX path
        alignment = get_system_page_size();
    #endif
    // ...
    
  2. Re-evaluate static_assert:

    The static_assert(alignof(T) <= 4096, ...) logic needs to be updated. Since page size is dynamic, a compile-time check against a fixed value is problematic.

    • If alignof(T) is generally expected to be small (e.g., <= 64 bytes), it will likely always be less than any system page size (4KB, 16KB, 64KB). The assert might be unnecessary or could check against a very generous maximum alignment if alignof(T) could be extremely large for some types used with this allocator.
    • Alternatively, if PAGE_SIZE becomes available as a reliable compile-time constant in target build environments (especially NDK r27+ for 16KB mode), it could be used. Otherwise, this check might need to be a runtime check or be removed if std_aligned_alloc is trusted to handle the alignof(T) passed to it.
  3. Windows Code Path: The Windows-specific code using GetLargePageMinimum() and VirtualAlloc seems generally fine as it queries system properties.

This change is crucial for future-proofing Stockfish, ensuring optimal performance on modern hardware (especially Android devices with 16KB pages), and adhering to evolving platform guidelines.

Operating system

Android

Stockfish version

94e6c04

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