浏览代码

ImmutableVar perf optimizations (#3814)

* Use lru_cache on expensive typing-related functions

* Skip instantiation of VarData unless data was actually merged

* Revert "integration: bump listening timeout to 1800 seconds"

This reverts commit a94eedff6d06d8f427cdaf2ef704ea10431d388a.

* Revert "integration: bump listening timeout to 1200 seconds"

This reverts commit 86563b66a443c9c9be10850442ae10e9594a630a.
Masen Furer 8 月之前
父节点
当前提交
8f396fc348
共有 6 个文件被更改,包括 33 次插入20 次删除
  1. 2 1
      reflex/ivars/base.py
  2. 1 3
      reflex/ivars/object.py
  3. 1 3
      reflex/ivars/sequence.py
  4. 21 2
      reflex/utils/types.py
  5. 7 10
      reflex/vars.py
  6. 1 1
      scripts/integration.sh

+ 2 - 1
reflex/ivars/base.py

@@ -32,7 +32,7 @@ from typing import (
     overload,
 )
 
-from typing_extensions import ParamSpec, get_origin, get_type_hints, override
+from typing_extensions import ParamSpec, get_type_hints, override
 
 from reflex import constants
 from reflex.base import Base
@@ -40,6 +40,7 @@ from reflex.constants.colors import Color
 from reflex.utils import console, imports, serializers, types
 from reflex.utils.exceptions import VarDependencyError, VarTypeError, VarValueError
 from reflex.utils.format import format_state_name
+from reflex.utils.types import get_origin
 from reflex.vars import (
     ComputedVar,
     ImmutableVarData,

+ 1 - 3
reflex/ivars/object.py

@@ -19,11 +19,9 @@ from typing import (
     overload,
 )
 
-from typing_extensions import get_origin
-
 from reflex.utils import types
 from reflex.utils.exceptions import VarAttributeError
-from reflex.utils.types import GenericType, get_attribute_access_type
+from reflex.utils.types import GenericType, get_attribute_access_type, get_origin
 from reflex.vars import ImmutableVarData, Var, VarData
 
 from .base import (

+ 1 - 3
reflex/ivars/sequence.py

@@ -22,11 +22,9 @@ from typing import (
     overload,
 )
 
-from typing_extensions import get_origin
-
 from reflex import constants
 from reflex.constants.base import REFLEX_VAR_OPENING_TAG
-from reflex.utils.types import GenericType
+from reflex.utils.types import GenericType, get_origin
 from reflex.vars import (
     ImmutableVarData,
     Var,

+ 21 - 2
reflex/utils/types.py

@@ -6,7 +6,7 @@ import contextlib
 import inspect
 import sys
 import types
-from functools import cached_property, wraps
+from functools import cached_property, lru_cache, wraps
 from typing import (
     Any,
     Callable,
@@ -21,9 +21,11 @@ from typing import (
     Union,
     _GenericAlias,  # type: ignore
     get_args,
-    get_origin,
     get_type_hints,
 )
+from typing import (
+    get_origin as get_origin_og,
+)
 
 import sqlalchemy
 
@@ -133,6 +135,20 @@ class Unset:
         return False
 
 
+@lru_cache()
+def get_origin(tp):
+    """Get the origin of a class.
+
+    Args:
+        tp: The class to get the origin of.
+
+    Returns:
+        The origin of the class.
+    """
+    return get_origin_og(tp)
+
+
+@lru_cache()
 def is_generic_alias(cls: GenericType) -> bool:
     """Check whether the class is a generic alias.
 
@@ -157,6 +173,7 @@ def is_none(cls: GenericType) -> bool:
     return cls is type(None) or cls is None
 
 
+@lru_cache()
 def is_union(cls: GenericType) -> bool:
     """Check if a class is a Union.
 
@@ -169,6 +186,7 @@ def is_union(cls: GenericType) -> bool:
     return get_origin(cls) in UnionTypes
 
 
+@lru_cache()
 def is_literal(cls: GenericType) -> bool:
     """Check if a class is a Literal.
 
@@ -314,6 +332,7 @@ def get_attribute_access_type(cls: GenericType, name: str) -> GenericType | None
     return None  # Attribute is not accessible.
 
 
+@lru_cache()
 def get_base_class(cls: GenericType) -> Type:
     """Get the base class of a class.
 

+ 7 - 10
reflex/vars.py

@@ -29,7 +29,6 @@ from typing import (
     _GenericAlias,  # type: ignore
     cast,
     get_args,
-    get_origin,
     get_type_hints,
 )
 
@@ -51,7 +50,7 @@ from reflex.utils.imports import (
     ParsedImportDict,
     parse_imports,
 )
-from reflex.utils.types import override
+from reflex.utils.types import get_origin, override
 
 if TYPE_CHECKING:
     from reflex.state import BaseState
@@ -182,15 +181,14 @@ class VarData(Base):
                 var_data.interpolations if isinstance(var_data, VarData) else []
             )
 
-        return (
-            cls(
+        if state or _imports or hooks or interpolations:
+            return cls(
                 state=state,
                 imports=_imports,
                 hooks=hooks,
                 interpolations=interpolations,
             )
-            or None
-        )
+        return None
 
     def __bool__(self) -> bool:
         """Check if the var data is non-empty.
@@ -302,14 +300,13 @@ class ImmutableVarData:
                 else {k: None for k in var_data.hooks}
             )
 
-        return (
-            ImmutableVarData(
+        if state or _imports or hooks:
+            return ImmutableVarData(
                 state=state,
                 imports=_imports,
                 hooks=hooks,
             )
-            or None
-        )
+        return None
 
     def __bool__(self) -> bool:
         """Check if the var data is non-empty.

+ 1 - 1
scripts/integration.sh

@@ -34,4 +34,4 @@ if [ -f /proc/$pid/winpid ]; then
   echo "Windows detected, passing winpid $pid to port waiter"
 fi
 
-python scripts/wait_for_listening_port.py $check_ports --timeout=1800 --server-pid "$pid"
+python scripts/wait_for_listening_port.py $check_ports --timeout=900 --server-pid "$pid"