Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use native maps when they're available #12715

Merged
30 commits merged into from Jan 17, 2017
Merged

Use native maps when they're available #12715

30 commits merged into from Jan 17, 2017

Conversation

ghost
Copy link

@ghost ghost commented Dec 7, 2016

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

Name Use
arrayFrom Converts 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, forEachKey These replace for-in loops with early termination, forEachProperty, and someProperties.
copyEntries Replaces copyProperties.
createMultiMap Replaces multiMapAdd and multiMapRemove.
mapsAreEqual Replaces a use of equalOwnProperties. Used only in reuseProgramStructure.ts, so moved it there.
multiMapSparseArrayAdd Replaces a use of multiMapAdd on what is now a sparse array. Used only in factory.ts, so moved it there.
mapEntries Replaces mapObject.

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

@ghost ghost force-pushed the map5 branch 3 times, most recently from 69b4ad5 to 62ad1e4 Compare December 7, 2016 19:24
@ghost ghost assigned rbuckton Dec 7, 2016
@ghost
Copy link
Author

ghost commented Dec 8, 2016

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

Monaco

node (v6.9.2, x64)
Project Baseline Current Delta Best Worst
Memory used 381,190k (± 0.01%) 358,147k (± 0.02%) -23,043k (- 6.04%) 358,085k 358,209k
Parse Time 2.44s (± 0.94%) 2.35s (± 1.23%) -0.09s (- 3.52%) 2.33s 2.39s
Bind Time 0.93s (± 2.62%) 0.77s (± 1.71%) -0.17s (- 17.94%) 0.75s 0.78s
Check Time 4.53s (± 3.18%) 4.33s (± 3.47%) -0.21s (- 4.53%) 4.23s 4.55s
Emit Time 2.09s (± 0.98%) 2.10s (± 3.31%) +0.01s (+ 0.55%) 2.04s 2.17s
Total Time 9.99s (± 1.94%) 9.55s (± 2.49%) -0.45s (- 4.46%) 9.36s 9.88s
node (v6.9.2, x86)
Project Baseline Current Delta Best Worst
Memory used 203,018k (± 0.01%) 191,475k (± 0.02%) -11,543k (- 5.69%) 191,450k 191,522k
Parse Time 2.30s (± 0.52%) 2.21s (± 0.87%) -0.10s (- 4.19%) 2.18s 2.22s
Bind Time 0.75s (± 1.50%) 0.64s (± 2.43%) -0.11s (- 14.63%) 0.62s 0.65s
Check Time 3.69s (± 2.45%) 3.41s (± 1.28%) -0.29s (- 7.81%) 3.35s 3.45s
Emit Time 2.09s (± 3.24%) 2.10s (± 0.63%) +0.01s (+ 0.50%) 2.08s 2.11s
Total Time 8.83s (± 1.25%) 8.34s (± 0.77%) -0.48s (- 5.49%) 8.27s 8.40s
tsc (x86)
Project Baseline Current Delta Best Worst
Parse Time 1.82s (± 0.83%) 1.72s (± 1.70%) -0.10s (- 5.39%) 1.69s 1.75s
Bind Time 0.77s (± 5.13%) 0.53s (± 2.82%) -0.23s (- 30.46%) 0.52s 0.55s
Check Time 3.38s (± 1.26%) 3.04s (± 3.24%) -0.34s (- 10.03%) 2.94s 3.15s
Emit Time 6.08s (± 3.39%) 6.09s (± 5.13%) +0.00s (+ 0.02%) 5.93s 6.57s
Total Time 12.05s (± 2.00%) 11.38s (± 2.47%) -0.67s (- 5.54%) 11.15s 11.78s

TFS

