Use native maps when they're available - #12715

Merged
30 commits merged into
masterfrom
map5
Jan 17, 2017
Merged

Use native maps when they're available#12715
30 commits merged into
masterfrom
map5

Conversation

@ghost

@ghostDeleted user (ghost) commented Dec 7, 2016

Copy link
Copy Markdown

Similar to #11354, but simpler.

  • Keeps using Map<T> instead of Map<K, V>, with K assumed to be string. For number-keyed maps we now use sparse arrays.
  • Does not include Set -- this can be done later.
  • Does not include sortInV8ObjectInsertionOrder; changes baselines instead. We won't be able to pass all tests when using the shim map, but we no longer run tests on node 0.10 anyway.

Added functions

NameUse
arrayFromConverts anIterator to an Array. arrayFrom(map.keys()) replaces Object.keys(map). arrayFrom(map.values()) also lets us remove reduceProperties, which was only used to compute that.
forEachEntry, forEachKeyThese replace for-in loops with early termination, forEachProperty, and someProperties.
copyEntriesReplaces copyProperties.
createMultiMapReplaces multiMapAdd and multiMapRemove.
mapsAreEqualReplaces a use of equalOwnProperties. Used only in reuseProgramStructure.ts, so moved it there.
multiMapSparseArrayAddReplaces a use of multiMapAdd on what is now a sparse array. Used only in factory.ts, so moved it there.
mapEntriesReplaces mapObject.

We also get rid of isEmpty and replace with map.size === 0.

@ghost

Deleted user (ghost) commented Dec 8, 2016

Copy link
Copy Markdown
Author

Comparing this branch to master, we now use 5% less memory and time.

Monaco

node (v6.9.2, x64)
ProjectBaselineCurrentDeltaBestWorst
Memory used381,190k (± 0.01%)358,147k (± 0.02%)-23,043k (- 6.04%)358,085k358,209k
Parse Time2.44s (± 0.94%)2.35s (± 1.23%)-0.09s (- 3.52%)2.33s2.39s
Bind Time0.93s (± 2.62%)0.77s (± 1.71%)-0.17s (- 17.94%)0.75s0.78s
Check Time4.53s (± 3.18%)4.33s (± 3.47%)-0.21s (- 4.53%)4.23s4.55s
Emit Time2.09s (± 0.98%)2.10s (± 3.31%)+0.01s (+ 0.55%)2.04s2.17s
Total Time9.99s (± 1.94%)9.55s (± 2.49%)-0.45s (- 4.46%)9.36s9.88s
node (v6.9.2, x86)
ProjectBaselineCurrentDeltaBestWorst
Memory used203,018k (± 0.01%)191,475k (± 0.02%)-11,543k (- 5.69%)191,450k191,522k
Parse Time2.30s (± 0.52%)2.21s (± 0.87%)-0.10s (- 4.19%)2.18s2.22s
Bind Time0.75s (± 1.50%)0.64s (± 2.43%)-0.11s (- 14.63%)0.62s0.65s
Check Time3.69s (± 2.45%)3.41s (± 1.28%)-0.29s (- 7.81%)3.35s3.45s
Emit Time2.09s (± 3.24%)2.10s (± 0.63%)+0.01s (+ 0.50%)2.08s2.11s
Total Time8.83s (± 1.25%)8.34s (± 0.77%)-0.48s (- 5.49%)8.27s8.40s
tsc (x86)
ProjectBaselineCurrentDeltaBestWorst
Parse Time1.82s (± 0.83%)1.72s (± 1.70%)-0.10s (- 5.39%)1.69s1.75s
Bind Time0.77s (± 5.13%)0.53s (± 2.82%)-0.23s (- 30.46%)0.52s0.55s
Check Time3.38s (± 1.26%)3.04s (± 3.24%)-0.34s (- 10.03%)2.94s3.15s
Emit Time6.08s (± 3.39%)6.09s (± 5.13%)+0.00s (+ 0.02%)5.93s6.57s
Total Time12.05s (± 2.00%)11.38s (± 2.47%)-0.67s (- 5.54%)11.15s11.78s

TFS

node (v6.9.2, x64)
ProjectBaselineCurrentDeltaBestWorst
Memory used327,608k (± 0.01%)309,067k (± 0.02%)-18,541k (- 5.66%)308,995k309,106k
Parse Time1.52s (± 0.74%)1.44s (± 1.04%)-0.07s (- 4.85%)1.42s1.45s
Bind Time0.75s (± 0.79%)0.65s (± 2.30%)-0.10s (- 13.30%)0.64s0.67s
Check Time3.85s (± 0.88%)3.78s (± 1.25%)-0.07s (- 1.75%)3.73s3.82s
Emit Time1.79s (± 0.65%)1.93s (± 2.76%)+0.14s (+ 7.60%)1.89s2.00s
Total Time7.91s (± 0.48%)7.80s (± 1.08%)-0.11s (- 1.34%)7.73s7.91s
node (v6.9.2, x86)
ProjectBaselineCurrentDeltaBestWorst
Memory used174,790k (± 0.01%)165,324k (± 0.03%)-9,466k (- 5.42%)165,287k165,405k
Parse Time1.44s (± 0.81%)1.32s (± 1.90%)-0.11s (- 7.74%)1.31s1.36s
Bind Time0.64s (± 1.95%)0.55s (± 3.77%)-0.09s (- 13.64%)0.53s0.58s
Check Time2.96s (± 0.68%)2.70s (± 1.02%)-0.26s (- 8.81%)2.67s2.73s
Emit Time1.56s (± 0.71%)1.55s (± 5.97%)-0.01s (- 0.51%)1.50s1.69s
Total Time6.59s (± 0.65%)6.13s (± 1.85%)-0.46s (- 7.03%)6.06s6.30s
tsc (x86)
ProjectBaselineCurrentDeltaBestWorst
Parse Time1.27s (± 3.07%)1.11s (± 0.87%)-0.15s (- 12.16%)1.10s1.12s
Bind Time0.54s (± 5.05%)0.56s (± 4.23%)+0.02s (+ 3.49%)0.55s0.60s
Check Time2.89s (± 3.78%)2.66s (± 2.91%)-0.24s (- 8.15%)2.55s2.73s
Emit Time3.67s (± 1.73%)3.56s (± 2.75%)-0.11s (- 3.00%)3.48s3.68s
Total Time8.37s (± 2.42%)7.89s (± 2.01%)-0.48s (- 5.72%)7.70s8.04s

Older node versions

Surprisingly, memory is reduced for node v0.12.17 (x86)! I'm not sure why. (EDIT: node 0.12 provides Map; I'll have to go even earlier to test with shims.)

monaco
ProjectBaselineCurrentDeltaBestWorst
Memory used224,254k (± 0.22%)194,721k (± 0.32%)-29,533k (- 13.17%)194,176k195,473k
Parse Time2.49s (± 0.79%)2.40s (± 1.20%)-0.10s (- 3.95%)2.38s2.44s
Bind Time0.80s (± 1.35%)0.66s (± 1.97%)-0.14s (- 17.16%)0.65s0.68s
Check Time4.70s (± 1.24%)4.99s (± 2.81%)+0.29s (+ 6.11%)4.88s5.20s
Emit Time2.15s (± 1.22%)2.24s (± 4.84%)+0.09s (+ 4.29%)2.09s2.31s
Total Time10.15s (± 0.67%)10.29s (± 1.67%)+0.14s (+ 1.43%)10.08s10.49s
TFS
ProjectBaselineCurrentDeltaBestWorst
Memory used187,770k (± 0.06%)166,329k (± 0.16%)-21,441k (- 11.42%)166,051k166,633k
Parse Time1.46s (± 1.12%)1.39s (± 3.49%)-0.07s (- 4.60%)1.35s1.46s
Bind Time0.67s (± 0.90%)0.58s (± 1.09%)-0.09s (- 13.71%)0.57s0.58s
Check Time3.52s (± 1.13%)3.22s (± 1.09%)-0.30s (- 8.44%)3.19s3.26s
Emit Time1.96s (± 2.11%)1.85s (± 2.01%)-0.11s (- 5.76%)1.81s1.90s
Total Time7.60s (± 0.91%)7.04s (± 1.01%)-0.56s (- 7.42%)6.98s7.14s

