Skip to content

⚡ Bolt: [performance improvement] Parallelize exportIconsForYAML#445

Closed
aafre wants to merge 2 commits into
mainfrom
bolt-optimization-icon-registry-promise-all-7347780638239694315
Closed

⚡ Bolt: [performance improvement] Parallelize exportIconsForYAML#445
aafre wants to merge 2 commits into
mainfrom
bolt-optimization-icon-registry-promise-all-7347780638239694315

Conversation

@aafre
Copy link
Copy Markdown
Owner

@aafre aafre commented Apr 5, 2026

💡 What:
Replaced a sequential for...of loop with a concurrent Promise.all mapping in the useIconRegistry.ts hook's exportIconsForYAML function. Added inline code comments to explain the optimization.

🎯 Why:
The previous implementation processed fileToBase64 conversions sequentially, blocking execution for each file. This is an I/O bound task that can be effectively parallelized to drastically reduce the total wait time when dealing with multiple icons.

📊 Impact:
Wait time is decoupled. Rather than O(N) wait time, it reduces latency towards O(1) as all base64 reader calls execute concurrently. Memory/CPU usage will be slightly more peaked during parallel execution, but total operation time will be significantly reduced for multiple files.

🔬 Measurement:
A custom test measuring performance on 5-10 icons being exported to YAML should show reduced total time compared to a sequential wait block execution.


PR created automatically by Jules for task 7347780638239694315 started by @aafre

Co-authored-by: aafre <8656674+aafre@users.noreply.github.com>
@google-labs-jules
Copy link
Copy Markdown
Contributor

👋 Jules, reporting for duty! I'm here to lend a hand with this pull request.

When you start a review, I'll add a 👀 emoji to each comment to let you know I've read it. I'll focus on feedback directed at me and will do my best to stay out of conversations between you and other bots or reviewers to keep the noise down.

I'll push a commit with your requested changes shortly after. Please note there might be a delay between these steps, but rest assured I'm on the job!

For more direct control, you can switch me to Reactive Mode. When this mode is on, I will only act on comments where you specifically mention me with @jules. You can find this option in the Pull Request section of your global Jules UI settings. You can always switch back!

New to Jules? Learn more at jules.google/docs.


For security, I will only act on instructions from the user who triggered this task.

Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request refactors the icon export logic in useIconRegistry.ts to process file conversions concurrently using Promise.all, which improves performance for I/O-bound tasks. The feedback suggests a more idiomatic implementation using Object.fromEntries to avoid mutating an external object within the concurrent map, while also improving type safety and data preservation.

Comment on lines 170 to +192
const exportData: IconExportData = {};

for (const filename of targetFilenames) {
const entry = registry[filename];
if (entry) {
try {
const base64Data = await fileToBase64(entry.file);
exportData[filename] = {
data: base64Data,
type: entry.file.type,
size: entry.file.size,
uploadedAt: entry.uploadedAt.toISOString(),
};
} catch (error) {
console.warn(`Failed to export icon ${filename}:`, error);
// Performance Optimization: Refactored sequential `await` loop into concurrent
// execution using `Promise.all`. This significantly reduces total execution time
// for I/O bound tasks like FileReader base64 conversions when exporting multiple icons.
await Promise.all(
targetFilenames.map(async (filename) => {
const entry = registry[filename];
if (entry) {
try {
const base64Data = await fileToBase64(entry.file);
exportData[filename] = {
data: base64Data,
type: entry.file.type,
size: entry.file.size,
uploadedAt: entry.uploadedAt.toISOString(),
};
} catch (error) {
console.warn(`Failed to export icon ${filename}:`, error);
}
}
}
}
})
);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

While the parallelization is a great performance improvement, the current implementation relies on side-effects by mutating the exportData object from within concurrent promises. A more idiomatic and maintainable approach is to use Promise.all to return the transformed data and then assemble the final object using Object.fromEntries. Additionally, ensure all original properties are preserved during transformation to prevent accidental data loss (Rule 4), and use unknown instead of any for better type safety (Rule 5).

    const exportEntries = await Promise.all(
      targetFilenames.map(async (filename) => {
        const entry = registry[filename];
        if (!entry) return null;

        try {
          const base64Data = await fileToBase64(entry.file);
          return [
            filename,
            {
              ...entry,
              data: base64Data,
              type: entry.file.type,
              size: entry.file.size,
              uploadedAt: entry.uploadedAt.toISOString(),
            },
          ];
        } catch (error) {
          console.warn("Failed to export icon " + filename + ":", error);
          return null;
        }
      })
    );

    const exportData: IconExportData = Object.fromEntries(
      exportEntries.filter((entry): entry is [string, unknown] => entry !== null)
    );
References
  1. When transforming data objects, preserve all original properties by default to prevent accidental data loss, rather than hardcoding for an expected schema.
  2. When handling data from external sources, prefer using the unknown type over any to enforce type-checking and improve type safety.

Co-authored-by: aafre <8656674+aafre@users.noreply.github.com>
@aafre
Copy link
Copy Markdown
Owner Author

aafre commented Apr 11, 2026

Closing: superseded by #421 (cleanest version). Cherry-picked into #457.

@aafre aafre closed this Apr 11, 2026
@google-labs-jules
Copy link
Copy Markdown
Contributor

Closing: superseded by #421 (cleanest version). Cherry-picked into #457.

Understood. Acknowledging that this work is now obsolete and stopping work on this task.

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.

1 participant