見出し画像

Windows 11 25H2 の Working Set 挙動を Swift(Windows)で実測してみた

― Swift + Win32 API で Working Set / PageFault を“実測”してみた ―


本記事の検証は筆者の環境・条件での観測結果であり、
製品の不具合や仕様を断定するものではありません。
同様の結果が再現されるかは環境差があります。

概要

前回の記事では、
Delphi + 組み込みアセンブラを用いて
Windows 11 25H2 における Working Set(WS)と PageFault の挙動を実測した。

今回は同じ検証を、
Swift(Windows) を用いて行った。

  • Swift 6.x(Windows / MSVC)

  • Win32 API(K32GetProcessMemoryInfo)を直接呼び出し

  • UnsafeMutableRawPointer による生メモリアクセス

  • sleep なし

という条件で、

  • Touched(触ったメモリ量)

  • Working Set

  • PageFaultCount

Delphi / C / Rust と同じ粒度で取得した。


なぜ Swift(Windows)なのか

Swift は一般に、

  • Apple 専用言語

  • 高級言語

  • ARC やランタイムが重い

といった印象を持たれがちだ。

しかし Windows では、

  • LLVM + MSVC

  • Win32 API を直接呼び出し可能

  • GC なし(ARC)

  • ネイティブコード生成

という 完全なネイティブ言語として動作する。

👉
「言語やランタイム差ではなく、
OS のメモリ管理の判断なのか?」
を確認するには、むしろ都合が良い。


検証方針

前回の Delphi + ASM 版と揃え、以下を方針とした。

  • OS のメモリ管理を直接観測する

  • 言語ランタイムの影響を極力排除する

  • sleep による“時間の猶予”は与えない

そのため、

  • UnsafeMutableRawPointer を使用

  • ページ単位(4KB)で書き込みアクセス

  • Win32 API から WS / PF を直接取得

という構成を採用した。


検証内容

テスト条件

  • OS:Windows 11 25H2(x64)

  • メモリ確保:512MB

  • アクセス方式:Page stride(4KB ごと)

  • 表示周期:10MB ごと


使用コード(Swift)

メモリ確保

let sizeMB = 512
let size = sizeMB * 1024 * 1024
let page = 4096

let ptr = UnsafeMutableRawPointer.allocate(
    byteCount: size,
    alignment: page
)

Page Touch + WS / PF 取得

import WinSDK

let hProcess = GetCurrentProcess()

var pmc = PROCESS_MEMORY_COUNTERS_EX()
pmc.cb = DWORD(MemoryLayout<PROCESS_MEMORY_COUNTERS_EX>.size)

for offset in stride(from: 0, to: size, by: page) {
    ptr.storeBytes(of: UInt8(1), toByteOffset: offset, as: UInt8.self)

    if offset % (10 * 1024 * 1024) == 0 {
        withUnsafeMutablePointer(to: &pmc) {
            $0.withMemoryRebound(
                to: PROCESS_MEMORY_COUNTERS.self,
                capacity: 1
            ) {
                _ = K32GetProcessMemoryInfo(
                    hProcess,
                    $0,
                    DWORD(MemoryLayout<PROCESS_MEMORY_COUNTERS_EX>.size)
                )
            }
        }

        print(
          "Touched \(offset/1024/1024) MB | " +
          "WS \(Int(pmc.WorkingSetSize)/1024/1024) MB | " +
          "PF \(pmc.PageFaultCount)"
        )
    }
}

実行結果(Windows 11 25H2)

ログ抜粋

Touched 100 MB | WS 108 MB | PF 27930
Touched 200 MB | WS 208 MB | PF 53580
Touched 300 MB | WS 308 MB | PF 79230
Touched 400 MB | WS 408 MB | PF 104880
Touched 500 MB | WS 508 MB | PF 130530

観測から分かること

  • ページアクセスごとに PageFault が発生している

  • Touched と Working Set はほぼ比例

  • Swift 特有の挙動は見られない

このフェーズの挙動は、
Delphi / C / Rust / Zig で行った初期挙動と一致している。


Delphi + ASM 版との比較

観点Delphi + ASMSwiftメモリ確保VirtualAllocUnsafeMutableRawPointerアクセス組み込みASMSwiftWS / PF 取得GetProcessMemoryInfoK32GetProcessMemoryInfo初期挙動一致一致

👉
違いは言語ではなく、観測フェーズにある。


Working Set が「張り付く/落ちる」条件

今回の Swift 版でも、以下は前回と同様だった。

  • Touch を継続中
    → Working Set は増加

  • Touch 完了後、放置
    → 数秒〜十数秒で WS は縮小

  • sleep を挿入
    → WS 昇格が発生

👉
アクセス量ではなく、
アクセスの継続性・滞在時間が重要
に見える。


結論

  • Swift(Windows)でも
    Working Set は「量」ではなく「時間」を見て管理されている

  • PageFault が増えても
    WS として保持されるとは限らない

  • 言語差・ランタイム差は
    本検証条件では影響しなかった

👉
Windows 11 25H2 のメモリ管理の性格は、言語を選ばない


まとめ

  • Swift でも Win32 API を直接呼び出せる

  • Swift でも OS が返す WS / PF の値は他言語と一致する

  • 変わったのはアプリではなく、Windows 11 25H2 のメモリ管理

OS の判断は言語を問わない。
Working Set をどう扱うかは、
アプリ側の設計で吸収する必要がある。

いいなと思ったら応援しよう!