22 return {block % PROBE_BLOCKS_PER_AXIS, (block / PROBE_BLOCKS_PER_AXIS) % PROBE_BLOCKS_PER_AXIS,
23 block / (PROBE_BLOCKS_PER_AXIS * PROBE_BLOCKS_PER_AXIS)};
27 return {cell % PROBE_GRID_SIZE, (cell / PROBE_GRID_SIZE) % PROBE_GRID_SIZE, cell / PROBE_SLICE_CELL_COUNT};
31 return std::format(
"({}, {}, {})", coordinates[0], coordinates[1], coordinates[2]);
36 constexpr auto AXIS =
static_cast<int32_t
>(PROBE_BLOCKS_PER_AXIS);
37 constexpr std::array<std::array<int32_t, 3>, PROBE_WAKE_MAX_RECORDS> OFFSETS = {
38 {{0, 0, 0}, {-1, 0, 0}, {1, 0, 0}, {0, -1, 0}, {0, 1, 0}, {0, 0, -1}, {0, 0, 1}}};
41 std::vector<uint32_t> blocks;
42 for (
const auto& offset : OFFSETS) {
43 const int32_t x =
static_cast<int32_t
>(center[0]) + offset[0];
44 const int32_t y =
static_cast<int32_t
>(center[1]) + offset[1];
45 const int32_t z =
static_cast<int32_t
>(center[2]) + offset[2];
46 if (x < 0 || y < 0 || z < 0 || x >= AXIS || y >= AXIS || z >= AXIS)
50 ProbeBlockIndex(
static_cast<uint32_t
>(x),
static_cast<uint32_t
>(y),
static_cast<uint32_t
>(z)));
64 for (uint32_t axis = 0; axis < 3; ++axis) {
65 if (place[axis] < filter.
boxMin[axis] || place[axis] >= filter.
boxMax[axis])
75 std::map<uint32_t, std::vector<uint32_t>>
pokes;
85 if (record.kind == PROBE_TRACE_POKE) {
86 tree.
pokes[record.subject].push_back(record.object);
87 tree.
roots.insert(record.subject);
88 }
else if (record.kind == PROBE_TRACE_WAKE) {
89 tree.
roots.insert(record.subject);
90 if (record.subject == record.object)
93 const auto [parent, inserted] = tree.
parents.try_emplace(record.object, record.subject);
95 parent->second = std::min(parent->second, record.subject);
96 }
else if (record.kind == PROBE_TRACE_CONDUCT)
97 tree.
conducted[record.subject] = record.object;
102 const auto conducted = tree.
conducted.find(block);
103 const char* state = conducted == tree.
conducted.end() ?
"(計算の記録なし)"
104 : (conducted->second & PROBE_BLOCK_FLAG_CHANGED) != 0 ?
"変わった"
105 : (conducted->second & PROBE_BLOCK_FLAG_POSSIBLE) != 0 ?
"変わらない・次の刻みも計算"
112 const std::set<uint32_t>& changedBefore,
bool previousTraced) {
114 if (
const auto poked = tree.
pokes.find(root); poked != tree.
pokes.end()) {
116 for (
const uint32_t cell : poked->second)
118 }
else if (changedBefore.contains(root))
119 origin = std::format(
"刻み {} で変わった(か、まだ進めた)", tick - 1);
121 origin = previousTraced ?
"(前の刻みで変わった記録なし。箱の外から)" :
"(前の刻みは記録していない)";
123 const auto self = tree.
selfWakes.find(root);
124 const std::string repeat = self != tree.
selfWakes.end() && self->second > 1
125 ? std::format(
" ×{}", self->second)
132 bool previousTraced,
size_t recordCount) {
133 const auto changed = rng::count_if(
134 tree.
conducted, [](
const auto& entry) { return (entry.second & PROBE_BLOCK_FLAG_CHANGED) != 0; });
135 std::string text = std::format(
"刻み {}: 根 {}・計算 {} ブロック(変わった {})・記録 {}\n", tick,
139 for (
const uint32_t root : tree.
roots) {
140 text +=
FormatRoot(tree, root, tick, changedBefore, previousTraced);
141 for (
const auto& [block, parent] : tree.
parents) {
148 bool orphanHeader =
false;
149 for (
const auto& [block, flags] : tree.
conducted) {
150 if (tree.
parents.contains(block))
154 text +=
" (起こした記録なし)\n";
165 bool SameCellWords(std::span<const uint32_t> gpuCells, std::span<const uint32_t> cpuCells, uint32_t index) {
166 const size_t base =
size_t{index} * PROBE_EXTRACTION_CELL_WORDS;
168 return rng::equal(gpuCells.subspan(base, PROBE_EXTRACTION_CELL_WORDS),
169 cpuCells.subspan(base, PROBE_EXTRACTION_CELL_WORDS));
173 std::span<const uint32_t> cpuCells,
Coordinates& first) {
174 constexpr uint32_t CELLS_PER_BLOCK = PROBE_BLOCK_SIZE * PROBE_BLOCK_SIZE * PROBE_BLOCK_SIZE;
176 uint32_t differences = 0;
178 for (uint32_t local = 0; local < CELLS_PER_BLOCK; ++local) {
179 const Coordinates cell = {origin[0] * PROBE_BLOCK_SIZE + local % PROBE_BLOCK_SIZE,
180 origin[1] * PROBE_BLOCK_SIZE + (local / PROBE_BLOCK_SIZE) % PROBE_BLOCK_SIZE,
181 origin[2] * PROBE_BLOCK_SIZE + local / (PROBE_BLOCK_SIZE * PROBE_BLOCK_SIZE)};
182 const uint32_t index = ProbeCellIndex(cell[0], cell[1], cell[2]);
186 if (differences == 0)
196 const char* kind = record.
kind == PROBE_TRACE_POKE ?
"つつき"
197 : record.
kind == PROBE_TRACE_WAKE ?
"起こす"
198 : record.
kind == PROBE_TRACE_CONDUCT ?
"計算"
201 return std::format(
"刻み {} {} 主 {} 従 {}", record.
tick, kind, record.
subject, record.
object);
207 std::array<uint32_t, 3> cellMin, std::array<uint32_t, 3> cellMax,
209 GraphTraceFilter filter{.enabled =
true, .capacity = capacity, .tickBegin = tickBegin, .tickEnd = tickEnd};
210 for (uint32_t axis = 0; axis < 3; ++axis) {
211 const uint32_t last = std::min(cellMax[axis], PROBE_GRID_SIZE);
212 filter.
boxMin[axis] = std::min(cellMin[axis], last) / PROBE_BLOCK_SIZE;
213 filter.
boxMax[axis] = (last + PROBE_BLOCK_SIZE - 1) / PROBE_BLOCK_SIZE;
221 std::set<uint32_t> changedBefore;
222 std::optional<uint64_t> previousTick;
224 for (
size_t begin = 0; begin < sorted.size();) {
225 const uint64_t tick = sorted[begin].tick;
227 while (end < sorted.size() && sorted[end].tick == tick)
230 const std::span<const GraphTraceRecord> records = sorted.subspan(begin, end - begin);
232 BuildTickTree(records, tree);
233 const bool previousTraced = previousTick == tick - 1;
235 changedBefore.clear();
237 text += FormatTick(tick, tree, changedBefore, previousTraced, records.size());
239 changedBefore.clear();
240 for (
const auto& [block, flags] : tree.conducted) {
242 changedBefore.insert(block);
253 std::vector<gpu::GraphTraceRecord> records,
256 std::ofstream file(path, std::ios::binary);
258 return std::unexpected(
"トレースのファイルを開けない");
261 file <<
"# 伝導の連鎖のトレース(T-0087。読み方は engine/src/sim/probe_trace.h)\n"
262 << std::format(
"# 刻み [{}, {})・ブロックの箱 [{}, {}, {}] 〜 [{}, {}, {})・記録 {} 件", filter.
tickBegin,
265 << (droppedCount > 0 ? std::format(
"・容量を越えて落とした {} 件(欠けている)\n", droppedCount) :
"\n")
269 return std::unexpected(
"トレースのファイルに書けない");
274 std::vector<gpu::GraphTraceRecord>
UniqueProbeTrace(std::vector<gpu::GraphTraceRecord> records) {
276 const auto duplicates = rng::unique(records);
277 records.erase(duplicates.begin(), duplicates.end());
283 std::span<const ProbeCommand> commands, std::span<const uint8_t> flagsBefore,
284 std::span<const uint8_t> flagsAfter, std::vector<gpu::GraphTraceRecord>& expected) {
285 if (!WantsTick(filter, tick))
289 std::vector<uint8_t> seeds(flagsBefore.begin(), flagsBefore.end());
291 const uint32_t x = command.payload[0];
292 const uint32_t y = command.payload[1];
293 const uint32_t z = command.payload[2];
294 if (command.targetTick != tick || command.type != PROBE_COMMAND_TYPE_POKE || x >= PROBE_GRID_SIZE ||
295 y >= PROBE_GRID_SIZE || z >= PROBE_GRID_SIZE) {
299 const uint32_t block = ProbeBlockOfCell(x, y, z);
301 if (WantsBlock(filter, block))
303 {.tick = tick, .kind = PROBE_TRACE_POKE, .subject = block, .object = ProbeCellIndex(x, y, z)});
307 std::vector<uint8_t> scheduled(PROBE_BLOCK_COUNT, 0);
308 for (uint32_t source = 0; source < PROBE_BLOCK_COUNT; ++source) {
309 if (seeds[source] == 0)
312 const bool sourceWanted = WantsBlock(filter, source);
313 for (
const uint32_t target : BlockAndNeighbors(source)) {
314 scheduled[target] = 1;
315 if (sourceWanted || WantsBlock(filter, target))
316 expected.push_back({.tick = tick, .kind = PROBE_TRACE_WAKE, .subject = source, .object = target});
320 for (uint32_t block = 0; block < PROBE_BLOCK_COUNT; ++block) {
321 if (scheduled[block] != 0 && WantsBlock(filter, block)) {
323 {.tick = tick, .kind = PROBE_TRACE_CONDUCT, .subject = block, .object = flagsAfter[block]});
329 std::span<const gpu::GraphTraceRecord> cpu) {
330 const auto [gpuAt, cpuAt] = rng::mismatch(
gpu, cpu);
331 if (gpuAt ==
gpu.end() && cpuAt == cpu.end())
334 const std::string gpuText = gpuAt ==
gpu.end() ?
"(終わり)" : FormatRecord(*gpuAt);
335 const std::string cpuText = cpuAt == cpu.end() ?
"(終わり)" : FormatRecord(*cpuAt);
337 return std::format(
"トレースの {} 件目で食い違った: GPU {} / CPU {}", gpuAt -
gpu.begin(), gpuText, cpuText);
340 std::optional<uint64_t>
FirstDivergentTick(std::span<const ProbeTickHash>
gpu, std::span<const ProbeTickHash> cpu) {
343 if (cpuTick == cpu.end())
346 if (cpuTick->hash != gpuTick.hash || cpuTick->energy != gpuTick.energy)
354 std::span<const uint32_t> cpuCells) {
355 constexpr size_t WORDS =
size_t{PROBE_CELL_COUNT} * PROBE_EXTRACTION_CELL_WORDS;
356 if (gpuCells.size() < WORDS || cpuCells.size() < WORDS)
359 std::optional<ProbeDivergence> divergence;
360 uint32_t differingCells = 0;
361 uint32_t differingBlocks = 0;
364 for (uint32_t block = 0; block < PROBE_BLOCK_COUNT; ++block) {
366 const uint32_t differences = CountBlockDifferences(block, gpuCells, cpuCells, first);
367 if (differences == 0)
370 differingCells += differences;
373 const uint32_t index = ProbeCellIndex(first[0], first[1], first[2]);
377 .gpuValue = gpuCells[
size_t{index} * PROBE_EXTRACTION_CELL_WORDS],
378 .cpuValue = cpuCells[
size_t{index} * PROBE_EXTRACTION_CELL_WORDS]};
383 divergence->differingCells = differingCells;
384 divergence->differingBlocks = differingBlocks;
392 "S({}) で CPU リファレンスと食い違った: 最初のブロック {} {}・セル {} 温度 GPU {} CPU {} mK"
393 "(食い違ったセル {}・ブロック {})",
394 divergence.
tick, divergence.
block, FormatCoordinates(BlockCoordinates(divergence.
block)),
void SortGraphTrace(std::vector< GraphTraceRecord > &records)
bool SameCellWords(std::span< const uint32_t > gpuCells, std::span< const uint32_t > cpuCells, uint32_t index)
bool WantsBlock(const GraphTraceFilter &filter, uint32_t block)
void BuildTickTree(std::span< const GraphTraceRecord > records, TickTree &tree)
Coordinates BlockCoordinates(uint32_t block)
uint32_t CountBlockDifferences(uint32_t block, std::span< const uint32_t > gpuCells, std::span< const uint32_t > cpuCells, Coordinates &first)
std::string FormatRecord(const GraphTraceRecord &record)
Coordinates CellCoordinates(uint32_t cell)
bool WantsTick(const GraphTraceFilter &filter, uint64_t tick)
std::vector< uint32_t > BlockAndNeighbors(uint32_t block)
std::string FormatCoordinates(const Coordinates &coordinates)
std::string FormatTick(uint64_t tick, const TickTree &tree, const std::set< uint32_t > &changedBefore, bool previousTraced, size_t recordCount)
std::string FormatChild(const TickTree &tree, uint32_t block)
std::array< uint32_t, 3 > Coordinates
std::string FormatRoot(const TickTree &tree, uint32_t root, uint64_t tick, const std::set< uint32_t > &changedBefore, bool previousTraced)
std::optional< ProbeDivergence > FindCellDivergence(uint64_t tick, std::span< const uint32_t > gpuCells, std::span< const uint32_t > cpuCells)
std::optional< uint64_t > FirstDivergentTick(std::span< const ProbeTickHash > gpu, std::span< const ProbeTickHash > cpu)
void AppendExpectedProbeTrace(const gpu::GraphTraceFilter &filter, uint64_t tick, std::span< const ProbeCommand > commands, std::span< const uint8_t > flagsBefore, std::span< const uint8_t > flagsAfter, std::vector< gpu::GraphTraceRecord > &expected)
std::string FormatProbeDivergence(const ProbeDivergence &divergence)
std::vector< gpu::GraphTraceRecord > UniqueProbeTrace(std::vector< gpu::GraphTraceRecord > records)
std::string FormatProbeTrace(std::span< const gpu::GraphTraceRecord > sorted)
std::optional< std::string > FirstProbeTraceMismatch(std::span< const gpu::GraphTraceRecord > gpu, std::span< const gpu::GraphTraceRecord > cpu)
std::expected< void, std::string > WriteProbeTraceFile(const fs::path &path, std::vector< gpu::GraphTraceRecord > records, const gpu::GraphTraceFilter &filter, uint64_t droppedCount)
gpu::GraphTraceFilter ProbeTraceFilterForCells(uint64_t tickBegin, uint64_t tickEnd, std::array< uint32_t, 3 > cellMin, std::array< uint32_t, 3 > cellMax, uint32_t capacity)
std::array< uint32_t, 3 > boxMax
std::array< uint32_t, 3 > boxMin
std::array< uint32_t, 3 > cell
std::array< uint32_t, 3 > boxMax
std::array< uint32_t, 3 > boxMin
std::set< uint32_t > roots
std::map< uint32_t, uint32_t > parents
std::map< uint32_t, std::vector< uint32_t > > pokes
std::map< uint32_t, uint32_t > conducted
std::map< uint32_t, uint32_t > selfWakes