Skip to content

Commit

Permalink
fix(core): Use lower threshold for NumericDictionary fast store (#125) (
Browse files Browse the repository at this point in the history
#125)

Summary:
User lower fast store size threhosld for NumericDictionary as per OSS feedback: #125

Closes #125

Reviewed By: twobassdrum

Differential Revision: D61559911

fbshipit-source-id: 91eda9d9d04f9cdc8e9b339c26a8612c34cd82e2
  • Loading branch information
JacksonGL authored and facebook-github-bot committed Aug 21, 2024
1 parent 69e87a7 commit 88bce92
Show file tree
Hide file tree
Showing 13 changed files with 105 additions and 8 deletions.
2 changes: 2 additions & 0 deletions packages/cli/src/commands/MemLabRunCommand.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import TargetFileOption from '../options/heap/TargetFileOption';
import FinalFileOption from '../options/heap/FinalFileOption';
import SnapshotDirectoryOption from '../options/heap/SnapshotDirectoryOption';
import JSEngineOption from '../options/heap/JSEngineOption';
import HeapParserDictFastStoreSizeOption from '../options/heap/HeapParserDictFastStoreSizeOption';

export default class MemLabRunCommand extends BaseCommand {
getCommandName(): string {
Expand Down Expand Up @@ -73,6 +74,7 @@ export default class MemLabRunCommand extends BaseCommand {
new FinalFileOption(),
new SnapshotDirectoryOption(),
new JSEngineOption(),
new HeapParserDictFastStoreSizeOption(),
];
}

Expand Down
2 changes: 2 additions & 0 deletions packages/cli/src/commands/heap/CheckLeakCommand.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ import MLClusteringMaxDFOption from '../../options/MLClusteringMaxDFOption';
import CleanupSnapshotOption from '../../options/heap/CleanupSnapshotOption';
import SetWorkingDirectoryOption from '../../options/SetWorkingDirectoryOption';
import OptionConstant from '../../options/lib/OptionConstant';
import HeapParserDictFastStoreSizeOption from '../../options/heap/HeapParserDictFastStoreSizeOption';

export type CheckLeakCommandOptions = {
isMLClustering?: boolean;
Expand Down Expand Up @@ -148,6 +149,7 @@ or option 2 mentioned above).
new MLClusteringMaxDFOption(),
new CleanupSnapshotOption(),
new SetWorkingDirectoryOption(),
new HeapParserDictFastStoreSizeOption(),
];
}

Expand Down
2 changes: 2 additions & 0 deletions packages/cli/src/commands/heap/DiffLeakCommand.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import SetMaxClusterSampleSizeOption from '../../options/SetMaxClusterSampleSize
import SetTraceContainsFilterOption from '../../options/heap/SetTraceContainsFilterOption';
import SetControlSnapshotOption from '../../options/experiment/SetControlSnapshotOption';
import SetTreatmentSnapshotOption from '../../options/experiment/SetTreatmentSnapshotOption';
import HeapParserDictFastStoreSizeOption from '../../options/heap/HeapParserDictFastStoreSizeOption';

export type WorkDirSettings = {
controlWorkDirs: Array<string>;
Expand Down Expand Up @@ -91,6 +92,7 @@ export default class CheckLeakCommand extends BaseCommand {
new MLClusteringMaxDFOption(),
new SetMaxClusterSampleSizeOption(),
new SetTraceContainsFilterOption(),
new HeapParserDictFastStoreSizeOption(),
];
}

Expand Down
2 changes: 2 additions & 0 deletions packages/cli/src/commands/heap/GetRetainerTraceCommand.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import JSEngineOption from '../../options/heap/JSEngineOption';
import HeapNodeIdOption from '../../options/heap/HeapNodeIdOption';
import SnapshotDirectoryOption from '../../options/heap/SnapshotDirectoryOption';
import {fileManager} from '@memlab/core';
import HeapParserDictFastStoreSizeOption from '../../options/heap/HeapParserDictFastStoreSizeOption';

async function calculateRetainerTrace(): Promise<void> {
const snapshotPath = utils.getSingleSnapshotFileForAnalysis();
Expand Down Expand Up @@ -51,6 +52,7 @@ export default class GetRetainerTraceCommand extends BaseCommand {
new SnapshotDirectoryOption(),
new JSEngineOption(),
new HeapNodeIdOption().required(),
new HeapParserDictFastStoreSizeOption(),
];
}

