Skip to content

Commit 0845fec

Browse files
authored
Merge pull request #17 from 343dev/improvement/lossless
Disable parallel optimization of JPEG files in Lossless mode
2 parents 989eb60 + f40065b commit 0845fec

File tree

5 files changed

+77
-69
lines changed

5 files changed

+77
-69
lines changed

CHANGELOG.md

+9
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,14 @@
11
# Changelog
22

3+
## 9.1.0 (2024-10-16)
4+
5+
- Disabled parallel optimization of JPEG files in Lossless mode.
6+
7+
Guetzli uses a huge amount of RAM. In my case, when optimizing a file of about 30 MB, one process could use up to 12 GB of memory. If there are multiple files, parallel optimization with Guetzli consumes all available RAM, causing the system to use Swap, leading to slowdowns and freezes.
8+
9+
For this reason I decided to disable parallel optimization of JPEG files in Lossless mode. Now, it will take more time but will have less impact on the OS performance.
10+
11+
312
## 9.0.2 (2024-10-08)
413

514
- Fixed Guetzli install.

convert.js

+50-52
Original file line numberDiff line numberDiff line change
@@ -49,59 +49,57 @@ export async function convert({ filePaths, config }) {
4949

5050
const totalSize = { before: 0, after: 0 };
5151

52-
const avifConfig = isLossless
53-
? config?.avif?.lossless
54-
: config?.avif?.lossy;
55-
const webpConfig = isLossless
56-
? config?.webp?.lossless
57-
: config?.webp?.lossy;
58-
const webpGifConfig = isLossless
59-
? config?.webpGif?.lossless
60-
: config?.webpGif?.lossy;
61-
62-
const tasksSimultaneousLimit = pLimit(os.cpus().length);
63-
const tasksPromises = filePaths.reduce((accumulator, filePath) => {
64-
if (shouldConvertToAvif) {
65-
accumulator.push(
66-
tasksSimultaneousLimit(
67-
() => processFile({
68-
filePath,
69-
config: avifConfig || {},
70-
progressBarContainer,
71-
progressBar,
72-
totalSize,
73-
isForced,
74-
format: 'AVIF',
75-
processFunction: processAvif,
76-
}),
77-
),
78-
);
79-
}
80-
81-
if (shouldConvertToWebp) {
82-
accumulator.push(
83-
tasksSimultaneousLimit(
84-
() => processFile({
85-
filePath,
86-
config: (path.extname(filePath.input).toLowerCase() === '.gif'
87-
? webpGifConfig
88-
: webpConfig)
89-
|| {},
90-
progressBarContainer,
91-
progressBar,
92-
totalSize,
93-
isForced,
94-
format: 'WebP',
95-
processFunction: processWebp,
96-
}),
97-
),
98-
);
99-
}
100-
101-
return accumulator;
102-
}, []);
52+
const getConfig = format => config?.[format]?.[isLossless ? 'lossless' : 'lossy'];
53+
54+
const avifConfig = getConfig('avif');
55+
const webpConfig = getConfig('webp');
56+
const webpGifConfig = getConfig('webpGif');
57+
58+
const cpuCount = os.cpus().length;
59+
const tasksSimultaneousLimit = pLimit(cpuCount);
60+
61+
await Promise.all(
62+
filePaths.reduce((accumulator, filePath) => {
63+
if (shouldConvertToAvif) {
64+
accumulator.push(
65+
tasksSimultaneousLimit(
66+
() => processFile({
67+
filePath,
68+
config: avifConfig || {},
69+
progressBarContainer,
70+
progressBar,
71+
totalSize,
72+
isForced,
73+
format: 'AVIF',
74+
processFunction: processAvif,
75+
}),
76+
),
77+
);
78+
}
79+
80+
if (shouldConvertToWebp) {
81+
const isGif = path.extname(filePath.input).toLowerCase() === '.gif';
82+
83+
accumulator.push(
84+
tasksSimultaneousLimit(
85+
() => processFile({
86+
filePath,
87+
config: (isGif ? webpGifConfig : webpConfig) || {},
88+
progressBarContainer,
89+
progressBar,
90+
totalSize,
91+
isForced,
92+
format: 'WebP',
93+
processFunction: processWebp,
94+
}),
95+
),
96+
);
97+
}
98+
99+
return accumulator;
100+
}, []),
101+
);
103102

104-
await Promise.all(tasksPromises);
105103
progressBarContainer.update(); // Prevent logs lost. See: https://github.com/npkgz/cli-progress/issues/145#issuecomment-1859594159
106104
progressBarContainer.stop();
107105

optimize.js

+15-14
Original file line numberDiff line numberDiff line change
@@ -42,28 +42,29 @@ export async function optimize({ filePaths, config }) {
4242
const totalSize = { before: 0, after: 0 };
4343

4444
const cpuCount = os.cpus().length;
45-
const tasksSimultaneousLimit = pLimit(
46-
/*
47-
Guetzli uses a large amount of memory and a significant amount of CPU time.
48-
To reduce the processor load in lossless mode, we reduce the number
49-
of simultaneous tasks by half.
50-
*/
51-
isLossless ? Math.round(cpuCount / 2) : cpuCount,
52-
);
53-
const tasksPromises = filePaths.map(
54-
filePath => tasksSimultaneousLimit(
55-
() => processFile({
45+
const tasksSimultaneousLimit = pLimit(cpuCount);
46+
const guetzliTasksSimultaneousLimit = pLimit(1); // Guetzli uses a large amount of memory and a significant amount of CPU time. To reduce system load, we only allow one instance of guetzli to run at the same time.
47+
48+
await Promise.all(
49+
filePaths.map(filePath => {
50+
const extension = path.extname(filePath.input).toLowerCase();
51+
const isJpeg = extension === '.jpg' || extension === '.jpeg';
52+
53+
const limit = isJpeg && isLossless
54+
? guetzliTasksSimultaneousLimit
55+
: tasksSimultaneousLimit;
56+
57+
return limit(() => processFile({
5658
filePath,
5759
config,
5860
progressBarContainer,
5961
progressBar,
6062
totalSize,
6163
isLossless,
62-
}),
63-
),
64+
}));
65+
}),
6466
);
6567

66-
await Promise.all(tasksPromises);
6768
progressBarContainer.update(); // Prevent logs lost. See: https://github.com/npkgz/cli-progress/issues/145#issuecomment-1859594159
6869
progressBarContainer.stop();
6970

package-lock.json

+2-2
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@343dev/optimizt",
3-
"version": "9.0.3",
3+
"version": "9.1.0",
44
"description": "CLI image optimization tool",
55
"keywords": [
66
"svg",

0 commit comments

Comments
 (0)