Step 6 - 统计模块
This commit is contained in:
parent
61e0034800
commit
785e92ec8b
|
|
@ -0,0 +1,77 @@
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace VMdemo.Simulation
|
||||||
|
{
|
||||||
|
public class StatsCollector
|
||||||
|
{
|
||||||
|
public int TotalAccess { get; private set; }
|
||||||
|
public int TlbHit { get; private set; }
|
||||||
|
public int PageTableHit { get; private set; }
|
||||||
|
public int PageFault { get; private set; }
|
||||||
|
public long TotalCost { get; private set; }
|
||||||
|
|
||||||
|
public double TlbHitRate => TotalAccess > 0 ? (double)TlbHit / TotalAccess : 0.0;
|
||||||
|
public double PageTableHitRate => TotalAccess > 0 ? (double)PageTableHit / TotalAccess : 0.0;
|
||||||
|
public double AvgCost => TotalAccess > 0 ? (double)TotalCost / TotalAccess : 0.0;
|
||||||
|
|
||||||
|
public void Reset()
|
||||||
|
{
|
||||||
|
TotalAccess = 0;
|
||||||
|
TlbHit = 0;
|
||||||
|
PageTableHit = 0;
|
||||||
|
PageFault = 0;
|
||||||
|
TotalCost = 0L;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int CalculateCost(bool tlbHit, bool pageTableHit, bool pageFault, int pageFaultPenalty)
|
||||||
|
{
|
||||||
|
if (pageFaultPenalty <= 0)
|
||||||
|
{
|
||||||
|
throw new ArgumentOutOfRangeException(nameof(pageFaultPenalty), "pageFaultPenalty 必须是正整数。");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tlbHit)
|
||||||
|
{
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pageFault)
|
||||||
|
{
|
||||||
|
return 4 + pageFaultPenalty;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pageTableHit)
|
||||||
|
{
|
||||||
|
return 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new InvalidOperationException("访问结果无效:TLB miss 时必须是页表命中或缺页之一。");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void RecordAccess(bool tlbHit, bool pageTableHit, bool pageFault, int cost)
|
||||||
|
{
|
||||||
|
if (cost < 0)
|
||||||
|
{
|
||||||
|
throw new ArgumentOutOfRangeException(nameof(cost), "cost 不能为负数。");
|
||||||
|
}
|
||||||
|
|
||||||
|
TotalAccess++;
|
||||||
|
if (tlbHit)
|
||||||
|
{
|
||||||
|
TlbHit++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pageTableHit)
|
||||||
|
{
|
||||||
|
PageTableHit++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pageFault)
|
||||||
|
{
|
||||||
|
PageFault++;
|
||||||
|
}
|
||||||
|
|
||||||
|
TotalCost += cost;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 5fc1bac53447fa34db019e4b6ed2d26c
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
|
|
@ -75,6 +75,7 @@ namespace VMdemo.Simulation
|
||||||
_derivedConfig = derived;
|
_derivedConfig = derived;
|
||||||
_seed = seed;
|
_seed = seed;
|
||||||
State = new SimulationState();
|
State = new SimulationState();
|
||||||
|
Stats = new StatsCollector();
|
||||||
|
|
||||||
AddressTranslatorUtils.GetPageTableBitLayout(derived.VpnBits, out var l1Bits, out var l2Bits);
|
AddressTranslatorUtils.GetPageTableBitLayout(derived.VpnBits, out var l1Bits, out var l2Bits);
|
||||||
L1Bits = l1Bits;
|
L1Bits = l1Bits;
|
||||||
|
|
@ -97,6 +98,7 @@ namespace VMdemo.Simulation
|
||||||
public TlbCache TlbCache => _tlbCache;
|
public TlbCache TlbCache => _tlbCache;
|
||||||
public TwoLevelPageTable PageTable => _pageTable;
|
public TwoLevelPageTable PageTable => _pageTable;
|
||||||
public PhysicalMemoryManager PhysicalMemoryManager => _physicalMemoryManager;
|
public PhysicalMemoryManager PhysicalMemoryManager => _physicalMemoryManager;
|
||||||
|
public StatsCollector Stats { get; }
|
||||||
public EvictionInfo LastEviction => _lastEviction;
|
public EvictionInfo LastEviction => _lastEviction;
|
||||||
|
|
||||||
public void Reset()
|
public void Reset()
|
||||||
|
|
@ -105,6 +107,7 @@ namespace VMdemo.Simulation
|
||||||
_lastEviction = EvictionInfo.None;
|
_lastEviction = EvictionInfo.None;
|
||||||
_hasPendingForcedVirtualAddress = false;
|
_hasPendingForcedVirtualAddress = false;
|
||||||
_pendingForcedVirtualAddress = 0UL;
|
_pendingForcedVirtualAddress = 0UL;
|
||||||
|
Stats.Reset();
|
||||||
|
|
||||||
_addressGenerator = new AddressGenerator(_config.vaBits, _seed);
|
_addressGenerator = new AddressGenerator(_config.vaBits, _seed);
|
||||||
_tlbCache = new TlbCache(_config.tlbEntries);
|
_tlbCache = new TlbCache(_config.tlbEntries);
|
||||||
|
|
@ -278,6 +281,17 @@ namespace VMdemo.Simulation
|
||||||
|
|
||||||
private void ExecuteFinalize()
|
private void ExecuteFinalize()
|
||||||
{
|
{
|
||||||
|
State.CurrentCost = Stats.CalculateCost(
|
||||||
|
State.IsTlbHit,
|
||||||
|
State.IsPageTableHit,
|
||||||
|
State.IsPageFault,
|
||||||
|
_config.pageFaultPenalty);
|
||||||
|
Stats.RecordAccess(
|
||||||
|
State.IsTlbHit,
|
||||||
|
State.IsPageTableHit,
|
||||||
|
State.IsPageFault,
|
||||||
|
State.CurrentCost);
|
||||||
|
|
||||||
State.CurrentRound += 1;
|
State.CurrentRound += 1;
|
||||||
State.CurrentStep = TranslationStep.GenerateVA;
|
State.CurrentStep = TranslationStep.GenerateVA;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,135 @@
|
||||||
|
using NUnit.Framework;
|
||||||
|
using VMdemo.Core;
|
||||||
|
using VMdemo.Simulation;
|
||||||
|
|
||||||
|
namespace VMdemo.Tests.EditMode
|
||||||
|
{
|
||||||
|
public class Step6StatsCollectorTests
|
||||||
|
{
|
||||||
|
[Test]
|
||||||
|
public void StatsCollector_Reset_ClearsAllCountersAndRates()
|
||||||
|
{
|
||||||
|
var stats = new StatsCollector();
|
||||||
|
stats.RecordAccess(tlbHit: true, pageTableHit: false, pageFault: false, cost: 1);
|
||||||
|
stats.RecordAccess(tlbHit: false, pageTableHit: false, pageFault: true, cost: 104);
|
||||||
|
|
||||||
|
stats.Reset();
|
||||||
|
|
||||||
|
Assert.That(stats.TotalAccess, Is.EqualTo(0));
|
||||||
|
Assert.That(stats.TlbHit, Is.EqualTo(0));
|
||||||
|
Assert.That(stats.PageTableHit, Is.EqualTo(0));
|
||||||
|
Assert.That(stats.PageFault, Is.EqualTo(0));
|
||||||
|
Assert.That(stats.TotalCost, Is.EqualTo(0L));
|
||||||
|
Assert.That(stats.TlbHitRate, Is.EqualTo(0.0));
|
||||||
|
Assert.That(stats.PageTableHitRate, Is.EqualTo(0.0));
|
||||||
|
Assert.That(stats.AvgCost, Is.EqualTo(0.0));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void StatsCollector_CalculateCost_FollowsMvpCostModel()
|
||||||
|
{
|
||||||
|
var stats = new StatsCollector();
|
||||||
|
|
||||||
|
Assert.That(stats.CalculateCost(tlbHit: true, pageTableHit: false, pageFault: false, pageFaultPenalty: 100), Is.EqualTo(1));
|
||||||
|
Assert.That(stats.CalculateCost(tlbHit: false, pageTableHit: true, pageFault: false, pageFaultPenalty: 100), Is.EqualTo(4));
|
||||||
|
Assert.That(stats.CalculateCost(tlbHit: false, pageTableHit: false, pageFault: true, pageFaultPenalty: 100), Is.EqualTo(104));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void StatsCollector_RecordAccess_AggregatesCountersRatesAndAverage()
|
||||||
|
{
|
||||||
|
var stats = new StatsCollector();
|
||||||
|
|
||||||
|
stats.RecordAccess(tlbHit: true, pageTableHit: false, pageFault: false, cost: 1);
|
||||||
|
stats.RecordAccess(tlbHit: false, pageTableHit: true, pageFault: false, cost: 4);
|
||||||
|
stats.RecordAccess(tlbHit: false, pageTableHit: false, pageFault: true, cost: 104);
|
||||||
|
|
||||||
|
Assert.That(stats.TotalAccess, Is.EqualTo(3));
|
||||||
|
Assert.That(stats.TlbHit, Is.EqualTo(1));
|
||||||
|
Assert.That(stats.PageTableHit, Is.EqualTo(1));
|
||||||
|
Assert.That(stats.PageFault, Is.EqualTo(1));
|
||||||
|
Assert.That(stats.TotalCost, Is.EqualTo(109L));
|
||||||
|
Assert.That(stats.TlbHitRate, Is.EqualTo(1.0 / 3.0).Within(1e-12));
|
||||||
|
Assert.That(stats.PageTableHitRate, Is.EqualTo(1.0 / 3.0).Within(1e-12));
|
||||||
|
Assert.That(stats.AvgCost, Is.EqualTo(109.0 / 3.0).Within(1e-12));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TranslatorEngine_StepOnce_ToFinalize_UpdatesStatsAndCurrentCost()
|
||||||
|
{
|
||||||
|
var engine = CreateEngine(pageFaultPenalty: 120, seed: 31);
|
||||||
|
engine.SetNextVirtualAddress(0x0000_1234UL);
|
||||||
|
|
||||||
|
while (!engine.StepOnce())
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
Assert.That(engine.State.CurrentRound, Is.EqualTo(1));
|
||||||
|
Assert.That(engine.State.IsPageFault, Is.True);
|
||||||
|
Assert.That(engine.State.CurrentCost, Is.EqualTo(124));
|
||||||
|
Assert.That(engine.Stats.TotalAccess, Is.EqualTo(1));
|
||||||
|
Assert.That(engine.Stats.PageFault, Is.EqualTo(1));
|
||||||
|
Assert.That(engine.Stats.TotalCost, Is.EqualTo(124L));
|
||||||
|
Assert.That(engine.Stats.AvgCost, Is.EqualTo(124.0));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TranslatorEngine_RunOneAccess_TracksBatchSummaryWithMixedPaths()
|
||||||
|
{
|
||||||
|
var engine = CreateEngine(pageFaultPenalty: 100, seed: 41);
|
||||||
|
const ulong va = 0x0000_5678UL;
|
||||||
|
|
||||||
|
var first = engine.RunOneAccess(va); // page fault
|
||||||
|
var second = engine.RunOneAccess(va); // tlb hit
|
||||||
|
|
||||||
|
engine.TlbCache.Clear();
|
||||||
|
var third = engine.RunOneAccess(va); // page table hit
|
||||||
|
|
||||||
|
Assert.IsTrue(first.PageFault);
|
||||||
|
Assert.IsTrue(second.TlbHit);
|
||||||
|
Assert.IsTrue(third.PageTableHit);
|
||||||
|
|
||||||
|
Assert.That(engine.Stats.TotalAccess, Is.EqualTo(3));
|
||||||
|
Assert.That(engine.Stats.TlbHit, Is.EqualTo(1));
|
||||||
|
Assert.That(engine.Stats.PageTableHit, Is.EqualTo(1));
|
||||||
|
Assert.That(engine.Stats.PageFault, Is.EqualTo(1));
|
||||||
|
Assert.That(engine.Stats.TotalCost, Is.EqualTo(109L));
|
||||||
|
Assert.That(engine.Stats.TlbHitRate, Is.EqualTo(1.0 / 3.0).Within(1e-12));
|
||||||
|
Assert.That(engine.Stats.PageTableHitRate, Is.EqualTo(1.0 / 3.0).Within(1e-12));
|
||||||
|
Assert.That(engine.Stats.AvgCost, Is.EqualTo(109.0 / 3.0).Within(1e-12));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TranslatorEngine_Reset_ClearsStats()
|
||||||
|
{
|
||||||
|
var engine = CreateEngine(pageFaultPenalty: 100, seed: 51);
|
||||||
|
engine.RunOneAccess(0x0000_9ABCUL);
|
||||||
|
Assert.That(engine.Stats.TotalAccess, Is.EqualTo(1));
|
||||||
|
|
||||||
|
engine.Reset();
|
||||||
|
|
||||||
|
Assert.That(engine.Stats.TotalAccess, Is.EqualTo(0));
|
||||||
|
Assert.That(engine.Stats.TlbHit, Is.EqualTo(0));
|
||||||
|
Assert.That(engine.Stats.PageTableHit, Is.EqualTo(0));
|
||||||
|
Assert.That(engine.Stats.PageFault, Is.EqualTo(0));
|
||||||
|
Assert.That(engine.Stats.TotalCost, Is.EqualTo(0L));
|
||||||
|
Assert.That(engine.Stats.AvgCost, Is.EqualTo(0.0));
|
||||||
|
Assert.That(engine.State.CurrentCost, Is.EqualTo(0));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static TranslatorEngine CreateEngine(int pageFaultPenalty, int seed)
|
||||||
|
{
|
||||||
|
var config = new SimulationConfig
|
||||||
|
{
|
||||||
|
vaBits = 32,
|
||||||
|
pageSizeKB = 4,
|
||||||
|
physicalMemoryMB = 1,
|
||||||
|
tlbEntries = 4,
|
||||||
|
accessCount = 20,
|
||||||
|
pageFaultPenalty = pageFaultPenalty
|
||||||
|
};
|
||||||
|
|
||||||
|
return new TranslatorEngine(config, seed);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 85f0d67cd32ddbe4ca06ce696b2426cf
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
|
|
@ -143,20 +143,20 @@
|
||||||
|
|
||||||
### TODO
|
### TODO
|
||||||
|
|
||||||
- [ ] 新建 `StatsCollector.cs`
|
- [x] 新建 `StatsCollector.cs`
|
||||||
- [ ] 累计:`totalAccess`、`tlbHit`、`pageTableHit`、`pageFault`
|
- [x] 累计:`totalAccess`、`tlbHit`、`pageTableHit`、`pageFault`
|
||||||
- [ ] 计算:`tlbHitRate`、`pageTableHitRate`、`avgCost`
|
- [x] 计算:`tlbHitRate`、`pageTableHitRate`、`avgCost`
|
||||||
|
|
||||||
### 开销模型(MVP)
|
### 开销模型(MVP)
|
||||||
|
|
||||||
- [ ] TLB 命中:`+1`
|
- [x] TLB 命中:`+1`
|
||||||
- [ ] TLB miss + 页表命中:`+4`
|
- [x] TLB miss + 页表命中:`+4`
|
||||||
- [ ] 缺页:`+4 + pageFaultPenalty`
|
- [x] 缺页:`+4 + pageFaultPenalty`
|
||||||
|
|
||||||
### 完成标准
|
### 完成标准
|
||||||
|
|
||||||
- [ ] 指标在单步和批量模式下都持续更新
|
- [x] 指标在单步和批量模式下都持续更新
|
||||||
- [ ] 批量完成后给出最终汇总
|
- [x] 批量完成后给出最终汇总
|
||||||
|
|
||||||
## 8. Step 7 - UI 绑定与流程动画
|
## 8. Step 7 - UI 绑定与流程动画
|
||||||
|
|
||||||
|
|
|
||||||
Reference in New Issue