Skip to content

Conversation

sheetalkamat
Copy link
Member

  • Buildinfo was on "toBuildInfo" object made it separate allocation so that it doesnt form cycle with incremental program and snapshot
  • Do not store incremental program past compilation as we are storing buildInfo anyways
  • handled single threaded build to build in order to ensure that it does not create a deadlock
  • Handled the case where same file exists multiple times in program
  • optimized to not calculate "referenceMap" and other incremental state when we are building for tsc -b for non incremental programs

@Copilot Copilot AI review requested due to automatic review settings September 22, 2025 21:52
Copy link
Contributor

@Copilot Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR implements memory optimizations for TypeScript incremental compilation and build functionality. The changes focus on reducing memory usage by breaking cycles between incremental programs and snapshots, avoiding unnecessary state tracking for non-incremental builds, and ensuring proper cleanup of resources.

Key changes include:

  • Refactored buildInfo allocation to be separate from incremental program snapshots
  • Optimized incremental state computation to only run when needed
  • Restructured build task execution to support both single-threaded and multi-threaded modes

Reviewed Changes

Copilot reviewed 37 out of 37 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
internal/execute/incremental/snapshottobuildinfo.go Separates buildInfo allocation and handles duplicate files in programs
internal/execute/incremental/snapshot.go Adds method to determine when incremental state tracking is needed
internal/execute/incremental/programtosnapshot.go Conditionally computes incremental state based on build requirements
internal/execute/incremental/program.go Removes MakeReadonly method and optimizes diagnostic collection
internal/execute/incremental/emitfileshandler.go Restructures emit handling to support both incremental and non-incremental paths
internal/execute/build/orchestrator.go Refactors build execution to handle single-threaded builds sequentially
internal/execute/build/buildtask.go Restructures task state management and reporting
internal/execute/tsc/statistics.go Updates statistics aggregation to work incrementally
testdata/baselines/reference/* Updates test baselines reflecting changes in incremental behavior

Comment on lines +207 to +210
if int(fileId) != len(t.buildInfo.FileNames) {
// Duplicate - for now ignore
return nil
}
Copy link
Preview

Copilot AI Sep 22, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The duplicate file handling logic should include a more descriptive comment explaining why duplicates occur and what the implications are of ignoring them. Consider adding logging or metrics to track when this condition is hit.

Copilot uses AI. Check for mistakes.

Comment on lines 314 to +317
func emitFiles(ctx context.Context, program *Program, options compiler.EmitOptions, isForDtsErrors bool) *compiler.EmitResult {
emitHandler := &emitFilesHandler{ctx: ctx, program: program, isForDtsErrors: isForDtsErrors}

if options.TargetSourceFile != nil {
if !isForDtsErrors && options.TargetSourceFile != nil {
Copy link
Preview

Copilot AI Sep 22, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This condition combines two boolean checks that serve different purposes. Consider extracting this into a well-named helper method like shouldUseDirectEmit(isForDtsErrors, targetSourceFile) to improve readability and make the intent clearer.

Copilot uses AI. Check for mistakes.

Comment on lines +309 to +315
if o.opts.Command.CompilerOptions.SingleThreaded.IsTrue() {
for _, config := range o.Order() {
path := o.toPath(config)
task := o.getTask(path)
o.buildOrCleanProject(task, path, &buildResult)
}
} else {
Copy link
Preview

Copilot AI Sep 22, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The single-threaded execution path processes tasks in dependency order, while the multi-threaded path processes all tasks concurrently. Consider adding a comment explaining why the order matters for single-threaded builds but not for multi-threaded builds, as this could be a source of confusion.

Copilot uses AI. Check for mistakes.

@mjames-c
Copy link
Contributor

mjames-c commented Sep 23, 2025

FYI I tried this out (commit 21e97b927) with an uncached build of our (Canva's) monorepo TS project with 5k+ transitive project ref deps (see #1622) and the build finished (before this change it would OOM)! However it still used extreme amounts of memory (116GB of my 124GB machines RAM).

./built/local/tsgo --build ../canva/<omitted>/tsconfig.json   2180.76s user 103.13s system 1424% cpu 2:40.29 total
image

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants