diff --git a/docs/shared/recipes/running-tasks/run-tasks-in-parallel.md b/docs/shared/recipes/running-tasks/run-tasks-in-parallel.md index 1b57c771e2a4c1..244b49129afabd 100644 --- a/docs/shared/recipes/running-tasks/run-tasks-in-parallel.md +++ b/docs/shared/recipes/running-tasks/run-tasks-in-parallel.md @@ -11,6 +11,12 @@ If you want to increase the number of processes running tasks to, say, 5 (by def npx nx build myapp --parallel=5 ``` +You can also set parallel based on the percentage of the number of logical CPUs. + +```shell +npx nx build myapp --parallel=50% +``` + Note, you can also change the default in `nx.json`, like this: {% tabs %} diff --git a/packages/nx/src/command-line/yargs-utils/shared-options.spec.ts b/packages/nx/src/command-line/yargs-utils/shared-options.spec.ts index 9cdb05d6d32a1d..6f1b4e738f3b3e 100644 --- a/packages/nx/src/command-line/yargs-utils/shared-options.spec.ts +++ b/packages/nx/src/command-line/yargs-utils/shared-options.spec.ts @@ -1,6 +1,7 @@ import * as yargs from 'yargs'; import { + readParallelFromArgsAndEnv, withAffectedOptions, withOutputStyleOption, withRunManyOptions, @@ -130,3 +131,47 @@ describe('shared-options', () => { ); }); }); + +describe('readParallelFromArgsAndEnv', () => { + it('default parallel should be 3', () => { + const result = readParallelFromArgsAndEnv({ parallel: 'true' }); + expect(result).toEqual(3); + }); + + it('use maxParallel', () => { + const result = readParallelFromArgsAndEnv({ + parallel: '', + maxParallel: '4', + }); + expect(result).toEqual(4); + }); + + it('use max-parallel', () => { + const result = readParallelFromArgsAndEnv({ + parallel: '', + 'max-parallel': '5', + }); + expect(result).toEqual(5); + }); + + it('should read parallel 6', () => { + const result = readParallelFromArgsAndEnv({ + parallel: '6', + }); + expect(result).toEqual(6); + }); + + it('0% parallel should be 1', () => { + const result = readParallelFromArgsAndEnv({ + parallel: '0%', + }); + expect(result).toEqual(1); + }); + + it('100% parallel should not be less than 1', () => { + const result = readParallelFromArgsAndEnv({ + parallel: '100%', + }); + expect(result).toBeGreaterThanOrEqual(1); + }); +}); diff --git a/packages/nx/src/command-line/yargs-utils/shared-options.ts b/packages/nx/src/command-line/yargs-utils/shared-options.ts index 33ce973661bf02..d646b1b1923918 100644 --- a/packages/nx/src/command-line/yargs-utils/shared-options.ts +++ b/packages/nx/src/command-line/yargs-utils/shared-options.ts @@ -2,6 +2,7 @@ import { readNxJson } from '../../config/nx-json'; import { shouldUseTui } from '../../tasks-runner/is-tui-enabled'; import { NxArgs } from '../../utils/command-line-utils'; import { Argv, coerce, ParserConfigurationOptions } from 'yargs'; +import { availableParallelism, cpus } from 'node:os'; interface ExcludeOptions { exclude: string[]; @@ -380,17 +381,17 @@ export function readParallelFromArgsAndEnv(args: { [k: string]: any }) { args['parallel'] === 'true' || args['parallel'] === true || args['parallel'] === '' || - // dont require passing --parallel if NX_PARALLEL is set, but allow overriding it + // don't require passing --parallel if NX_PARALLEL is set, but allow overriding it (process.env.NX_PARALLEL && args['parallel'] === undefined) ) { - return Number( + return concurrency( args['maxParallel'] || args['max-parallel'] || process.env.NX_PARALLEL || - 3 + '3' ); } else if (args['parallel'] !== undefined) { - return Number(args['parallel']); + return concurrency(args['parallel']); } } @@ -407,3 +408,13 @@ const coerceTuiAutoExit = (value: string) => { } throw new Error(`Invalid value for --tui-auto-exit: ${value}`); }; + +function concurrency(val: string | number) { + let parallel = typeof val === 'number' ? val : parseInt(val); + + if (typeof val === 'string' && val.at(-1) === '%') { + const maxCores = availableParallelism?.() ?? cpus().length; + parallel = (maxCores * parallel) / 100; + } + return Math.max(1, Math.floor(parallel)); +}