node (v6.9.2, x64)
Project Baseline Current Delta Best Worst
Memory used 327,608k (± 0.01%) 309,067k (± 0.02%) -18,541k (- 5.66%) 308,995k 309,106k
Parse Time 1.52s (± 0.74%) 1.44s (± 1.04%) -0.07s (- 4.85%) 1.42s 1.45s
Bind Time 0.75s (± 0.79%) 0.65s (± 2.30%) -0.10s (- 13.30%) 0.64s 0.67s
Check Time 3.85s (± 0.88%) 3.78s (± 1.25%) -0.07s (- 1.75%) 3.73s 3.82s
Emit Time 1.79s (± 0.65%) 1.93s (± 2.76%) +0.14s (+ 7.60%) 1.89s 2.00s
Total Time 7.91s (± 0.48%) 7.80s (± 1.08%) -0.11s (- 1.34%) 7.73s 7.91s
node (v6.9.2, x86)
Project Baseline Current Delta Best Worst
Memory used 174,790k (± 0.01%) 165,324k (± 0.03%) -9,466k (- 5.42%) 165,287k 165,405k
Parse Time 1.44s (± 0.81%) 1.32s (± 1.90%) -0.11s (- 7.74%) 1.31s 1.36s
Bind Time 0.64s (± 1.95%) 0.55s (± 3.77%) -0.09s (- 13.64%) 0.53s 0.58s
Check Time 2.96s (± 0.68%) 2.70s (± 1.02%) -0.26s (- 8.81%) 2.67s 2.73s
Emit Time 1.56s (± 0.71%) 1.55s (± 5.97%) -0.01s (- 0.51%) 1.50s 1.69s
Total Time 6.59s (± 0.65%) 6.13s (± 1.85%) -0.46s (- 7.03%) 6.06s 6.30s
tsc (x86)
Project Baseline Current Delta Best Worst
Parse Time 1.27s (± 3.07%) 1.11s (± 0.87%) -0.15s (- 12.16%) 1.10s 1.12s
Bind Time 0.54s (± 5.05%) 0.56s (± 4.23%) +0.02s (+ 3.49%) 0.55s 0.60s
Check Time 2.89s (± 3.78%) 2.66s (± 2.91%) -0.24s (- 8.15%) 2.55s 2.73s
Emit Time 3.67s (± 1.73%) 3.56s (± 2.75%) -0.11s (- 3.00%) 3.48s 3.68s
Total Time 8.37s (± 2.42%) 7.89s (± 2.01%) -0.48s (- 5.72%) 7.70s 8.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
Project Baseline Current Delta Best Worst
Memory used 224,254k (± 0.22%) 194,721k (± 0.32%) -29,533k (- 13.17%) 194,176k 195,473k
Parse Time 2.49s (± 0.79%) 2.40s (± 1.20%) -0.10s (- 3.95%) 2.38s 2.44s
Bind Time 0.80s (± 1.35%) 0.66s (± 1.97%) -0.14s (- 17.16%) 0.65s 0.68s
Check Time 4.70s (± 1.24%) 4.99s (± 2.81%) +0.29s (+ 6.11%) 4.88s 5.20s
Emit Time 2.15s (± 1.22%) 2.24s (± 4.84%) +0.09s (+ 4.29%) 2.09s 2.31s
Total Time 10.15s (± 0.67%) 10.29s (± 1.67%) +0.14s (+ 1.43%) 10.08s 10.49s
TFS
Project Baseline Current Delta Best Worst
Memory used 187,770k (± 0.06%) 166,329k (± 0.16%) -21,441k (- 11.42%) 166,051k 166,633k
Parse Time 1.46s (± 1.12%) 1.39s (± 3.49%) -0.07s (- 4.60%) 1.35s 1.46s
Bind Time 0.67s (± 0.90%) 0.58s (± 1.09%) -0.09s (- 13.71%) 0.57s 0.58s
Check Time 3.52s (± 1.13%) 3.22s (± 1.09%) -0.30s (- 8.44%) 3.19s 3.26s
Emit Time 1.96s (± 2.11%) 1.85s (± 2.01%) -0.11s (- 5.76%) 1.81s 1.90s
Total Time 7.60s (± 0.91%) 7.04s (± 1.01%) -0.56s (- 7.42%) 6.98s 7.14s

@ghost ghost assigned ahejlsberg Dec 9, 2016
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
Member

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 @ahejlsberg's suggestion to use sparse arrays for "maps" with only numeric keys.

Copy link
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.

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
Member

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 {}.

Copy link
Author

@ghost ghost Dec 12, 2016

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?

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
Member

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.

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
Member

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.

: <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
Member

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?

@@ -262,7 +262,7 @@ namespace Harness.LanguageService {
this.getModuleResolutionsForFile = (fileName) => {
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
Member

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.

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
Member

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.

@@ -291,38 +291,30 @@ namespace ts.projectSystem {
}

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

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
Member

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.

@@ -532,7 +532,7 @@ namespace ts {
}

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

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.

@@ -39,6 +39,8 @@ namespace ts {
return map;
}

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

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.

}
}
return false;
return someInMap(moduleSymbol.exports, (_, id) => id !== "export=");
Copy link
Member

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.

