Skip to content

Commit

Permalink
perf: reduce style evaluator memory signature in reearth/core (#563)
Browse files Browse the repository at this point in the history
* perf: cache evaluated expression output

* chore: remove evalExpression cache

* fix: export EXPRESSION_CACHES
  • Loading branch information
pyshx committed Mar 21, 2023
1 parent 25bdff5 commit f74b561
Show file tree
Hide file tree
Showing 8 changed files with 267 additions and 222 deletions.
61 changes: 30 additions & 31 deletions src/core/mantle/evaluator/simple/conditionalExpression/index.ts
@@ -1,53 +1,52 @@
import { Feature, ConditionsExpression } from "../../../types";
import { defined } from "../../../utils";
import { Expression } from "../expression";

export class ConditionalExpression {
#conditions: [string, string][];
#runtimeConditions: Statement[];
#feature?: Feature;
private _conditions: [string, string][];
private _runtimeConditions: Statement[];
private _feature?: Feature;
private _memoizedResult: any;

constructor(conditionsExpression: ConditionsExpression, feature?: Feature, defines?: any) {
this.#conditions = conditionsExpression.conditions;
this.#runtimeConditions = [];
this.#feature = feature;
this._conditions = conditionsExpression.conditions;
this._runtimeConditions = [];
this._feature = feature;
this._memoizedResult = undefined;

this.setRuntime(defines);
}

setRuntime(defines: any) {
const runtimeConditions = [];
const conditions = this.#conditions;
if (!defined(conditions)) {
return;
}
const length = conditions.length;
for (let i = 0; i < length; i++) {
const statement = conditions[i];
const runtimeConditions = this._conditions?.map(statement => {
const cond = String(statement[0]);
const condExpression = String(statement[1]);
runtimeConditions.push(
new Statement(
new Expression(cond, this.#feature, defines),
new Expression(condExpression, this.#feature, defines),
),
return new Statement(
new Expression(cond, this._feature, defines),
new Expression(condExpression, this._feature, defines),
);
}
this.#runtimeConditions = runtimeConditions;
});
this._runtimeConditions = runtimeConditions ?? [];
this._memoizedResult = undefined;
}

evaluate() {
const conditions = this.#runtimeConditions;
if (defined(conditions)) {
const length = conditions.length;
for (let i = 0; i < length; ++i) {
const statement = conditions[i];
if (statement.condition.evaluate()) {
return statement.expression.evaluate();
}
if (typeof this._memoizedResult !== "undefined") {
return this._memoizedResult;
}

const conditions = this._runtimeConditions;
const length = conditions.length;
for (let i = 0; i < length; i++) {
const statement = conditions[i];
if (statement.condition.evaluate()) {
const result = statement.expression.evaluate();
this._memoizedResult = result;
return result;
}
}
return undefined;
const undefinedResult = undefined;
this._memoizedResult = undefinedResult;
return undefinedResult;
}
}

Expand Down
1 change: 0 additions & 1 deletion src/core/mantle/evaluator/simple/expression/constants.ts
Expand Up @@ -39,7 +39,6 @@ export const binaryOperators = [
"=~",
];

export const variableRegex = /\${(.*?)}/g; // Matches ${variable_name}
export const backslashRegex = /\\/g;
export const backslashReplacement = "@#%";
export const replacementRegex = /@#%/g;
Expand Down
18 changes: 9 additions & 9 deletions src/core/mantle/evaluator/simple/expression/expression.ts
@@ -1,7 +1,6 @@
import jsep from "jsep";

import { Feature } from "../../../types";
import { defined } from "../../../utils";

import { backslashRegex, backslashReplacement } from "./constants";
import { Node } from "./node";
Expand All @@ -14,6 +13,7 @@ export type JPLiteral = {
};

export const EXPRESSION_CACHES = new Map<string, Node | Error>();
const DEFINE_PLACEHOLDER_REGEX_CACHE = new Map<string, RegExp>();

export class Expression {
private _expression: string;
Expand Down Expand Up @@ -63,17 +63,17 @@ export class Expression {
}

export function replaceDefines(expression: string, defines: any): string {
if (!defined(defines)) {
if (typeof defines === "undefined") {
return expression;
}
for (const key in defines) {
const definePlaceholder = new RegExp(`\\$\\{${key}\\}`, "g");
const defineReplace = `(${defines[key]})`;
if (defined(defineReplace)) {
expression = expression.replace(definePlaceholder, defineReplace);
}
let definePlaceholderRegex = DEFINE_PLACEHOLDER_REGEX_CACHE.get(expression);
if (!definePlaceholderRegex) {
definePlaceholderRegex = new RegExp(`\\$\\{(${Object.keys(defines).join("|")})\\}`, "g");
DEFINE_PLACEHOLDER_REGEX_CACHE.set(expression, definePlaceholderRegex);
}
return expression;
return expression.replace(definePlaceholderRegex, (_, key) =>
typeof defines[key] !== "undefined" ? `(${defines[key]})` : "",
);
}

export function removeBackslashes(expression: string): string {
Expand Down
@@ -1,4 +1,4 @@
import { defaultValue, defined } from "../../../../utils";
import { defaultValue } from "../../../../utils";
import { rgbaMatcher, rrggbbaaMatcher, builtInColor } from "../constants";

//rgb(), rgba(), or rgb%()
Expand Down Expand Up @@ -32,7 +32,7 @@ export class Color {
color = color.replace(/\s/g, "");

const namedColor = getColor(color);
if (defined(namedColor)) {
if (typeof namedColor !== "undefined") {
return namedColor;
}

Expand Down Expand Up @@ -109,7 +109,7 @@ export class Color {
blue = hue2rgb(m1, m2, hue - 1 / 3);
}

if (!defined(result)) {
if (typeof result === "undefined") {
return new Color(red, green, blue, alpha);
}

Expand Down

0 comments on commit f74b561

Please sign in to comment.