-
Notifications
You must be signed in to change notification settings - Fork 2.8k
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
Dex*: Consistent field init order in constructors #10608
Dex*: Consistent field init order in constructors #10608
Conversation
sim/dex-data.ts
Outdated
/** | ||
* Pass 'false' for the second parameter if you only want the declared fields of BasicEffect | ||
* to be initialized - the other properties of data will ignored. | ||
* This is to help w/ V8 hidden classes (want to init fields in consistent order) | ||
*/ | ||
constructor(data: AnyObject, copyOtherFields = true) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It looks as if BasicEffect
should be an abstract class, so that it will never have to worry about copying other fields itself.
Do you happen to know how many other fields there actually are?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(I will get back to you on this, I'm just working on making the test suite run faster)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, abstract class makes sense here and would simplify it.
Documented # of other fields in later comment in PR.
@urkerab Here's some stats on extra fields. codeInserted at export const extra_fields = new Map();
export function assignNewFields(self: AnyObject, data: AnyObject) {
for (const k in data) {
// to behave like Object.assign, skip data's inherited properties
if (!Object.prototype.hasOwnProperty.call(data, k)) continue;
if (Object.prototype.hasOwnProperty.call(self, k)) continue;
extra_fields.set(k, (extra_fields.get(k) || 0) + 1);
self[k] = data[k];
}
} Inserted at the bottom of Dex.includeMods();
Dex.formats.all();
console.log('start', Dex.formats.all().length, Data.extra_fields);
Data.extra_fields.clear();
let sum;
sum = 0;
for (const d in dexes) sum += dexes[d].species.all().length;
console.log('species', sum, Data.extra_fields);
Data.extra_fields.clear();
sum = 0;
for (const d in dexes) sum += dexes[d].moves.all().length;
console.log('moves', sum, Data.extra_fields);
Data.extra_fields.clear();
sum = 0;
for (const d in dexes) sum += dexes[d].items.all().length;
console.log('items', sum, Data.extra_fields);
Data.extra_fields.clear();
sum = 0;
for (const d in dexes) sum += dexes[d].abilities.all().length;
console.log('abilities', sum, Data.extra_fields);
Data.extra_fields.clear();
sum = 0;
for (const d in dexes) sum += dexes[d].natures.all().length;
console.log('natures', sum, Data.extra_fields);
Data.extra_fields.clear();
sum = 0;
for (const d in dexes) sum += dexes[d].types.all().length;
console.log('types', sum, Data.extra_fields);
Data.extra_fields.clear(); Format:
Start (I think this is Dex.formats)start 274 Map(40) {
'team' => 43,
'section' => 274,
'column' => 274,
'challengeShow' => 274,
'searchShow' => 274,
'tournamentShow' => 274,
'bestOfDefault' => 274,
'teraPreviewDefault' => 274,
'battle' => 18,
'onSwitchIn' => 13,
'getEvoFamily' => 1,
'validateSet' => 5,
'onValidateTeam' => 7,
'onSwitchOut' => 3,
'onBeforeSwitchIn' => 6,
'onFaint' => 2,
'onValidateRule' => 5,
'getSharedPower' => 1,
'onSwitchInPriority' => 6,
'checkCanLearn' => 1,
'onModifySpecies' => 2,
'onValidateSet' => 5,
'onModifyMove' => 5,
'onModifyMovePriority' => 3,
'onModifyPriority' => 1,
'onModifyTypePriority' => 1,
'onModifyType' => 2,
'onHitPriority' => 1,
'onHit' => 1,
'onAfterSubDamage' => 1,
'onModifySecondaries' => 1,
'onAfterMoveSecondaryPriority' => 1,
'onAfterMoveSecondarySelf' => 1,
'onBasePowerPriority' => 1,
'onBasePower' => 1,
'pokemon' => 2,
'onAfterMega' => 2,
'getSharedItems' => 1,
'onPrepareHit' => 1,
'actions' => 1
} Speciesspecies 55427 Map(93) {
'evoCondition' => 2046,
'evoItem' => 3420,
'evoRegion' => 190,
'mother' => 114,
'requiredAbility' => 684,
'requiredMove' => 114,
'forceTeraType' => 418,
'onModifyAccuracyPriority' => 3,
'onModifyAccuracy' => 3,
'onBasePowerPriority' => 11,
'onBasePower' => 11,
'onDamage' => 7,
'onSetStatus' => 7,
'onAnyTryMove' => 1,
'onAnyDamage' => 1,
'onTryBoost' => 10,
'onTryAddVolatile' => 5,
'onTryHit' => 14,
'onDamagePriority' => 2,
'onSourceModifyDamage' => 2,
'onModifyMove' => 9,
'onModifyMovePriority' => 3,
'onModifyAtkPriority' => 3,
'onModifyAtk' => 3,
'onModifySpe' => 5,
'onFlinch' => 1,
'onUpdate' => 8,
'onStart' => 15,
'onDamagingHit' => 11,
'onImmunity' => 7,
'onModifyCritRatio' => 1,
'onSourceDamagingHit' => 1,
'onModifySTAB' => 1,
'onSourceAfterFaint' => 1,
'onAfterEachBoost' => 1,
'onModifySpA' => 3,
'onResidualOrder' => 2,
'onResidualSubOrder' => 2,
'onResidual' => 2,
'onFoeTrapPokemon' => 2,
'onFoeMaybeTrapPokemon' => 2,
'onCheckShow' => 1,
'onSwitchOut' => 2,
'onModifyTypePriority' => 1,
'onModifyType' => 1,
'onModifyDamage' => 2,
'onCriticalHit' => 2,
'onAllyTryHitSide' => 2,
'onTryHitPriority' => 2,
'onSourceModifyAccuracyPriority' => 2,
'onSourceModifyAccuracy' => 2,
'onSourceModifyAtk' => 1,
'onSourceModifySpA' => 1,
'onSourceBasePowerPriority' => 3,
'onSourceBasePower' => 3,
'onSourceModifyAtkPriority' => 1,
'onSourceModifySpAPriority' => 1,
'onFoeRedirectTarget' => 1,
'condition' => 2,
'onAfterUseItem' => 1,
'onTakeItem' => 2,
'onEnd' => 6,
'onWeather' => 5,
'onSwitchIn' => 1,
'onAfterSubDamage' => 1,
'onHit' => 2,
'onAnyInvulnerabilityPriority' => 1,
'onAnyInvulnerability' => 1,
'onAnyAccuracy' => 1,
'onSourceTryHeal' => 1,
'onAllySwitchIn' => 1,
'onAllySetStatus' => 1,
'onPreStart' => 2,
'onDeductPP' => 1,
'onModifySpAPriority' => 1,
'onFoeTryMove' => 2,
'onFractionalPriority' => 1,
'onModifyBoost' => 1,
'onAnyModifyBoost' => 1,
'onAnyRedirectTarget' => 1,
'onDamagingHitOrder' => 2,
'onAfterSetStatus' => 1,
'onFoeTryEatItem' => 1,
'onEatItem' => 1,
'onDragOutPriority' => 2,
'onDragOut' => 2,
'onModifySecondaries' => 1,
'onModifyPriority' => 2,
'onModifyWeightPriority' => 1,
'onModifyWeight' => 1,
'onAfterMoveSecondary' => 1,
'onAllyTryAddVolatile' => 1,
'inherit' => 3
} Movesmoves 37924 Map(63) {
'contestType' => 30876,
'drain' => 496,
'boosts' => 2744,
'zMove' => 10732,
'basePowerCallback' => 2059,
'onHit' => 4097,
'onPrepareHit' => 971,
'condition' => 4708,
'self' => 3392,
'multihit' => 1192,
'callsMove' => 266,
'onTryImmunity' => 380,
'onTry' => 1602,
'onModifyType' => 503,
'sideCondition' => 558,
'onTryHit' => 1820,
'hasCrashDamage' => 153,
'onMoveFail' => 210,
'stallingMove' => 445,
'onBasePower' => 718,
'priorityChargeCallback' => 115,
'onAfterMove' => 438,
'onModifyMove' => 1098,
'onDisableMove' => 78,
'beforeMoveCallback' => 77,
'maxMove' => 1900,
'onTryMove' => 910,
'recoil' => 465,
'onAfterHit' => 310,
'onAfterSubDamage' => 421,
'ignoreEvasion' => 114,
'forceSwitch' => 153,
'selfBoost' => 114,
'damageCallback' => 419,
'onModifyTarget' => 76,
'beforeTurnCallback' => 126,
'onHitField' => 231,
'smartTarget' => 38,
'terrain' => 153,
'selfdestruct' => 267,
'pseudoWeather' => 345,
'onDamagePriority' => 77,
'onDamage' => 77,
'breaksProtect' => 202,
'onAfterMoveSecondarySelf' => 118,
'ohko' => 152,
'willCrit' => 248,
'onEffectiveness' => 119,
'onHitSide' => 152,
'onModifyPriority' => 46,
'slotCondition' => 165,
'heal' => 278,
'realMove' => 608,
'thawsTarget' => 190,
'onUseMoveMessage' => 38,
'mindBlownRecoil' => 76,
'multiaccuracy' => 116,
'sleepUsable' => 77,
'tracksTarget' => 38,
'stealsBoosts' => 41,
'struggleRecoil' => 38,
'noDamageVariance' => 12,
'ignoreAccuracy' => 1
} Itemsitems 20949 Map(79) {
'spritenum' => 20412,
'onSetAbility' => 38,
'onTakeItem' => 5121,
'onDamagingHit' => 342,
'boosts' => 494,
'onBasePowerPriority' => 2052,
'onBasePower' => 2150,
'forcedForme' => 2379,
'onAfterBoost' => 76,
'naturalGift' => 2919,
'onUpdate' => 1368,
'onTryEatItem' => 373,
'onEat' => 2919,
'onStart' => 456,
'onAfterSubDamage' => 38,
'onModifySpDPriority' => 129,
'onModifySpD' => 136,
'onDisableMove' => 38,
'onSourceModifyDamage' => 684,
'onTryHealPriority' => 38,
'onTryHeal' => 38,
'onResidualOrder' => 372,
'onResidualSubOrder' => 296,
'onResidual' => 410,
'onSwitchIn' => 76,
'onPrimal' => 76,
'onModifyAccuracyPriority' => 76,
'onModifyAccuracy' => 76,
'onSourceTryPrimaryHit' => 684,
'onModifyMove' => 266,
'onModifyAtkPriority' => 177,
'onModifyAtk' => 177,
'isChoice' => 114,
'onModifySpe' => 381,
'onModifySpAPriority' => 192,
'onModifySpA' => 193,
'onTryBoost' => 38,
'onModifySecondaries' => 38,
'onFractionalPriorityPriority' => 76,
'onFractionalPriority' => 152,
'onAttractPriority' => 38,
'onAttract' => 38,
'onAfterMoveSecondaryPriority' => 38,
'onAfterMoveSecondary' => 152,
'onTerrainChange' => 152,
'onHit' => 69,
'onModifyDefPriority' => 76,
'onModifyDef' => 77,
'onModifyDamage' => 185,
'onModifyWeight' => 38,
'onDamagePriority' => 76,
'onDamage' => 77,
'onEffectiveness' => 38,
'onModifyMovePriority' => 114,
'onModifyCritRatio' => 190,
'onAfterMoveSecondarySelf' => 116,
'onAfterSetStatusPriority' => 38,
'onAfterSetStatus' => 38,
'condition' => 94,
'onFoeAfterBoost' => 38,
'onChargeMove' => 38,
'onNegateImmunity' => 38,
'onDamagingHitOrder' => 38,
'onAnyPseudoWeatherChange' => 38,
'onImmunity' => 38,
'onTryHit' => 47,
'onTrapPokemonPriority' => 38,
'onTrapPokemon' => 38,
'onAfterMoveSecondarySelfPriority' => 38,
'onEnd' => 38,
'onSourceModifyAccuracyPriority' => 76,
'onSourceModifyAccuracy' => 76,
'onModifyCritRatioPriority' => 12,
'onAfterMove' => 27,
'onBeforeTurn' => 9,
'onCustap' => 9,
'onModifyDamagePhase2' => 9,
'inherit' => 1,
'onFoeBasePower' => 1
} Abilitiesabilities 12375 Map(152) {
'onModifySTAB' => 37,
'onModifyTypePriority' => 232,
'onModifyType' => 231,
'onBasePowerPriority' => 801,
'onBasePower' => 802,
'onDamagingHitOrder' => 227,
'onDamagingHit' => 1184,
'onSwitchIn' => 386,
'onStart' => 2502,
'onEnd' => 723,
'onHit' => 75,
'onDamage' => 439,
'onTryEatItem' => 114,
'onAfterMoveSecondary' => 151,
'onFoeTrapPokemon' => 114,
'onFoeMaybeTrapPokemon' => 114,
'onFoeTryMove' => 113,
'onAllyTryAddVolatile' => 113,
'onPreStart' => 188,
'onFoeTryEatItem' => 113,
'onSourceAfterFaint' => 267,
'onAnyTryPrimaryHit' => 38,
'onResidualOrder' => 495,
'onResidualSubOrder' => 295,
'onResidual' => 504,
'onAllyBasePowerPriority' => 114,
'onAllyBasePower' => 114,
'onCriticalHit' => 151,
'onAnyModifySpD' => 38,
'onTryBoost' => 524,
'onModifyAtkPriority' => 574,
'onModifyAtk' => 611,
'onModifySpAPriority' => 481,
'onModifySpA' => 536,
'onTryHit' => 833,
'onEatItem' => 114,
'onModifySpe' => 227,
'onSetStatus' => 460,
'onUpdate' => 567,
'onAfterEachBoost' => 75,
'onSourceModifyAccuracyPriority' => 77,
'onSourceModifyAccuracy' => 77,
'onChangeBoost' => 105,
'condition' => 389,
'onAnyTryMove' => 37,
'onAnyDamage' => 37,
'onAnyBasePowerPriority' => 77,
'onAnyBasePower' => 77,
'onAnySetWeather' => 117,
'onDamagePriority' => 153,
'onEffectiveness' => 115,
'onSourceBasePowerPriority' => 67,
'onSourceBasePower' => 70,
'onWeather' => 156,
'onEmergencyExit' => 76,
'onSourceModifyDamage' => 349,
'onWeatherChange' => 152,
'onAllyModifyAtkPriority' => 38,
'onAllyModifyAtk' => 38,
'onAllyModifySpDPriority' => 38,
'onAllyModifySpD' => 38,
'onAllyTryBoost' => 38,
'onAllySetStatus' => 113,
'onAnyModifyDamage' => 38,
'onModifyDefPriority' => 117,
'onModifyDef' => 118,
'onModifyPriority' => 117,
'onBeforeMove' => 93,
'onModifyMove' => 762,
'onDisableMove' => 38,
'onDragOutPriority' => 74,
'onDragOut' => 74,
'onSourceTryPrimaryHit' => 38,
'onSourceModifyAtkPriority' => 145,
'onSourceModifyAtk' => 145,
'onSourceModifySpAPriority' => 145,
'onSourceModifySpA' => 145,
'onModifyWeightPriority' => 38,
'onModifyWeight' => 76,
'onImmunity' => 302,
'onBeforeSwitchIn' => 38,
'onFaint' => 39,
'onTryAddVolatile' => 261,
'onPrepareHit' => 116,
'onAnyRedirectTarget' => 69,
'onSourceTryHeal' => 37,
'onTryHitPriority' => 152,
'onAllyTryHitSide' => 151,
'onAfterMoveSecondarySelf' => 39,
'onModifyCritRatio' => 77,
'onTerrainChange' => 76,
'onModifyMovePriority' => 299,
'onFractionalPriorityPriority' => 76,
'onFractionalPriority' => 115,
'onCheckShow' => 37,
'onSwitchOut' => 116,
'onModifyDamage' => 113,
'onAnyInvulnerabilityPriority' => 39,
'onAnyInvulnerability' => 39,
'onAnyAccuracy' => 39,
'onFoeAfterBoost' => 39,
'onSourceModifySecondaries' => 39,
'onAllySwitchIn' => 37,
'onAnyAfterSetStatus' => 38,
'onSourceDamagingHit' => 75,
'onAllyFaint' => 76,
'onDeductPP' => 37,
'onAfterBoost' => 19,
'onTryHeal' => 38,
'onSourceModifyDamagePriority' => 38,
'onTryEatItemPriority' => 38,
'onModifyAccuracyPriority' => 149,
'onModifyAccuracy' => 149,
'onModifySecondaries' => 38,
'onAnyFaintPriority' => 38,
'onAnyFaint' => 38,
'onFlinch' => 37,
'onTakeItem' => 74,
'onAllyAfterUseItem' => 38,
'onAfterSetStatus' => 37,
'onAnyModifyDef' => 39,
'onAnyModifyAtk' => 39,
'onAfterTerastallization' => 38,
'onAnyAfterMove' => 38,
'onBeforeMovePriority' => 55,
'onAnyModifyBoost' => 40,
'onAfterUseItem' => 37,
'onAnyModifySpA' => 38,
'onAnyModifyAccuracyPriority' => 38,
'onAnyModifyAccuracy' => 38,
'onAllySideConditionStart' => 76,
'onFoeRedirectTarget' => 6,
'onAfterSubDamage' => 8,
'onModifyBoost' => 8,
'onFoeBeforeMovePriority' => 1,
'onFoeBeforeMove' => 3,
'inherit' => 1,
'onModifySpDPriority' => 3,
'onModifySpD' => 4,
'onAnyModifySpe' => 1,
'onBeforeSwitchOutPriority' => 1,
'onBeforeSwitchOut' => 1,
'onSourceHit' => 1,
'onAnyDeductPP' => 1,
'onAnyModifyType' => 1,
'onAfterMove' => 3,
'onFoeBasePower' => 2,
'onBeforeTurn' => 1,
'onSourceAccuracy' => 1,
'onAllyModifyMove' => 1,
'onFoeDisableMove' => 1,
'onResidualPriority' => 1
} Naturesnatures 975 Map(0) {} Typestypes 741 Map(0) {} None of my dedup code is in, so those object counts should reflect* how many times the constructor was called from that type. *Edit: Nope those are slightly inflated (by 1 mod's worth) because 'base' and 'gen9' are both in dexes. I will look at the impact of sorting next |
What's weird is that some of those keys don't appear in the corresponding ./data files for the type. Like I don't know why that happens. |
Came up with a much better way to analyze the # of object shapes. Old state (master)
current PR stateAmong the dex* objects, there are Breakdown by type:
With sorting
Those remaining extra shapes seem to come from the position of
Conclusion: will be put in a later comment setup code(bottom of Dex.includeMods();
let obj_list = [...Dex.formats.all()];
for (const d in dexes) {
if (d === 'base') continue;
const m = dexes[d];
// slow but w/e
obj_list = obj_list.concat(
m.species.all(),
m.moves.all(),
m.items.all(),
m.abilities.all(),
m.types.all(),
m.natures.all()
);
}
const key_seqs = new Map();
for (const obj of obj_list) {
const sorted = obj.effectType + '|' + Object.keys(obj).sort().join(';');
const keys = Object.keys(obj).join(';');
let bucket = key_seqs.get(sorted);
if (!bucket) key_seqs.set(sorted, bucket = new Map());
bucket.set(keys, (bucket.get(keys) || 0) + 1);
}
let num_total_shapes = 0;
for (const v of key_seqs.values()) num_total_shapes += v.size;
console.log("# total object shapes:", num_total_shapes);
const num_act_shapes_per_effectType = new Map();
const num_min_shapes_per_effectType = new Map();
for (const [k, v] of key_seqs.entries()) {
const key = k.split('|')[0];
num_act_shapes_per_effectType.set(key, (num_act_shapes_per_effectType.get(key) || 0) + v.size);
num_min_shapes_per_effectType.set(key, (num_min_shapes_per_effectType.get(key) || 0) + 1);
}
console.log("# actual shapes per effectType", num_act_shapes_per_effectType);
console.log("min possible # shapes per effectType", num_min_shapes_per_effectType);
for (const k of key_seqs.keys()) {
if (key_seqs.get(k).size === 1) key_seqs.delete(k);
}
console.log(key_seqs);
console.log("# REDUNDANT SHAPES PER KEYSET");
let num_redundant_shapes = 0;
for (const [k, v] of key_seqs.entries()) {
console.log(k, v.size);
num_redundant_shapes += v.size;
}
console.log("# TOTAL REDUNDANT SHAPES, uniq vs actual:", key_seqs.size, ",", num_redundant_shapes); |
sim/dex-data.ts
Outdated
/** | ||
* Like Object.assign but only assigns fields missing from self. | ||
* Facilitates consistent field ordering in constructors. | ||
* Modifies self in-place. | ||
*/ | ||
export function assignNewFields(self: AnyObject, data: AnyObject) { | ||
for (const k in data) { | ||
// to behave like Object.assign, skip data's inherited properties | ||
if (!Object.prototype.hasOwnProperty.call(data, k)) continue; | ||
if (Object.prototype.hasOwnProperty.call(self, k)) continue; | ||
self[k] = data[k]; | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd prefer for this to be a method on BasicEffect, assuming there are no performance impacts there.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think there's any perf problems with that, but I do think it would be counter-intuitive to have a method that can only be called during construction. I see this more as a utility method than something specific to BasicEffect
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I guess we rarely export bare functions outside of lib/
. My mind is saying either move to lib/utils
or make it a static method on BasicEffect
, either would make it feel more right, but yeah it's not really a big deal.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm happy to follow your lead here - I'm pretty much a blank slate on Typescript code style.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hm. On further reflection, this is probably fine this way.
This is likely the doing of a deleted mod which hasn't been correspondingly deleted from the built files. Recreate the dist folder, probably? |
So, the issue goes away when I group the extra fields by pokemon-showdown/sim/dex-species.ts Line 566 in 5a23223
Well, that's good to know for the future. Edit: This was only an issue for the (first version) benchmark code that was trying to count extra fields. This is orthogonal to the correctness of |
To conclude my yapping in #10608 (comment): (Context)
(How to further reduce # of obj shapes) Most of the remaining shapes (700 ish) stem from several optional properties in All that said, I don't know how much further benefit there would be to reducing those last few hundred shapes. I guess I'd have to make a PoC to collect stats on that. But that belongs in a separate PR. |
this.name = Utils.getString(data.name).trim(); | ||
this.id = data.realMove ? toID(data.realMove) : toID(this.name); // Hidden Power hack | ||
this.fullname = Utils.getString(data.fullname) || this.name; | ||
this.effectType = Utils.getString(data.effectType) as EffectType || 'Condition'; | ||
this.exists = !!(this.exists && this.id); | ||
this.exists = data.exists ?? !!this.id; | ||
this.num = data.num || 0; | ||
this.gen = data.gen || 0; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It occurs to me that if you want the common prefix as long as possible, fullname
and num
should be below gen
, to match TypeInfo
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I plan to eventually explore re-ordering fields. I suspect it will be a smaller effect than this PR, but I won't rule it out yet.
My gut says that it matters most within a class - previously, different instances of Species
could have wildly different field orders. Same for other classes.
Outside of micro-benchmarks, it gets very hard to reason about what works and what doesn't, which is why I try to make sure I have measurements to justify code changes.
Thanks! |
Spinoff of #10549
Rather simple change to get consistent field init order where possible.
I realized at the end that there's a very simple set of rules for making sure the ctors have the same behavior as they did with
Object.assign()
:this.field
before it's been initializedit doesn't exist.
data.field
afterthis.field
has been initializedyou'll miss out on the default values and other fix-up logic.
And... that's it.
Results
Memory
Heap snapshots, analyzed w/ Chrome's devtools.
Going by 'Constructor' and 'Alloc. Size' columns.
All mods
According to Chrome's UI, total memory went from 279 to 260 MB.
The point of consistent field ordering was to greatly reduce the number of hidden classes.
The 13M decrease in the
(system)
category indicates this worked.Ta-da.
Just Dex
According to Chrome's UI, total memory went from 53.6 to 50.0 MB.
Times
My test runs were taken on a local branch with #10616 merged in.
npm test
npm run full-test
Where '*' is [moves, items, species, abilities, types, natures]
Dex also loads Formats as part of loadData.
About 8 seconds saved on
npm run full-test
.Benchmark code
ticks for
npm
commands (./package.json
)mocha --v8-prof ... # Note: enabling prof adds a few % overhead
milliseconds for Dex loading
Heap snapshots
Covers all relevant classes.
old todo
TODO:
Simple testing indicates that Formats has a lot of extra fields.
Makes sense when you check the class definition - it has many optional properties.
In that scenario, one could try sorting the keys before iterating over them.
You'd want to measure whether sorting the keys meaningfully reduces the number of unique sequences.