@@ -20070,7 +20097,7 @@ namespace ts {
// otherwise - check if at least one export is value
symbolLinks.exportsSomeValue = hasExportAssignment
? !!(moduleSymbol.flags & SymbolFlags.Value)
: forEachProperty(getExportsOfModule(moduleSymbol), isValue);
: someInMap(getExportsOfModule(moduleSymbol), isValue);
Copy link
Member

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?

return map;
}

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

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.

}

/** 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
Member

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.

}

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

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.

}

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

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.

return values;
}

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

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
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.

@@ -942,15 +1030,26 @@ namespace ts {
* Adds the value to an array of values associated with the key, and returns the array.
* Creates the array if it does not already exist.
*/
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
Member

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>:

interface MultiMap<T> extends Map<T[]> {
  add(key: string, value: T): T[];
  remove(key: string, value: T): void;
}
function createMultiMap<T>() {
  const map = <MultiMap<T>>createMap<T[]>();
  map.add = multiMapAdd;
  map.remove = multiMapRemove;
  return map;
}
function multiMapAdd<T>(this: MultiMap<T>, key: string, value: T) { ... }
function multiMapRemove<T>(this: MultiMap<T>, key: string, value: T) { ... }

let result: U;
for (const key in map) {
if (result = callback(map[key], key)) break;
function arrayFrom<T>(iterator: Iterator<T>): T[] {
Copy link
Member

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
Copy link
Author

ghost commented Dec 28, 2016

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

Copy link
Member

@rbuckton rbuckton left a comment

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 @ahejlsberg's feedback before merging.

@rbuckton
Copy link
Member

@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
Author

ghost commented Dec 29, 2016

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

Monaco

node (v0.12.17, x86)

Project Baseline Current Delta Best Worst
Memory used 224,333k (± 0.05%) 190,498k (± 0.19%) -33,835k (- 15.08%) 190,298k 191,058k
Parse Time 1.93s (± 1.50%) 1.83s (± 1.40%) -0.10s (- 4.96%) 1.80s 1.86s
Bind Time 0.76s (± 1.09%) 0.71s (± 4.64%) -0.05s (- 6.54%) 0.68s 0.76s
Check Time 5.59s (± 5.16%) 5.42s (± 4.48%) -0.17s (- 3.13%) 5.15s 5.72s
Emit Time 2.32s (± 4.21%) 2.31s (± 7.73%) -0.01s (- 0.50%) 2.20s 2.56s
Total Time 10.60s (± 2.98%) 10.26s (± 4.10%) -0.33s (- 3.14%) 10.01s 10.90s

node (v6.9.2, x64)

Project Baseline Current Delta Best Worst
Memory used 381,413k (± 0.01%) 357,526k (± 0.02%) -23,887k (- 6.26%) 357,458k 357,590k
Parse Time 2.08s (± 1.02%) 2.17s (± 25.08%) +0.08s (+ 3.98%) 1.93s 3.01s
Bind Time 1.00s (± 2.21%) 0.85s (± 5.81%) -0.15s (- 15.27%) 0.81s 0.92s
Check Time 4.85s (± 1.50%) 4.33s (± 4.81%) -0.51s (- 10.58%) 4.12s 4.53s
Emit Time 2.20s (± 1.30%) 2.23s (± 2.19%) +0.03s (+ 1.34%) 2.16s 2.26s
Total Time 10.13s (± 0.81%) 9.58s (± 5.42%) -0.55s (- 5.46%) 9.04s 10.29s

node (v6.9.2, x86)

Project Baseline Current Delta Best Worst
Memory used 203,159k (± 0.00%) 190,871k (± 0.01%) -12,288k (- 6.05%) 190,849k 190,883k
Parse Time 1.94s (± 1.41%) 1.81s (± 1.00%) -0.13s (- 6.56%) 1.79s 1.83s
Bind Time 0.80s (± 2.36%) 0.66s (± 0.78%) -0.14s (- 17.35%) 0.66s 0.67s
Check Time 3.79s (± 0.95%) 3.18s (± 2.62%) -0.61s (- 16.12%) 3.12s 3.30s
Emit Time 2.11s (± 1.47%) 1.93s (± 0.90%) -0.17s (- 8.15%) 1.91s 1.95s
Total Time 8.63s (± 0.79%) 7.58s (± 0.87%) -1.05s (- 12.12%) 7.53s 7.68s

tsc (x86)

Project Baseline Current Delta Best Worst
Parse Time 1.62s (± 1.56%) 1.49s (± 1.64%) -0.13s (- 8.05%) 1.46s 1.51s
Bind Time 0.71s (± 3.11%) 0.54s (± 4.08%) -0.17s (- 24.20%) 0.52s 0.57s
Check Time 3.91s (± 1.49%) 3.36s (± 2.07%) -0.55s (- 14.11%) 3.29s 3.45s
Emit Time 5.99s (± 2.95%) 5.91s (± 11.64%) -0.08s (- 1.35%) 5.59s 6.97s
Total Time 12.23s (± 1.75%) 11.29s (± 6.91%) -0.94s (- 7.67%) 10.92s 12.50s

TFS

node (v0.12.17, x86)

Project Baseline Current Delta Best Worst
Memory used 187,646k (± 0.06%) 166,272k (± 0.13%) -21,374k (- 11.39%) 165,952k 166,408k
Parse Time 1.35s (± 0.71%) 1.28s (± 1.48%) -0.07s (- 5.05%) 1.26s 1.30s
Bind Time 0.70s (± 1.22%) 0.61s (± 1.58%) -0.09s (- 13.45%) 0.60s 0.62s
Check Time 3.71s (± 0.72%) 3.46s (± 1.46%) -0.25s (- 6.87%) 3.42s 3.53s
Emit Time 1.97s (± 2.05%) 1.93s (± 0.98%) -0.04s (- 2.16%) 1.91s 1.95s
Total Time 7.73s (± 0.71%) 7.27s (± 0.69%) -0.46s (- 5.96%) 7.23s 7.34s

node (v6.9.2, x64)

Project Baseline Current Delta Best Worst
Memory used 327,882k (± 0.01%) 308,975k (± 0.01%) -18,907k (- 5.77%) 308,943k 308,995k
Parse Time 1.52s (± 0.46%) 1.42s (± 1.05%) -0.10s (- 6.63%) 1.41s 1.44s
Bind Time 0.80s (± 0.67%) 0.68s (± 0.76%) -0.12s (- 15.04%) 0.67s 0.68s
Check Time 4.34s (± 0.90%) 3.72s (± 4.74%) -0.62s (- 14.23%) 3.60s 3.99s
Emit Time 1.98s (± 1.32%) 2.17s (± 5.77%) +0.19s (+ 9.37%) 1.99s 2.27s
Total Time 8.64s (± 0.69%) 7.99s (± 1.32%) -0.65s (- 7.53%) 7.85s 8.07s

node (v6.9.2, x86)

Project Baseline Current Delta Best Worst
Memory used 174,963k (± 0.01%) 165,150k (± 0.02%) -9,813k (- 5.61%) 165,127k 165,195k
Parse Time 1.41s (± 0.77%) 1.30s (± 2.38%) -0.10s (- 7.29%) 1.28s 1.35s
Bind Time 0.67s (± 0.93%) 0.58s (± 3.34%) -0.10s (- 14.48%) 0.56s 0.60s
Check Time 3.18s (± 0.58%) 2.81s (± 1.62%) -0.37s (- 11.52%) 2.76s 2.87s
Emit Time 1.62s (± 0.66%) 1.62s (± 3.73%) +0.00s (+ 0.06%) 1.57s 1.68s
Total Time 6.88s (± 0.50%) 6.32s (± 1.58%) -0.57s (- 8.24%) 6.20s 6.43s

tsc (x86)

Project Baseline Current Delta Best Worst
Parse Time 1.23s (± 2.63%) 1.06s (± 1.23%) -0.16s (- 13.39%) 1.05s 1.08s
Bind Time 0.58s (± 1.82%) 0.51s (± 2.55%) -0.07s (- 11.46%) 0.50s 0.53s
Check Time 3.25s (± 1.51%) 2.76s (± 2.30%) -0.49s (- 15.15%) 2.71s 2.83s
Emit Time 3.58s (± 1.51%) 3.49s (± 4.20%) -0.09s (- 2.45%) 3.39s 3.70s
Total Time 8.63s (± 1.20%) 7.82s (± 2.28%) -0.81s (- 9.38%) 7.70s 8.09s

}

/** 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
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.

target[id] = source[id];
if (!source) return;

source.forEach((sourceSymbol, id) => {
Copy link
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
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
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 @DanielRosenwasser

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

Successfully merging this pull request may close these issues.

None yet

3 participants