Comment threadsrc/compiler/types.ts Outdated
export interface Map<T> extends MapLike<T> {
__mapBrand: any;
/** It's allowed to get/set into a map with numbers. However, when iterating, you may get strings back due to the shim being an ordinary object (which only allows string keys). */
export type MapKey = string | number;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should only allow strings for the keys of Map<T> and go with Anders Hejlsberg (@ahejlsberg)'s suggestion to use sparse arrays for "maps" with only numeric keys.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My test shows a number-keyed Map to be slightly more efficient.

Comment threadsrc/compiler/core.ts Outdated
const map: Map<T> = createObject(null); // tslint:disable-line:no-null-keyword
const createObject = Object.create;
/** Create a MapLike with good performance. Prefer this over a literal `{}`. */
export function createMapLike<T>(): MapLike<T> {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd call this createDictionaryObject and keep it internal to the module (don't export it). We only use it in four places across all of src/ and those cases don't look like they'd suffer from just using {}.

@ghostDeleted user (ghost)Dec 12, 2016

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If someone modifies Object.prototype that will cause problems for {}. Isn't that why we have this function?

Comment threadsrc/compiler/core.ts
const MapCtr = usingNativeMaps ? Map : shimMap();

// Keep the class inside a function so it doesn't get compiled if it's not used.
function shimMap(): { new<T>(): Map<T> } {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you want the Map shim to have support for keys/values/entries iterators, take a look at https://gist.github.com/rbuckton/06d2c79bea46778f9e8bbeca77292087. That way there is less differentiation between what we need out of a native Map and what our shim provides.

Comment threadsrc/compiler/core.ts Outdated
export function someProperties<T>(map: Map<T>, predicate?: (value: T, key: string) => boolean) {
for (const key in map) {
if (!predicate || predicate(map[key], key)) return true;
export const forEachInMap: <T, U>(map: Map<T>, callback: (value: T, key: string) => U | undefined) => U | undefined = usingNativeMaps

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we use the MapIterator shim in my gist/comment above, then we don't need to differentiate.

Comment threadsrc/compiler/core.ts Outdated
: <T, U>(map: ShimMap<T>, callback: (value: T, key: string) => U | undefined) => map.forEachInMap(callback);

/** `forEachInMap` for just keys. */
export function forEachKeyInMap<T>(map: Map<{}>, callback: (key: string) => T | undefined): T | undefined {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it be more efficient to iterate over map.keys() and avoid the closure?

const scriptInfo = this.getScriptInfo(fileName);
const preprocessInfo = ts.preProcessFile(scriptInfo.content, /*readImportFiles*/ true);
const imports = ts.createMap<string>();
const imports = ts.createMapLike<string>();

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should just use {} for these cases.

Comment threadsrc/harness/unittests/session.ts Outdated
if (response.request_seq in this.callbacks) {
this.callbacks[response.request_seq](response);
delete this.callbacks[response.request_seq];
const handler = this.callbacks.get(response.request_seq);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Either callbacks should be a sparse array, or we should coerce response.request_seq to a string.


export class Callbacks {
private map: { [n: number]: TimeOutCallback } = {};
private map = createMap<TimeOutCallback>();

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use a sparse array here.

this.symbolIdToActionMap[symbolId] = [newAction];
const actions = this.symbolIdToActionMap.get(symbolId);
if (!actions) {
this.symbolIdToActionMap.set(symbolId, [newAction]);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be a sparse array.

Comment threadsrc/services/services.ts Outdated

function getDeclarations(name: string) {
return result[name] || (result[name] = []);
return result.get(name) || set(result, name, []);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just use multiple lines so we don't need to use set.

Comment threadsrc/compiler/core.ts Outdated
return map;
}

export const sparseArray: <T>() => SparseArray<T> = createDictionaryObject;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is the purpose of this? This isn't a sparse array.

Comment threadsrc/compiler/checker.ts Outdated
}
}
return false;
return someInMap(moduleSymbol.exports, (_, id) => id !== "export=");

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not just use forEachInMap. The someInMap function seems only marginally useful.

Comment threadsrc/compiler/checker.ts Outdated
symbolLinks.exportsSomeValue = hasExportAssignment
? !!(moduleSymbol.flags & SymbolFlags.Value)
: forEachProperty(getExportsOfModule(moduleSymbol), isValue);
: someInMap(getExportsOfModule(moduleSymbol), isValue);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not just use forEachInMap?

Comment threadsrc/compiler/core.ts Outdated
return map;
}

export const sparseArray: <T>() => SparseArray<T> = createDictionaryObject;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I know you ran some benchmarks regarding the performance of objects vs sparse arrays, but unless the performance difference is fairly significant for our use cases, I'd rather we just continue to leverage actual arrays for sparse arrays. If the difference is significant, then we should use a different name for this function and type.

Comment threadsrc/compiler/core.ts Outdated
}

/** Whether `predicate` is true for some entry in the map. */
export function someInMap<T>(map: Map<T>, predicate: (value: T, key: string) => boolean): boolean {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This function seems unnecessary, forEachInMap serves the same purpose.

Comment threadsrc/compiler/core.ts Outdated
}

/** `someInMap` for just keys. */
export function someKeyInMap(map: Map<{}>, predicate: (key: string) => boolean): boolean {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This also seems like overkill as we could just use forEachInMap and ignore the first argument.

Comment threadsrc/compiler/core.ts Outdated
}

/** Copy entries from `source` to `target`. */
export function copyMapEntries<T>(source: Map<T>, target: Map<T>): void {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: Consider a simpler name like copyEntries.

Comment threadsrc/compiler/core.ts Outdated
return values;
}

export function multiMapSparseArrayAdd<V>(map: SparseArray<V[]>, key: number, value: V): V[] {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is only used in factory.ts. Why not just use multiMapAdd and cast the key to string?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is hard to do since we return the value as a SparseArray, so we'd have to add as any as string at all the external uses too. At least I've moved this function to factory.ts.

Comment threadsrc/compiler/core.ts Outdated
*/
export function multiMapAdd<V>(map: Map<V[]>, key: string | number, value: V): V[] {
const values = map[key];
export function multiMapAdd<V>(map: Map<V[]>, key: string, value: V): V[] {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe we should consider adding a createMultiMap<T> that returns a MultiMap<T>:

interfaceMultiMap<T>extendsMap<T[]>{add(key: string,value: T): T[];remove(key: string,value: T): void;}functioncreateMultiMap<T>(){constmap=<MultiMap<T>>createMap<T[]>();map.add=multiMapAdd;map.remove=multiMapRemove;returnmap;}functionmultiMapAdd<T>(this: MultiMap<T>,key: string,value: T){ ... }functionmultiMapRemove<T>(this: MultiMap<T>,key: string,value: T){ ... }

Comment threadsrc/compiler/core.ts Outdated
let result: U;
for (const key in map) {
if (result = callback(map[key], key)) break;
function arrayFrom<T>(iterator: Iterator<T>): T[] {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe we should just export this and replace all references to keysOfMap and valuesOfMap.

@ghost

Deleted user (ghost) commented Dec 28, 2016

Copy link
Copy Markdown
Author

Not a big fan of 2e6f369 as it would make me suspicious of every array in the codebase.

@rbucktonRon Buckton (rbuckton) left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks good, though we should wait on Anders Hejlsberg (@ahejlsberg)'s feedback before merging.

@rbuckton

Copy link
Copy Markdown
Contributor

Andy (Andrewkraft) (@Andy-MS) I would recommend you update the description of the PR with respect to the final set of new functions added to support using native maps.

@ghost

Copy link
Copy Markdown
Author

Ron Buckton (@rbuckton) Done. And just to be sure, I ran baselines again. Results are still good.

Monaco

node (v0.12.17, x86)

ProjectBaselineCurrentDeltaBestWorst
Memory used224,333k (± 0.05%)190,498k (± 0.19%)-33,835k (- 15.08%)190,298k191,058k
Parse Time1.93s (± 1.50%)1.83s (± 1.40%)-0.10s (- 4.96%)1.80s1.86s
Bind Time0.76s (± 1.09%)0.71s (± 4.64%)-0.05s (- 6.54%)0.68s0.76s
Check Time5.59s (± 5.16%)5.42s (± 4.48%)-0.17s (- 3.13%)5.15s5.72s
Emit Time2.32s (± 4.21%)2.31s (± 7.73%)-0.01s (- 0.50%)2.20s2.56s
Total Time10.60s (± 2.98%)10.26s (± 4.10%)-0.33s (- 3.14%)10.01s10.90s

node (v6.9.2, x64)

ProjectBaselineCurrentDeltaBestWorst
Memory used381,413k (± 0.01%)357,526k (± 0.02%)-23,887k (- 6.26%)357,458k357,590k
Parse Time2.08s (± 1.02%)2.17s (± 25.08%)+0.08s (+ 3.98%)1.93s3.01s
Bind Time1.00s (± 2.21%)0.85s (± 5.81%)-0.15s (- 15.27%)0.81s0.92s
Check Time4.85s (± 1.50%)4.33s (± 4.81%)-0.51s (- 10.58%)4.12s4.53s
Emit Time2.20s (± 1.30%)2.23s (± 2.19%)+0.03s (+ 1.34%)2.16s2.26s
Total Time10.13s (± 0.81%)9.58s (± 5.42%)-0.55s (- 5.46%)9.04s10.29s

node (v6.9.2, x86)

ProjectBaselineCurrentDeltaBestWorst
Memory used203,159k (± 0.00%)190,871k (± 0.01%)-12,288k (- 6.05%)190,849k190,883k
Parse Time1.94s (± 1.41%)1.81s (± 1.00%)-0.13s (- 6.56%)1.79s1.83s
Bind Time0.80s (± 2.36%)0.66s (± 0.78%)-0.14s (- 17.35%)0.66s0.67s
Check Time3.79s (± 0.95%)3.18s (± 2.62%)-0.61s (- 16.12%)3.12s3.30s
Emit Time2.11s (± 1.47%)1.93s (± 0.90%)-0.17s (- 8.15%)1.91s1.95s
Total Time8.63s (± 0.79%)7.58s (± 0.87%)-1.05s (- 12.12%)7.53s7.68s

tsc (x86)

ProjectBaselineCurrentDeltaBestWorst
Parse Time1.62s (± 1.56%)1.49s (± 1.64%)-0.13s (- 8.05%)1.46s1.51s
Bind Time0.71s (± 3.11%)0.54s (± 4.08%)-0.17s (- 24.20%)0.52s0.57s
Check Time3.91s (± 1.49%)3.36s (± 2.07%)-0.55s (- 14.11%)3.29s3.45s
Emit Time5.99s (± 2.95%)5.91s (± 11.64%)-0.08s (- 1.35%)5.59s6.97s
Total Time12.23s (± 1.75%)11.29s (± 6.91%)-0.94s (- 7.67%)10.92s12.50s

TFS

node (v0.12.17, x86)

ProjectBaselineCurrentDeltaBestWorst
Memory used187,646k (± 0.06%)166,272k (± 0.13%)-21,374k (- 11.39%)165,952k166,408k
Parse Time1.35s (± 0.71%)1.28s (± 1.48%)-0.07s (- 5.05%)1.26s1.30s
Bind Time0.70s (± 1.22%)0.61s (± 1.58%)-0.09s (- 13.45%)0.60s0.62s
Check Time3.71s (± 0.72%)3.46s (± 1.46%)-0.25s (- 6.87%)3.42s3.53s
Emit Time1.97s (± 2.05%)1.93s (± 0.98%)-0.04s (- 2.16%)1.91s1.95s
Total Time7.73s (± 0.71%)7.27s (± 0.69%)-0.46s (- 5.96%)7.23s7.34s

node (v6.9.2, x64)

ProjectBaselineCurrentDeltaBestWorst
Memory used327,882k (± 0.01%)308,975k (± 0.01%)-18,907k (- 5.77%)308,943k308,995k
Parse Time1.52s (± 0.46%)1.42s (± 1.05%)-0.10s (- 6.63%)1.41s1.44s
Bind Time0.80s (± 0.67%)0.68s (± 0.76%)-0.12s (- 15.04%)0.67s0.68s
Check Time4.34s (± 0.90%)3.72s (± 4.74%)-0.62s (- 14.23%)3.60s3.99s
Emit Time1.98s (± 1.32%)2.17s (± 5.77%)+0.19s (+ 9.37%)1.99s2.27s
Total Time8.64s (± 0.69%)7.99s (± 1.32%)-0.65s (- 7.53%)7.85s8.07s

node (v6.9.2, x86)

ProjectBaselineCurrentDeltaBestWorst
Memory used174,963k (± 0.01%)165,150k (± 0.02%)-9,813k (- 5.61%)165,127k165,195k
Parse Time1.41s (± 0.77%)1.30s (± 2.38%)-0.10s (- 7.29%)1.28s1.35s
Bind Time0.67s (± 0.93%)0.58s (± 3.34%)-0.10s (- 14.48%)0.56s0.60s
Check Time3.18s (± 0.58%)2.81s (± 1.62%)-0.37s (- 11.52%)2.76s2.87s
Emit Time1.62s (± 0.66%)1.62s (± 3.73%)+0.00s (+ 0.06%)1.57s1.68s
Total Time6.88s (± 0.50%)6.32s (± 1.58%)-0.57s (- 8.24%)6.20s6.43s

tsc (x86)

ProjectBaselineCurrentDeltaBestWorst
Parse Time1.23s (± 2.63%)1.06s (± 1.23%)-0.16s (- 13.39%)1.05s1.08s
Bind Time0.58s (± 1.82%)0.51s (± 2.55%)-0.07s (- 11.46%)0.50s0.53s
Check Time3.25s (± 1.51%)2.76s (± 2.30%)-0.49s (- 15.15%)2.71s2.83s
Emit Time3.58s (± 1.51%)3.49s (± 4.20%)-0.09s (- 2.45%)3.39s3.70s
Total Time8.63s (± 1.20%)7.82s (± 2.28%)-0.81s (- 9.38%)7.70s8.09s

Comment threadsrc/compiler/core.ts Outdated
}

/** Create a new map. If a template object is provided, the map will copy entries from it. */
export function createMap<T>(template?: MapLike<T>): Map<T> {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How often do we actually pass a template to createMap? I seem to remember that functions containing for-in loops are not optimized by V8, so having this optional template stuff may be costly. Perhaps filling a map from a template should just be a separate function.

Comment threadsrc/compiler/checker.ts Outdated
target[id] = source[id];
if (!source) return;

source.forEach((sourceSymbol, id) => {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe use source && source.forEach(...) and get rid of the if (!source) return above.

@@ -1,4 +1,4 @@
/// <reference path="../factory.ts" />
/// <reference path="../factory.ts" />

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's up with these ^M line breaks? There are a lot of them in the following files.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've looked at this and don't know why it shows a diff here.

  • It only shows ^M on lines that I've changed -- if I had changed line endings in the file, presumably they would show as changed in the whole file.
  • When I look at the file, it appears to have CRLF line endings (on all lines) on both master and map5. I have core.autocrlf set to false. Tested on both Windows and Linux.

After merge I'll look back to this file to see if it has mixed line endings, but I doubt that.
CC Daniel Rosenwasser (@DanielRosenwasser)

@ghostghost added the Breaking Change Would introduce errors in existing code label Jan 17, 2017
@ghost
ghost merged commit 65ef51d into masterJan 17, 2017
@ghost
ghost deleted the map5 branch January 17, 2017 19:04
@microsoftMicrosoft (microsoft) locked and limited conversation to collaborators Jun 19, 2018
This pull request was closed.
Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

Breaking ChangeWould introduce errors in existing code

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

@rbuckton@ahejlsberg@msftclas
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

Use native maps when they're available - #12715

Merged
30 commits merged into
masterfrom
map5
Jan 17, 2017
Merged

Use native maps when they're available#12715
30 commits merged into
masterfrom
map5

Conversation

@ghost

@ghostDeleted user (ghost) commented Dec 7, 2016

Copy link
Copy Markdown

Similar to #11354, but simpler.

  • Keeps using Map<T> instead of Map<K, V>, with K assumed to be string. For number-keyed maps we now use sparse arrays.
  • Does not include Set -- this can be done later.
  • Does not include sortInV8ObjectInsertionOrder; changes baselines instead. We won't be able to pass all tests when using the shim map, but we no longer run tests on node 0.10 anyway.

Added functions

NameUse
arrayFromConverts anIterator to an Array. arrayFrom(map.keys()) replaces Object.keys(map). arrayFrom(map.values()) also lets us remove reduceProperties, which was only used to compute that.
forEachEntry, forEachKeyThese replace for-in loops with early termination, forEachProperty, and someProperties.
copyEntriesReplaces copyProperties.
createMultiMapReplaces multiMapAdd and multiMapRemove.
mapsAreEqualReplaces a use of equalOwnProperties. Used only in reuseProgramStructure.ts, so moved it there.
multiMapSparseArrayAddReplaces a use of multiMapAdd on what is now a sparse array. Used only in factory.ts, so moved it there.
mapEntriesReplaces mapObject.

We also get rid of isEmpty and replace with map.size === 0.

@ghost

Deleted user (ghost) commented Dec 8, 2016

Copy link
Copy Markdown
Author

Comparing this branch to master, we now use 5% less memory and time.

Monaco

node (v6.9.2, x64)
ProjectBaselineCurrentDeltaBestWorst
Memory used381,190k (± 0.01%)358,147k (± 0.02%)-23,043k (- 6.04%)358,085k358,209k
Parse Time2.44s (± 0.94%)2.35s (± 1.23%)-0.09s (- 3.52%)2.33s2.39s
Bind Time0.93s (± 2.62%)0.77s (± 1.71%)-0.17s (- 17.94%)0.75s0.78s
Check Time4.53s (± 3.18%)4.33s (± 3.47%)-0.21s (- 4.53%)4.23s4.55s
Emit Time2.09s (± 0.98%)2.10s (± 3.31%)+0.01s (+ 0.55%)2.04s2.17s
Total Time9.99s (± 1.94%)9.55s (± 2.49%)-0.45s (- 4.46%)9.36s9.88s
node (v6.9.2, x86)
ProjectBaselineCurrentDeltaBestWorst
Memory used203,018k (± 0.01%)191,475k (± 0.02%)-11,543k (- 5.69%)191,450k191,522k
Parse Time2.30s (± 0.52%)2.21s (± 0.87%)-0.10s (- 4.19%)2.18s2.22s
Bind Time0.75s (± 1.50%)0.64s (± 2.43%)-0.11s (- 14.63%)0.62s0.65s
Check Time3.69s (± 2.45%)3.41s (± 1.28%)-0.29s (- 7.81%)3.35s3.45s
Emit Time2.09s (± 3.24%)2.10s (± 0.63%)+0.01s (+ 0.50%)2.08s2.11s
Total Time8.83s (± 1.25%)8.34s (± 0.77%)-0.48s (- 5.49%)8.27s8.40s
tsc (x86)
ProjectBaselineCurrentDeltaBestWorst
Parse Time1.82s (± 0.83%)1.72s (± 1.70%)-0.10s (- 5.39%)1.69s1.75s
Bind Time0.77s (± 5.13%)0.53s (± 2.82%)-0.23s (- 30.46%)0.52s0.55s
Check Time3.38s (± 1.26%)3.04s (± 3.24%)-0.34s (- 10.03%)2.94s3.15s
Emit Time6.08s (± 3.39%)6.09s (± 5.13%)+0.00s (+ 0.02%)5.93s6.57s
Total Time12.05s (± 2.00%)11.38s (± 2.47%)-0.67s (- 5.54%)11.15s11.78s

TFS

node (v6.9.2, x64)
ProjectBaselineCurrentDeltaBestWorst
Memory used327,608k (± 0.01%)309,067k (± 0.02%)-18,541k (- 5.66%)308,995k309,106k
Parse Time1.52s (± 0.74%)1.44s (± 1.04%)-0.07s (- 4.85%)1.42s1.45s
Bind Time0.75s (± 0.79%)0.65s (± 2.30%)-0.10s (- 13.30%)0.64s0.67s
Check Time3.85s (± 0.88%)3.78s (± 1.25%)-0.07s (- 1.75%)3.73s3.82s
Emit Time1.79s (± 0.65%)1.93s (± 2.76%)+0.14s (+ 7.60%)1.89s2.00s
Total Time7.91s (± 0.48%)7.80s (± 1.08%)-0.11s (- 1.34%)7.73s7.91s
node (v6.9.2, x86)
ProjectBaselineCurrentDeltaBestWorst
Memory used174,790k (± 0.01%)165,324k (± 0.03%)-9,466k (- 5.42%)165,287k165,405k
Parse Time1.44s (± 0.81%)1.32s (± 1.90%)-0.11s (- 7.74%)1.31s1.36s
Bind Time0.64s (± 1.95%)0.55s (± 3.77%)-0.09s (- 13.64%)0.53s0.58s
Check Time2.96s (± 0.68%)2.70s (± 1.02%)-0.26s (- 8.81%)2.67s2.73s
Emit Time1.56s (± 0.71%)1.55s (± 5.97%)-0.01s (- 0.51%)1.50s1.69s
Total Time6.59s (± 0.65%)6.13s (± 1.85%)-0.46s (- 7.03%)6.06s6.30s
tsc (x86)
ProjectBaselineCurrentDeltaBestWorst
Parse Time1.27s (± 3.07%)1.11s (± 0.87%)-0.15s (- 12.16%)1.10s1.12s
Bind Time0.54s (± 5.05%)0.56s (± 4.23%)+0.02s (+ 3.49%)0.55s0.60s
Check Time2.89s (± 3.78%)2.66s (± 2.91%)-0.24s (- 8.15%)2.55s2.73s
Emit Time3.67s (± 1.73%)3.56s (± 2.75%)-0.11s (- 3.00%)3.48s3.68s
Total Time8.37s (± 2.42%)7.89s (± 2.01%)-0.48s (- 5.72%)7.70s8.04s

Older node versions

Surprisingly, memory is reduced for node v0.12.17 (x86)! I'm not sure why. (EDIT: node 0.12 provides Map; I'll have to go even earlier to test with shims.)

monaco
ProjectBaselineCurrentDeltaBestWorst
Memory used224,254k (± 0.22%)194,721k (± 0.32%)-29,533k (- 13.17%)194,176k195,473k
Parse Time2.49s (± 0.79%)2.40s (± 1.20%)-0.10s (- 3.95%)2.38s2.44s
Bind Time0.80s (± 1.35%)0.66s (± 1.97%)-0.14s (- 17.16%)0.65s0.68s
Check Time4.70s (± 1.24%)4.99s (± 2.81%)+0.29s (+ 6.11%)4.88s5.20s
Emit Time2.15s (± 1.22%)2.24s (± 4.84%)+0.09s (+ 4.29%)2.09s2.31s
Total Time10.15s (± 0.67%)10.29s (± 1.67%)+0.14s (+ 1.43%)10.08s10.49s
TFS
ProjectBaselineCurrentDeltaBestWorst
Memory used187,770k (± 0.06%)166,329k (± 0.16%)-21,441k (- 11.42%)166,051k166,633k
Parse Time1.46s (± 1.12%)1.39s (± 3.49%)-0.07s (- 4.60%)1.35s1.46s
Bind Time0.67s (± 0.90%)0.58s (± 1.09%)-0.09s (- 13.71%)0.57s0.58s
Check Time3.52s (± 1.13%)3.22s (± 1.09%)-0.30s (- 8.44%)3.19s3.26s
Emit Time1.96s (± 2.11%)1.85s (± 2.01%)-0.11s (- 5.76%)1.81s1.90s
Total Time7.60s (± 0.91%)7.04s (± 1.01%)-0.56s (- 7.42%)6.98s7.14s

Comment threadsrc/compiler/types.ts Outdated
export interface Map<T> extends MapLike<T> {
__mapBrand: any;
/** It's allowed to get/set into a map with numbers. However, when iterating, you may get strings back due to the shim being an ordinary object (which only allows string keys). */
export type MapKey = string | number;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should only allow strings for the keys of Map<T> and go with Anders Hejlsberg (@ahejlsberg)'s suggestion to use sparse arrays for "maps" with only numeric keys.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My test shows a number-keyed Map to be slightly more efficient.

Comment threadsrc/compiler/core.ts Outdated
const map: Map<T> = createObject(null); // tslint:disable-line:no-null-keyword
const createObject = Object.create;
/** Create a MapLike with good performance. Prefer this over a literal `{}`. */
export function createMapLike<T>(): MapLike<T> {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd call this createDictionaryObject and keep it internal to the module (don't export it). We only use it in four places across all of src/ and those cases don't look like they'd suffer from just using {}.

@ghostDeleted user (ghost)Dec 12, 2016

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If someone modifies Object.prototype that will cause problems for {}. Isn't that why we have this function?

Comment threadsrc/compiler/core.ts
const MapCtr = usingNativeMaps ? Map : shimMap();

// Keep the class inside a function so it doesn't get compiled if it's not used.
function shimMap(): { new<T>(): Map<T> } {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you want the Map shim to have support for keys/values/entries iterators, take a look at https://gist.github.com/rbuckton/06d2c79bea46778f9e8bbeca77292087. That way there is less differentiation between what we need out of a native Map and what our shim provides.

Comment threadsrc/compiler/core.ts Outdated
export function someProperties<T>(map: Map<T>, predicate?: (value: T, key: string) => boolean) {
for (const key in map) {
if (!predicate || predicate(map[key], key)) return true;
export const forEachInMap: <T, U>(map: Map<T>, callback: (value: T, key: string) => U | undefined) => U | undefined = usingNativeMaps

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we use the MapIterator shim in my gist/comment above, then we don't need to differentiate.

Comment threadsrc/compiler/core.ts Outdated
: <T, U>(map: ShimMap<T>, callback: (value: T, key: string) => U | undefined) => map.forEachInMap(callback);

/** `forEachInMap` for just keys. */
export function forEachKeyInMap<T>(map: Map<{}>, callback: (key: string) => T | undefined): T | undefined {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it be more efficient to iterate over map.keys() and avoid the closure?

const scriptInfo = this.getScriptInfo(fileName);
const preprocessInfo = ts.preProcessFile(scriptInfo.content, /*readImportFiles*/ true);
const imports = ts.createMap<string>();
const imports = ts.createMapLike<string>();

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should just use {} for these cases.

Comment threadsrc/harness/unittests/session.ts Outdated
if (response.request_seq in this.callbacks) {
this.callbacks[response.request_seq](response);
delete this.callbacks[response.request_seq];
const handler = this.callbacks.get(response.request_seq);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Either callbacks should be a sparse array, or we should coerce response.request_seq to a string.


export class Callbacks {
private map: { [n: number]: TimeOutCallback } = {};
private map = createMap<TimeOutCallback>();

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use a sparse array here.

this.symbolIdToActionMap[symbolId] = [newAction];
const actions = this.symbolIdToActionMap.get(symbolId);
if (!actions) {
this.symbolIdToActionMap.set(symbolId, [newAction]);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be a sparse array.

Comment threadsrc/services/services.ts Outdated

function getDeclarations(name: string) {
return result[name] || (result[name] = []);
return result.get(name) || set(result, name, []);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just use multiple lines so we don't need to use set.

Comment threadsrc/compiler/core.ts Outdated
return map;
}

export const sparseArray: <T>() => SparseArray<T> = createDictionaryObject;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is the purpose of this? This isn't a sparse array.

Comment threadsrc/compiler/checker.ts Outdated
}
}
return false;
return someInMap(moduleSymbol.exports, (_, id) => id !== "export=");

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not just use forEachInMap. The someInMap function seems only marginally useful.

Comment threadsrc/compiler/checker.ts Outdated
symbolLinks.exportsSomeValue = hasExportAssignment
? !!(moduleSymbol.flags & SymbolFlags.Value)
: forEachProperty(getExportsOfModule(moduleSymbol), isValue);
: someInMap(getExportsOfModule(moduleSymbol), isValue);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not just use forEachInMap?

Comment threadsrc/compiler/core.ts Outdated
return map;
}

export const sparseArray: <T>() => SparseArray<T> = createDictionaryObject;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I know you ran some benchmarks regarding the performance of objects vs sparse arrays, but unless the performance difference is fairly significant for our use cases, I'd rather we just continue to leverage actual arrays for sparse arrays. If the difference is significant, then we should use a different name for this function and type.

Comment threadsrc/compiler/core.ts Outdated
}

/** Whether `predicate` is true for some entry in the map. */
export function someInMap<T>(map: Map<T>, predicate: (value: T, key: string) => boolean): boolean {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This function seems unnecessary, forEachInMap serves the same purpose.

Comment threadsrc/compiler/core.ts Outdated
}

/** `someInMap` for just keys. */
export function someKeyInMap(map: Map<{}>, predicate: (key: string) => boolean): boolean {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This also seems like overkill as we could just use forEachInMap and ignore the first argument.

Comment threadsrc/compiler/core.ts Outdated
}

/** Copy entries from `source` to `target`. */
export function copyMapEntries<T>(source: Map<T>, target: Map<T>): void {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: Consider a simpler name like copyEntries.

Comment threadsrc/compiler/core.ts Outdated
return values;
}

export function multiMapSparseArrayAdd<V>(map: SparseArray<V[]>, key: number, value: V): V[] {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is only used in factory.ts. Why not just use multiMapAdd and cast the key to string?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is hard to do since we return the value as a SparseArray, so we'd have to add as any as string at all the external uses too. At least I've moved this function to factory.ts.

Comment threadsrc/compiler/core.ts Outdated
*/
export function multiMapAdd<V>(map: Map<V[]>, key: string | number, value: V): V[] {
const values = map[key];
export function multiMapAdd<V>(map: Map<V[]>, key: string, value: V): V[] {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe we should consider adding a createMultiMap<T> that returns a MultiMap<T>:

interfaceMultiMap<T>extendsMap<T[]>{add(key: string,value: T): T[];remove(key: string,value: T): void;}functioncreateMultiMap<T>(){constmap=<MultiMap<T>>createMap<T[]>();map.add=multiMapAdd;map.remove=multiMapRemove;returnmap;}functionmultiMapAdd<T>(this: MultiMap<T>,key: string,value: T){ ... }functionmultiMapRemove<T>(this: MultiMap<T>,key: string,value: T){ ... }

Comment threadsrc/compiler/core.ts Outdated
let result: U;
for (const key in map) {
if (result = callback(map[key], key)) break;
function arrayFrom<T>(iterator: Iterator<T>): T[] {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe we should just export this and replace all references to keysOfMap and valuesOfMap.

@ghost

Deleted user (ghost) commented Dec 28, 2016

Copy link
Copy Markdown
Author

Not a big fan of 2e6f369 as it would make me suspicious of every array in the codebase.

@rbucktonRon Buckton (rbuckton) left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks good, though we should wait on Anders Hejlsberg (@ahejlsberg)'s feedback before merging.

@rbuckton

Copy link
Copy Markdown
Contributor

Andy (Andrewkraft) (@Andy-MS) I would recommend you update the description of the PR with respect to the final set of new functions added to support using native maps.

@ghost

Copy link
Copy Markdown
Author

Ron Buckton (@rbuckton) Done. And just to be sure, I ran baselines again. Results are still good.

Monaco

node (v0.12.17, x86)

ProjectBaselineCurrentDeltaBestWorst
Memory used224,333k (± 0.05%)190,498k (± 0.19%)-33,835k (- 15.08%)190,298k191,058k
Parse Time1.93s (± 1.50%)1.83s (± 1.40%)-0.10s (- 4.96%)1.80s1.86s
Bind Time0.76s (± 1.09%)0.71s (± 4.64%)-0.05s (- 6.54%)0.68s0.76s
Check Time5.59s (± 5.16%)5.42s (± 4.48%)-0.17s (- 3.13%)5.15s5.72s
Emit Time2.32s (± 4.21%)2.31s (± 7.73%)-0.01s (- 0.50%)2.20s2.56s
Total Time10.60s (± 2.98%)10.26s (± 4.10%)-0.33s (- 3.14%)10.01s10.90s

node (v6.9.2, x64)

ProjectBaselineCurrentDeltaBestWorst
Memory used381,413k (± 0.01%)357,526k (± 0.02%)-23,887k (- 6.26%)357,458k357,590k
Parse Time2.08s (± 1.02%)2.17s (± 25.08%)+0.08s (+ 3.98%)1.93s3.01s
Bind Time1.00s (± 2.21%)0.85s (± 5.81%)-0.15s (- 15.27%)0.81s0.92s
Check Time4.85s (± 1.50%)4.33s (± 4.81%)-0.51s (- 10.58%)4.12s4.53s
Emit Time2.20s (± 1.30%)2.23s (± 2.19%)+0.03s (+ 1.34%)2.16s2.26s
Total Time10.13s (± 0.81%)9.58s (± 5.42%)-0.55s (- 5.46%)9.04s10.29s

node (v6.9.2, x86)

ProjectBaselineCurrentDeltaBestWorst
Memory used203,159k (± 0.00%)190,871k (± 0.01%)-12,288k (- 6.05%)190,849k190,883k
Parse Time1.94s (± 1.41%)1.81s (± 1.00%)-0.13s (- 6.56%)1.79s1.83s
Bind Time0.80s (± 2.36%)0.66s (± 0.78%)-0.14s (- 17.35%)0.66s0.67s
Check Time3.79s (± 0.95%)3.18s (± 2.62%)-0.61s (- 16.12%)3.12s3.30s
Emit Time2.11s (± 1.47%)1.93s (± 0.90%)-0.17s (- 8.15%)1.91s1.95s
Total Time8.63s (± 0.79%)7.58s (± 0.87%)-1.05s (- 12.12%)7.53s7.68s

tsc (x86)

ProjectBaselineCurrentDeltaBestWorst
Parse Time1.62s (± 1.56%)1.49s (± 1.64%)-0.13s (- 8.05%)1.46s1.51s
Bind Time0.71s (± 3.11%)0.54s (± 4.08%)-0.17s (- 24.20%)0.52s0.57s
Check Time3.91s (± 1.49%)3.36s (± 2.07%)-0.55s (- 14.11%)3.29s3.45s
Emit Time5.99s (± 2.95%)5.91s (± 11.64%)-0.08s (- 1.35%)5.59s6.97s
Total Time12.23s (± 1.75%)11.29s (± 6.91%)-0.94s (- 7.67%)10.92s12.50s

TFS

node (v0.12.17, x86)

ProjectBaselineCurrentDeltaBestWorst
Memory used187,646k (± 0.06%)166,272k (± 0.13%)-21,374k (- 11.39%)165,952k166,408k
Parse Time1.35s (± 0.71%)1.28s (± 1.48%)-0.07s (- 5.05%)1.26s1.30s
Bind Time0.70s (± 1.22%)0.61s (± 1.58%)-0.09s (- 13.45%)0.60s0.62s
Check Time3.71s (± 0.72%)3.46s (± 1.46%)-0.25s (- 6.87%)3.42s3.53s
Emit Time1.97s (± 2.05%)1.93s (± 0.98%)-0.04s (- 2.16%)1.91s1.95s
Total Time7.73s (± 0.71%)7.27s (± 0.69%)-0.46s (- 5.96%)7.23s7.34s

node (v6.9.2, x64)

ProjectBaselineCurrentDeltaBestWorst
Memory used327,882k (± 0.01%)308,975k (± 0.01%)-18,907k (- 5.77%)308,943k308,995k
Parse Time1.52s (± 0.46%)1.42s (± 1.05%)-0.10s (- 6.63%)1.41s1.44s
Bind Time0.80s (± 0.67%)0.68s (± 0.76%)-0.12s (- 15.04%)0.67s0.68s
Check Time4.34s (± 0.90%)3.72s (± 4.74%)-0.62s (- 14.23%)3.60s3.99s
Emit Time1.98s (± 1.32%)2.17s (± 5.77%)+0.19s (+ 9.37%)1.99s2.27s
Total Time8.64s (± 0.69%)7.99s (± 1.32%)-0.65s (- 7.53%)7.85s8.07s

node (v6.9.2, x86)

ProjectBaselineCurrentDeltaBestWorst
Memory used174,963k (± 0.01%)165,150k (± 0.02%)-9,813k (- 5.61%)165,127k165,195k
Parse Time1.41s (± 0.77%)1.30s (± 2.38%)-0.10s (- 7.29%)1.28s1.35s
Bind Time0.67s (± 0.93%)0.58s (± 3.34%)-0.10s (- 14.48%)0.56s0.60s
Check Time3.18s (± 0.58%)2.81s (± 1.62%)-0.37s (- 11.52%)2.76s2.87s
Emit Time1.62s (± 0.66%)1.62s (± 3.73%)+0.00s (+ 0.06%)1.57s1.68s
Total Time6.88s (± 0.50%)6.32s (± 1.58%)-0.57s (- 8.24%)6.20s6.43s

tsc (x86)

ProjectBaselineCurrentDeltaBestWorst
Parse Time1.23s (± 2.63%)1.06s (± 1.23%)-0.16s (- 13.39%)1.05s1.08s
Bind Time0.58s (± 1.82%)0.51s (± 2.55%)-0.07s (- 11.46%)0.50s0.53s
Check Time3.25s (± 1.51%)2.76s (± 2.30%)-0.49s (- 15.15%)2.71s2.83s
Emit Time3.58s (± 1.51%)3.49s (± 4.20%)-0.09s (- 2.45%)3.39s3.70s
Total Time8.63s (± 1.20%)7.82s (± 2.28%)-0.81s (- 9.38%)7.70s8.09s

Comment threadsrc/compiler/core.ts Outdated
}

/** Create a new map. If a template object is provided, the map will copy entries from it. */
export function createMap<T>(template?: MapLike<T>): Map<T> {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How often do we actually pass a template to createMap? I seem to remember that functions containing for-in loops are not optimized by V8, so having this optional template stuff may be costly. Perhaps filling a map from a template should just be a separate function.

Comment threadsrc/compiler/checker.ts Outdated
target[id] = source[id];
if (!source) return;

source.forEach((sourceSymbol, id) => {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe use source && source.forEach(...) and get rid of the if (!source) return above.

@@ -1,4 +1,4 @@
/// <reference path="../factory.ts" />
/// <reference path="../factory.ts" />

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's up with these ^M line breaks? There are a lot of them in the following files.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've looked at this and don't know why it shows a diff here.

  • It only shows ^M on lines that I've changed -- if I had changed line endings in the file, presumably they would show as changed in the whole file.
  • When I look at the file, it appears to have CRLF line endings (on all lines) on both master and map5. I have core.autocrlf set to false. Tested on both Windows and Linux.

After merge I'll look back to this file to see if it has mixed line endings, but I doubt that.
CC Daniel Rosenwasser (@DanielRosenwasser)

@ghostghost added the Breaking Change Would introduce errors in existing code label Jan 17, 2017
@ghost
ghost merged commit 65ef51d into masterJan 17, 2017
@ghost
ghost deleted the map5 branch January 17, 2017 19:04
@microsoftMicrosoft (microsoft) locked and limited conversation to collaborators Jun 19, 2018
This pull request was closed.
Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

Breaking ChangeWould introduce errors in existing code

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

@rbuckton@ahejlsberg@msftclas
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Use native maps when they're available - #12715

Merged
30 commits merged into
masterfrom
map5
Jan 17, 2017
Merged

Use native maps when they're available#12715
30 commits merged into
masterfrom
map5

Conversation

@ghost

@ghostDeleted user (ghost) commented Dec 7, 2016

Copy link
Copy Markdown

Similar to #11354, but simpler.

  • Keeps using Map<T> instead of Map<K, V>, with K assumed to be string. For number-keyed maps we now use sparse arrays.
  • Does not include Set -- this can be done later.
  • Does not include sortInV8ObjectInsertionOrder; changes baselines instead. We won't be able to pass all tests when using the shim map, but we no longer run tests on node 0.10 anyway.

Added functions

NameUse
arrayFromConverts anIterator to an Array. arrayFrom(map.keys()) replaces Object.keys(map). arrayFrom(map.values()) also lets us remove reduceProperties, which was only used to compute that.
forEachEntry, forEachKeyThese replace for-in loops with early termination, forEachProperty, and someProperties.
copyEntriesReplaces copyProperties.
createMultiMapReplaces multiMapAdd and multiMapRemove.
mapsAreEqualReplaces a use of equalOwnProperties. Used only in reuseProgramStructure.ts, so moved it there.
multiMapSparseArrayAddReplaces a use of multiMapAdd on what is now a sparse array. Used only in factory.ts, so moved it there.
mapEntriesReplaces mapObject.

We also get rid of isEmpty and replace with map.size === 0.

@ghost

Deleted user (ghost) commented Dec 8, 2016

Copy link
Copy Markdown
Author

Comparing this branch to master, we now use 5% less memory and time.

Monaco

node (v6.9.2, x64)
ProjectBaselineCurrentDeltaBestWorst
Memory used381,190k (± 0.01%)358,147k (± 0.02%)-23,043k (- 6.04%)358,085k358,209k
Parse Time2.44s (± 0.94%)2.35s (± 1.23%)-0.09s (- 3.52%)2.33s2.39s
Bind Time0.93s (± 2.62%)0.77s (± 1.71%)-0.17s (- 17.94%)0.75s0.78s
Check Time4.53s (± 3.18%)4.33s (± 3.47%)-0.21s (- 4.53%)4.23s4.55s
Emit Time2.09s (± 0.98%)2.10s (± 3.31%)+0.01s (+ 0.55%)2.04s2.17s
Total Time9.99s (± 1.94%)9.55s (± 2.49%)-0.45s (- 4.46%)9.36s9.88s
node (v6.9.2, x86)
ProjectBaselineCurrentDeltaBestWorst
Memory used203,018k (± 0.01%)191,475k (± 0.02%)-11,543k (- 5.69%)191,450k191,522k
Parse Time2.30s (± 0.52%)2.21s (± 0.87%)-0.10s (- 4.19%)2.18s2.22s
Bind Time0.75s (± 1.50%)0.64s (± 2.43%)-0.11s (- 14.63%)0.62s0.65s
Check Time3.69s (± 2.45%)3.41s (± 1.28%)-0.29s (- 7.81%)3.35s3.45s
Emit Time2.09s (± 3.24%)2.10s (± 0.63%)+0.01s (+ 0.50%)2.08s2.11s
Total Time8.83s (± 1.25%)8.34s (± 0.77%)-0.48s (- 5.49%)8.27s8.40s
tsc (x86)
ProjectBaselineCurrentDeltaBestWorst
Parse Time1.82s (± 0.83%)1.72s (± 1.70%)-0.10s (- 5.39%)1.69s1.75s
Bind Time0.77s (± 5.13%)0.53s (± 2.82%)-0.23s (- 30.46%)0.52s0.55s
Check Time3.38s (± 1.26%)3.04s (± 3.24%)-0.34s (- 10.03%)2.94s3.15s
Emit Time6.08s (± 3.39%)6.09s (± 5.13%)+0.00s (+ 0.02%)5.93s6.57s
Total Time12.05s (± 2.00%)11.38s (± 2.47%)-0.67s (- 5.54%)11.15s11.78s

TFS

node (v6.9.2, x64)
ProjectBaselineCurrentDeltaBestWorst
Memory used327,608k (± 0.01%)309,067k (± 0.02%)-18,541k (- 5.66%)308,995k309,106k
Parse Time1.52s (± 0.74%)1.44s (± 1.04%)-0.07s (- 4.85%)1.42s1.45s
Bind Time0.75s (± 0.79%)0.65s (± 2.30%)-0.10s (- 13.30%)0.64s0.67s
Check Time3.85s (± 0.88%)3.78s (± 1.25%)-0.07s (- 1.75%)3.73s3.82s
Emit Time1.79s (± 0.65%)1.93s (± 2.76%)+0.14s (+ 7.60%)1.89s2.00s
Total Time7.91s (± 0.48%)7.80s (± 1.08%)-0.11s (- 1.34%)7.73s7.91s
node (v6.9.2, x86)
ProjectBaselineCurrentDeltaBestWorst
Memory used174,790k (± 0.01%)165,324k (± 0.03%)-9,466k (- 5.42%)165,287k165,405k
Parse Time1.44s (± 0.81%)1.32s (± 1.90%)-0.11s (- 7.74%)1.31s1.36s
Bind Time0.64s (± 1.95%)0.55s (± 3.77%)-0.09s (- 13.64%)0.53s0.58s
Check Time2.96s (± 0.68%)2.70s (± 1.02%)-0.26s (- 8.81%)2.67s2.73s
Emit Time1.56s (± 0.71%)1.55s (± 5.97%)-0.01s (- 0.51%)1.50s1.69s
Total Time6.59s (± 0.65%)6.13s (± 1.85%)-0.46s (- 7.03%)6.06s6.30s
tsc (x86)
ProjectBaselineCurrentDeltaBestWorst
Parse Time1.27s (± 3.07%)1.11s (± 0.87%)-0.15s (- 12.16%)1.10s1.12s
Bind Time0.54s (± 5.05%)0.56s (± 4.23%)+0.02s (+ 3.49%)0.55s0.60s
Check Time2.89s (± 3.78%)2.66s (± 2.91%)-0.24s (- 8.15%)2.55s2.73s
Emit Time3.67s (± 1.73%)3.56s (± 2.75%)-0.11s (- 3.00%)3.48s3.68s
Total Time8.37s (± 2.42%)7.89s (± 2.01%)-0.48s (- 5.72%)7.70s8.04s

Older node versions

Surprisingly, memory is reduced for node v0.12.17 (x86)! I'm not sure why. (EDIT: node 0.12 provides Map; I'll have to go even earlier to test with shims.)

monaco
ProjectBaselineCurrentDeltaBestWorst
Memory used224,254k (± 0.22%)194,721k (± 0.32%)-29,533k (- 13.17%)194,176k195,473k
Parse Time2.49s (± 0.79%)2.40s (± 1.20%)-0.10s (- 3.95%)2.38s2.44s
Bind Time0.80s (± 1.35%)0.66s (± 1.97%)-0.14s (- 17.16%)0.65s0.68s
Check Time4.70s (± 1.24%)4.99s (± 2.81%)+0.29s (+ 6.11%)4.88s5.20s
Emit Time2.15s (± 1.22%)2.24s (± 4.84%)+0.09s (+ 4.29%)2.09s2.31s
Total Time10.15s (± 0.67%)10.29s (± 1.67%)+0.14s (+ 1.43%)10.08s10.49s
TFS
ProjectBaselineCurrentDeltaBestWorst
Memory used187,770k (± 0.06%)166,329k (± 0.16%)-21,441k (- 11.42%)166,051k166,633k
Parse Time1.46s (± 1.12%)1.39s (± 3.49%)-0.07s (- 4.60%)1.35s1.46s
Bind Time0.67s (± 0.90%)0.58s (± 1.09%)-0.09s (- 13.71%)0.57s0.58s
Check Time3.52s (± 1.13%)3.22s (± 1.09%)-0.30s (- 8.44%)3.19s3.26s
Emit Time1.96s (± 2.11%)1.85s (± 2.01%)-0.11s (- 5.76%)1.81s1.90s
Total Time7.60s (± 0.91%)7.04s (± 1.01%)-0.56s (- 7.42%)6.98s7.14s

Comment threadsrc/compiler/types.ts Outdated
export interface Map<T> extends MapLike<T> {
__mapBrand: any;
/** It's allowed to get/set into a map with numbers. However, when iterating, you may get strings back due to the shim being an ordinary object (which only allows string keys). */
export type MapKey = string | number;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should only allow strings for the keys of Map<T> and go with Anders Hejlsberg (@ahejlsberg)'s suggestion to use sparse arrays for "maps" with only numeric keys.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My test shows a number-keyed Map to be slightly more efficient.

Comment threadsrc/compiler/core.ts Outdated
const map: Map<T> = createObject(null); // tslint:disable-line:no-null-keyword
const createObject = Object.create;
/** Create a MapLike with good performance. Prefer this over a literal `{}`. */
export function createMapLike<T>(): MapLike<T> {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd call this createDictionaryObject and keep it internal to the module (don't export it). We only use it in four places across all of src/ and those cases don't look like they'd suffer from just using {}.

@ghostDeleted user (ghost)Dec 12, 2016

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If someone modifies Object.prototype that will cause problems for {}. Isn't that why we have this function?

Comment threadsrc/compiler/core.ts
const MapCtr = usingNativeMaps ? Map : shimMap();

// Keep the class inside a function so it doesn't get compiled if it's not used.
function shimMap(): { new<T>(): Map<T> } {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you want the Map shim to have support for keys/values/entries iterators, take a look at https://gist.github.com/rbuckton/06d2c79bea46778f9e8bbeca77292087. That way there is less differentiation between what we need out of a native Map and what our shim provides.

Comment threadsrc/compiler/core.ts Outdated
export function someProperties<T>(map: Map<T>, predicate?: (value: T, key: string) => boolean) {
for (const key in map) {
if (!predicate || predicate(map[key], key)) return true;
export const forEachInMap: <T, U>(map: Map<T>, callback: (value: T, key: string) => U | undefined) => U | undefined = usingNativeMaps

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we use the MapIterator shim in my gist/comment above, then we don't need to differentiate.

Comment threadsrc/compiler/core.ts Outdated
: <T, U>(map: ShimMap<T>, callback: (value: T, key: string) => U | undefined) => map.forEachInMap(callback);

/** `forEachInMap` for just keys. */
export function forEachKeyInMap<T>(map: Map<{}>, callback: (key: string) => T | undefined): T | undefined {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it be more efficient to iterate over map.keys() and avoid the closure?

const scriptInfo = this.getScriptInfo(fileName);
const preprocessInfo = ts.preProcessFile(scriptInfo.content, /*readImportFiles*/ true);
const imports = ts.createMap<string>();
const imports = ts.createMapLike<string>();

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should just use {} for these cases.

Comment threadsrc/harness/unittests/session.ts Outdated
if (response.request_seq in this.callbacks) {
this.callbacks[response.request_seq](response);
delete this.callbacks[response.request_seq];
const handler = this.callbacks.get(response.request_seq);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Either callbacks should be a sparse array, or we should coerce response.request_seq to a string.


export class Callbacks {
private map: { [n: number]: TimeOutCallback } = {};
private map = createMap<TimeOutCallback>();

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use a sparse array here.

this.symbolIdToActionMap[symbolId] = [newAction];
const actions = this.symbolIdToActionMap.get(symbolId);
if (!actions) {
this.symbolIdToActionMap.set(symbolId, [newAction]);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be a sparse array.

Comment threadsrc/services/services.ts Outdated

function getDeclarations(name: string) {
return result[name] || (result[name] = []);
return result.get(name) || set(result, name, []);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just use multiple lines so we don't need to use set.

Comment threadsrc/compiler/core.ts Outdated
return map;
}

export const sparseArray: <T>() => SparseArray<T> = createDictionaryObject;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is the purpose of this? This isn't a sparse array.

Comment threadsrc/compiler/checker.ts Outdated
}
}
return false;
return someInMap(moduleSymbol.exports, (_, id) => id !== "export=");

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not just use forEachInMap. The someInMap function seems only marginally useful.

Comment threadsrc/compiler/checker.ts Outdated
symbolLinks.exportsSomeValue = hasExportAssignment
? !!(moduleSymbol.flags & SymbolFlags.Value)
: forEachProperty(getExportsOfModule(moduleSymbol), isValue);
: someInMap(getExportsOfModule(moduleSymbol), isValue);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not just use forEachInMap?

Comment threadsrc/compiler/core.ts Outdated
return map;
}

export const sparseArray: <T>() => SparseArray<T> = createDictionaryObject;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I know you ran some benchmarks regarding the performance of objects vs sparse arrays, but unless the performance difference is fairly significant for our use cases, I'd rather we just continue to leverage actual arrays for sparse arrays. If the difference is significant, then we should use a different name for this function and type.

Comment threadsrc/compiler/core.ts Outdated
}

/** Whether `predicate` is true for some entry in the map. */
export function someInMap<T>(map: Map<T>, predicate: (value: T, key: string) => boolean): boolean {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This function seems unnecessary, forEachInMap serves the same purpose.

Comment threadsrc/compiler/core.ts Outdated
}

/** `someInMap` for just keys. */
export function someKeyInMap(map: Map<{}>, predicate: (key: string) => boolean): boolean {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This also seems like overkill as we could just use forEachInMap and ignore the first argument.

Comment threadsrc/compiler/core.ts Outdated
}

/** Copy entries from `source` to `target`. */
export function copyMapEntries<T>(source: Map<T>, target: Map<T>): void {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: Consider a simpler name like copyEntries.

Comment threadsrc/compiler/core.ts Outdated
return values;
}

export function multiMapSparseArrayAdd<V>(map: SparseArray<V[]>, key: number, value: V): V[] {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is only used in factory.ts. Why not just use multiMapAdd and cast the key to string?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is hard to do since we return the value as a SparseArray, so we'd have to add as any as string at all the external uses too. At least I've moved this function to factory.ts.

Comment threadsrc/compiler/core.ts Outdated
*/
export function multiMapAdd<V>(map: Map<V[]>, key: string | number, value: V): V[] {
const values = map[key];
export function multiMapAdd<V>(map: Map<V[]>, key: string, value: V): V[] {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe we should consider adding a createMultiMap<T> that returns a MultiMap<T>:

interfaceMultiMap<T>extendsMap<T[]>{add(key: string,value: T): T[];remove(key: string,value: T): void;}functioncreateMultiMap<T>(){constmap=<MultiMap<T>>createMap<T[]>();map.add=multiMapAdd;map.remove=multiMapRemove;returnmap;}functionmultiMapAdd<T>(this: MultiMap<T>,key: string,value: T){ ... }functionmultiMapRemove<T>(this: MultiMap<T>,key: string,value: T){ ... }

Comment threadsrc/compiler/core.ts Outdated
let result: U;
for (const key in map) {
if (result = callback(map[key], key)) break;
function arrayFrom<T>(iterator: Iterator<T>): T[] {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe we should just export this and replace all references to keysOfMap and valuesOfMap.

@ghost

Deleted user (ghost) commented Dec 28, 2016

Copy link
Copy Markdown
Author

Not a big fan of 2e6f369 as it would make me suspicious of every array in the codebase.

@rbucktonRon Buckton (rbuckton) left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks good, though we should wait on Anders Hejlsberg (@ahejlsberg)'s feedback before merging.

@rbuckton

Copy link
Copy Markdown
Contributor

Andy (Andrewkraft) (@Andy-MS) I would recommend you update the description of the PR with respect to the final set of new functions added to support using native maps.

@ghost

Copy link
Copy Markdown
Author

Ron Buckton (@rbuckton) Done. And just to be sure, I ran baselines again. Results are still good.

Monaco

node (v0.12.17, x86)

ProjectBaselineCurrentDeltaBestWorst
Memory used224,333k (± 0.05%)190,498k (± 0.19%)-33,835k (- 15.08%)190,298k191,058k
Parse Time1.93s (± 1.50%)1.83s (± 1.40%)-0.10s (- 4.96%)1.80s1.86s
Bind Time0.76s (± 1.09%)0.71s (± 4.64%)-0.05s (- 6.54%)0.68s0.76s
Check Time5.59s (± 5.16%)5.42s (± 4.48%)-0.17s (- 3.13%)5.15s5.72s
Emit Time2.32s (± 4.21%)2.31s (± 7.73%)-0.01s (- 0.50%)2.20s2.56s
Total Time10.60s (± 2.98%)10.26s (± 4.10%)-0.33s (- 3.14%)10.01s10.90s

node (v6.9.2, x64)

ProjectBaselineCurrentDeltaBestWorst
Memory used381,413k (± 0.01%)357,526k (± 0.02%)-23,887k (- 6.26%)357,458k357,590k
Parse Time2.08s (± 1.02%)2.17s (± 25.08%)+0.08s (+ 3.98%)1.93s3.01s
Bind Time1.00s (± 2.21%)0.85s (± 5.81%)-0.15s (- 15.27%)0.81s0.92s
Check Time4.85s (± 1.50%)4.33s (± 4.81%)-0.51s (- 10.58%)4.12s4.53s
Emit Time2.20s (± 1.30%)2.23s (± 2.19%)+0.03s (+ 1.34%)2.16s2.26s
Total Time10.13s (± 0.81%)9.58s (± 5.42%)-0.55s (- 5.46%)9.04s10.29s

node (v6.9.2, x86)

ProjectBaselineCurrentDeltaBestWorst
Memory used203,159k (± 0.00%)190,871k (± 0.01%)-12,288k (- 6.05%)190,849k190,883k
Parse Time1.94s (± 1.41%)1.81s (± 1.00%)-0.13s (- 6.56%)1.79s1.83s
Bind Time0.80s (± 2.36%)0.66s (± 0.78%)-0.14s (- 17.35%)0.66s0.67s
Check Time3.79s (± 0.95%)3.18s (± 2.62%)-0.61s (- 16.12%)3.12s3.30s
Emit Time2.11s (± 1.47%)1.93s (± 0.90%)-0.17s (- 8.15%)1.91s1.95s
Total Time8.63s (± 0.79%)7.58s (± 0.87%)-1.05s (- 12.12%)7.53s7.68s

tsc (x86)

ProjectBaselineCurrentDeltaBestWorst
Parse Time1.62s (± 1.56%)1.49s (± 1.64%)-0.13s (- 8.05%)1.46s1.51s
Bind Time0.71s (± 3.11%)0.54s (± 4.08%)-0.17s (- 24.20%)0.52s0.57s
Check Time3.91s (± 1.49%)3.36s (± 2.07%)-0.55s (- 14.11%)3.29s3.45s
Emit Time5.99s (± 2.95%)5.91s (± 11.64%)-0.08s (- 1.35%)5.59s6.97s
Total Time12.23s (± 1.75%)11.29s (± 6.91%)-0.94s (- 7.67%)10.92s12.50s

TFS

node (v0.12.17, x86)

ProjectBaselineCurrentDeltaBestWorst
Memory used187,646k (± 0.06%)166,272k (± 0.13%)-21,374k (- 11.39%)165,952k166,408k
Parse Time1.35s (± 0.71%)1.28s (± 1.48%)-0.07s (- 5.05%)1.26s1.30s
Bind Time0.70s (± 1.22%)0.61s (± 1.58%)-0.09s (- 13.45%)0.60s0.62s
Check Time3.71s (± 0.72%)3.46s (± 1.46%)-0.25s (- 6.87%)3.42s3.53s
Emit Time1.97s (± 2.05%)1.93s (± 0.98%)-0.04s (- 2.16%)1.91s1.95s
Total Time7.73s (± 0.71%)7.27s (± 0.69%)-0.46s (- 5.96%)7.23s7.34s

node (v6.9.2, x64)

ProjectBaselineCurrentDeltaBestWorst
Memory used327,882k (± 0.01%)308,975k (± 0.01%)-18,907k (- 5.77%)308,943k308,995k
Parse Time1.52s (± 0.46%)1.42s (± 1.05%)-0.10s (- 6.63%)1.41s1.44s
Bind Time0.80s (± 0.67%)0.68s (± 0.76%)-0.12s (- 15.04%)0.67s0.68s
Check Time4.34s (± 0.90%)3.72s (± 4.74%)-0.62s (- 14.23%)3.60s3.99s
Emit Time1.98s (± 1.32%)2.17s (± 5.77%)+0.19s (+ 9.37%)1.99s2.27s
Total Time8.64s (± 0.69%)7.99s (± 1.32%)-0.65s (- 7.53%)7.85s8.07s

node (v6.9.2, x86)

ProjectBaselineCurrentDeltaBestWorst
Memory used174,963k (± 0.01%)165,150k (± 0.02%)-9,813k (- 5.61%)165,127k165,195k
Parse Time1.41s (± 0.77%)1.30s (± 2.38%)-0.10s (- 7.29%)1.28s1.35s
Bind Time0.67s (± 0.93%)0.58s (± 3.34%)-0.10s (- 14.48%)0.56s0.60s
Check Time3.18s (± 0.58%)2.81s (± 1.62%)-0.37s (- 11.52%)2.76s2.87s
Emit Time1.62s (± 0.66%)1.62s (± 3.73%)+0.00s (+ 0.06%)1.57s1.68s
Total Time6.88s (± 0.50%)6.32s (± 1.58%)-0.57s (- 8.24%)6.20s6.43s

tsc (x86)

ProjectBaselineCurrentDeltaBestWorst
Parse Time1.23s (± 2.63%)1.06s (± 1.23%)-0.16s (- 13.39%)1.05s1.08s
Bind Time0.58s (± 1.82%)0.51s (± 2.55%)-0.07s (- 11.46%)0.50s0.53s
Check Time3.25s (± 1.51%)2.76s (± 2.30%)-0.49s (- 15.15%)2.71s2.83s
Emit Time3.58s (± 1.51%)3.49s (± 4.20%)-0.09s (- 2.45%)3.39s3.70s
Total Time8.63s (± 1.20%)7.82s (± 2.28%)-0.81s (- 9.38%)7.70s8.09s

Comment threadsrc/compiler/core.ts Outdated
}

/** Create a new map. If a template object is provided, the map will copy entries from it. */
export function createMap<T>(template?: MapLike<T>): Map<T> {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How often do we actually pass a template to createMap? I seem to remember that functions containing for-in loops are not optimized by V8, so having this optional template stuff may be costly. Perhaps filling a map from a template should just be a separate function.

Comment threadsrc/compiler/checker.ts Outdated
target[id] = source[id];
if (!source) return;

source.forEach((sourceSymbol, id) => {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe use source && source.forEach(...) and get rid of the if (!source) return above.

@@ -1,4 +1,4 @@
/// <reference path="../factory.ts" />
/// <reference path="../factory.ts" />

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's up with these ^M line breaks? There are a lot of them in the following files.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've looked at this and don't know why it shows a diff here.

  • It only shows ^M on lines that I've changed -- if I had changed line endings in the file, presumably they would show as changed in the whole file.
  • When I look at the file, it appears to have CRLF line endings (on all lines) on both master and map5. I have core.autocrlf set to false. Tested on both Windows and Linux.

After merge I'll look back to this file to see if it has mixed line endings, but I doubt that.
CC Daniel Rosenwasser (@DanielRosenwasser)

@ghostghost added the Breaking Change Would introduce errors in existing code label Jan 17, 2017
@ghost
ghost merged commit 65ef51d into masterJan 17, 2017
@ghost
ghost deleted the map5 branch January 17, 2017 19:04
@microsoftMicrosoft (microsoft) locked and limited conversation to collaborators Jun 19, 2018
This pull request was closed.
Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

Breaking ChangeWould introduce errors in existing code

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

@rbuckton@ahejlsberg@msftclas
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Use native maps when they're available - #12715

Merged
30 commits merged into
masterfrom
map5
Jan 17, 2017
Merged

Use native maps when they're available#12715
30 commits merged into
masterfrom
map5

Conversation

@ghost

@ghostDeleted user (ghost) commented Dec 7, 2016

Copy link
Copy Markdown

Similar to #11354, but simpler.

  • Keeps using Map<T> instead of Map<K, V>, with K assumed to be string. For number-keyed maps we now use sparse arrays.
  • Does not include Set -- this can be done later.
  • Does not include sortInV8ObjectInsertionOrder; changes baselines instead. We won't be able to pass all tests when using the shim map, but we no longer run tests on node 0.10 anyway.

Added functions

NameUse
arrayFromConverts anIterator to an Array. arrayFrom(map.keys()) replaces Object.keys(map). arrayFrom(map.values()) also lets us remove reduceProperties, which was only used to compute that.
forEachEntry, forEachKeyThese replace for-in loops with early termination, forEachProperty, and someProperties.
copyEntriesReplaces copyProperties.
createMultiMapReplaces multiMapAdd and multiMapRemove.
mapsAreEqualReplaces a use of equalOwnProperties. Used only in reuseProgramStructure.ts, so moved it there.
multiMapSparseArrayAddReplaces a use of multiMapAdd on what is now a sparse array. Used only in factory.ts, so moved it there.
mapEntriesReplaces mapObject.

We also get rid of isEmpty and replace with map.size === 0.

@ghost

Deleted user (ghost) commented Dec 8, 2016

Copy link
Copy Markdown
Author

Comparing this branch to master, we now use 5% less memory and time.

Monaco

node (v6.9.2, x64)
ProjectBaselineCurrentDeltaBestWorst
Memory used381,190k (± 0.01%)358,147k (± 0.02%)-23,043k (- 6.04%)358,085k358,209k
Parse Time2.44s (± 0.94%)2.35s (± 1.23%)-0.09s (- 3.52%)2.33s2.39s
Bind Time0.93s (± 2.62%)0.77s (± 1.71%)-0.17s (- 17.94%)0.75s0.78s
Check Time4.53s (± 3.18%)4.33s (± 3.47%)-0.21s (- 4.53%)4.23s4.55s
Emit Time2.09s (± 0.98%)2.10s (± 3.31%)+0.01s (+ 0.55%)2.04s2.17s
Total Time9.99s (± 1.94%)9.55s (± 2.49%)-0.45s (- 4.46%)9.36s9.88s
node (v6.9.2, x86)
ProjectBaselineCurrentDeltaBestWorst
Memory used203,018k (± 0.01%)191,475k (± 0.02%)-11,543k (- 5.69%)191,450k191,522k
Parse Time2.30s (± 0.52%)2.21s (± 0.87%)-0.10s (- 4.19%)2.18s2.22s
Bind Time0.75s (± 1.50%)0.64s (± 2.43%)-0.11s (- 14.63%)0.62s0.65s
Check Time3.69s (± 2.45%)3.41s (± 1.28%)-0.29s (- 7.81%)3.35s3.45s
Emit Time2.09s (± 3.24%)2.10s (± 0.63%)+0.01s (+ 0.50%)2.08s2.11s
Total Time8.83s (± 1.25%)8.34s (± 0.77%)-0.48s (- 5.49%)8.27s8.40s
tsc (x86)
ProjectBaselineCurrentDeltaBestWorst
Parse Time1.82s (± 0.83%)1.72s (± 1.70%)-0.10s (- 5.39%)1.69s1.75s
Bind Time0.77s (± 5.13%)0.53s (± 2.82%)-0.23s (- 30.46%)0.52s0.55s
Check Time3.38s (± 1.26%)3.04s (± 3.24%)-0.34s (- 10.03%)2.94s3.15s
Emit Time6.08s (± 3.39%)6.09s (± 5.13%)+0.00s (+ 0.02%)5.93s6.57s
Total Time12.05s (± 2.00%)11.38s (± 2.47%)-0.67s (- 5.54%)11.15s11.78s

TFS

node (v6.9.2, x64)
ProjectBaselineCurrentDeltaBestWorst
Memory used327,608k (± 0.01%)309,067k (± 0.02%)-18,541k (- 5.66%)308,995k309,106k
Parse Time1.52s (± 0.74%)1.44s (± 1.04%)-0.07s (- 4.85%)1.42s1.45s
Bind Time0.75s (± 0.79%)0.65s (± 2.30%)-0.10s (- 13.30%)0.64s0.67s
Check Time3.85s (± 0.88%)3.78s (± 1.25%)-0.07s (- 1.75%)3.73s3.82s
Emit Time1.79s (± 0.65%)1.93s (± 2.76%)+0.14s (+ 7.60%)1.89s2.00s
Total Time7.91s (± 0.48%)7.80s (± 1.08%)-0.11s (- 1.34%)7.73s7.91s
node (v6.9.2, x86)
ProjectBaselineCurrentDeltaBestWorst
Memory used174,790k (± 0.01%)165,324k (± 0.03%)-9,466k (- 5.42%)165,287k165,405k
Parse Time1.44s (± 0.81%)1.32s (± 1.90%)-0.11s (- 7.74%)1.31s1.36s
Bind Time0.64s (± 1.95%)0.55s (± 3.77%)-0.09s (- 13.64%)0.53s0.58s
Check Time2.96s (± 0.68%)2.70s (± 1.02%)-0.26s (- 8.81%)2.67s2.73s
Emit Time1.56s (± 0.71%)1.55s (± 5.97%)-0.01s (- 0.51%)1.50s1.69s
Total Time6.59s (± 0.65%)6.13s (± 1.85%)-0.46s (- 7.03%)6.06s6.30s
tsc (x86)
ProjectBaselineCurrentDeltaBestWorst
Parse Time1.27s (± 3.07%)1.11s (± 0.87%)-0.15s (- 12.16%)1.10s1.12s
Bind Time0.54s (± 5.05%)0.56s (± 4.23%)+0.02s (+ 3.49%)0.55s0.60s
Check Time2.89s (± 3.78%)2.66s (± 2.91%)-0.24s (- 8.15%)2.55s2.73s
Emit Time3.67s (± 1.73%)3.56s (± 2.75%)-0.11s (- 3.00%)3.48s3.68s
Total Time8.37s (± 2.42%)7.89s (± 2.01%)-0.48s (- 5.72%)7.70s8.04s

Older node versions

Surprisingly, memory is reduced for node v0.12.17 (x86)! I'm not sure why. (EDIT: node 0.12 provides Map; I'll have to go even earlier to test with shims.)

monaco
ProjectBaselineCurrentDeltaBestWorst
Memory used224,254k (± 0.22%)194,721k (± 0.32%)-29,533k (- 13.17%)194,176k195,473k
Parse Time2.49s (± 0.79%)2.40s (± 1.20%)-0.10s (- 3.95%)2.38s2.44s
Bind Time0.80s (± 1.35%)0.66s (± 1.97%)-0.14s (- 17.16%)0.65s0.68s
Check Time4.70s (± 1.24%)4.99s (± 2.81%)+0.29s (+ 6.11%)4.88s5.20s
Emit Time2.15s (± 1.22%)2.24s (± 4.84%)+0.09s (+ 4.29%)2.09s2.31s
Total Time10.15s (± 0.67%)10.29s (± 1.67%)+0.14s (+ 1.43%)10.08s10.49s
TFS
ProjectBaselineCurrentDeltaBestWorst
Memory used187,770k (± 0.06%)166,329k (± 0.16%)-21,441k (- 11.42%)166,051k166,633k
Parse Time1.46s (± 1.12%)1.39s (± 3.49%)-0.07s (- 4.60%)1.35s1.46s
Bind Time0.67s (± 0.90%)0.58s (± 1.09%)-0.09s (- 13.71%)0.57s0.58s
Check Time3.52s (± 1.13%)3.22s (± 1.09%)-0.30s (- 8.44%)3.19s3.26s
Emit Time1.96s (± 2.11%)1.85s (± 2.01%)-0.11s (- 5.76%)1.81s1.90s
Total Time7.60s (± 0.91%)7.04s (± 1.01%)-0.56s (- 7.42%)6.98s7.14s

Comment threadsrc/compiler/types.ts Outdated
export interface Map<T> extends MapLike<T> {
__mapBrand: any;
/** It's allowed to get/set into a map with numbers. However, when iterating, you may get strings back due to the shim being an ordinary object (which only allows string keys). */
export type MapKey = string | number;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should only allow strings for the keys of Map<T> and go with Anders Hejlsberg (@ahejlsberg)'s suggestion to use sparse arrays for "maps" with only numeric keys.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My test shows a number-keyed Map to be slightly more efficient.

Comment threadsrc/compiler/core.ts Outdated
const map: Map<T> = createObject(null); // tslint:disable-line:no-null-keyword
const createObject = Object.create;
/** Create a MapLike with good performance. Prefer this over a literal `{}`. */
export function createMapLike<T>(): MapLike<T> {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd call this createDictionaryObject and keep it internal to the module (don't export it). We only use it in four places across all of src/ and those cases don't look like they'd suffer from just using {}.

@ghostDeleted user (ghost)Dec 12, 2016

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If someone modifies Object.prototype that will cause problems for {}. Isn't that why we have this function?

Comment threadsrc/compiler/core.ts
const MapCtr = usingNativeMaps ? Map : shimMap();

// Keep the class inside a function so it doesn't get compiled if it's not used.
function shimMap(): { new<T>(): Map<T> } {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you want the Map shim to have support for keys/values/entries iterators, take a look at https://gist.github.com/rbuckton/06d2c79bea46778f9e8bbeca77292087. That way there is less differentiation between what we need out of a native Map and what our shim provides.

Comment threadsrc/compiler/core.ts Outdated
export function someProperties<T>(map: Map<T>, predicate?: (value: T, key: string) => boolean) {
for (const key in map) {
if (!predicate || predicate(map[key], key)) return true;
export const forEachInMap: <T, U>(map: Map<T>, callback: (value: T, key: string) => U | undefined) => U | undefined = usingNativeMaps

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we use the MapIterator shim in my gist/comment above, then we don't need to differentiate.

Comment threadsrc/compiler/core.ts Outdated
: <T, U>(map: ShimMap<T>, callback: (value: T, key: string) => U | undefined) => map.forEachInMap(callback);

/** `forEachInMap` for just keys. */
export function forEachKeyInMap<T>(map: Map<{}>, callback: (key: string) => T | undefined): T | undefined {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it be more efficient to iterate over map.keys() and avoid the closure?

const scriptInfo = this.getScriptInfo(fileName);
const preprocessInfo = ts.preProcessFile(scriptInfo.content, /*readImportFiles*/ true);
const imports = ts.createMap<string>();
const imports = ts.createMapLike<string>();

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should just use {} for these cases.

Comment threadsrc/harness/unittests/session.ts Outdated
if (response.request_seq in this.callbacks) {
this.callbacks[response.request_seq](response);
delete this.callbacks[response.request_seq];
const handler = this.callbacks.get(response.request_seq);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Either callbacks should be a sparse array, or we should coerce response.request_seq to a string.


export class Callbacks {
private map: { [n: number]: TimeOutCallback } = {};
private map = createMap<TimeOutCallback>();

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use a sparse array here.

this.symbolIdToActionMap[symbolId] = [newAction];
const actions = this.symbolIdToActionMap.get(symbolId);
if (!actions) {
this.symbolIdToActionMap.set(symbolId, [newAction]);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be a sparse array.

Comment threadsrc/services/services.ts Outdated

function getDeclarations(name: string) {
return result[name] || (result[name] = []);
return result.get(name) || set(result, name, []);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just use multiple lines so we don't need to use set.

Comment threadsrc/compiler/core.ts Outdated
return map;
}

export const sparseArray: <T>() => SparseArray<T> = createDictionaryObject;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is the purpose of this? This isn't a sparse array.

Comment threadsrc/compiler/checker.ts Outdated
}
}
return false;
return someInMap(moduleSymbol.exports, (_, id) => id !== "export=");

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not just use forEachInMap. The someInMap function seems only marginally useful.

Comment threadsrc/compiler/checker.ts Outdated
symbolLinks.exportsSomeValue = hasExportAssignment
? !!(moduleSymbol.flags & SymbolFlags.Value)
: forEachProperty(getExportsOfModule(moduleSymbol), isValue);
: someInMap(getExportsOfModule(moduleSymbol), isValue);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not just use forEachInMap?

Comment threadsrc/compiler/core.ts Outdated
return map;
}

export const sparseArray: <T>() => SparseArray<T> = createDictionaryObject;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I know you ran some benchmarks regarding the performance of objects vs sparse arrays, but unless the performance difference is fairly significant for our use cases, I'd rather we just continue to leverage actual arrays for sparse arrays. If the difference is significant, then we should use a different name for this function and type.

Comment threadsrc/compiler/core.ts Outdated
}

/** Whether `predicate` is true for some entry in the map. */
export function someInMap<T>(map: Map<T>, predicate: (value: T, key: string) => boolean): boolean {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This function seems unnecessary, forEachInMap serves the same purpose.

Comment threadsrc/compiler/core.ts Outdated
}

/** `someInMap` for just keys. */
export function someKeyInMap(map: Map<{}>, predicate: (key: string) => boolean): boolean {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This also seems like overkill as we could just use forEachInMap and ignore the first argument.

Comment threadsrc/compiler/core.ts Outdated
}

/** Copy entries from `source` to `target`. */
export function copyMapEntries<T>(source: Map<T>, target: Map<T>): void {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: Consider a simpler name like copyEntries.

Comment threadsrc/compiler/core.ts Outdated
return values;
}

export function multiMapSparseArrayAdd<V>(map: SparseArray<V[]>, key: number, value: V): V[] {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is only used in factory.ts. Why not just use multiMapAdd and cast the key to string?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is hard to do since we return the value as a SparseArray, so we'd have to add as any as string at all the external uses too. At least I've moved this function to factory.ts.

Comment threadsrc/compiler/core.ts Outdated
*/
export function multiMapAdd<V>(map: Map<V[]>, key: string | number, value: V): V[] {
const values = map[key];
export function multiMapAdd<V>(map: Map<V[]>, key: string, value: V): V[] {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe we should consider adding a createMultiMap<T> that returns a MultiMap<T>:

interfaceMultiMap<T>extendsMap<T[]>{add(key: string,value: T): T[];remove(key: string,value: T): void;}functioncreateMultiMap<T>(){constmap=<MultiMap<T>>createMap<T[]>();map.add=multiMapAdd;map.remove=multiMapRemove;returnmap;}functionmultiMapAdd<T>(this: MultiMap<T>,key: string,value: T){ ... }functionmultiMapRemove<T>(this: MultiMap<T>,key: string,value: T){ ... }

Comment threadsrc/compiler/core.ts Outdated
let result: U;
for (const key in map) {
if (result = callback(map[key], key)) break;
function arrayFrom<T>(iterator: Iterator<T>): T[] {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe we should just export this and replace all references to keysOfMap and valuesOfMap.

@ghost

Deleted user (ghost) commented Dec 28, 2016

Copy link
Copy Markdown
Author

Not a big fan of 2e6f369 as it would make me suspicious of every array in the codebase.

@rbucktonRon Buckton (rbuckton) left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks good, though we should wait on Anders Hejlsberg (@ahejlsberg)'s feedback before merging.

@rbuckton

Copy link
Copy Markdown
Contributor

Andy (Andrewkraft) (@Andy-MS) I would recommend you update the description of the PR with respect to the final set of new functions added to support using native maps.

@ghost

Copy link
Copy Markdown
Author

Ron Buckton (@rbuckton) Done. And just to be sure, I ran baselines again. Results are still good.

Monaco

node (v0.12.17, x86)

ProjectBaselineCurrentDeltaBestWorst
Memory used224,333k (± 0.05%)190,498k (± 0.19%)-33,835k (- 15.08%)190,298k191,058k
Parse Time1.93s (± 1.50%)1.83s (± 1.40%)-0.10s (- 4.96%)1.80s1.86s
Bind Time0.76s (± 1.09%)0.71s (± 4.64%)-0.05s (- 6.54%)0.68s0.76s
Check Time5.59s (± 5.16%)5.42s (± 4.48%)-0.17s (- 3.13%)5.15s5.72s
Emit Time2.32s (± 4.21%)2.31s (± 7.73%)-0.01s (- 0.50%)2.20s2.56s
Total Time10.60s (± 2.98%)10.26s (± 4.10%)-0.33s (- 3.14%)10.01s10.90s

node (v6.9.2, x64)

ProjectBaselineCurrentDeltaBestWorst
Memory used381,413k (± 0.01%)357,526k (± 0.02%)-23,887k (- 6.26%)357,458k357,590k
Parse Time2.08s (± 1.02%)2.17s (± 25.08%)+0.08s (+ 3.98%)1.93s3.01s
Bind Time1.00s (± 2.21%)0.85s (± 5.81%)-0.15s (- 15.27%)0.81s0.92s
Check Time4.85s (± 1.50%)4.33s (± 4.81%)-0.51s (- 10.58%)4.12s4.53s
Emit Time2.20s (± 1.30%)2.23s (± 2.19%)+0.03s (+ 1.34%)2.16s2.26s
Total Time10.13s (± 0.81%)9.58s (± 5.42%)-0.55s (- 5.46%)9.04s10.29s

node (v6.9.2, x86)

ProjectBaselineCurrentDeltaBestWorst
Memory used203,159k (± 0.00%)190,871k (± 0.01%)-12,288k (- 6.05%)190,849k190,883k
Parse Time1.94s (± 1.41%)1.81s (± 1.00%)-0.13s (- 6.56%)1.79s1.83s
Bind Time0.80s (± 2.36%)0.66s (± 0.78%)-0.14s (- 17.35%)0.66s0.67s
Check Time3.79s (± 0.95%)3.18s (± 2.62%)-0.61s (- 16.12%)3.12s3.30s
Emit Time2.11s (± 1.47%)1.93s (± 0.90%)-0.17s (- 8.15%)1.91s1.95s
Total Time8.63s (± 0.79%)7.58s (± 0.87%)-1.05s (- 12.12%)7.53s7.68s

tsc (x86)

ProjectBaselineCurrentDeltaBestWorst
Parse Time1.62s (± 1.56%)1.49s (± 1.64%)-0.13s (- 8.05%)1.46s1.51s
Bind Time0.71s (± 3.11%)0.54s (± 4.08%)-0.17s (- 24.20%)0.52s0.57s
Check Time3.91s (± 1.49%)3.36s (± 2.07%)-0.55s (- 14.11%)3.29s3.45s
Emit Time5.99s (± 2.95%)5.91s (± 11.64%)-0.08s (- 1.35%)5.59s6.97s
Total Time12.23s (± 1.75%)11.29s (± 6.91%)-0.94s (- 7.67%)10.92s12.50s

TFS

node (v0.12.17, x86)

ProjectBaselineCurrentDeltaBestWorst
Memory used187,646k (± 0.06%)166,272k (± 0.13%)-21,374k (- 11.39%)165,952k166,408k
Parse Time1.35s (± 0.71%)1.28s (± 1.48%)-0.07s (- 5.05%)1.26s1.30s
Bind Time0.70s (± 1.22%)0.61s (± 1.58%)-0.09s (- 13.45%)0.60s0.62s
Check Time3.71s (± 0.72%)3.46s (± 1.46%)-0.25s (- 6.87%)3.42s3.53s
Emit Time1.97s (± 2.05%)1.93s (± 0.98%)-0.04s (- 2.16%)1.91s1.95s
Total Time7.73s (± 0.71%)7.27s (± 0.69%)-0.46s (- 5.96%)7.23s7.34s

node (v6.9.2, x64)

ProjectBaselineCurrentDeltaBestWorst
Memory used327,882k (± 0.01%)308,975k (± 0.01%)-18,907k (- 5.77%)308,943k308,995k
Parse Time1.52s (± 0.46%)1.42s (± 1.05%)-0.10s (- 6.63%)1.41s1.44s
Bind Time0.80s (± 0.67%)0.68s (± 0.76%)-0.12s (- 15.04%)0.67s0.68s
Check Time4.34s (± 0.90%)3.72s (± 4.74%)-0.62s (- 14.23%)3.60s3.99s
Emit Time1.98s (± 1.32%)2.17s (± 5.77%)+0.19s (+ 9.37%)1.99s2.27s
Total Time8.64s (± 0.69%)7.99s (± 1.32%)-0.65s (- 7.53%)7.85s8.07s

node (v6.9.2, x86)

ProjectBaselineCurrentDeltaBestWorst
Memory used174,963k (± 0.01%)165,150k (± 0.02%)-9,813k (- 5.61%)165,127k165,195k
Parse Time1.41s (± 0.77%)1.30s (± 2.38%)-0.10s (- 7.29%)1.28s1.35s
Bind Time0.67s (± 0.93%)0.58s (± 3.34%)-0.10s (- 14.48%)0.56s0.60s
Check Time3.18s (± 0.58%)2.81s (± 1.62%)-0.37s (- 11.52%)2.76s2.87s
Emit Time1.62s (± 0.66%)1.62s (± 3.73%)+0.00s (+ 0.06%)1.57s1.68s
Total Time6.88s (± 0.50%)6.32s (± 1.58%)-0.57s (- 8.24%)6.20s6.43s

tsc (x86)

ProjectBaselineCurrentDeltaBestWorst
Parse Time1.23s (± 2.63%)1.06s (± 1.23%)-0.16s (- 13.39%)1.05s1.08s
Bind Time0.58s (± 1.82%)0.51s (± 2.55%)-0.07s (- 11.46%)0.50s0.53s
Check Time3.25s (± 1.51%)2.76s (± 2.30%)-0.49s (- 15.15%)2.71s2.83s
Emit Time3.58s (± 1.51%)3.49s (± 4.20%)-0.09s (- 2.45%)3.39s3.70s
Total Time8.63s (± 1.20%)7.82s (± 2.28%)-0.81s (- 9.38%)7.70s8.09s

Comment threadsrc/compiler/core.ts Outdated
}

/** Create a new map. If a template object is provided, the map will copy entries from it. */
export function createMap<T>(template?: MapLike<T>): Map<T> {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How often do we actually pass a template to createMap? I seem to remember that functions containing for-in loops are not optimized by V8, so having this optional template stuff may be costly. Perhaps filling a map from a template should just be a separate function.

Comment threadsrc/compiler/checker.ts Outdated
target[id] = source[id];
if (!source) return;

source.forEach((sourceSymbol, id) => {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe use source && source.forEach(...) and get rid of the if (!source) return above.

@@ -1,4 +1,4 @@
/// <reference path="../factory.ts" />
/// <reference path="../factory.ts" />

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's up with these ^M line breaks? There are a lot of them in the following files.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've looked at this and don't know why it shows a diff here.

  • It only shows ^M on lines that I've changed -- if I had changed line endings in the file, presumably they would show as changed in the whole file.
  • When I look at the file, it appears to have CRLF line endings (on all lines) on both master and map5. I have core.autocrlf set to false. Tested on both Windows and Linux.

After merge I'll look back to this file to see if it has mixed line endings, but I doubt that.
CC Daniel Rosenwasser (@DanielRosenwasser)

@ghostghost added the Breaking Change Would introduce errors in existing code label Jan 17, 2017
@ghost
ghost merged commit 65ef51d into masterJan 17, 2017
@ghost
ghost deleted the map5 branch January 17, 2017 19:04
@microsoftMicrosoft (microsoft) locked and limited conversation to collaborators Jun 19, 2018
This pull request was closed.
Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

Breaking ChangeWould introduce errors in existing code

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

@rbuckton@ahejlsberg@msftclas
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

Use native maps when they're available - #12715

Merged
30 commits merged into
masterfrom
map5
Jan 17, 2017
Merged

Use native maps when they're available#12715
30 commits merged into
masterfrom
map5

Conversation

@ghost

@ghostDeleted user (ghost) commented Dec 7, 2016

Copy link
Copy Markdown

Similar to #11354, but simpler.

  • Keeps using Map<T> instead of Map<K, V>, with K assumed to be string. For number-keyed maps we now use sparse arrays.
  • Does not include Set -- this can be done later.
  • Does not include sortInV8ObjectInsertionOrder; changes baselines instead. We won't be able to pass all tests when using the shim map, but we no longer run tests on node 0.10 anyway.

Added functions

NameUse
arrayFromConverts anIterator to an Array. arrayFrom(map.keys()) replaces Object.keys(map). arrayFrom(map.values()) also lets us remove reduceProperties, which was only used to compute that.
forEachEntry, forEachKeyThese replace for-in loops with early termination, forEachProperty, and someProperties.
copyEntriesReplaces copyProperties.
createMultiMapReplaces multiMapAdd and multiMapRemove.
mapsAreEqualReplaces a use of equalOwnProperties. Used only in reuseProgramStructure.ts, so moved it there.
multiMapSparseArrayAddReplaces a use of multiMapAdd on what is now a sparse array. Used only in factory.ts, so moved it there.
mapEntriesReplaces mapObject.

We also get rid of isEmpty and replace with map.size === 0.

@ghost

Deleted user (ghost) commented Dec 8, 2016

Copy link
Copy Markdown
Author

Comparing this branch to master, we now use 5% less memory and time.

Monaco

node (v6.9.2, x64)
ProjectBaselineCurrentDeltaBestWorst
Memory used381,190k (± 0.01%)358,147k (± 0.02%)-23,043k (- 6.04%)358,085k358,209k
Parse Time2.44s (± 0.94%)2.35s (± 1.23%)-0.09s (- 3.52%)2.33s2.39s
Bind Time0.93s (± 2.62%)0.77s (± 1.71%)-0.17s (- 17.94%)0.75s0.78s
Check Time4.53s (± 3.18%)4.33s (± 3.47%)-0.21s (- 4.53%)4.23s4.55s
Emit Time2.09s (± 0.98%)2.10s (± 3.31%)+0.01s (+ 0.55%)2.04s2.17s
Total Time9.99s (± 1.94%)9.55s (± 2.49%)-0.45s (- 4.46%)9.36s9.88s
node (v6.9.2, x86)
ProjectBaselineCurrentDeltaBestWorst
Memory used203,018k (± 0.01%)191,475k (± 0.02%)-11,543k (- 5.69%)191,450k191,522k
Parse Time2.30s (± 0.52%)2.21s (± 0.87%)-0.10s (- 4.19%)2.18s2.22s
Bind Time0.75s (± 1.50%)0.64s (± 2.43%)-0.11s (- 14.63%)0.62s0.65s
Check Time3.69s (± 2.45%)3.41s (± 1.28%)-0.29s (- 7.81%)3.35s3.45s
Emit Time2.09s (± 3.24%)2.10s (± 0.63%)+0.01s (+ 0.50%)2.08s2.11s
Total Time8.83s (± 1.25%)8.34s (± 0.77%)-0.48s (- 5.49%)8.27s8.40s
tsc (x86)
ProjectBaselineCurrentDeltaBestWorst
Parse Time1.82s (± 0.83%)1.72s (± 1.70%)-0.10s (- 5.39%)1.69s1.75s
Bind Time0.77s (± 5.13%)0.53s (± 2.82%)-0.23s (- 30.46%)0.52s0.55s
Check Time3.38s (± 1.26%)3.04s (± 3.24%)-0.34s (- 10.03%)2.94s3.15s
Emit Time6.08s (± 3.39%)6.09s (± 5.13%)+0.00s (+ 0.02%)5.93s6.57s
Total Time12.05s (± 2.00%)11.38s (± 2.47%)-0.67s (- 5.54%)11.15s11.78s

TFS

node (v6.9.2, x64)
ProjectBaselineCurrentDeltaBestWorst
Memory used327,608k (± 0.01%)309,067k (± 0.02%)-18,541k (- 5.66%)308,995k309,106k
Parse Time1.52s (± 0.74%)1.44s (± 1.04%)-0.07s (- 4.85%)1.42s1.45s
Bind Time0.75s (± 0.79%)0.65s (± 2.30%)-0.10s (- 13.30%)0.64s0.67s
Check Time3.85s (± 0.88%)3.78s (± 1.25%)-0.07s (- 1.75%)3.73s3.82s
Emit Time1.79s (± 0.65%)1.93s (± 2.76%)+0.14s (+ 7.60%)1.89s2.00s
Total Time7.91s (± 0.48%)7.80s (± 1.08%)-0.11s (- 1.34%)7.73s7.91s
node (v6.9.2, x86)
ProjectBaselineCurrentDeltaBestWorst
Memory used174,790k (± 0.01%)165,324k (± 0.03%)-9,466k (- 5.42%)165,287k165,405k
Parse Time1.44s (± 0.81%)1.32s (± 1.90%)-0.11s (- 7.74%)1.31s1.36s
Bind Time0.64s (± 1.95%)0.55s (± 3.77%)-0.09s (- 13.64%)0.53s0.58s
Check Time2.96s (± 0.68%)2.70s (± 1.02%)-0.26s (- 8.81%)2.67s2.73s
Emit Time1.56s (± 0.71%)1.55s (± 5.97%)-0.01s (- 0.51%)1.50s1.69s
Total Time6.59s (± 0.65%)6.13s (± 1.85%)-0.46s (- 7.03%)6.06s6.30s
tsc (x86)
ProjectBaselineCurrentDeltaBestWorst
Parse Time1.27s (± 3.07%)1.11s (± 0.87%)-0.15s (- 12.16%)1.10s1.12s
Bind Time0.54s (± 5.05%)0.56s (± 4.23%)+0.02s (+ 3.49%)0.55s0.60s
Check Time2.89s (± 3.78%)2.66s (± 2.91%)-0.24s (- 8.15%)2.55s2.73s
Emit Time3.67s (± 1.73%)3.56s (± 2.75%)-0.11s (- 3.00%)3.48s3.68s
Total Time8.37s (± 2.42%)7.89s (± 2.01%)-0.48s (- 5.72%)7.70s8.04s

Older node versions

Surprisingly, memory is reduced for node v0.12.17 (x86)! I'm not sure why. (EDIT: node 0.12 provides Map; I'll have to go even earlier to test with shims.)

monaco
ProjectBaselineCurrentDeltaBestWorst
Memory used224,254k (± 0.22%)194,721k (± 0.32%)-29,533k (- 13.17%)194,176k195,473k
Parse Time2.49s (± 0.79%)2.40s (± 1.20%)-0.10s (- 3.95%)2.38s2.44s
Bind Time0.80s (± 1.35%)0.66s (± 1.97%)-0.14s (- 17.16%)0.65s0.68s
Check Time4.70s (± 1.24%)4.99s (± 2.81%)+0.29s (+ 6.11%)4.88s5.20s
Emit Time2.15s (± 1.22%)2.24s (± 4.84%)+0.09s (+ 4.29%)2.09s2.31s
Total Time10.15s (± 0.67%)10.29s (± 1.67%)+0.14s (+ 1.43%)10.08s10.49s
TFS
ProjectBaselineCurrentDeltaBestWorst
Memory used187,770k (± 0.06%)166,329k (± 0.16%)-21,441k (- 11.42%)166,051k166,633k
Parse Time1.46s (± 1.12%)1.39s (± 3.49%)-0.07s (- 4.60%)1.35s1.46s
Bind Time0.67s (± 0.90%)0.58s (± 1.09%)-0.09s (- 13.71%)0.57s0.58s
Check Time3.52s (± 1.13%)3.22s (± 1.09%)-0.30s (- 8.44%)3.19s3.26s
Emit Time1.96s (± 2.11%)1.85s (± 2.01%)-0.11s (- 5.76%)1.81s1.90s
Total Time7.60s (± 0.91%)7.04s (± 1.01%)-0.56s (- 7.42%)6.98s7.14s

Comment threadsrc/compiler/types.ts Outdated
export interface Map<T> extends MapLike<T> {
__mapBrand: any;
/** It's allowed to get/set into a map with numbers. However, when iterating, you may get strings back due to the shim being an ordinary object (which only allows string keys). */
export type MapKey = string | number;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should only allow strings for the keys of Map<T> and go with Anders Hejlsberg (@ahejlsberg)'s suggestion to use sparse arrays for "maps" with only numeric keys.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My test shows a number-keyed Map to be slightly more efficient.

Comment threadsrc/compiler/core.ts Outdated
const map: Map<T> = createObject(null); // tslint:disable-line:no-null-keyword
const createObject = Object.create;
/** Create a MapLike with good performance. Prefer this over a literal `{}`. */
export function createMapLike<T>(): MapLike<T> {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd call this createDictionaryObject and keep it internal to the module (don't export it). We only use it in four places across all of src/ and those cases don't look like they'd suffer from just using {}.

@ghostDeleted user (ghost)Dec 12, 2016

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If someone modifies Object.prototype that will cause problems for {}. Isn't that why we have this function?

Comment threadsrc/compiler/core.ts
const MapCtr = usingNativeMaps ? Map : shimMap();

// Keep the class inside a function so it doesn't get compiled if it's not used.
function shimMap(): { new<T>(): Map<T> } {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you want the Map shim to have support for keys/values/entries iterators, take a look at https://gist.github.com/rbuckton/06d2c79bea46778f9e8bbeca77292087. That way there is less differentiation between what we need out of a native Map and what our shim provides.

Comment threadsrc/compiler/core.ts Outdated
export function someProperties<T>(map: Map<T>, predicate?: (value: T, key: string) => boolean) {
for (const key in map) {
if (!predicate || predicate(map[key], key)) return true;
export const forEachInMap: <T, U>(map: Map<T>, callback: (value: T, key: string) => U | undefined) => U | undefined = usingNativeMaps

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we use the MapIterator shim in my gist/comment above, then we don't need to differentiate.

Comment threadsrc/compiler/core.ts Outdated
: <T, U>(map: ShimMap<T>, callback: (value: T, key: string) => U | undefined) => map.forEachInMap(callback);

/** `forEachInMap` for just keys. */
export function forEachKeyInMap<T>(map: Map<{}>, callback: (key: string) => T | undefined): T | undefined {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it be more efficient to iterate over map.keys() and avoid the closure?

const scriptInfo = this.getScriptInfo(fileName);
const preprocessInfo = ts.preProcessFile(scriptInfo.content, /*readImportFiles*/ true);
const imports = ts.createMap<string>();
const imports = ts.createMapLike<string>();

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should just use {} for these cases.

Comment threadsrc/harness/unittests/session.ts Outdated
if (response.request_seq in this.callbacks) {
this.callbacks[response.request_seq](response);
delete this.callbacks[response.request_seq];
const handler = this.callbacks.get(response.request_seq);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Either callbacks should be a sparse array, or we should coerce response.request_seq to a string.


export class Callbacks {
private map: { [n: number]: TimeOutCallback } = {};
private map = createMap<TimeOutCallback>();

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use a sparse array here.

this.symbolIdToActionMap[symbolId] = [newAction];
const actions = this.symbolIdToActionMap.get(symbolId);
if (!actions) {
this.symbolIdToActionMap.set(symbolId, [newAction]);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be a sparse array.

Comment threadsrc/services/services.ts Outdated

function getDeclarations(name: string) {
return result[name] || (result[name] = []);
return result.get(name) || set(result, name, []);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just use multiple lines so we don't need to use set.

Comment threadsrc/compiler/core.ts Outdated
return map;
}

export const sparseArray: <T>() => SparseArray<T> = createDictionaryObject;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is the purpose of this? This isn't a sparse array.

Comment threadsrc/compiler/checker.ts Outdated
}
}
return false;
return someInMap(moduleSymbol.exports, (_, id) => id !== "export=");

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not just use forEachInMap. The someInMap function seems only marginally useful.

Comment threadsrc/compiler/checker.ts Outdated
symbolLinks.exportsSomeValue = hasExportAssignment
? !!(moduleSymbol.flags & SymbolFlags.Value)
: forEachProperty(getExportsOfModule(moduleSymbol), isValue);
: someInMap(getExportsOfModule(moduleSymbol), isValue);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not just use forEachInMap?

Comment threadsrc/compiler/core.ts Outdated
return map;
}

export const sparseArray: <T>() => SparseArray<T> = createDictionaryObject;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I know you ran some benchmarks regarding the performance of objects vs sparse arrays, but unless the performance difference is fairly significant for our use cases, I'd rather we just continue to leverage actual arrays for sparse arrays. If the difference is significant, then we should use a different name for this function and type.

Comment threadsrc/compiler/core.ts Outdated
}

/** Whether `predicate` is true for some entry in the map. */
export function someInMap<T>(map: Map<T>, predicate: (value: T, key: string) => boolean): boolean {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This function seems unnecessary, forEachInMap serves the same purpose.

Comment threadsrc/compiler/core.ts Outdated
}

/** `someInMap` for just keys. */
export function someKeyInMap(map: Map<{}>, predicate: (key: string) => boolean): boolean {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This also seems like overkill as we could just use forEachInMap and ignore the first argument.

Comment threadsrc/compiler/core.ts Outdated
}

/** Copy entries from `source` to `target`. */
export function copyMapEntries<T>(source: Map<T>, target: Map<T>): void {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: Consider a simpler name like copyEntries.

Comment threadsrc/compiler/core.ts Outdated
return values;
}

export function multiMapSparseArrayAdd<V>(map: SparseArray<V[]>, key: number, value: V): V[] {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is only used in factory.ts. Why not just use multiMapAdd and cast the key to string?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is hard to do since we return the value as a SparseArray, so we'd have to add as any as string at all the external uses too. At least I've moved this function to factory.ts.

Comment threadsrc/compiler/core.ts Outdated
*/
export function multiMapAdd<V>(map: Map<V[]>, key: string | number, value: V): V[] {
const values = map[key];
export function multiMapAdd<V>(map: Map<V[]>, key: string, value: V): V[] {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe we should consider adding a createMultiMap<T> that returns a MultiMap<T>:

interfaceMultiMap<T>extendsMap<T[]>{add(key: string,value: T): T[];remove(key: string,value: T): void;}functioncreateMultiMap<T>(){constmap=<MultiMap<T>>createMap<T[]>();map.add=multiMapAdd;map.remove=multiMapRemove;returnmap;}functionmultiMapAdd<T>(this: MultiMap<T>,key: string,value: T){ ... }functionmultiMapRemove<T>(this: MultiMap<T>,key: string,value: T){ ... }

Comment threadsrc/compiler/core.ts Outdated
let result: U;
for (const key in map) {
if (result = callback(map[key], key)) break;
function arrayFrom<T>(iterator: Iterator<T>): T[] {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe we should just export this and replace all references to keysOfMap and valuesOfMap.

@ghost

Deleted user (ghost) commented Dec 28, 2016

Copy link
Copy Markdown
Author

Not a big fan of 2e6f369 as it would make me suspicious of every array in the codebase.

@rbucktonRon Buckton (rbuckton) left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks good, though we should wait on Anders Hejlsberg (@ahejlsberg)'s feedback before merging.

@rbuckton

Copy link
Copy Markdown
Contributor

Andy (Andrewkraft) (@Andy-MS) I would recommend you update the description of the PR with respect to the final set of new functions added to support using native maps.

@ghost

Copy link
Copy Markdown
Author

Ron Buckton (@rbuckton) Done. And just to be sure, I ran baselines again. Results are still good.

Monaco

node (v0.12.17, x86)

ProjectBaselineCurrentDeltaBestWorst
Memory used224,333k (± 0.05%)190,498k (± 0.19%)-33,835k (- 15.08%)190,298k191,058k
Parse Time1.93s (± 1.50%)1.83s (± 1.40%)-0.10s (- 4.96%)1.80s1.86s
Bind Time0.76s (± 1.09%)0.71s (± 4.64%)-0.05s (- 6.54%)0.68s0.76s
Check Time5.59s (± 5.16%)5.42s (± 4.48%)-0.17s (- 3.13%)5.15s5.72s
Emit Time2.32s (± 4.21%)2.31s (± 7.73%)-0.01s (- 0.50%)2.20s2.56s
Total Time10.60s (± 2.98%)10.26s (± 4.10%)-0.33s (- 3.14%)10.01s10.90s

node (v6.9.2, x64)

ProjectBaselineCurrentDeltaBestWorst
Memory used381,413k (± 0.01%)357,526k (± 0.02%)-23,887k (- 6.26%)357,458k357,590k
Parse Time2.08s (± 1.02%)2.17s (± 25.08%)+0.08s (+ 3.98%)1.93s3.01s
Bind Time1.00s (± 2.21%)0.85s (± 5.81%)-0.15s (- 15.27%)0.81s0.92s
Check Time4.85s (± 1.50%)4.33s (± 4.81%)-0.51s (- 10.58%)4.12s4.53s
Emit Time2.20s (± 1.30%)2.23s (± 2.19%)+0.03s (+ 1.34%)2.16s2.26s
Total Time10.13s (± 0.81%)9.58s (± 5.42%)-0.55s (- 5.46%)9.04s10.29s

node (v6.9.2, x86)

ProjectBaselineCurrentDeltaBestWorst
Memory used203,159k (± 0.00%)190,871k (± 0.01%)-12,288k (- 6.05%)190,849k190,883k
Parse Time1.94s (± 1.41%)1.81s (± 1.00%)-0.13s (- 6.56%)1.79s1.83s
Bind Time0.80s (± 2.36%)0.66s (± 0.78%)-0.14s (- 17.35%)0.66s0.67s
Check Time3.79s (± 0.95%)3.18s (± 2.62%)-0.61s (- 16.12%)3.12s3.30s
Emit Time2.11s (± 1.47%)1.93s (± 0.90%)-0.17s (- 8.15%)1.91s1.95s
Total Time8.63s (± 0.79%)7.58s (± 0.87%)-1.05s (- 12.12%)7.53s7.68s

tsc (x86)

ProjectBaselineCurrentDeltaBestWorst
Parse Time1.62s (± 1.56%)1.49s (± 1.64%)-0.13s (- 8.05%)1.46s1.51s
Bind Time0.71s (± 3.11%)0.54s (± 4.08%)-0.17s (- 24.20%)0.52s0.57s
Check Time3.91s (± 1.49%)3.36s (± 2.07%)-0.55s (- 14.11%)3.29s3.45s
Emit Time5.99s (± 2.95%)5.91s (± 11.64%)-0.08s (- 1.35%)5.59s6.97s
Total Time12.23s (± 1.75%)11.29s (± 6.91%)-0.94s (- 7.67%)10.92s12.50s

TFS

node (v0.12.17, x86)

ProjectBaselineCurrentDeltaBestWorst
Memory used187,646k (± 0.06%)166,272k (± 0.13%)-21,374k (- 11.39%)165,952k166,408k
Parse Time1.35s (± 0.71%)1.28s (± 1.48%)-0.07s (- 5.05%)1.26s1.30s
Bind Time0.70s (± 1.22%)0.61s (± 1.58%)-0.09s (- 13.45%)0.60s0.62s
Check Time3.71s (± 0.72%)3.46s (± 1.46%)-0.25s (- 6.87%)3.42s3.53s
Emit Time1.97s (± 2.05%)1.93s (± 0.98%)-0.04s (- 2.16%)1.91s1.95s
Total Time7.73s (± 0.71%)7.27s (± 0.69%)-0.46s (- 5.96%)7.23s7.34s

node (v6.9.2, x64)

ProjectBaselineCurrentDeltaBestWorst
Memory used327,882k (± 0.01%)308,975k (± 0.01%)-18,907k (- 5.77%)308,943k308,995k
Parse Time1.52s (± 0.46%)1.42s (± 1.05%)-0.10s (- 6.63%)1.41s1.44s
Bind Time0.80s (± 0.67%)0.68s (± 0.76%)-0.12s (- 15.04%)0.67s0.68s
Check Time4.34s (± 0.90%)3.72s (± 4.74%)-0.62s (- 14.23%)3.60s3.99s
Emit Time1.98s (± 1.32%)2.17s (± 5.77%)+0.19s (+ 9.37%)1.99s2.27s
Total Time8.64s (± 0.69%)7.99s (± 1.32%)-0.65s (- 7.53%)7.85s8.07s

node (v6.9.2, x86)

ProjectBaselineCurrentDeltaBestWorst
Memory used174,963k (± 0.01%)165,150k (± 0.02%)-9,813k (- 5.61%)165,127k165,195k
Parse Time1.41s (± 0.77%)1.30s (± 2.38%)-0.10s (- 7.29%)1.28s1.35s
Bind Time0.67s (± 0.93%)0.58s (± 3.34%)-0.10s (- 14.48%)0.56s0.60s
Check Time3.18s (± 0.58%)2.81s (± 1.62%)-0.37s (- 11.52%)2.76s2.87s
Emit Time1.62s (± 0.66%)1.62s (± 3.73%)+0.00s (+ 0.06%)1.57s1.68s
Total Time6.88s (± 0.50%)6.32s (± 1.58%)-0.57s (- 8.24%)6.20s6.43s

tsc (x86)

ProjectBaselineCurrentDeltaBestWorst
Parse Time1.23s (± 2.63%)1.06s (± 1.23%)-0.16s (- 13.39%)1.05s1.08s
Bind Time0.58s (± 1.82%)0.51s (± 2.55%)-0.07s (- 11.46%)0.50s0.53s
Check Time3.25s (± 1.51%)2.76s (± 2.30%)-0.49s (- 15.15%)2.71s2.83s
Emit Time3.58s (± 1.51%)3.49s (± 4.20%)-0.09s (- 2.45%)3.39s3.70s
Total Time8.63s (± 1.20%)7.82s (± 2.28%)-0.81s (- 9.38%)7.70s8.09s

Comment threadsrc/compiler/core.ts Outdated
}

/** Create a new map. If a template object is provided, the map will copy entries from it. */
export function createMap<T>(template?: MapLike<T>): Map<T> {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How often do we actually pass a template to createMap? I seem to remember that functions containing for-in loops are not optimized by V8, so having this optional template stuff may be costly. Perhaps filling a map from a template should just be a separate function.

Comment threadsrc/compiler/checker.ts Outdated
target[id] = source[id];
if (!source) return;

source.forEach((sourceSymbol, id) => {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe use source && source.forEach(...) and get rid of the if (!source) return above.

@@ -1,4 +1,4 @@
/// <reference path="../factory.ts" />
/// <reference path="../factory.ts" />

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's up with these ^M line breaks? There are a lot of them in the following files.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've looked at this and don't know why it shows a diff here.

  • It only shows ^M on lines that I've changed -- if I had changed line endings in the file, presumably they would show as changed in the whole file.
  • When I look at the file, it appears to have CRLF line endings (on all lines) on both master and map5. I have core.autocrlf set to false. Tested on both Windows and Linux.

After merge I'll look back to this file to see if it has mixed line endings, but I doubt that.
CC Daniel Rosenwasser (@DanielRosenwasser)

@ghostghost added the Breaking Change Would introduce errors in existing code label Jan 17, 2017
@ghost
ghost merged commit 65ef51d into masterJan 17, 2017
@ghost
ghost deleted the map5 branch January 17, 2017 19:04
@microsoftMicrosoft (microsoft) locked and limited conversation to collaborators Jun 19, 2018
This pull request was closed.
Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

Breaking ChangeWould introduce errors in existing code

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

@rbuckton@ahejlsberg@msftclas
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Use native maps when they're available - #12715

Merged
30 commits merged into
masterfrom
map5
Jan 17, 2017
Merged

Use native maps when they're available#12715
30 commits merged into
masterfrom
map5

Conversation

@ghost

@ghostDeleted user (ghost) commented Dec 7, 2016

Copy link
Copy Markdown

Similar to #11354, but simpler.

  • Keeps using Map<T> instead of Map<K, V>, with K assumed to be string. For number-keyed maps we now use sparse arrays.
  • Does not include Set -- this can be done later.
  • Does not include sortInV8ObjectInsertionOrder; changes baselines instead. We won't be able to pass all tests when using the shim map, but we no longer run tests on node 0.10 anyway.

Added functions

NameUse
arrayFromConverts anIterator to an Array. arrayFrom(map.keys()) replaces Object.keys(map). arrayFrom(map.values()) also lets us remove reduceProperties, which was only used to compute that.
forEachEntry, forEachKeyThese replace for-in loops with early termination, forEachProperty, and someProperties.
copyEntriesReplaces copyProperties.
createMultiMapReplaces multiMapAdd and multiMapRemove.
mapsAreEqualReplaces a use of equalOwnProperties. Used only in reuseProgramStructure.ts, so moved it there.
multiMapSparseArrayAddReplaces a use of multiMapAdd on what is now a sparse array. Used only in factory.ts, so moved it there.
mapEntriesReplaces mapObject.

We also get rid of isEmpty and replace with map.size === 0.

@ghost

Deleted user (ghost) commented Dec 8, 2016

Copy link
Copy Markdown
Author

Comparing this branch to master, we now use 5% less memory and time.

Monaco

node (v6.9.2, x64)
ProjectBaselineCurrentDeltaBestWorst
Memory used381,190k (± 0.01%)358,147k (± 0.02%)-23,043k (- 6.04%)358,085k358,209k
Parse Time2.44s (± 0.94%)2.35s (± 1.23%)-0.09s (- 3.52%)2.33s2.39s
Bind Time0.93s (± 2.62%)0.77s (± 1.71%)-0.17s (- 17.94%)0.75s0.78s
Check Time4.53s (± 3.18%)4.33s (± 3.47%)-0.21s (- 4.53%)4.23s4.55s
Emit Time2.09s (± 0.98%)2.10s (± 3.31%)+0.01s (+ 0.55%)2.04s2.17s
Total Time9.99s (± 1.94%)9.55s (± 2.49%)-0.45s (- 4.46%)9.36s9.88s
node (v6.9.2, x86)
ProjectBaselineCurrentDeltaBestWorst
Memory used203,018k (± 0.01%)191,475k (± 0.02%)-11,543k (- 5.69%)191,450k191,522k
Parse Time2.30s (± 0.52%)2.21s (± 0.87%)-0.10s (- 4.19%)2.18s2.22s
Bind Time0.75s (± 1.50%)0.64s (± 2.43%)-0.11s (- 14.63%)0.62s0.65s
Check Time3.69s (± 2.45%)3.41s (± 1.28%)-0.29s (- 7.81%)3.35s3.45s
Emit Time2.09s (± 3.24%)2.10s (± 0.63%)+0.01s (+ 0.50%)2.08s2.11s
Total Time8.83s (± 1.25%)8.34s (± 0.77%)-0.48s (- 5.49%)8.27s8.40s
tsc (x86)
ProjectBaselineCurrentDeltaBestWorst
Parse Time1.82s (± 0.83%)1.72s (± 1.70%)-0.10s (- 5.39%)1.69s1.75s
Bind Time0.77s (± 5.13%)0.53s (± 2.82%)-0.23s (- 30.46%)0.52s0.55s
Check Time3.38s (± 1.26%)3.04s (± 3.24%)-0.34s (- 10.03%)2.94s3.15s
Emit Time6.08s (± 3.39%)6.09s (± 5.13%)+0.00s (+ 0.02%)5.93s6.57s
Total Time12.05s (± 2.00%)11.38s (± 2.47%)-0.67s (- 5.54%)11.15s11.78s

TFS

node (v6.9.2, x64)
ProjectBaselineCurrentDeltaBestWorst
Memory used327,608k (± 0.01%)309,067k (± 0.02%)-18,541k (- 5.66%)308,995k309,106k
Parse Time1.52s (± 0.74%)1.44s (± 1.04%)-0.07s (- 4.85%)1.42s1.45s
Bind Time0.75s (± 0.79%)0.65s (± 2.30%)-0.10s (- 13.30%)0.64s0.67s
Check Time3.85s (± 0.88%)3.78s (± 1.25%)-0.07s (- 1.75%)3.73s3.82s
Emit Time1.79s (± 0.65%)1.93s (± 2.76%)+0.14s (+ 7.60%)1.89s2.00s
Total Time7.91s (± 0.48%)7.80s (± 1.08%)-0.11s (- 1.34%)7.73s7.91s
node (v6.9.2, x86)
ProjectBaselineCurrentDeltaBestWorst
Memory used174,790k (± 0.01%)165,324k (± 0.03%)-9,466k (- 5.42%)165,287k165,405k
Parse Time1.44s (± 0.81%)1.32s (± 1.90%)-0.11s (- 7.74%)1.31s1.36s
Bind Time0.64s (± 1.95%)0.55s (± 3.77%)-0.09s (- 13.64%)0.53s0.58s
Check Time2.96s (± 0.68%)2.70s (± 1.02%)-0.26s (- 8.81%)2.67s2.73s
Emit Time1.56s (± 0.71%)1.55s (± 5.97%)-0.01s (- 0.51%)1.50s1.69s
Total Time6.59s (± 0.65%)6.13s (± 1.85%)-0.46s (- 7.03%)6.06s6.30s
tsc (x86)
ProjectBaselineCurrentDeltaBestWorst
Parse Time1.27s (± 3.07%)1.11s (± 0.87%)-0.15s (- 12.16%)1.10s1.12s
Bind Time0.54s (± 5.05%)0.56s (± 4.23%)+0.02s (+ 3.49%)0.55s0.60s
Check Time2.89s (± 3.78%)2.66s (± 2.91%)-0.24s (- 8.15%)2.55s2.73s
Emit Time3.67s (± 1.73%)3.56s (± 2.75%)-0.11s (- 3.00%)3.48s3.68s
Total Time8.37s (± 2.42%)7.89s (± 2.01%)-0.48s (- 5.72%)7.70s8.04s

Older node versions

Surprisingly, memory is reduced for node v0.12.17 (x86)! I'm not sure why. (EDIT: node 0.12 provides Map; I'll have to go even earlier to test with shims.)

monaco
ProjectBaselineCurrentDeltaBestWorst
Memory used224,254k (± 0.22%)194,721k (± 0.32%)-29,533k (- 13.17%)194,176k195,473k
Parse Time2.49s (± 0.79%)2.40s (± 1.20%)-0.10s (- 3.95%)2.38s2.44s
Bind Time0.80s (± 1.35%)0.66s (± 1.97%)-0.14s (- 17.16%)0.65s0.68s
Check Time4.70s (± 1.24%)4.99s (± 2.81%)+0.29s (+ 6.11%)4.88s5.20s
Emit Time2.15s (± 1.22%)2.24s (± 4.84%)+0.09s (+ 4.29%)2.09s2.31s
Total Time10.15s (± 0.67%)10.29s (± 1.67%)+0.14s (+ 1.43%)10.08s10.49s
TFS
ProjectBaselineCurrentDeltaBestWorst
Memory used187,770k (± 0.06%)166,329k (± 0.16%)-21,441k (- 11.42%)166,051k166,633k
Parse Time1.46s (± 1.12%)1.39s (± 3.49%)-0.07s (- 4.60%)1.35s1.46s
Bind Time0.67s (± 0.90%)0.58s (± 1.09%)-0.09s (- 13.71%)0.57s0.58s
Check Time3.52s (± 1.13%)3.22s (± 1.09%)-0.30s (- 8.44%)3.19s3.26s
Emit Time1.96s (± 2.11%)1.85s (± 2.01%)-0.11s (- 5.76%)1.81s1.90s
Total Time7.60s (± 0.91%)7.04s (± 1.01%)-0.56s (- 7.42%)6.98s7.14s

Comment threadsrc/compiler/types.ts Outdated
export interface Map<T> extends MapLike<T> {
__mapBrand: any;
/** It's allowed to get/set into a map with numbers. However, when iterating, you may get strings back due to the shim being an ordinary object (which only allows string keys). */
export type MapKey = string | number;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should only allow strings for the keys of Map<T> and go with Anders Hejlsberg (@ahejlsberg)'s suggestion to use sparse arrays for "maps" with only numeric keys.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My test shows a number-keyed Map to be slightly more efficient.

Comment threadsrc/compiler/core.ts Outdated
const map: Map<T> = createObject(null); // tslint:disable-line:no-null-keyword
const createObject = Object.create;
/** Create a MapLike with good performance. Prefer this over a literal `{}`. */
export function createMapLike<T>(): MapLike<T> {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd call this createDictionaryObject and keep it internal to the module (don't export it). We only use it in four places across all of src/ and those cases don't look like they'd suffer from just using {}.

@ghostDeleted user (ghost)Dec 12, 2016

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If someone modifies Object.prototype that will cause problems for {}. Isn't that why we have this function?

Comment threadsrc/compiler/core.ts
const MapCtr = usingNativeMaps ? Map : shimMap();

// Keep the class inside a function so it doesn't get compiled if it's not used.
function shimMap(): { new<T>(): Map<T> } {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you want the Map shim to have support for keys/values/entries iterators, take a look at https://gist.github.com/rbuckton/06d2c79bea46778f9e8bbeca77292087. That way there is less differentiation between what we need out of a native Map and what our shim provides.

Comment threadsrc/compiler/core.ts Outdated
export function someProperties<T>(map: Map<T>, predicate?: (value: T, key: string) => boolean) {
for (const key in map) {
if (!predicate || predicate(map[key], key)) return true;
export const forEachInMap: <T, U>(map: Map<T>, callback: (value: T, key: string) => U | undefined) => U | undefined = usingNativeMaps

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we use the MapIterator shim in my gist/comment above, then we don't need to differentiate.

Comment threadsrc/compiler/core.ts Outdated
: <T, U>(map: ShimMap<T>, callback: (value: T, key: string) => U | undefined) => map.forEachInMap(callback);

/** `forEachInMap` for just keys. */
export function forEachKeyInMap<T>(map: Map<{}>, callback: (key: string) => T | undefined): T | undefined {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it be more efficient to iterate over map.keys() and avoid the closure?

const scriptInfo = this.getScriptInfo(fileName);
const preprocessInfo = ts.preProcessFile(scriptInfo.content, /*readImportFiles*/ true);
const imports = ts.createMap<string>();
const imports = ts.createMapLike<string>();

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should just use {} for these cases.

Comment threadsrc/harness/unittests/session.ts Outdated
if (response.request_seq in this.callbacks) {
this.callbacks[response.request_seq](response);
delete this.callbacks[response.request_seq];
const handler = this.callbacks.get(response.request_seq);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Either callbacks should be a sparse array, or we should coerce response.request_seq to a string.


export class Callbacks {
private map: { [n: number]: TimeOutCallback } = {};
private map = createMap<TimeOutCallback>();

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use a sparse array here.

this.symbolIdToActionMap[symbolId] = [newAction];
const actions = this.symbolIdToActionMap.get(symbolId);
if (!actions) {
this.symbolIdToActionMap.set(symbolId, [newAction]);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be a sparse array.

Comment threadsrc/services/services.ts Outdated

function getDeclarations(name: string) {
return result[name] || (result[name] = []);
return result.get(name) || set(result, name, []);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just use multiple lines so we don't need to use set.

Comment threadsrc/compiler/core.ts Outdated
return map;
}

export const sparseArray: <T>() => SparseArray<T> = createDictionaryObject;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is the purpose of this? This isn't a sparse array.

Comment threadsrc/compiler/checker.ts Outdated
}
}
return false;
return someInMap(moduleSymbol.exports, (_, id) => id !== "export=");

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not just use forEachInMap. The someInMap function seems only marginally useful.

Comment threadsrc/compiler/checker.ts Outdated
symbolLinks.exportsSomeValue = hasExportAssignment
? !!(moduleSymbol.flags & SymbolFlags.Value)
: forEachProperty(getExportsOfModule(moduleSymbol), isValue);
: someInMap(getExportsOfModule(moduleSymbol), isValue);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not just use forEachInMap?

Comment threadsrc/compiler/core.ts Outdated
return map;
}

export const sparseArray: <T>() => SparseArray<T> = createDictionaryObject;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I know you ran some benchmarks regarding the performance of objects vs sparse arrays, but unless the performance difference is fairly significant for our use cases, I'd rather we just continue to leverage actual arrays for sparse arrays. If the difference is significant, then we should use a different name for this function and type.

Comment threadsrc/compiler/core.ts Outdated
}

/** Whether `predicate` is true for some entry in the map. */
export function someInMap<T>(map: Map<T>, predicate: (value: T, key: string) => boolean): boolean {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This function seems unnecessary, forEachInMap serves the same purpose.

Comment threadsrc/compiler/core.ts Outdated
}

/** `someInMap` for just keys. */
export function someKeyInMap(map: Map<{}>, predicate: (key: string) => boolean): boolean {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This also seems like overkill as we could just use forEachInMap and ignore the first argument.

Comment threadsrc/compiler/core.ts Outdated
}

/** Copy entries from `source` to `target`. */
export function copyMapEntries<T>(source: Map<T>, target: Map<T>): void {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: Consider a simpler name like copyEntries.

Comment threadsrc/compiler/core.ts Outdated
return values;
}

export function multiMapSparseArrayAdd<V>(map: SparseArray<V[]>, key: number, value: V): V[] {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is only used in factory.ts. Why not just use multiMapAdd and cast the key to string?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is hard to do since we return the value as a SparseArray, so we'd have to add as any as string at all the external uses too. At least I've moved this function to factory.ts.

Comment threadsrc/compiler/core.ts Outdated
*/
export function multiMapAdd<V>(map: Map<V[]>, key: string | number, value: V): V[] {
const values = map[key];
export function multiMapAdd<V>(map: Map<V[]>, key: string, value: V): V[] {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe we should consider adding a createMultiMap<T> that returns a MultiMap<T>:

interfaceMultiMap<T>extendsMap<T[]>{add(key: string,value: T): T[];remove(key: string,value: T): void;}functioncreateMultiMap<T>(){constmap=<MultiMap<T>>createMap<T[]>();map.add=multiMapAdd;map.remove=multiMapRemove;returnmap;}functionmultiMapAdd<T>(this: MultiMap<T>,key: string,value: T){ ... }functionmultiMapRemove<T>(this: MultiMap<T>,key: string,value: T){ ... }

Comment threadsrc/compiler/core.ts Outdated
let result: U;
for (const key in map) {
if (result = callback(map[key], key)) break;
function arrayFrom<T>(iterator: Iterator<T>): T[] {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe we should just export this and replace all references to keysOfMap and valuesOfMap.

@ghost

Deleted user (ghost) commented Dec 28, 2016

Copy link
Copy Markdown
Author

Not a big fan of 2e6f369 as it would make me suspicious of every array in the codebase.

@rbucktonRon Buckton (rbuckton) left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks good, though we should wait on Anders Hejlsberg (@ahejlsberg)'s feedback before merging.

@rbuckton

Copy link
Copy Markdown
Contributor

Andy (Andrewkraft) (@Andy-MS) I would recommend you update the description of the PR with respect to the final set of new functions added to support using native maps.

@ghost

Copy link
Copy Markdown
Author

Ron Buckton (@rbuckton) Done. And just to be sure, I ran baselines again. Results are still good.

Monaco

node (v0.12.17, x86)

ProjectBaselineCurrentDeltaBestWorst
Memory used224,333k (± 0.05%)190,498k (± 0.19%)-33,835k (- 15.08%)190,298k191,058k
Parse Time1.93s (± 1.50%)1.83s (± 1.40%)-0.10s (- 4.96%)1.80s1.86s
Bind Time0.76s (± 1.09%)0.71s (± 4.64%)-0.05s (- 6.54%)0.68s0.76s
Check Time5.59s (± 5.16%)5.42s (± 4.48%)-0.17s (- 3.13%)5.15s5.72s
Emit Time2.32s (± 4.21%)2.31s (± 7.73%)-0.01s (- 0.50%)2.20s2.56s
Total Time10.60s (± 2.98%)10.26s (± 4.10%)-0.33s (- 3.14%)10.01s10.90s

node (v6.9.2, x64)

ProjectBaselineCurrentDeltaBestWorst
Memory used381,413k (± 0.01%)357,526k (± 0.02%)-23,887k (- 6.26%)357,458k357,590k
Parse Time2.08s (± 1.02%)2.17s (± 25.08%)+0.08s (+ 3.98%)1.93s3.01s
Bind Time1.00s (± 2.21%)0.85s (± 5.81%)-0.15s (- 15.27%)0.81s0.92s
Check Time4.85s (± 1.50%)4.33s (± 4.81%)-0.51s (- 10.58%)4.12s4.53s
Emit Time2.20s (± 1.30%)2.23s (± 2.19%)+0.03s (+ 1.34%)2.16s2.26s
Total Time10.13s (± 0.81%)9.58s (± 5.42%)-0.55s (- 5.46%)9.04s10.29s

node (v6.9.2, x86)

ProjectBaselineCurrentDeltaBestWorst
Memory used203,159k (± 0.00%)190,871k (± 0.01%)-12,288k (- 6.05%)190,849k190,883k
Parse Time1.94s (± 1.41%)1.81s (± 1.00%)-0.13s (- 6.56%)1.79s1.83s
Bind Time0.80s (± 2.36%)0.66s (± 0.78%)-0.14s (- 17.35%)0.66s0.67s
Check Time3.79s (± 0.95%)3.18s (± 2.62%)-0.61s (- 16.12%)3.12s3.30s
Emit Time2.11s (± 1.47%)1.93s (± 0.90%)-0.17s (- 8.15%)1.91s1.95s
Total Time8.63s (± 0.79%)7.58s (± 0.87%)-1.05s (- 12.12%)7.53s7.68s

tsc (x86)

ProjectBaselineCurrentDeltaBestWorst
Parse Time1.62s (± 1.56%)1.49s (± 1.64%)-0.13s (- 8.05%)1.46s1.51s
Bind Time0.71s (± 3.11%)0.54s (± 4.08%)-0.17s (- 24.20%)0.52s0.57s
Check Time3.91s (± 1.49%)3.36s (± 2.07%)-0.55s (- 14.11%)3.29s3.45s
Emit Time5.99s (± 2.95%)5.91s (± 11.64%)-0.08s (- 1.35%)5.59s6.97s
Total Time12.23s (± 1.75%)11.29s (± 6.91%)-0.94s (- 7.67%)10.92s12.50s

TFS

node (v0.12.17, x86)

ProjectBaselineCurrentDeltaBestWorst
Memory used187,646k (± 0.06%)166,272k (± 0.13%)-21,374k (- 11.39%)165,952k166,408k
Parse Time1.35s (± 0.71%)1.28s (± 1.48%)-0.07s (- 5.05%)1.26s1.30s
Bind Time0.70s (± 1.22%)0.61s (± 1.58%)-0.09s (- 13.45%)0.60s0.62s
Check Time3.71s (± 0.72%)3.46s (± 1.46%)-0.25s (- 6.87%)3.42s3.53s
Emit Time1.97s (± 2.05%)1.93s (± 0.98%)-0.04s (- 2.16%)1.91s1.95s
Total Time7.73s (± 0.71%)7.27s (± 0.69%)-0.46s (- 5.96%)7.23s7.34s

node (v6.9.2, x64)

ProjectBaselineCurrentDeltaBestWorst
Memory used327,882k (± 0.01%)308,975k (± 0.01%)-18,907k (- 5.77%)308,943k308,995k
Parse Time1.52s (± 0.46%)1.42s (± 1.05%)-0.10s (- 6.63%)1.41s1.44s
Bind Time0.80s (± 0.67%)0.68s (± 0.76%)-0.12s (- 15.04%)0.67s0.68s
Check Time4.34s (± 0.90%)3.72s (± 4.74%)-0.62s (- 14.23%)3.60s3.99s
Emit Time1.98s (± 1.32%)2.17s (± 5.77%)+0.19s (+ 9.37%)1.99s2.27s
Total Time8.64s (± 0.69%)7.99s (± 1.32%)-0.65s (- 7.53%)7.85s8.07s

node (v6.9.2, x86)

ProjectBaselineCurrentDeltaBestWorst
Memory used174,963k (± 0.01%)165,150k (± 0.02%)-9,813k (- 5.61%)165,127k165,195k
Parse Time1.41s (± 0.77%)1.30s (± 2.38%)-0.10s (- 7.29%)1.28s1.35s
Bind Time0.67s (± 0.93%)0.58s (± 3.34%)-0.10s (- 14.48%)0.56s0.60s
Check Time3.18s (± 0.58%)2.81s (± 1.62%)-0.37s (- 11.52%)2.76s2.87s
Emit Time1.62s (± 0.66%)1.62s (± 3.73%)+0.00s (+ 0.06%)1.57s1.68s
Total Time6.88s (± 0.50%)6.32s (± 1.58%)-0.57s (- 8.24%)6.20s6.43s

tsc (x86)

ProjectBaselineCurrentDeltaBestWorst
Parse Time1.23s (± 2.63%)1.06s (± 1.23%)-0.16s (- 13.39%)1.05s1.08s
Bind Time0.58s (± 1.82%)0.51s (± 2.55%)-0.07s (- 11.46%)0.50s0.53s
Check Time3.25s (± 1.51%)2.76s (± 2.30%)-0.49s (- 15.15%)2.71s2.83s
Emit Time3.58s (± 1.51%)3.49s (± 4.20%)-0.09s (- 2.45%)3.39s3.70s
Total Time8.63s (± 1.20%)7.82s (± 2.28%)-0.81s (- 9.38%)7.70s8.09s

Comment threadsrc/compiler/core.ts Outdated
}

/** Create a new map. If a template object is provided, the map will copy entries from it. */
export function createMap<T>(template?: MapLike<T>): Map<T> {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How often do we actually pass a template to createMap? I seem to remember that functions containing for-in loops are not optimized by V8, so having this optional template stuff may be costly. Perhaps filling a map from a template should just be a separate function.

Comment threadsrc/compiler/checker.ts Outdated
target[id] = source[id];
if (!source) return;

source.forEach((sourceSymbol, id) => {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe use source && source.forEach(...) and get rid of the if (!source) return above.

@@ -1,4 +1,4 @@
/// <reference path="../factory.ts" />
/// <reference path="../factory.ts" />

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's up with these ^M line breaks? There are a lot of them in the following files.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've looked at this and don't know why it shows a diff here.

  • It only shows ^M on lines that I've changed -- if I had changed line endings in the file, presumably they would show as changed in the whole file.
  • When I look at the file, it appears to have CRLF line endings (on all lines) on both master and map5. I have core.autocrlf set to false. Tested on both Windows and Linux.

After merge I'll look back to this file to see if it has mixed line endings, but I doubt that.
CC Daniel Rosenwasser (@DanielRosenwasser)

@ghostghost added the Breaking Change Would introduce errors in existing code label Jan 17, 2017
@ghost
ghost merged commit 65ef51d into masterJan 17, 2017
@ghost
ghost deleted the map5 branch January 17, 2017 19:04
@microsoftMicrosoft (microsoft) locked and limited conversation to collaborators Jun 19, 2018
This pull request was closed.
Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

Breaking ChangeWould introduce errors in existing code

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

@rbuckton@ahejlsberg@msftclas
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Use native maps when they're available - #12715

Merged
30 commits merged into
masterfrom
map5
Jan 17, 2017
Merged

Use native maps when they're available#12715
30 commits merged into
masterfrom
map5

Conversation

@ghost

@ghostDeleted user (ghost) commented Dec 7, 2016

Copy link
Copy Markdown

Similar to #11354, but simpler.

  • Keeps using Map<T> instead of Map<K, V>, with K assumed to be string. For number-keyed maps we now use sparse arrays.
  • Does not include Set -- this can be done later.
  • Does not include sortInV8ObjectInsertionOrder; changes baselines instead. We won't be able to pass all tests when using the shim map, but we no longer run tests on node 0.10 anyway.

Added functions

NameUse
arrayFromConverts anIterator to an Array. arrayFrom(map.keys()) replaces Object.keys(map). arrayFrom(map.values()) also lets us remove reduceProperties, which was only used to compute that.
forEachEntry, forEachKeyThese replace for-in loops with early termination, forEachProperty, and someProperties.
copyEntriesReplaces copyProperties.
createMultiMapReplaces multiMapAdd and multiMapRemove.
mapsAreEqualReplaces a use of equalOwnProperties. Used only in reuseProgramStructure.ts, so moved it there.
multiMapSparseArrayAddReplaces a use of multiMapAdd on what is now a sparse array. Used only in factory.ts, so moved it there.
mapEntriesReplaces mapObject.

We also get rid of isEmpty and replace with map.size === 0.

@ghost

Deleted user (ghost) commented Dec 8, 2016

Copy link
Copy Markdown
Author

Comparing this branch to master, we now use 5% less memory and time.

Monaco

node (v6.9.2, x64)
ProjectBaselineCurrentDeltaBestWorst
Memory used381,190k (± 0.01%)358,147k (± 0.02%)-23,043k (- 6.04%)358,085k358,209k
Parse Time2.44s (± 0.94%)2.35s (± 1.23%)-0.09s (- 3.52%)2.33s2.39s
Bind Time0.93s (± 2.62%)0.77s (± 1.71%)-0.17s (- 17.94%)0.75s0.78s
Check Time4.53s (± 3.18%)4.33s (± 3.47%)-0.21s (- 4.53%)4.23s4.55s
Emit Time2.09s (± 0.98%)2.10s (± 3.31%)+0.01s (+ 0.55%)2.04s2.17s
Total Time9.99s (± 1.94%)9.55s (± 2.49%)-0.45s (- 4.46%)9.36s9.88s
node (v6.9.2, x86)
ProjectBaselineCurrentDeltaBestWorst
Memory used203,018k (± 0.01%)191,475k (± 0.02%)-11,543k (- 5.69%)191,450k191,522k
Parse Time2.30s (± 0.52%)2.21s (± 0.87%)-0.10s (- 4.19%)2.18s2.22s
Bind Time0.75s (± 1.50%)0.64s (± 2.43%)-0.11s (- 14.63%)0.62s0.65s
Check Time3.69s (± 2.45%)3.41s (± 1.28%)-0.29s (- 7.81%)3.35s3.45s
Emit Time2.09s (± 3.24%)2.10s (± 0.63%)+0.01s (+ 0.50%)2.08s2.11s
Total Time8.83s (± 1.25%)8.34s (± 0.77%)-0.48s (- 5.49%)8.27s8.40s
tsc (x86)
ProjectBaselineCurrentDeltaBestWorst
Parse Time1.82s (± 0.83%)1.72s (± 1.70%)-0.10s (- 5.39%)1.69s1.75s
Bind Time0.77s (± 5.13%)0.53s (± 2.82%)-0.23s (- 30.46%)0.52s0.55s
Check Time3.38s (± 1.26%)3.04s (± 3.24%)-0.34s (- 10.03%)2.94s3.15s
Emit Time6.08s (± 3.39%)6.09s (± 5.13%)+0.00s (+ 0.02%)5.93s6.57s
Total Time12.05s (± 2.00%)11.38s (± 2.47%)-0.67s (- 5.54%)11.15s11.78s

TFS

node (v6.9.2, x64)
ProjectBaselineCurrentDeltaBestWorst
Memory used327,608k (± 0.01%)309,067k (± 0.02%)-18,541k (- 5.66%)308,995k309,106k
Parse Time1.52s (± 0.74%)1.44s (± 1.04%)-0.07s (- 4.85%)1.42s1.45s
Bind Time0.75s (± 0.79%)0.65s (± 2.30%)-0.10s (- 13.30%)0.64s0.67s
Check Time3.85s (± 0.88%)3.78s (± 1.25%)-0.07s (- 1.75%)3.73s3.82s
Emit Time1.79s (± 0.65%)1.93s (± 2.76%)+0.14s (+ 7.60%)1.89s2.00s
Total Time7.91s (± 0.48%)7.80s (± 1.08%)-0.11s (- 1.34%)7.73s7.91s
node (v6.9.2, x86)
ProjectBaselineCurrentDeltaBestWorst
Memory used174,790k (± 0.01%)165,324k (± 0.03%)-9,466k (- 5.42%)165,287k165,405k
Parse Time1.44s (± 0.81%)1.32s (± 1.90%)-0.11s (- 7.74%)1.31s1.36s
Bind Time0.64s (± 1.95%)0.55s (± 3.77%)-0.09s (- 13.64%)0.53s0.58s
Check Time2.96s (± 0.68%)2.70s (± 1.02%)-0.26s (- 8.81%)2.67s2.73s
Emit Time1.56s (± 0.71%)1.55s (± 5.97%)-0.01s (- 0.51%)1.50s1.69s
Total Time6.59s (± 0.65%)6.13s (± 1.85%)-0.46s (- 7.03%)6.06s6.30s
tsc (x86)
ProjectBaselineCurrentDeltaBestWorst
Parse Time1.27s (± 3.07%)1.11s (± 0.87%)-0.15s (- 12.16%)1.10s1.12s
Bind Time0.54s (± 5.05%)0.56s (± 4.23%)+0.02s (+ 3.49%)0.55s0.60s
Check Time2.89s (± 3.78%)2.66s (± 2.91%)-0.24s (- 8.15%)2.55s2.73s
Emit Time3.67s (± 1.73%)3.56s (± 2.75%)-0.11s (- 3.00%)3.48s3.68s
Total Time8.37s (± 2.42%)7.89s (± 2.01%)-0.48s (- 5.72%)7.70s8.04s

Older node versions

Surprisingly, memory is reduced for node v0.12.17 (x86)! I'm not sure why. (EDIT: node 0.12 provides Map; I'll have to go even earlier to test with shims.)

monaco
ProjectBaselineCurrentDeltaBestWorst
Memory used224,254k (± 0.22%)194,721k (± 0.32%)-29,533k (- 13.17%)194,176k195,473k
Parse Time2.49s (± 0.79%)2.40s (± 1.20%)-0.10s (- 3.95%)2.38s2.44s
Bind Time0.80s (± 1.35%)0.66s (± 1.97%)-0.14s (- 17.16%)0.65s0.68s
Check Time4.70s (± 1.24%)4.99s (± 2.81%)+0.29s (+ 6.11%)4.88s5.20s
Emit Time2.15s (± 1.22%)2.24s (± 4.84%)+0.09s (+ 4.29%)2.09s2.31s
Total Time10.15s (± 0.67%)10.29s (± 1.67%)+0.14s (+ 1.43%)10.08s10.49s
TFS
ProjectBaselineCurrentDeltaBestWorst
Memory used187,770k (± 0.06%)166,329k (± 0.16%)-21,441k (- 11.42%)166,051k166,633k
Parse Time1.46s (± 1.12%)1.39s (± 3.49%)-0.07s (- 4.60%)1.35s1.46s
Bind Time0.67s (± 0.90%)0.58s (± 1.09%)-0.09s (- 13.71%)0.57s0.58s
Check Time3.52s (± 1.13%)3.22s (± 1.09%)-0.30s (- 8.44%)3.19s3.26s
Emit Time1.96s (± 2.11%)1.85s (± 2.01%)-0.11s (- 5.76%)1.81s1.90s
Total Time7.60s (± 0.91%)7.04s (± 1.01%)-0.56s (- 7.42%)6.98s7.14s

Comment threadsrc/compiler/types.ts Outdated
export interface Map<T> extends MapLike<T> {
__mapBrand: any;
/** It's allowed to get/set into a map with numbers. However, when iterating, you may get strings back due to the shim being an ordinary object (which only allows string keys). */
export type MapKey = string | number;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should only allow strings for the keys of Map<T> and go with Anders Hejlsberg (@ahejlsberg)'s suggestion to use sparse arrays for "maps" with only numeric keys.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My test shows a number-keyed Map to be slightly more efficient.

Comment threadsrc/compiler/core.ts Outdated
const map: Map<T> = createObject(null); // tslint:disable-line:no-null-keyword
const createObject = Object.create;
/** Create a MapLike with good performance. Prefer this over a literal `{}`. */
export function createMapLike<T>(): MapLike<T> {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd call this createDictionaryObject and keep it internal to the module (don't export it). We only use it in four places across all of src/ and those cases don't look like they'd suffer from just using {}.

@ghostDeleted user (ghost)Dec 12, 2016

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If someone modifies Object.prototype that will cause problems for {}. Isn't that why we have this function?

Comment threadsrc/compiler/core.ts
const MapCtr = usingNativeMaps ? Map : shimMap();

// Keep the class inside a function so it doesn't get compiled if it's not used.
function shimMap(): { new<T>(): Map<T> } {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you want the Map shim to have support for keys/values/entries iterators, take a look at https://gist.github.com/rbuckton/06d2c79bea46778f9e8bbeca77292087. That way there is less differentiation between what we need out of a native Map and what our shim provides.

Comment threadsrc/compiler/core.ts Outdated
export function someProperties<T>(map: Map<T>, predicate?: (value: T, key: string) => boolean) {
for (const key in map) {
if (!predicate || predicate(map[key], key)) return true;
export const forEachInMap: <T, U>(map: Map<T>, callback: (value: T, key: string) => U | undefined) => U | undefined = usingNativeMaps

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we use the MapIterator shim in my gist/comment above, then we don't need to differentiate.

Comment threadsrc/compiler/core.ts Outdated
: <T, U>(map: ShimMap<T>, callback: (value: T, key: string) => U | undefined) => map.forEachInMap(callback);

/** `forEachInMap` for just keys. */
export function forEachKeyInMap<T>(map: Map<{}>, callback: (key: string) => T | undefined): T | undefined {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it be more efficient to iterate over map.keys() and avoid the closure?

const scriptInfo = this.getScriptInfo(fileName);
const preprocessInfo = ts.preProcessFile(scriptInfo.content, /*readImportFiles*/ true);
const imports = ts.createMap<string>();
const imports = ts.createMapLike<string>();

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should just use {} for these cases.

Comment threadsrc/harness/unittests/session.ts Outdated
if (response.request_seq in this.callbacks) {
this.callbacks[response.request_seq](response);
delete this.callbacks[response.request_seq];
const handler = this.callbacks.get(response.request_seq);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Either callbacks should be a sparse array, or we should coerce response.request_seq to a string.


export class Callbacks {
private map: { [n: number]: TimeOutCallback } = {};
private map = createMap<TimeOutCallback>();

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use a sparse array here.

this.symbolIdToActionMap[symbolId] = [newAction];
const actions = this.symbolIdToActionMap.get(symbolId);
if (!actions) {
this.symbolIdToActionMap.set(symbolId, [newAction]);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be a sparse array.

Comment threadsrc/services/services.ts Outdated

function getDeclarations(name: string) {
return result[name] || (result[name] = []);
return result.get(name) || set(result, name, []);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just use multiple lines so we don't need to use set.

Comment threadsrc/compiler/core.ts Outdated
return map;
}

export const sparseArray: <T>() => SparseArray<T> = createDictionaryObject;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is the purpose of this? This isn't a sparse array.

Comment threadsrc/compiler/checker.ts Outdated
}
}
return false;
return someInMap(moduleSymbol.exports, (_, id) => id !== "export=");

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not just use forEachInMap. The someInMap function seems only marginally useful.

Comment threadsrc/compiler/checker.ts Outdated
symbolLinks.exportsSomeValue = hasExportAssignment
? !!(moduleSymbol.flags & SymbolFlags.Value)
: forEachProperty(getExportsOfModule(moduleSymbol), isValue);
: someInMap(getExportsOfModule(moduleSymbol), isValue);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not just use forEachInMap?

Comment threadsrc/compiler/core.ts Outdated
return map;
}

export const sparseArray: <T>() => SparseArray<T> = createDictionaryObject;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I know you ran some benchmarks regarding the performance of objects vs sparse arrays, but unless the performance difference is fairly significant for our use cases, I'd rather we just continue to leverage actual arrays for sparse arrays. If the difference is significant, then we should use a different name for this function and type.

Comment threadsrc/compiler/core.ts Outdated
}

/** Whether `predicate` is true for some entry in the map. */
export function someInMap<T>(map: Map<T>, predicate: (value: T, key: string) => boolean): boolean {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This function seems unnecessary, forEachInMap serves the same purpose.

Comment threadsrc/compiler/core.ts Outdated
}

/** `someInMap` for just keys. */
export function someKeyInMap(map: Map<{}>, predicate: (key: string) => boolean): boolean {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This also seems like overkill as we could just use forEachInMap and ignore the first argument.

Comment threadsrc/compiler/core.ts Outdated
}

/** Copy entries from `source` to `target`. */
export function copyMapEntries<T>(source: Map<T>, target: Map<T>): void {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: Consider a simpler name like copyEntries.

Comment threadsrc/compiler/core.ts Outdated
return values;
}

export function multiMapSparseArrayAdd<V>(map: SparseArray<V[]>, key: number, value: V): V[] {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is only used in factory.ts. Why not just use multiMapAdd and cast the key to string?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is hard to do since we return the value as a SparseArray, so we'd have to add as any as string at all the external uses too. At least I've moved this function to factory.ts.

Comment threadsrc/compiler/core.ts Outdated
*/
export function multiMapAdd<V>(map: Map<V[]>, key: string | number, value: V): V[] {
const values = map[key];
export function multiMapAdd<V>(map: Map<V[]>, key: string, value: V): V[] {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe we should consider adding a createMultiMap<T> that returns a MultiMap<T>:

interfaceMultiMap<T>extendsMap<T[]>{add(key: string,value: T): T[];remove(key: string,value: T): void;}functioncreateMultiMap<T>(){constmap=<MultiMap<T>>createMap<T[]>();map.add=multiMapAdd;map.remove=multiMapRemove;returnmap;}functionmultiMapAdd<T>(this: MultiMap<T>,key: string,value: T){ ... }functionmultiMapRemove<T>(this: MultiMap<T>,key: string,value: T){ ... }

Comment threadsrc/compiler/core.ts Outdated
let result: U;
for (const key in map) {
if (result = callback(map[key], key)) break;
function arrayFrom<T>(iterator: Iterator<T>): T[] {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe we should just export this and replace all references to keysOfMap and valuesOfMap.

@ghost

Deleted user (ghost) commented Dec 28, 2016

Copy link
Copy Markdown
Author

Not a big fan of 2e6f369 as it would make me suspicious of every array in the codebase.

@rbucktonRon Buckton (rbuckton) left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks good, though we should wait on Anders Hejlsberg (@ahejlsberg)'s feedback before merging.

@rbuckton

Copy link
Copy Markdown
Contributor

Andy (Andrewkraft) (@Andy-MS) I would recommend you update the description of the PR with respect to the final set of new functions added to support using native maps.

@ghost

Copy link
Copy Markdown
Author

Ron Buckton (@rbuckton) Done. And just to be sure, I ran baselines again. Results are still good.

Monaco

node (v0.12.17, x86)

ProjectBaselineCurrentDeltaBestWorst
Memory used224,333k (± 0.05%)190,498k (± 0.19%)-33,835k (- 15.08%)190,298k191,058k
Parse Time1.93s (± 1.50%)1.83s (± 1.40%)-0.10s (- 4.96%)1.80s1.86s
Bind Time0.76s (± 1.09%)0.71s (± 4.64%)-0.05s (- 6.54%)0.68s0.76s
Check Time5.59s (± 5.16%)5.42s (± 4.48%)-0.17s (- 3.13%)5.15s5.72s
Emit Time2.32s (± 4.21%)2.31s (± 7.73%)-0.01s (- 0.50%)2.20s2.56s
Total Time10.60s (± 2.98%)10.26s (± 4.10%)-0.33s (- 3.14%)10.01s10.90s

node (v6.9.2, x64)

ProjectBaselineCurrentDeltaBestWorst
Memory used381,413k (± 0.01%)357,526k (± 0.02%)-23,887k (- 6.26%)357,458k357,590k
Parse Time2.08s (± 1.02%)2.17s (± 25.08%)+0.08s (+ 3.98%)1.93s3.01s
Bind Time1.00s (± 2.21%)0.85s (± 5.81%)-0.15s (- 15.27%)0.81s0.92s
Check Time4.85s (± 1.50%)4.33s (± 4.81%)-0.51s (- 10.58%)4.12s4.53s
Emit Time2.20s (± 1.30%)2.23s (± 2.19%)+0.03s (+ 1.34%)2.16s2.26s
Total Time10.13s (± 0.81%)9.58s (± 5.42%)-0.55s (- 5.46%)9.04s10.29s

node (v6.9.2, x86)

ProjectBaselineCurrentDeltaBestWorst
Memory used203,159k (± 0.00%)190,871k (± 0.01%)-12,288k (- 6.05%)190,849k190,883k
Parse Time1.94s (± 1.41%)1.81s (± 1.00%)-0.13s (- 6.56%)1.79s1.83s
Bind Time0.80s (± 2.36%)0.66s (± 0.78%)-0.14s (- 17.35%)0.66s0.67s
Check Time3.79s (± 0.95%)3.18s (± 2.62%)-0.61s (- 16.12%)3.12s3.30s
Emit Time2.11s (± 1.47%)1.93s (± 0.90%)-0.17s (- 8.15%)1.91s1.95s
Total Time8.63s (± 0.79%)7.58s (± 0.87%)-1.05s (- 12.12%)7.53s7.68s

tsc (x86)

ProjectBaselineCurrentDeltaBestWorst
Parse Time1.62s (± 1.56%)1.49s (± 1.64%)-0.13s (- 8.05%)1.46s1.51s
Bind Time0.71s (± 3.11%)0.54s (± 4.08%)-0.17s (- 24.20%)0.52s0.57s
Check Time3.91s (± 1.49%)3.36s (± 2.07%)-0.55s (- 14.11%)3.29s3.45s
Emit Time5.99s (± 2.95%)5.91s (± 11.64%)-0.08s (- 1.35%)5.59s6.97s
Total Time12.23s (± 1.75%)11.29s (± 6.91%)-0.94s (- 7.67%)10.92s12.50s

TFS

node (v0.12.17, x86)

ProjectBaselineCurrentDeltaBestWorst
Memory used187,646k (± 0.06%)166,272k (± 0.13%)-21,374k (- 11.39%)165,952k166,408k
Parse Time1.35s (± 0.71%)1.28s (± 1.48%)-0.07s (- 5.05%)1.26s1.30s
Bind Time0.70s (± 1.22%)0.61s (± 1.58%)-0.09s (- 13.45%)0.60s0.62s
Check Time3.71s (± 0.72%)3.46s (± 1.46%)-0.25s (- 6.87%)3.42s3.53s
Emit Time1.97s (± 2.05%)1.93s (± 0.98%)-0.04s (- 2.16%)1.91s1.95s
Total Time7.73s (± 0.71%)7.27s (± 0.69%)-0.46s (- 5.96%)7.23s7.34s

node (v6.9.2, x64)

ProjectBaselineCurrentDeltaBestWorst
Memory used327,882k (± 0.01%)308,975k (± 0.01%)-18,907k (- 5.77%)308,943k308,995k
Parse Time1.52s (± 0.46%)1.42s (± 1.05%)-0.10s (- 6.63%)1.41s1.44s
Bind Time0.80s (± 0.67%)0.68s (± 0.76%)-0.12s (- 15.04%)0.67s0.68s
Check Time4.34s (± 0.90%)3.72s (± 4.74%)-0.62s (- 14.23%)3.60s3.99s
Emit Time1.98s (± 1.32%)2.17s (± 5.77%)+0.19s (+ 9.37%)1.99s2.27s
Total Time8.64s (± 0.69%)7.99s (± 1.32%)-0.65s (- 7.53%)7.85s8.07s

node (v6.9.2, x86)

ProjectBaselineCurrentDeltaBestWorst
Memory used174,963k (± 0.01%)165,150k (± 0.02%)-9,813k (- 5.61%)165,127k165,195k
Parse Time1.41s (± 0.77%)1.30s (± 2.38%)-0.10s (- 7.29%)1.28s1.35s
Bind Time0.67s (± 0.93%)0.58s (± 3.34%)-0.10s (- 14.48%)0.56s0.60s
Check Time3.18s (± 0.58%)2.81s (± 1.62%)-0.37s (- 11.52%)2.76s2.87s
Emit Time1.62s (± 0.66%)1.62s (± 3.73%)+0.00s (+ 0.06%)1.57s1.68s
Total Time6.88s (± 0.50%)6.32s (± 1.58%)-0.57s (- 8.24%)6.20s6.43s

tsc (x86)

ProjectBaselineCurrentDeltaBestWorst
Parse Time1.23s (± 2.63%)1.06s (± 1.23%)-0.16s (- 13.39%)1.05s1.08s
Bind Time0.58s (± 1.82%)0.51s (± 2.55%)-0.07s (- 11.46%)0.50s0.53s
Check Time3.25s (± 1.51%)2.76s (± 2.30%)-0.49s (- 15.15%)2.71s2.83s
Emit Time3.58s (± 1.51%)3.49s (± 4.20%)-0.09s (- 2.45%)3.39s3.70s
Total Time8.63s (± 1.20%)7.82s (± 2.28%)-0.81s (- 9.38%)7.70s8.09s

Comment threadsrc/compiler/core.ts Outdated
}

/** Create a new map. If a template object is provided, the map will copy entries from it. */
export function createMap<T>(template?: MapLike<T>): Map<T> {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How often do we actually pass a template to createMap? I seem to remember that functions containing for-in loops are not optimized by V8, so having this optional template stuff may be costly. Perhaps filling a map from a template should just be a separate function.

Comment threadsrc/compiler/checker.ts Outdated
target[id] = source[id];
if (!source) return;

source.forEach((sourceSymbol, id) => {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe use source && source.forEach(...) and get rid of the if (!source) return above.

@@ -1,4 +1,4 @@
/// <reference path="../factory.ts" />
/// <reference path="../factory.ts" />

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's up with these ^M line breaks? There are a lot of them in the following files.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've looked at this and don't know why it shows a diff here.

  • It only shows ^M on lines that I've changed -- if I had changed line endings in the file, presumably they would show as changed in the whole file.
  • When I look at the file, it appears to have CRLF line endings (on all lines) on both master and map5. I have core.autocrlf set to false. Tested on both Windows and Linux.

After merge I'll look back to this file to see if it has mixed line endings, but I doubt that.
CC Daniel Rosenwasser (@DanielRosenwasser)

@ghostghost added the Breaking Change Would introduce errors in existing code label Jan 17, 2017
@ghost
ghost merged commit 65ef51d into masterJan 17, 2017
@ghost
ghost deleted the map5 branch January 17, 2017 19:04
@microsoftMicrosoft (microsoft) locked and limited conversation to collaborators Jun 19, 2018
This pull request was closed.
Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

Breaking ChangeWould introduce errors in existing code

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

@rbuckton@ahejlsberg@msftclas
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

Use native maps when they're available - #12715

Merged
30 commits merged into
masterfrom
map5
Jan 17, 2017
Merged

Use native maps when they're available#12715
30 commits merged into
masterfrom
map5

Conversation

@ghost

@ghostDeleted user (ghost) commented Dec 7, 2016

Copy link
Copy Markdown

Similar to #11354, but simpler.

  • Keeps using Map<T> instead of Map<K, V>, with K assumed to be string. For number-keyed maps we now use sparse arrays.
  • Does not include Set -- this can be done later.
  • Does not include sortInV8ObjectInsertionOrder; changes baselines instead. We won't be able to pass all tests when using the shim map, but we no longer run tests on node 0.10 anyway.

Added functions

NameUse
arrayFromConverts anIterator to an Array. arrayFrom(map.keys()) replaces Object.keys(map). arrayFrom(map.values()) also lets us remove reduceProperties, which was only used to compute that.
forEachEntry, forEachKeyThese replace for-in loops with early termination, forEachProperty, and someProperties.
copyEntriesReplaces copyProperties.
createMultiMapReplaces multiMapAdd and multiMapRemove.
mapsAreEqualReplaces a use of equalOwnProperties. Used only in reuseProgramStructure.ts, so moved it there.
multiMapSparseArrayAddReplaces a use of multiMapAdd on what is now a sparse array. Used only in factory.ts, so moved it there.
mapEntriesReplaces mapObject.

We also get rid of isEmpty and replace with map.size === 0.

@ghost

Deleted user (ghost) commented Dec 8, 2016

Copy link
Copy Markdown
Author

Comparing this branch to master, we now use 5% less memory and time.

Monaco

node (v6.9.2, x64)
ProjectBaselineCurrentDeltaBestWorst
Memory used381,190k (± 0.01%)358,147k (± 0.02%)-23,043k (- 6.04%)358,085k358,209k
Parse Time2.44s (± 0.94%)2.35s (± 1.23%)-0.09s (- 3.52%)2.33s2.39s
Bind Time0.93s (± 2.62%)0.77s (± 1.71%)-0.17s (- 17.94%)0.75s0.78s
Check Time4.53s (± 3.18%)4.33s (± 3.47%)-0.21s (- 4.53%)4.23s4.55s
Emit Time2.09s (± 0.98%)2.10s (± 3.31%)+0.01s (+ 0.55%)2.04s2.17s
Total Time9.99s (± 1.94%)9.55s (± 2.49%)-0.45s (- 4.46%)9.36s9.88s
node (v6.9.2, x86)
ProjectBaselineCurrentDeltaBestWorst
Memory used203,018k (± 0.01%)191,475k (± 0.02%)-11,543k (- 5.69%)191,450k191,522k
Parse Time2.30s (± 0.52%)2.21s (± 0.87%)-0.10s (- 4.19%)2.18s2.22s
Bind Time0.75s (± 1.50%)0.64s (± 2.43%)-0.11s (- 14.63%)0.62s0.65s
Check Time3.69s (± 2.45%)3.41s (± 1.28%)-0.29s (- 7.81%)3.35s3.45s
Emit Time2.09s (± 3.24%)2.10s (± 0.63%)+0.01s (+ 0.50%)2.08s2.11s
Total Time8.83s (± 1.25%)8.34s (± 0.77%)-0.48s (- 5.49%)8.27s8.40s
tsc (x86)
ProjectBaselineCurrentDeltaBestWorst
Parse Time1.82s (± 0.83%)1.72s (± 1.70%)-0.10s (- 5.39%)1.69s1.75s
Bind Time0.77s (± 5.13%)0.53s (± 2.82%)-0.23s (- 30.46%)0.52s0.55s
Check Time3.38s (± 1.26%)3.04s (± 3.24%)-0.34s (- 10.03%)2.94s3.15s
Emit Time6.08s (± 3.39%)6.09s (± 5.13%)+0.00s (+ 0.02%)5.93s6.57s
Total Time12.05s (± 2.00%)11.38s (± 2.47%)-0.67s (- 5.54%)11.15s11.78s

TFS

node (v6.9.2, x64)
ProjectBaselineCurrentDeltaBestWorst
Memory used327,608k (± 0.01%)309,067k (± 0.02%)-18,541k (- 5.66%)308,995k309,106k
Parse Time1.52s (± 0.74%)1.44s (± 1.04%)-0.07s (- 4.85%)1.42s1.45s
Bind Time0.75s (± 0.79%)0.65s (± 2.30%)-0.10s (- 13.30%)0.64s0.67s
Check Time3.85s (± 0.88%)3.78s (± 1.25%)-0.07s (- 1.75%)3.73s3.82s
Emit Time1.79s (± 0.65%)1.93s (± 2.76%)+0.14s (+ 7.60%)1.89s2.00s
Total Time7.91s (± 0.48%)7.80s (± 1.08%)-0.11s (- 1.34%)7.73s7.91s
node (v6.9.2, x86)
ProjectBaselineCurrentDeltaBestWorst
Memory used174,790k (± 0.01%)165,324k (± 0.03%)-9,466k (- 5.42%)165,287k165,405k
Parse Time1.44s (± 0.81%)1.32s (± 1.90%)-0.11s (- 7.74%)1.31s1.36s
Bind Time0.64s (± 1.95%)0.55s (± 3.77%)-0.09s (- 13.64%)0.53s0.58s
Check Time2.96s (± 0.68%)2.70s (± 1.02%)-0.26s (- 8.81%)2.67s2.73s
Emit Time1.56s (± 0.71%)1.55s (± 5.97%)-0.01s (- 0.51%)1.50s1.69s
Total Time6.59s (± 0.65%)6.13s (± 1.85%)-0.46s (- 7.03%)6.06s6.30s
tsc (x86)
ProjectBaselineCurrentDeltaBestWorst
Parse Time1.27s (± 3.07%)1.11s (± 0.87%)-0.15s (- 12.16%)1.10s1.12s
Bind Time0.54s (± 5.05%)0.56s (± 4.23%)+0.02s (+ 3.49%)0.55s0.60s
Check Time2.89s (± 3.78%)2.66s (± 2.91%)-0.24s (- 8.15%)2.55s2.73s
Emit Time3.67s (± 1.73%)3.56s (± 2.75%)-0.11s (- 3.00%)3.48s3.68s
Total Time8.37s (± 2.42%)7.89s (± 2.01%)-0.48s (- 5.72%)7.70s8.04s

Older node versions

Surprisingly, memory is reduced for node v0.12.17 (x86)! I'm not sure why. (EDIT: node 0.12 provides Map; I'll have to go even earlier to test with shims.)

monaco
ProjectBaselineCurrentDeltaBestWorst
Memory used224,254k (± 0.22%)194,721k (± 0.32%)-29,533k (- 13.17%)194,176k195,473k
Parse Time2.49s (± 0.79%)2.40s (± 1.20%)-0.10s (- 3.95%)2.38s2.44s
Bind Time0.80s (± 1.35%)0.66s (± 1.97%)-0.14s (- 17.16%)0.65s0.68s
Check Time4.70s (± 1.24%)4.99s (± 2.81%)+0.29s (+ 6.11%)4.88s5.20s
Emit Time2.15s (± 1.22%)2.24s (± 4.84%)+0.09s (+ 4.29%)2.09s2.31s
Total Time10.15s (± 0.67%)10.29s (± 1.67%)+0.14s (+ 1.43%)10.08s10.49s
TFS
ProjectBaselineCurrentDeltaBestWorst
Memory used187,770k (± 0.06%)166,329k (± 0.16%)-21,441k (- 11.42%)166,051k166,633k
Parse Time1.46s (± 1.12%)1.39s (± 3.49%)-0.07s (- 4.60%)1.35s1.46s
Bind Time0.67s (± 0.90%)0.58s (± 1.09%)-0.09s (- 13.71%)0.57s0.58s
Check Time3.52s (± 1.13%)3.22s (± 1.09%)-0.30s (- 8.44%)3.19s3.26s
Emit Time1.96s (± 2.11%)1.85s (± 2.01%)-0.11s (- 5.76%)1.81s1.90s
Total Time7.60s (± 0.91%)7.04s (± 1.01%)-0.56s (- 7.42%)6.98s7.14s

Comment threadsrc/compiler/types.ts Outdated
export interface Map<T> extends MapLike<T> {
__mapBrand: any;
/** It's allowed to get/set into a map with numbers. However, when iterating, you may get strings back due to the shim being an ordinary object (which only allows string keys). */
export type MapKey = string | number;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should only allow strings for the keys of Map<T> and go with Anders Hejlsberg (@ahejlsberg)'s suggestion to use sparse arrays for "maps" with only numeric keys.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My test shows a number-keyed Map to be slightly more efficient.

Comment threadsrc/compiler/core.ts Outdated
const map: Map<T> = createObject(null); // tslint:disable-line:no-null-keyword
const createObject = Object.create;
/** Create a MapLike with good performance. Prefer this over a literal `{}`. */
export function createMapLike<T>(): MapLike<T> {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd call this createDictionaryObject and keep it internal to the module (don't export it). We only use it in four places across all of src/ and those cases don't look like they'd suffer from just using {}.

@ghostDeleted user (ghost)Dec 12, 2016

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If someone modifies Object.prototype that will cause problems for {}. Isn't that why we have this function?

Comment threadsrc/compiler/core.ts
const MapCtr = usingNativeMaps ? Map : shimMap();

// Keep the class inside a function so it doesn't get compiled if it's not used.
function shimMap(): { new<T>(): Map<T> } {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you want the Map shim to have support for keys/values/entries iterators, take a look at https://gist.github.com/rbuckton/06d2c79bea46778f9e8bbeca77292087. That way there is less differentiation between what we need out of a native Map and what our shim provides.

Comment threadsrc/compiler/core.ts Outdated
export function someProperties<T>(map: Map<T>, predicate?: (value: T, key: string) => boolean) {
for (const key in map) {
if (!predicate || predicate(map[key], key)) return true;
export const forEachInMap: <T, U>(map: Map<T>, callback: (value: T, key: string) => U | undefined) => U | undefined = usingNativeMaps

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we use the MapIterator shim in my gist/comment above, then we don't need to differentiate.

Comment threadsrc/compiler/core.ts Outdated
: <T, U>(map: ShimMap<T>, callback: (value: T, key: string) => U | undefined) => map.forEachInMap(callback);

/** `forEachInMap` for just keys. */
export function forEachKeyInMap<T>(map: Map<{}>, callback: (key: string) => T | undefined): T | undefined {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it be more efficient to iterate over map.keys() and avoid the closure?

const scriptInfo = this.getScriptInfo(fileName);
const preprocessInfo = ts.preProcessFile(scriptInfo.content, /*readImportFiles*/ true);
const imports = ts.createMap<string>();
const imports = ts.createMapLike<string>();

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should just use {} for these cases.

Comment threadsrc/harness/unittests/session.ts Outdated
if (response.request_seq in this.callbacks) {
this.callbacks[response.request_seq](response);
delete this.callbacks[response.request_seq];
const handler = this.callbacks.get(response.request_seq);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Either callbacks should be a sparse array, or we should coerce response.request_seq to a string.


export class Callbacks {
private map: { [n: number]: TimeOutCallback } = {};
private map = createMap<TimeOutCallback>();

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use a sparse array here.

this.symbolIdToActionMap[symbolId] = [newAction];
const actions = this.symbolIdToActionMap.get(symbolId);
if (!actions) {
this.symbolIdToActionMap.set(symbolId, [newAction]);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be a sparse array.

Comment threadsrc/services/services.ts Outdated

function getDeclarations(name: string) {
return result[name] || (result[name] = []);
return result.get(name) || set(result, name, []);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just use multiple lines so we don't need to use set.

Comment threadsrc/compiler/core.ts Outdated
return map;
}

export const sparseArray: <T>() => SparseArray<T> = createDictionaryObject;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is the purpose of this? This isn't a sparse array.

Comment threadsrc/compiler/checker.ts Outdated
}
}
return false;
return someInMap(moduleSymbol.exports, (_, id) => id !== "export=");

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not just use forEachInMap. The someInMap function seems only marginally useful.

Comment threadsrc/compiler/checker.ts Outdated
symbolLinks.exportsSomeValue = hasExportAssignment
? !!(moduleSymbol.flags & SymbolFlags.Value)
: forEachProperty(getExportsOfModule(moduleSymbol), isValue);
: someInMap(getExportsOfModule(moduleSymbol), isValue);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not just use forEachInMap?

Comment threadsrc/compiler/core.ts Outdated
return map;
}

export const sparseArray: <T>() => SparseArray<T> = createDictionaryObject;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I know you ran some benchmarks regarding the performance of objects vs sparse arrays, but unless the performance difference is fairly significant for our use cases, I'd rather we just continue to leverage actual arrays for sparse arrays. If the difference is significant, then we should use a different name for this function and type.

Comment threadsrc/compiler/core.ts Outdated
}

/** Whether `predicate` is true for some entry in the map. */
export function someInMap<T>(map: Map<T>, predicate: (value: T, key: string) => boolean): boolean {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This function seems unnecessary, forEachInMap serves the same purpose.

Comment threadsrc/compiler/core.ts Outdated
}

/** `someInMap` for just keys. */
export function someKeyInMap(map: Map<{}>, predicate: (key: string) => boolean): boolean {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This also seems like overkill as we could just use forEachInMap and ignore the first argument.

Comment threadsrc/compiler/core.ts Outdated
}

/** Copy entries from `source` to `target`. */
export function copyMapEntries<T>(source: Map<T>, target: Map<T>): void {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: Consider a simpler name like copyEntries.

Comment threadsrc/compiler/core.ts Outdated
return values;
}

export function multiMapSparseArrayAdd<V>(map: SparseArray<V[]>, key: number, value: V): V[] {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is only used in factory.ts. Why not just use multiMapAdd and cast the key to string?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is hard to do since we return the value as a SparseArray, so we'd have to add as any as string at all the external uses too. At least I've moved this function to factory.ts.

Comment threadsrc/compiler/core.ts Outdated
*/
export function multiMapAdd<V>(map: Map<V[]>, key: string | number, value: V): V[] {
const values = map[key];
export function multiMapAdd<V>(map: Map<V[]>, key: string, value: V): V[] {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe we should consider adding a createMultiMap<T> that returns a MultiMap<T>:

interfaceMultiMap<T>extendsMap<T[]>{add(key: string,value: T): T[];remove(key: string,value: T): void;}functioncreateMultiMap<T>(){constmap=<MultiMap<T>>createMap<T[]>();map.add=multiMapAdd;map.remove=multiMapRemove;returnmap;}functionmultiMapAdd<T>(this: MultiMap<T>,key: string,value: T){ ... }functionmultiMapRemove<T>(this: MultiMap<T>,key: string,value: T){ ... }

Comment threadsrc/compiler/core.ts Outdated
let result: U;
for (const key in map) {
if (result = callback(map[key], key)) break;
function arrayFrom<T>(iterator: Iterator<T>): T[] {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe we should just export this and replace all references to keysOfMap and valuesOfMap.

@ghost

Deleted user (ghost) commented Dec 28, 2016

Copy link
Copy Markdown
Author

Not a big fan of 2e6f369 as it would make me suspicious of every array in the codebase.

@rbucktonRon Buckton (rbuckton) left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks good, though we should wait on Anders Hejlsberg (@ahejlsberg)'s feedback before merging.

@rbuckton

Copy link
Copy Markdown
Contributor

Andy (Andrewkraft) (@Andy-MS) I would recommend you update the description of the PR with respect to the final set of new functions added to support using native maps.

@ghost

Copy link
Copy Markdown
Author

Ron Buckton (@rbuckton) Done. And just to be sure, I ran baselines again. Results are still good.

Monaco

node (v0.12.17, x86)

ProjectBaselineCurrentDeltaBestWorst
Memory used224,333k (± 0.05%)190,498k (± 0.19%)-33,835k (- 15.08%)190,298k191,058k
Parse Time1.93s (± 1.50%)1.83s (± 1.40%)-0.10s (- 4.96%)1.80s1.86s
Bind Time0.76s (± 1.09%)0.71s (± 4.64%)-0.05s (- 6.54%)0.68s0.76s
Check Time5.59s (± 5.16%)5.42s (± 4.48%)-0.17s (- 3.13%)5.15s5.72s
Emit Time2.32s (± 4.21%)2.31s (± 7.73%)-0.01s (- 0.50%)2.20s2.56s
Total Time10.60s (± 2.98%)10.26s (± 4.10%)-0.33s (- 3.14%)10.01s10.90s

node (v6.9.2, x64)

ProjectBaselineCurrentDeltaBestWorst
Memory used381,413k (± 0.01%)357,526k (± 0.02%)-23,887k (- 6.26%)357,458k357,590k
Parse Time2.08s (± 1.02%)2.17s (± 25.08%)+0.08s (+ 3.98%)1.93s3.01s
Bind Time1.00s (± 2.21%)0.85s (± 5.81%)-0.15s (- 15.27%)0.81s0.92s
Check Time4.85s (± 1.50%)4.33s (± 4.81%)-0.51s (- 10.58%)4.12s4.53s
Emit Time2.20s (± 1.30%)2.23s (± 2.19%)+0.03s (+ 1.34%)2.16s2.26s
Total Time10.13s (± 0.81%)9.58s (± 5.42%)-0.55s (- 5.46%)9.04s10.29s

node (v6.9.2, x86)

ProjectBaselineCurrentDeltaBestWorst
Memory used203,159k (± 0.00%)190,871k (± 0.01%)-12,288k (- 6.05%)190,849k190,883k
Parse Time1.94s (± 1.41%)1.81s (± 1.00%)-0.13s (- 6.56%)1.79s1.83s
Bind Time0.80s (± 2.36%)0.66s (± 0.78%)-0.14s (- 17.35%)0.66s0.67s
Check Time3.79s (± 0.95%)3.18s (± 2.62%)-0.61s (- 16.12%)3.12s3.30s
Emit Time2.11s (± 1.47%)1.93s (± 0.90%)-0.17s (- 8.15%)1.91s1.95s
Total Time8.63s (± 0.79%)7.58s (± 0.87%)-1.05s (- 12.12%)7.53s7.68s

tsc (x86)

ProjectBaselineCurrentDeltaBestWorst
Parse Time1.62s (± 1.56%)1.49s (± 1.64%)-0.13s (- 8.05%)1.46s1.51s
Bind Time0.71s (± 3.11%)0.54s (± 4.08%)-0.17s (- 24.20%)0.52s0.57s
Check Time3.91s (± 1.49%)3.36s (± 2.07%)-0.55s (- 14.11%)3.29s3.45s
Emit Time5.99s (± 2.95%)5.91s (± 11.64%)-0.08s (- 1.35%)5.59s6.97s
Total Time12.23s (± 1.75%)11.29s (± 6.91%)-0.94s (- 7.67%)10.92s12.50s

TFS

node (v0.12.17, x86)

ProjectBaselineCurrentDeltaBestWorst
Memory used187,646k (± 0.06%)166,272k (± 0.13%)-21,374k (- 11.39%)165,952k166,408k
Parse Time1.35s (± 0.71%)1.28s (± 1.48%)-0.07s (- 5.05%)1.26s1.30s
Bind Time0.70s (± 1.22%)0.61s (± 1.58%)-0.09s (- 13.45%)0.60s0.62s
Check Time3.71s (± 0.72%)3.46s (± 1.46%)-0.25s (- 6.87%)3.42s3.53s
Emit Time1.97s (± 2.05%)1.93s (± 0.98%)-0.04s (- 2.16%)1.91s1.95s
Total Time7.73s (± 0.71%)7.27s (± 0.69%)-0.46s (- 5.96%)7.23s7.34s

node (v6.9.2, x64)

ProjectBaselineCurrentDeltaBestWorst
Memory used327,882k (± 0.01%)308,975k (± 0.01%)-18,907k (- 5.77%)308,943k308,995k
Parse Time1.52s (± 0.46%)1.42s (± 1.05%)-0.10s (- 6.63%)1.41s1.44s
Bind Time0.80s (± 0.67%)0.68s (± 0.76%)-0.12s (- 15.04%)0.67s0.68s
Check Time4.34s (± 0.90%)3.72s (± 4.74%)-0.62s (- 14.23%)3.60s3.99s
Emit Time1.98s (± 1.32%)2.17s (± 5.77%)+0.19s (+ 9.37%)1.99s2.27s
Total Time8.64s (± 0.69%)7.99s (± 1.32%)-0.65s (- 7.53%)7.85s8.07s

node (v6.9.2, x86)

ProjectBaselineCurrentDeltaBestWorst
Memory used174,963k (± 0.01%)165,150k (± 0.02%)-9,813k (- 5.61%)165,127k165,195k
Parse Time1.41s (± 0.77%)1.30s (± 2.38%)-0.10s (- 7.29%)1.28s1.35s
Bind Time0.67s (± 0.93%)0.58s (± 3.34%)-0.10s (- 14.48%)0.56s0.60s
Check Time3.18s (± 0.58%)2.81s (± 1.62%)-0.37s (- 11.52%)2.76s2.87s
Emit Time1.62s (± 0.66%)1.62s (± 3.73%)+0.00s (+ 0.06%)1.57s1.68s
Total Time6.88s (± 0.50%)6.32s (± 1.58%)-0.57s (- 8.24%)6.20s6.43s

tsc (x86)

ProjectBaselineCurrentDeltaBestWorst
Parse Time1.23s (± 2.63%)1.06s (± 1.23%)-0.16s (- 13.39%)1.05s1.08s
Bind Time0.58s (± 1.82%)0.51s (± 2.55%)-0.07s (- 11.46%)0.50s0.53s
Check Time3.25s (± 1.51%)2.76s (± 2.30%)-0.49s (- 15.15%)2.71s2.83s
Emit Time3.58s (± 1.51%)3.49s (± 4.20%)-0.09s (- 2.45%)3.39s3.70s
Total Time8.63s (± 1.20%)7.82s (± 2.28%)-0.81s (- 9.38%)7.70s8.09s

Comment threadsrc/compiler/core.ts Outdated
}

/** Create a new map. If a template object is provided, the map will copy entries from it. */
export function createMap<T>(template?: MapLike<T>): Map<T> {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How often do we actually pass a template to createMap? I seem to remember that functions containing for-in loops are not optimized by V8, so having this optional template stuff may be costly. Perhaps filling a map from a template should just be a separate function.

Comment threadsrc/compiler/checker.ts Outdated
target[id] = source[id];
if (!source) return;

source.forEach((sourceSymbol, id) => {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe use source && source.forEach(...) and get rid of the if (!source) return above.

@@ -1,4 +1,4 @@
/// <reference path="../factory.ts" />
/// <reference path="../factory.ts" />

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's up with these ^M line breaks? There are a lot of them in the following files.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've looked at this and don't know why it shows a diff here.

  • It only shows ^M on lines that I've changed -- if I had changed line endings in the file, presumably they would show as changed in the whole file.
  • When I look at the file, it appears to have CRLF line endings (on all lines) on both master and map5. I have core.autocrlf set to false. Tested on both Windows and Linux.

After merge I'll look back to this file to see if it has mixed line endings, but I doubt that.
CC Daniel Rosenwasser (@DanielRosenwasser)

@ghostghost added the Breaking Change Would introduce errors in existing code label Jan 17, 2017
@ghost
ghost merged commit 65ef51d into masterJan 17, 2017
@ghost
ghost deleted the map5 branch January 17, 2017 19:04
@microsoftMicrosoft (microsoft) locked and limited conversation to collaborators Jun 19, 2018
This pull request was closed.
Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

Breaking ChangeWould introduce errors in existing code

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

@rbuckton@ahejlsberg@msftclas