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

Optimize memoized selector comparators #1214

Merged
merged 1 commit into from
May 21, 2018
Merged

Conversation

brusherru
Copy link
Contributor

Still tweaks IDE performance, that was reduced in the v0.20.0 by adding generic types and type deduction.

@brusherru brusherru self-assigned this May 16, 2018
@brusherru brusherru requested a review from a team May 16, 2018 17:22
@brusherru brusherru changed the base branch from master to 0.20.x May 16, 2018 17:22
);

// :: (Patch -> Patch -> Boolean) -> Maybe Patch -> Maybe Patch -> Boolean
export const maybePatchEqualsBy = R.curry(
Copy link
Member

Choose a reason for hiding this comment

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

Hmm… looks like it’s a job for lift:

export const maybePatchEqualsBy = R.curry(
  (compFn, prevMaybePatch, nextMaybePatch) => 
    R.liftN(3, patchListEqualsBy)
      (Maybe(compFn), prevMaybePatch, nextMaybePatch)
      .getOrElse(false)
  )
);

Copy link
Contributor Author

Choose a reason for hiding this comment

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

🤔

  1. It's not for a Maybe [Patch]
  2. See code in Ramtuary

Copy link
Member

Choose a reason for hiding this comment

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

Ah, oh, OK

const getSetOfPatchPaths = R.pipe(R.keys, x => new Set(x));