Expand Down
6 changes: 5 additions & 1 deletion packages/cli/src/commands/heap/HeapAnalysisCommand.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import HelperCommand from '../helper/HelperCommand';
import InitDirectoryCommand from '../InitDirectoryCommand';
import HeapAnalysisPluginOption from '../../options/heap/HeapAnalysisPluginOption';
import {ParsedArgs} from 'minimist';
import HeapParserDictFastStoreSizeOption from '../../options/heap/HeapParserDictFastStoreSizeOption';

export default class RunHeapAnalysisCommand extends BaseCommand {
getCommandName(): string {
Expand All @@ -38,7 +39,10 @@ export default class RunHeapAnalysisCommand extends BaseCommand {
}

getOptions(): BaseOption[] {
return [new HeapAnalysisPluginOption()];
return [
new HeapAnalysisPluginOption(),
new HeapParserDictFastStoreSizeOption(),
];
}

getSubCommands(): BaseCommand[] {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import {fileManager} from '@memlab/core';
import {heapConfig, loadHeapSnapshot} from '@memlab/heap-analysis';
import {CommandDispatcher} from '../../../Dispatcher';
import InteractiveCommandLoader from './InteractiveCommandLoader';
import HeapParserDictFastStoreSizeOption from '../../../options/heap/HeapParserDictFastStoreSizeOption';

export default class InteractiveHeapCommand extends BaseCommand {
getCommandName(): string {
Expand All @@ -46,7 +47,11 @@ export default class InteractiveHeapCommand extends BaseCommand {
}

getOptions(): BaseOption[] {
return [new SnapshotFileOption(), new JSEngineOption()];
return [
new SnapshotFileOption(),
new JSEngineOption(),
new HeapParserDictFastStoreSizeOption(),
];
}

private exitAttempt = 0;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import CliScreen from './ui-components/CliScreen';
import HeapNodeIdOption from '../../../options/heap/HeapNodeIdOption';
import MLClusteringOption from '../../../options/MLClusteringOption';
import SetWorkingDirectoryOption from '../../../options/SetWorkingDirectoryOption';
import HeapParserDictFastStoreSizeOption from '../../../options/heap/HeapParserDictFastStoreSizeOption';

export default class InteractiveHeapViewCommand extends BaseCommand {
getCommandName(): string {
Expand All @@ -53,6 +54,7 @@ export default class InteractiveHeapViewCommand extends BaseCommand {
new HeapNodeIdOption(),
new MLClusteringOption(),
new SetWorkingDirectoryOption(),
new HeapParserDictFastStoreSizeOption(),
];
}

Expand Down
48 changes: 48 additions & 0 deletions packages/cli/src/options/heap/HeapParserDictFastStoreSizeOption.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @format
* @oncall web_perf_infra
*/

import type {ParsedArgs} from 'minimist';
import type {MemLabConfig} from '@memlab/core';
import {BaseOption, utils} from '@memlab/core';
import optionConstants from '../lib/OptionConstant';

export default class HeapParserDictFastStoreSizeOption extends BaseOption {
getOptionName(): string {
return optionConstants.optionNames.HEAP_PARSER_DICT_FAST_STORE_SIZE;
}

getDescription(): string {
return (
'the size threshold for swtiching from fast store to slower store in ' +
'the heap snapshot parser. The default value is 5,000,000. If you get ' +
'the `FATAL ERROR: invalid table size Allocation failed - JavaScript ' +
'heap out of memory` error, try to decrease the threshold here'
);
}

getExampleValues(): string[] {
return ['500000', '1000000'];
}

async parse(config: MemLabConfig, args: ParsedArgs): Promise<void> {
if (this.getOptionName() in args) {
const sizeThreshold = parseInt(args[this.getOptionName()], 10);
if (!isNaN(sizeThreshold)) {
if (sizeThreshold <= 0 || sizeThreshold > 10_000_000) {
utils.haltOrThrow(
`Invalid value for ${this.getOptionName()}: ${sizeThreshold}. ` +
'Valid range is [1, 10_000_000]',
);
}
config.heapParserDictFastStoreSize = sizeThreshold;
}
}
}
}
1 change: 1 addition & 0 deletions packages/cli/src/options/lib/OptionConstant.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ const optionNames = {
FULL: 'full',
HEADFUL: 'headful',
HEAP_ANALYSIS_PLUGIN_FILE: 'analysis-plugin',
HEAP_PARSER_DICT_FAST_STORE_SIZE: 'heap-parser-dict-fast-store-size',
HELP: 'help',
IGNORE_LEAK_CLUSTER_SIZE_BELOW: 'ignore-leak-cluster-size-below',
INTERACTION: 'interaction',
Expand Down
4 changes: 4 additions & 0 deletions packages/core/src/lib/Config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -259,6 +259,7 @@ export class MemLabConfig {
filterTraceByName: Nullable<string>;
skipBrowserCloseWait: boolean;
simplifyCodeSerialization: boolean;
heapParserDictFastStoreSize: number;

constructor(options: ConfigOption = {}) {
// init properties, they can be configured manually
Expand Down Expand Up @@ -508,6 +509,9 @@ export class MemLabConfig {
// object size below the threshold won't be reported
this.unboundSizeThreshold = 10 * 1024;

// threshold for swtiching between fast store and slower store
// of NumericDictionary used by heap snapshot parser
this.heapParserDictFastStoreSize = 5_000_000;
// if true reset the GK list in visit synthesizer
this.resetGK = false;
// default userAgent, if undefined use puppeteer's default value
Expand Down
4 changes: 3 additions & 1 deletion packages/core/src/lib/heap-data/HeapSnapshot.ts
Original file line number Diff line number Diff line change
Expand Up @@ -284,7 +284,9 @@ export default class HeapSnapshot implements IHeapSnapshot {

_buildNodeIdx(): void {
info.overwrite('building node index...');
this._nodeId2NodeIdx = new NumericDictionary(this._nodeCount);
this._nodeId2NodeIdx = new NumericDictionary(this._nodeCount, {
fastStoreSize: config.heapParserDictFastStoreSize,
});
// iterate over each node
const nodeValues = this.snapshot.nodes;
const nodeFieldsCount = this._nodeFieldsCount;
Expand Down
27 changes: 22 additions & 5 deletions packages/core/src/lib/heap-data/utils/NumericDictionary.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,23 +14,40 @@
import {Nullable} from '../../Types';

// if the number is larger than this, we will use the slow store
const MAX_FAST_STORE_SIZE = 50_000_000;
const DEFAULT_FAST_STORE_SIZE = 5_000_000;
// maxmimum fast store size allowed to set to NumericDictionary,
// if the store size is bigger than this, it will cause exceptions
// in JS engines on some platforms
const MAX_FAST_STORE_SIZE = 10_000_000;

type DirectMap = Map<number, number>;
type IndirectMap = Map<number, Nullable<DirectMap>>;

export type NumericDictOptions = {
fastStoreSize?: number;
};

export default class NumericDictionary {
private useFastStore = true;
private fastStore: Nullable<DirectMap> = null;
private slowStore: Nullable<IndirectMap> = null;
private numberOfShards = 1;
private fastStoreSize = DEFAULT_FAST_STORE_SIZE;

constructor(size: number) {
this.useFastStore = size <= MAX_FAST_STORE_SIZE;
constructor(size: number, options: NumericDictOptions = {}) {
if (options.fastStoreSize != null) {
if (
options.fastStoreSize > 0 &&
options.fastStoreSize <= MAX_FAST_STORE_SIZE
) {
this.fastStoreSize = options.fastStoreSize;
}
}
this.useFastStore = size <= this.fastStoreSize;
if (this.useFastStore) {
this.fastStore = new Map();
} else {
this.numberOfShards = Math.ceil(size / MAX_FAST_STORE_SIZE);
this.numberOfShards = Math.ceil(size / this.fastStoreSize);
this.slowStore = new Map();
}
}
Expand All @@ -40,7 +57,7 @@ export default class NumericDictionary {
}

getShard(key: number): number {
return Math.floor(key / MAX_FAST_STORE_SIZE);
return Math.floor(key / this.fastStoreSize);
}

has(key: number): boolean {
Expand Down
6 changes: 6 additions & 0 deletions website/docs/cli/CLI-commands.md
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@ memlab find-leaks --work-dir /memlab/working/dir/generated/by/memlab/
* **`--ml-clustering-max-df`**: set percentage based max document frequency for limiting the terms that appear too often
* **`--clean-up-snapshot`**: clean up heap snapshots after running
* **`--work-dir`**: set the working directory of the current run
* **`--heap-parser-dict-fast-store-size`**: the size threshold for swtiching from fast store to slower store in the heap snapshot parser. The default value is 5,000,000. If you get the `FATAL ERROR: invalid table size Allocation failed - JavaScript heap out of memory` error, try to decrease the threshold here
* **`--help`**, **`-h`**: print helper text
* **`--verbose`**, **`-v`**: show more details
* **`--sc`**: set to continuous test mode
Expand Down Expand Up @@ -135,6 +136,7 @@ memlab diff-leaks
* **`--ml-clustering-max-df`**: set percentage based max document frequency for limiting the terms that appear too often
* **`--max-cluster-sample-size`**: specify the max number of leak traces as input to leak trace clustering algorithm. Big sample size will preserve more complete inforrmation, but may risk out-of-memory crash.
* **`--trace-contains`**: set the node name or edge name to filter leak traces that contain the name
* **`--heap-parser-dict-fast-store-size`**: the size threshold for swtiching from fast store to slower store in the heap snapshot parser. The default value is 5,000,000. If you get the `FATAL ERROR: invalid table size Allocation failed - JavaScript heap out of memory` error, try to decrease the threshold here
* **`--work-dir`**: set the working directory of the current run
* **`--help`**, **`-h`**: print helper text
* **`--verbose`**, **`-v`**: show more details
Expand Down Expand Up @@ -162,6 +164,7 @@ memlab trace --node-id=128127
* **`--snapshot-dir`**: set directory path containing all heap snapshots under analysis
* **`--engine`**: set the JavaScript engine (default to V8)
* **`--node-id`**: set heap node ID
* **`--heap-parser-dict-fast-store-size`**: the size threshold for swtiching from fast store to slower store in the heap snapshot parser. The default value is 5,000,000. If you get the `FATAL ERROR: invalid table size Allocation failed - JavaScript heap out of memory` error, try to decrease the threshold here
* **`--help`**, **`-h`**: print helper text
* **`--verbose`**, **`-v`**: show more details
* **`--sc`**: set to continuous test mode
Expand All @@ -178,6 +181,7 @@ memlab analyze <PLUGIN_NAME> [PLUGIN_OPTIONS]

**Options**:
* **`--analysis-plugin`**: specify the external heap analysis plugin file (must be a vanilla JS file ended with `Analysis.js` suffix)
* **`--heap-parser-dict-fast-store-size`**: the size threshold for swtiching from fast store to slower store in the heap snapshot parser. The default value is 5,000,000. If you get the `FATAL ERROR: invalid table size Allocation failed - JavaScript heap out of memory` error, try to decrease the threshold here
* **`--work-dir`**: set the working directory of the current run
* **`--help`**, **`-h`**: print helper text
* **`--verbose`**, **`-v`**: show more details
Expand Down Expand Up @@ -421,6 +425,7 @@ memlab heap --snapshot <HEAP_SNAPSHOT_FILE>
**Options**:
* **`--snapshot`**: set file path of the heap snapshot under analysis
* **`--engine`**: set the JavaScript engine (default to V8)
* **`--heap-parser-dict-fast-store-size`**: the size threshold for swtiching from fast store to slower store in the heap snapshot parser. The default value is 5,000,000. If you get the `FATAL ERROR: invalid table size Allocation failed - JavaScript heap out of memory` error, try to decrease the threshold here
* **`--help`**, **`-h`**: print helper text
* **`--verbose`**, **`-v`**: show more details
* **`--sc`**: set to continuous test mode
Expand All @@ -441,6 +446,7 @@ memlab view-heap --snapshot <HEAP_SNAPSHOT_FILE>
* **`--node-id`**: set heap node ID
* **`--ml-clustering`**: use machine learning algorithms for clustering leak traces (by default, traces are clustered by heuristics)
* **`--work-dir`**: set the working directory of the current run
* **`--heap-parser-dict-fast-store-size`**: the size threshold for swtiching from fast store to slower store in the heap snapshot parser. The default value is 5,000,000. If you get the `FATAL ERROR: invalid table size Allocation failed - JavaScript heap out of memory` error, try to decrease the threshold here
* **`--help`**, **`-h`**: print helper text
* **`--verbose`**, **`-v`**: show more details
* **`--sc`**: set to continuous test mode
Expand Down

0 comments on commit 88bce92

Please sign in to comment.