Skip to content

Commit 56d0a44

Browse files
authored
a few optimization to static-cpp generator (#3)
* output file content hash in index_impl.h * only write to file if changed * update tests
1 parent a62d1e5 commit 56d0a44

File tree

4 files changed

+48
-20
lines changed

4 files changed

+48
-20
lines changed

src/code-generator-static-impl.ts

Lines changed: 33 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { createHash } from "node:crypto";
12
import { WgslTemplateBuildError } from "./errors.js";
23
import type {
34
SourceBuilder,
@@ -188,6 +189,7 @@ export class StaticCodeGenerator implements CodeGenerator, SourceBuilder {
188189

189190
#buildGenerateIndexImpl(
190191
repo: TemplateRepository<TemplatePass2>,
192+
templateImplementationHash: Map<string, string>,
191193
includePathPrefix: string,
192194
templateExt: string
193195
): string {
@@ -238,7 +240,14 @@ std::string pass_as_string(T&& v) {
238240
throw new Error(`Template name "${name}" does not end with the expected extension "${templateExt}"`);
239241
}
240242
const baseName = name.slice(0, -templateExt.length);
241-
implContent.push(`#include "${includePathPrefix}generated/${baseName}.h"`);
243+
const hash = templateImplementationHash.get(name);
244+
if (!hash) {
245+
throw new WgslTemplateBuildError(
246+
`Missing hash for template implementation "${name}"`,
247+
"output-validation-failed"
248+
);
249+
}
250+
implContent.push(`#include "${includePathPrefix}generated/${baseName}.h" // ${hash}`);
242251
}
243252

244253
implContent.push("");
@@ -303,28 +312,36 @@ std::string pass_as_string(T&& v) {
303312
options: SourceBuilderOptions
304313
): TemplateRepository<TemplateBuildResult> {
305314
const result = new Map<string, TemplateBuildResult>();
315+
const templateImplementationHash = new Map<string, string>();
306316

307-
// STEP.1. Generate the index.h
308-
result.set("index.h", this.#buildGenerateIndex(repo));
309-
310-
// STEP.2. Generate the string table if needed
311-
if (this.#stringTable) {
312-
result.set("string_table.h", this.#buildGenerateStringTable());
317+
// STEP.1. Generate each template implementation
318+
for (const [name, template] of repo.templates) {
319+
if (!name.endsWith(options.templateExt)) {
320+
throw new Error(`Template name "${name}" does not end with the expected extension "${options.templateExt}"`);
321+
}
322+
const baseName = name.slice(0, -options.templateExt.length);
323+
const content = this.#buildTemplateImplementation(name, template);
324+
result.set(`generated/${baseName}.h`, content);
325+
templateImplementationHash.set(name, createHash("sha256").update(content).digest("hex"));
313326
}
314327

315-
// STEP.3. Generate implementation index_impl.h
328+
// STEP.2. Generate implementation index_impl.h
316329
result.set(
317330
"index_impl.h",
318-
this.#buildGenerateIndexImpl(repo, options.includePathPrefix ?? "", options.templateExt)
331+
this.#buildGenerateIndexImpl(
332+
repo,
333+
templateImplementationHash,
334+
options.includePathPrefix ?? "",
335+
options.templateExt
336+
)
319337
);
320338

321-
// STEP.4. Generate each template implementation
322-
for (const [name, template] of repo.templates) {
323-
if (!name.endsWith(options.templateExt)) {
324-
throw new Error(`Template name "${name}" does not end with the expected extension "${options.templateExt}"`);
325-
}
326-
const baseName = name.slice(0, -options.templateExt.length);
327-
result.set(`generated/${baseName}.h`, this.#buildTemplateImplementation(name, template));
339+
// STEP.3. Generate the index.h
340+
result.set("index.h", this.#buildGenerateIndex(repo));
341+
342+
// STEP.4. Generate the string table if needed
343+
if (this.#stringTable) {
344+
result.set("string_table.h", this.#buildGenerateStringTable());
328345
}
329346

330347
return {

src/index.ts

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -108,8 +108,19 @@ export const build = async (options: BuildOptions): Promise<BuildResult> => {
108108
// Create the directory if it doesn't exist
109109
await fs.mkdir(dirName, { recursive: true });
110110

111-
// Write the file
112-
await fs.writeFile(fullPath, result, "utf8");
111+
// Only write the file if content has changed
112+
let shouldWrite = true;
113+
try {
114+
const existingContent = await fs.readFile(fullPath, "utf8");
115+
shouldWrite = existingContent !== result;
116+
} catch {
117+
// File doesn't exist or can't be read, so we should write it
118+
shouldWrite = true;
119+
}
120+
121+
if (shouldWrite) {
122+
await fs.writeFile(fullPath, result, "utf8");
123+
}
113124

114125
// Record successful file write
115126
await fs.stat(fullPath);

test/testcases/build-basic/expected/static-cpp/index_impl.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ std::string pass_as_string(T&& v) {
3535

3636
// Include template implementations
3737

38-
#include "generated/shader/triangle.h"
38+
#include "generated/shader/triangle.h" // f5cec46558b917d8a4ec739f0ab02e71a31046d9c0d14028daa4aa0557a72da6
3939

4040
#pragma pop_macro("MainFunctionStart")
4141
#pragma pop_macro("MainFunctionEnd")

test/testcases/build-example-pad/expected/static-cpp-literal/index_impl.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ std::string pass_as_string(T&& v) {
3434

3535
// Include template implementations
3636

37-
#include "generated/tensor/pad.h"
37+
#include "generated/tensor/pad.h" // 6322dc19e1e013413b06fa369e2961e3683123a3f4a36e646798f3219b132f49
3838

3939
#pragma pop_macro("MainFunctionStart")
4040
#pragma pop_macro("MainFunctionEnd")

0 commit comments

Comments
 (0)