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

build: default to max jobs #1771

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open

Conversation

dsanders11
Copy link
Contributor

Checklist
Description of change

This PR probably needs a little discussion on potential ramifications of the change.

Currently, node-gyp defaults to a single job, which means it only uses a single core out of the box. This causes unnecessarily slow builds on machines with multiple cores. While any given project can manually use this setting, I personally think a more reasonable default is to use all available cores. Otherwise build times are just longer than they need to be, especially with 4+ cores becoming more common. Applying this change will speed up builds for a lot of people.

Any arguments for why this may not be a reasonable change? Is using more CPU during install an adverse change for some users?

@rvagg
Copy link
Member

rvagg commented Jun 20, 2019

Biggest problem I have with this is that I've seen require('os').cpus().length behave very poorly in some situations. The most notable being SmartOS containers which usually report the host CPUs but not what the container is constrained to (in nodejs/build we have to hard-wire a -j value just for building Node because you can't infer it easily!).
There's also the interesting situation of many-core ARM servers which are coming on to market now. In our Node CI infra we have 32-core machines so this change would result in a -j32. They can handle it but I/O becomes extremely saturated. x64 CPUs are heading into this same territory.
I think I'd be OK with something like Math.min(4, require('os').cpus().length as the default. 4 is a bit arbitrary, but it's in the safer region than a completely unconstrained number.

@dsanders11
Copy link
Contributor Author

dsanders11 commented Jun 20, 2019

@rvagg, thanks for the feedback. Those are both great points.

I'll add my own negative having played with this some more. On memory-constrained systems, a job per core might run it out of memory entirely, and could cause an OOM crash. A small system like the Raspberry Pi has 4 cores, but only 1 GB of memory, and each job can use a decent amount of memory.

What about reading a value from environment variable, something like NODE_GYP_JOBS? My biggest gripe is that in a project with multiple native modules being built with node-gyp, there's no way for me to use more jobs across all of them, leading to hacks like editing their package.json manually.

Then the default could be changed to a heuristic that also takes memory into account to reach a reasonable default (which can be easily overridden with the environment variable):

const os = require('os');

function defaultJobs () {
    const cpus = os.cpus().length;
    const totalmem = os.totalmem();
    const halfGig = 0.5*Math.pow(1024, 3);

    // Rule of thumb, give each job a half gig of memory
    return Math.min(4, cpus, Math.round(totalmem/halfGig));
}

@richardlau
Copy link
Member

What about reading a value from environment variable, something like NODE_GYP_JOBS?

AFAIK all options supported by node-gyp can be set as npm_config_* environment variables so setting npm_config_jobs should work right now without further code changes.

node-gyp/lib/node-gyp.js

Lines 137 to 150 in b2e5cf0

var npm_config_prefix = 'npm_config_'
Object.keys(process.env).forEach(function (name) {
if (name.indexOf(npm_config_prefix) !== 0) return
var val = process.env[name]
if (name === npm_config_prefix + 'loglevel') {
log.level = val
} else {
// add the user-defined options to the config
name = name.substring(npm_config_prefix.length)
// gyp@741b7f1 enters an infinite loop when it encounters
// zero-length options so ensure those don't get through.
if (name) this.opts[name] = val
}
}, this)

@dsanders11
Copy link
Contributor Author

@richardlau, you're right, I'd forgotten about that. The name is a bit awkward (especially if you're using yarn), but you're right, npm_config_jobs should provide that functionality.

So I guess the discussion is back to providing a new default that's greater than 1 job.

@rvagg
Copy link
Member

rvagg commented Jun 20, 2019

the thing about npm_config_jobs is that you can just npm config set jobs 2 to make it global, no more hackery required

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

Successfully merging this pull request may close these issues.

None yet

4 participants