Skip to content

Commit

Permalink
Reduce concurrency when using glibc-based Linux
Browse files Browse the repository at this point in the history
to help prevent memory fragmentation
  • Loading branch information
lovell committed Mar 14, 2021
1 parent 58526cc commit 5a9cc83
Show file tree
Hide file tree
Showing 6 changed files with 35 additions and 6 deletions.
2 changes: 2 additions & 0 deletions docs/changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ Requires libvips v8.10.6

* Reduce the default PNG `compressionLevel` to the more commonly used 6.

* Reduce concurrency on glibc-based Linux when using the default memory allocator to help prevent fragmentation.

## v0.27 - *avif*

Requires libvips v8.10.5
Expand Down
15 changes: 13 additions & 2 deletions lib/utility.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
'use strict';

const events = require('events');
const detectLibc = require('detect-libc');

const is = require('./is');
const sharp = require('../build/Release/sharp.node');

Expand Down Expand Up @@ -84,8 +86,12 @@ cache(true);
/**
* Gets or, when a concurrency is provided, sets
* the number of threads _libvips'_ should create to process each image.
* The default value is the number of CPU cores.
* A value of `0` will reset to this default.
*
* The default value is the number of CPU cores,
* except when using glibc-based Linux without jemalloc,
* where the default is `1` to help reduce memory fragmentation.
*
* A value of `0` will reset this to the number of CPU cores.
*
* The maximum number of images that can be processed in parallel
* is limited by libuv's `UV_THREADPOOL_SIZE` environment variable.
Expand All @@ -103,6 +109,11 @@ cache(true);
function concurrency (concurrency) {
return sharp.concurrency(is.integer(concurrency) ? concurrency : null);
}
/* istanbul ignore next */
if (detectLibc.family === detectLibc.GLIBC && !sharp._isUsingJemalloc()) {
// Reduce default concurrency to 1 when using glibc memory allocator
sharp.concurrency(1);
}

/**
* An EventEmitter that emits a `change` event when a task is either:
Expand Down
1 change: 1 addition & 0 deletions src/sharp.cc
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ Napi::Object init(Napi::Env env, Napi::Object exports) {
exports.Set("libvipsVersion", Napi::Function::New(env, libvipsVersion));
exports.Set("format", Napi::Function::New(env, format));
exports.Set("_maxColourDistance", Napi::Function::New(env, _maxColourDistance));
exports.Set("_isUsingJemalloc", Napi::Function::New(env, _isUsingJemalloc));
exports.Set("stats", Napi::Function::New(env, stats));
return exports;
}
Expand Down
16 changes: 16 additions & 0 deletions src/utilities.cc
Original file line number Diff line number Diff line change
Expand Up @@ -225,3 +225,19 @@ Napi::Value _maxColourDistance(const Napi::CallbackInfo& info) {

return Napi::Number::New(env, maxColourDistance);
}

#if defined(__GNUC__)
// mallctl will be resolved by the runtime linker when jemalloc is being used
extern "C" {
int mallctl(const char *name, void *oldp, size_t *oldlenp, void *newp, size_t newlen) __attribute__((weak));
}
Napi::Value _isUsingJemalloc(const Napi::CallbackInfo& info) {
Napi::Env env = info.Env();
return Napi::Boolean::New(env, mallctl != nullptr);
}
#else
Napi::Value _isUsingJemalloc(const Napi::CallbackInfo& info) {
Napi::Env env = info.Env();
return Napi::Boolean::New(env, false);
}
#endif
1 change: 1 addition & 0 deletions src/utilities.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,5 +24,6 @@ Napi::Value simd(const Napi::CallbackInfo& info);
Napi::Value libvipsVersion(const Napi::CallbackInfo& info);
Napi::Value format(const Napi::CallbackInfo& info);
Napi::Value _maxColourDistance(const Napi::CallbackInfo& info);
Napi::Value _isUsingJemalloc(const Napi::CallbackInfo& info);

#endif // SRC_UTILITIES_H_
6 changes: 2 additions & 4 deletions test/unit/util.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,6 @@
const assert = require('assert');
const sharp = require('../../');

const defaultConcurrency = sharp.concurrency();

describe('Utilities', function () {
describe('Cache', function () {
it('Can be disabled', function () {
Expand Down Expand Up @@ -60,10 +58,10 @@ describe('Utilities', function () {
});
it('Can be reset to default', function () {
sharp.concurrency(0);
assert.strictEqual(defaultConcurrency, sharp.concurrency());
assert.strictEqual(true, sharp.concurrency() > 0);
});
it('Ignores invalid values', function () {
sharp.concurrency(0);
const defaultConcurrency = sharp.concurrency();
sharp.concurrency('spoons');
assert.strictEqual(defaultConcurrency, sharp.concurrency());
});
Expand Down

0 comments on commit 5a9cc83

Please sign in to comment.