Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Parallelize @astrojs/image transforms #4626

Merged
merged 1 commit into from
Sep 6, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/rich-dolphins-teach.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@astrojs/image': patch
---

Parallelize image transforms
1 change: 1 addition & 0 deletions packages/integrations/image/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
"test": "mocha --exit --timeout 20000 test"
},
"dependencies": {
"@altano/tiny-async-pool": "^1.0.2",
Copy link
Contributor Author

@altano altano Sep 5, 2022

Choose a reason for hiding this comment

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

In case anyone is hesitant to take on @altano/tiny-async-pool, a new dependency:

  • The used export is super tiny: 315B gzipped
  • The package is a fork of async-pool by Rafael Xavier de Souza. I left it MIT licensed and barely modified the code other than to modernize it (typescript, esm export, vitest, etc). The original package gets ~900K weekly downloads so it's mature code and should be fairly bug free.

"image-size": "^1.0.2",
"magic-string": "^0.25.9",
"mime": "^3.0.0",
Expand Down
26 changes: 19 additions & 7 deletions packages/integrations/image/src/build/ssg.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import type { SSRImageService, TransformOptions } from '../loaders/index.js';
import { loadLocalImage, loadRemoteImage } from '../utils/images.js';
import { debug, info, LoggerLevel, warn } from '../utils/logger.js';
import { isRemoteImage } from '../utils/paths.js';
import OS from 'node:os';
import { doWork } from '@altano/tiny-async-pool';

function getTimeStat(timeStart: number, timeEnd: number) {
const buildTime = timeEnd - timeStart;
Expand All @@ -23,19 +25,26 @@ export interface SSGBuildParams {

export async function ssgBuild({ loader, staticImages, config, outDir, logLevel }: SSGBuildParams) {
const timer = performance.now();
const cpuCount = OS.cpus().length;

info({
level: logLevel,
prefix: false,
message: `${bgGreen(
black(` optimizing ${staticImages.size} image${staticImages.size > 1 ? 's' : ''} `)
black(
` optimizing ${staticImages.size} image${
staticImages.size > 1 ? 's' : ''
} in batches of ${cpuCount} `
)
)}`,
});

const inputFiles = new Set<string>();

// process transforms one original image file at a time
for (let [src, transformsMap] of staticImages) {
async function processStaticImage([src, transformsMap]: [
string,
Map<string, TransformOptions>
]): Promise<void> {
let inputFile: string | undefined = undefined;
let inputBuffer: Buffer | undefined = undefined;

Expand All @@ -60,15 +69,15 @@ export async function ssgBuild({ loader, staticImages, config, outDir, logLevel
if (!inputBuffer) {
// eslint-disable-next-line no-console
warn({ level: logLevel, message: `"${src}" image could not be fetched` });
continue;
return;
}

const transforms = Array.from(transformsMap.entries());

debug({ level: logLevel, prefix: false, message: `${green('▶')} ${src}` });
debug({ level: logLevel, prefix: false, message: `${green('▶')} transforming ${src}` });
let timeStart = performance.now();

// process each transformed versiono of the
// process each transformed version
for (const [filename, transform] of transforms) {
timeStart = performance.now();
let outputFile: string;
Expand All @@ -92,11 +101,14 @@ export async function ssgBuild({ loader, staticImages, config, outDir, logLevel
debug({
level: logLevel,
prefix: false,
message: ` ${cyan('└─')} ${dim(pathRelative)} ${dim(timeIncrease)}`,
message: ` ${cyan('created')} ${dim(pathRelative)} ${dim(timeIncrease)}`,
});
}
}

// transform each original image file in batches
await doWork(cpuCount, staticImages, processStaticImage);

info({
level: logLevel,
prefix: false,
Expand Down
6 changes: 6 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.