前端进度条性能优化、仿真结束后后处理优化;后端C代码生成流程优化:先识别来源,再按照已知未知量需求排序,最后局部求解

This commit is contained in:
ljz committed 2026-09-11 11:27:54 +08:00
1 parent 0dcb465d84
commit 91bd9fb252
77 files changed
+11223 -551

No files matched your search

+165
View File
@@ -0,0 +1,165 @@
import type { SimulationResultsSnapshot } from "./SimulationResultsView";
export const RESULT_SNAPSHOT_KEY = "system-simulation-flow:latest-result";
const DATABASE = "system-simulation-results";
const CHUNK_SIZE = 32768;
const BATCH_VALUES = 131072;
type Header = {
snapshot: SimulationResultsSnapshot;
lengths: Record<string, number>;
};
type Chunk = { key: IDBValidKey; values: Float64Array };
let database: Promise<IDBDatabase> | undefined;
let saveSequence = 0;
let pendingSaves = 0;
let ownedCacheId: string | undefined;
export function resultSavePending() { return pendingSaves > 0; }
function openDatabase() {
if (!database) database = new Promise<IDBDatabase>((resolve, reject) => {
let blocked = false;
const request = indexedDB.open(DATABASE, 1);
request.onupgradeneeded = () => {
request.result.createObjectStore("headers");
request.result.createObjectStore("chunks");
};
request.onerror = () => reject(request.error);
request.onblocked = () => {
blocked = true;
reject(new Error("结果数据库被其他页面占用,请关闭旧页面后重试"));
};
request.onsuccess = () => {
const db = request.result;
if (blocked) { db.close(); return; }
db.onversionchange = () => { db.close(); database = undefined; };
resolve(db);
};
}).catch((error) => { database = undefined; throw error; });
return database;
}
function writeBatch(db: IDBDatabase, chunks: Chunk[], header?: [string, Header]) {
return new Promise<void>((resolve, reject) => {
const transaction = db.transaction(["headers", "chunks"], "readwrite");
transaction.oncomplete = () => resolve();
transaction.onabort = () => reject(transaction.error ?? new Error("结果保存事务已中止"));
transaction.onerror = () => {}; // onabort reports the transaction failure once.
try {
for (const chunk of chunks) transaction.objectStore("chunks").put(chunk.values, chunk.key);
if (header) transaction.objectStore("headers").put(header[1], header[0]);
} catch (error) {
transaction.abort();
reject(error);
}
});
}
function deleteCache(db: IDBDatabase, cacheId: string) {
return new Promise<void>((resolve, reject) => {
const transaction = db.transaction(["headers", "chunks"], "readwrite");
transaction.oncomplete = () => resolve();
transaction.onabort = () => reject(transaction.error);
transaction.objectStore("headers").delete(cacheId);
transaction.objectStore("chunks").delete(IDBKeyRange.bound([cacheId], [cacheId, []]));
});
}
/** Publish the small session pointer only after every data chunk is durably committed. */
export async function storeResultSnapshot(snapshot: SimulationResultsSnapshot) {
const sequence = ++saveSequence;
const cacheId = typeof crypto.randomUUID === "function"
? crypto.randomUUID()
: `${Date.now()}-${sequence}-${Math.random().toString(36).slice(2)}`;
pendingSaves++;
let db: IDBDatabase | undefined;
let committed = false;
try {
// Yield before serialization so the ready result and controls can paint first.
await new Promise<void>((resolve) => setTimeout(resolve, 0));
if (sequence !== saveSequence) return false;
db = await openDatabase();
const lengths: Record<string, number> = Object.create(null);
let batch: Chunk[] = [];
let batchSize = 0;
for (const [name, values] of Object.entries(snapshot.result.series)) {
lengths[name] = values.length;
for (let offset = 0; offset < values.length; offset += CHUNK_SIZE) {
if (sequence !== saveSequence) return false;
const length = Math.min(CHUNK_SIZE, values.length - offset);
const block = new Float64Array(length);
for (let i = 0; i < length; i++) block[i] = values[offset + i];
batch.push({ key: [cacheId, name, offset], values: block });
batchSize += length;
if (batchSize >= BATCH_VALUES) {
await writeBatch(db, batch);
batch = [];
batchSize = 0;
await new Promise<void>((resolve) => setTimeout(resolve, 0));
}
}
}
if (sequence !== saveSequence) return false;
await writeBatch(db, batch, [cacheId, {
snapshot: { ...snapshot, result: { ...snapshot.result, series: Object.create(null) } }, lengths,
}]);
if (sequence !== saveSequence) return false;
sessionStorage.setItem(RESULT_SNAPSHOT_KEY, JSON.stringify({ storage: "indexeddb", version: 1, cacheId }));
committed = true;
// Only retire this page's own previous save, never another tab's loaded snapshot.
const previous = ownedCacheId;
ownedCacheId = cacheId;
if (previous) void deleteCache(db, previous).catch(() => {});
return true;
} finally {
pendingSaves--;
if (db && !committed) await deleteCache(db, cacheId).catch(() => {});
}
}
export async function loadStoredResultSnapshot(): Promise<unknown | null> {
const raw = sessionStorage.getItem(RESULT_SNAPSHOT_KEY);
if (!raw) return null;
const value = JSON.parse(raw);
// Read legacy sessionStorage data without rewriting or duplicating it at startup.
if (value?.storage !== "indexeddb") return value;
if (value.version !== 1 || typeof value.cacheId !== "string") return null;
const db = await openDatabase();
return new Promise<SimulationResultsSnapshot | null>((resolve, reject) => {
const transaction = db.transaction(["headers", "chunks"], "readonly");
let result: SimulationResultsSnapshot | null = null;
let failure: Error | null = null;
transaction.oncomplete = () => failure ? reject(failure) : resolve(result);
transaction.onabort = () => reject(transaction.error);
const request = transaction.objectStore("headers").get(value.cacheId);
request.onsuccess = () => {
const header = request.result as Header | undefined;
if (!header) { failure = new Error("结果缓存已丢失,请重新载入结果文件"); return; }
result = header.snapshot;
for (const [name, length] of Object.entries(header.lengths)) {
result.result.series[name] = new Array<number>(length);
}
const loaded: Record<string, number> = Object.create(null);
const cursor = transaction.objectStore("chunks").openCursor(IDBKeyRange.bound([value.cacheId], [value.cacheId, []]));
cursor.onsuccess = () => {
const entry = cursor.result;
if (!entry) {
for (const [name, length] of Object.entries(header.lengths)) {
if ((loaded[name] ?? 0) !== length) failure = new Error("结果缓存不完整,请重新载入结果文件");
}
return;
}
const [, name, offset] = entry.key as [string, string, number];
const block = entry.value as Float64Array;
const values = result!.result.series[name];
if (!values || offset < 0 || offset + block.length > values.length) {
failure = new Error("结果缓存分块无效");
} else {
for (let i = 0; i < block.length; i++) values[offset + i] = block[i];
loaded[name] = (loaded[name] ?? 0) + block.length;
}
entry.continue();
};
};
});
}