// :: (Patch -> Patch -> Boolean) -> [Patch] -> [Patch] -> Boolean
export const patchListEqualsBy = R.curry(
Copy link
Member

Choose a reason for hiding this comment

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

Looks quite general to be moved to xod-project

const diffPatchPaths = new Set(
[...prevPatchPaths].filter(x => !nextPatchPaths.has(x))
);
if (diffPatchPaths.size > 0) return false;
Copy link
Member

Choose a reason for hiding this comment

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

😮 such a shame, JS has no set comparisons out of the box

import { explodeMaybe } from 'xod-func-tools';
import * as XP from 'xod-project';

const getSetOfPatchPaths = R.pipe(R.keys, x => new Set(x));
Copy link
Member

Choose a reason for hiding this comment

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

Quite general to be named keySet and moved to xod-func-tools/set. Many missing ops might belong there.

// =============================================================================

// :: StrMap Node -> Set Node
const getSetOfNodeIds = R.pipe(R.keys, a => new Set(a));
Copy link
Member

Choose a reason for hiding this comment

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

Yup. keySet again


// =============================================================================
//
// Functions that checks could something change in the Patch
Copy link
Member

Choose a reason for hiding this comment

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

...checks whether something could change...

* could affect on generic pin deduction or marked as
* "deprecated", "utility" or "dead".
*
* True if have changes.
Copy link
Member

Choose a reason for hiding this comment

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

Pass it through Grammarly, please 😬

Checks if a Patch has some changes over the Nodes that
could affect on generic pin deduction, or the patch changed its
deprecated/utility/dead state
Returns true if the patch has such changes

* True if have changes.
*/
export const haveAddedNodesOrChangedTypesOrBoundValues = def(
'haveAddedNodesOrChangedTypesOrBoundValues :: Patch -> Patch -> Boolean',
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 the name does not scale well. You use isColdOrForecastIsBadOrLeaveForALongTime instead of shouldDressCoat. The latter will work better as it more straightforward and does not depend on the exact such of conditions that may evolve.

So, in this case, something like hasChangesObservableOutside will be more meaningful IMO, if the function is indeed about it. Please, confirm.

A proper naming will immediately show a problem. You use haveAddedNodesOrChangedTypesOrBoundValues for both: project browser selector and deduced pin types selector. But they are interested in different set of criterias. Should be three functions 🤔

  • couldChangeProjectBrowserLook (move to xod-client)
  • couldAffectTypeDeductionInside
  • couldAffectTypeDeductionOutside

*
* True if have changes.
*/
export const haveAddedOrChangedNodes = def(
Copy link
Member

Choose a reason for hiding this comment

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

Is it used?

@nkrkv nkrkv added the wip label May 18, 2018
@brusherru brusherru force-pushed the perf-type-deduction-2 branch 2 times, most recently from 664bcc5 to 4fffb89 Compare May 18, 2018 14:02
@brusherru brusherru removed the wip label May 18, 2018
@brusherru brusherru force-pushed the perf-type-deduction-2 branch from 37bc551 to f996f66 Compare May 18, 2018 15:29
export const compareMapsBy = def(
'compareMapsBy :: (b -> StrMap a) -> b -> b -> Boolean',
(mapGetter, prev, next) => {
// If same nodes maps — nothing changed
Copy link
Member

Choose a reason for hiding this comment

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

The node term is unknown in xod-func-tools. Please, rewrite in a more general fashion.

const prevMapKeys = setOfKeys(prevMap);
const nextMapKeys = setOfKeys(nextMap);
const diffMapKeys = diffSet(prevMapKeys, nextMapKeys);
// If there is some nodes added/deleted
Copy link
Member

Choose a reason for hiding this comment

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

ditto

@@ -117,3 +118,21 @@ export const invertMap = def(
'invertMap :: Map a b -> Map b a',
R.compose(R.fromPairs, R.map(R.reverse), R.toPairs)
);

export const compareMapsBy = def(
'compareMapsBy :: (b -> StrMap a) -> b -> b -> Boolean',
Copy link
Member

Choose a reason for hiding this comment

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

Exported func. A doc comment is missing. The name is misleading a bit. Hard to choose a proper name, but something like mapsToObjectsWithSameKeys or mapsToSameKeyset is closer to the truth.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

sameKeysetBy?

);

export const couldTypeDeductionChange = def(
'couldTypeDeductionChange :: Patch -> Patch -> Boolean',
Copy link
Member

Choose a reason for hiding this comment

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

Type deduction is a process. It can’t change. Did you mean couldDeducedTypesChange?

XP.patchListEqualsBy(R.complement(XP.couldValidityChange))
);

const projectChangesNotAffectProjectBrowserLook = R.either(
Copy link
Member

Choose a reason for hiding this comment

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

Quite hard to reason about functions which are inverted in their name. Subjective and arguable, but will it make the code simpler to understand if you inverse the meaning, i.e., make libChangesKeepBrowserLook and patchListChangesKeepBrowserLook.

listNodes
);

export const didUtilityOrDeprecatedMarkersChange = def(
Copy link
Member

Choose a reason for hiding this comment

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

How about didUtilityOrDeprecatedOrExampleOrFeaturedMarkersChange?

Suggest didCategoryMarkersChange

export const getDeducedPinTypes = createMemoizedSelector(
[getProject, getCurrentPatch],
[R.equals, R.equals],
[
projectHasSamePatchList,
Copy link
Member

Choose a reason for hiding this comment

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

A dangerous presumption. Will work only if no external impact can affect used nodes exterior. Will stale if we have split view.

@nkrkv nkrkv added the wip label May 18, 2018
@nkrkv nkrkv changed the title Tweak performance by optimisation of memoized selector comparators Optimize memoized selector comparators May 21, 2018
@brusherru brusherru force-pushed the perf-type-deduction-2 branch 2 times, most recently from 3d54b28 to cb92c80 Compare May 21, 2018 10:41
@brusherru brusherru removed the wip label May 21, 2018
@brusherru brusherru force-pushed the perf-type-deduction-2 branch from cb92c80 to 9801902 Compare May 21, 2018 12:28
@brusherru
Copy link
Contributor Author

@nkrkv @evgenykochetkov, check it out, please

'maybeEqualsWith :: (a -> a -> Boolean) -> Maybe a -> Maybe a -> Boolean',
(compFn, prevMaybe, nextMaybe) => {
if (prevMaybe === nextMaybe) return true;
if (Maybe.isNothing(nextMaybe) || Maybe.isNothing(prevMaybe)) return false;
Copy link
Contributor

Choose a reason for hiding this comment

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

Should it return true if both are Nothing?

@brusherru brusherru force-pushed the perf-type-deduction-2 branch from 9801902 to c843507 Compare May 21, 2018 13:10
nextPatches
);
},
],
Copy link
Member

Choose a reason for hiding this comment

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

Inlining the comparators makes getDeducedPinTypes harder to follow. Suggest extracting to a function

@@ -109,6 +109,32 @@ export const catMaybies = R.compose(
R.filter(Maybe.isJust)
);

export const maybeEqualsWith = def(
'maybeEqualsWith :: (a -> a -> Boolean) -> Maybe a -> Maybe a -> Boolean',
Copy link
Member

Choose a reason for hiding this comment

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

It’s an R.liftN(2) followed by Maybe.getOrDefault(false).

Copy link
Contributor

Choose a reason for hiding this comment

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

That would return false for two Nothings, which we (probably) don't want

@brusherru brusherru force-pushed the perf-type-deduction-2 branch from c843507 to 7d89d89 Compare May 21, 2018 13:32
Copy link
Member

@nkrkv nkrkv left a comment

Choose a reason for hiding this comment

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

LGTM. Extract the function and merge.

@brusherru brusherru force-pushed the perf-type-deduction-2 branch from 7d89d89 to 9dc27ed Compare May 21, 2018 14:42
@brusherru brusherru merged commit 4e49ff3 into 0.20.x May 21, 2018
@brusherru brusherru deleted the perf-type-deduction-2 branch May 21, 2018 15:07
@nkrkv nkrkv added the hotfix Issue/PR for a patch release label May 22, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
hotfix Issue/PR for a patch release t:perf
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants