Skip to content

Conversation

@codeflash-ai
Copy link

@codeflash-ai codeflash-ai bot commented Oct 4, 2025

📄 54% (0.54x) speedup for get_required_type_classes in src/datadog_api_client/model_utils.py

⏱️ Runtime : 1.52 milliseconds 990 microseconds (best of 25 runs)

📝 Explanation and details

The optimized code achieves a 53% speedup through two key optimizations:

1. Faster type checking in get_required_type_classes:

  • Replaced isinstance(required_type, list/tuple/dict) with type(required_type) is list/tuple/dict
  • This eliminates the overhead of isinstance() which checks inheritance hierarchies, since we only care about exact type matches
  • Line profiler shows this reduces time spent on type checking from ~16.9s to ~13.5s total across all isinstance calls

2. Early return optimization in get_possible_classes:

  • Moved the from_server_context check to the very beginning, avoiding unnecessary list allocation when from_server_context=True (which happens in 58% of calls based on profiler data)
  • When from_server_context=False, uses list concatenation [cls] + composed_model_input_classes(cls) instead of extend() to avoid an extra list creation and resize operation

Performance characteristics:

  • Best gains on workloads with many primitive types (75-92% faster on large primitive type tests)
  • Significant gains on dict-heavy workloads (67-74% faster) due to faster type checking
  • Minimal impact on container types like nested lists/tuples (within 5% either way)
  • The optimization is most effective when from_server_context=True or when processing many non-container types, which aligns with typical API client usage patterns

Correctness verification report:

Test Status
⚙️ Existing Unit Tests 🔘 None Found
🌀 Generated Regression Tests 60 Passed
⏪ Replay Tests 255 Passed
🔎 Concolic Coverage Tests 🔘 None Found
📊 Tests Coverage 100.0%
🌀 Generated Regression Tests and Runtime
import io
from datetime import date, datetime
from types import MappingProxyType
from uuid import UUID

# imports
import pytest  # used for our unit tests
from src.datadog_api_client.model_utils import get_required_type_classes


# Dummy model classes for testing
class ModelSimple: pass
class ModelNormal: pass

none_type = type(None)
file_type = io.IOBase
from src.datadog_api_client.model_utils import get_required_type_classes

# =========================
# Unit Tests for get_required_type_classes
# =========================

# 1. Basic Test Cases

def test_single_primitive_type():
    # Single primitive type (int)
    valid_classes, mapping = get_required_type_classes([int], True) # 2.58μs -> 2.11μs (22.1% faster)

def test_multiple_primitive_types():
    # Multiple primitive types
    valid_classes, mapping = get_required_type_classes([int, str, float], True) # 3.48μs -> 2.72μs (27.9% faster)

def test_list_of_types():
    # List containing a list of types
    valid_classes, mapping = get_required_type_classes([[int, str]], True) # 1.76μs -> 1.76μs (0.454% slower)

def test_tuple_of_types():
    # List containing a tuple of types
    valid_classes, mapping = get_required_type_classes([(int, str)], True) # 1.86μs -> 1.87μs (0.375% slower)

def test_dict_of_types():
    # List containing a dict of types
    valid_classes, mapping = get_required_type_classes([{str: int}], True) # 2.47μs -> 1.91μs (29.5% faster)

def test_mixed_types():
    # List containing a mix of primitive and container types
    valid_classes, mapping = get_required_type_classes([int, [str], {str: float}, (bool,)], True) # 4.05μs -> 3.48μs (16.5% faster)

def test_empty_required_types():
    # Empty required_types_mixed
    valid_classes, mapping = get_required_type_classes([], True) # 1.28μs -> 1.08μs (18.8% faster)

# 2. Edge Test Cases

def test_nested_list_of_types():
    # Nested list: list of lists
    valid_classes, mapping = get_required_type_classes([[[int, str]]], True) # 1.69μs -> 1.73μs (2.71% slower)

def test_nested_tuple_of_types():
    # Nested tuple: tuple of tuples
    valid_classes, mapping = get_required_type_classes([((int, str),)], True) # 1.91μs -> 1.84μs (3.47% faster)

def test_nested_dict_of_types():
    # Nested dict: dict of dicts
    valid_classes, mapping = get_required_type_classes([{str: {str: int}}], True) # 2.03μs -> 1.99μs (2.06% faster)

def test_multiple_container_types():
    # Multiple container types in one call
    valid_classes, mapping = get_required_type_classes([[int], (str, float), {str: bool}], True) # 2.60μs -> 2.48μs (5.17% faster)

def test_required_type_is_none_type():
    # NoneType as required type
    valid_classes, mapping = get_required_type_classes([none_type], True) # 2.40μs -> 2.01μs (19.2% faster)

def test_required_type_is_file_type():
    # FileType as required type
    valid_classes, mapping = get_required_type_classes([file_type], True) # 2.43μs -> 1.96μs (24.0% faster)

def test_required_type_is_uuid():
    # UUID as required type
    valid_classes, mapping = get_required_type_classes([UUID], True) # 2.78μs -> 1.98μs (40.3% faster)

def test_required_type_is_date_and_datetime():
    # date and datetime as required types
    valid_classes, mapping = get_required_type_classes([date, datetime], True) # 2.96μs -> 2.52μs (17.5% faster)

def test_required_type_is_empty_list():
    # Required type is an empty list
    valid_classes, mapping = get_required_type_classes([[]], True) # 1.71μs -> 1.79μs (4.47% slower)

def test_required_type_is_empty_tuple():
    # Required type is an empty tuple
    valid_classes, mapping = get_required_type_classes([()], True) # 2.50μs -> 1.80μs (39.2% faster)



def test_multiple_same_container_types():
    # Multiple lists, tuples, dicts in required_types_mixed
    valid_classes, mapping = get_required_type_classes([[int], [str], (float,), (bool,), {str: int}, {str: str}], True) # 3.50μs -> 3.22μs (8.74% faster)

def test_required_type_is_model_simple():
    # ModelSimple as required type
    valid_classes, mapping = get_required_type_classes([ModelSimple], True) # 2.39μs -> 2.03μs (17.7% faster)

def test_required_type_is_model_normal():
    # ModelNormal as required type
    valid_classes, mapping = get_required_type_classes([ModelNormal], True) # 2.74μs -> 1.95μs (40.7% faster)



def test_large_number_of_primitive_types():
    # Large number of primitive types
    types = [int, str, float, bool, date, datetime, UUID, file_type]
    valid_classes, mapping = get_required_type_classes(types * 100, True) # 240μs -> 125μs (92.6% faster)

def test_large_number_of_lists():
    # Large number of lists
    lists = [[int], [str], [float], [bool]]
    valid_classes, mapping = get_required_type_classes(lists * 200, True) # 60.9μs -> 60.5μs (0.572% faster)

def test_large_number_of_dicts():
    # Large number of dicts
    dicts = [{str: int}, {str: str}, {str: float}]
    valid_classes, mapping = get_required_type_classes(dicts * 300, True) # 175μs -> 100μs (73.8% faster)

def test_large_mixed_types():
    # Large mixed types
    types = [int, [str], (float,), {str: bool}]
    valid_classes, mapping = get_required_type_classes(types * 250, True) # 179μs -> 107μs (67.9% faster)


def test_large_nested_lists():
    # Deeply nested lists (but under 1000 elements)
    nested = [ [ [ [int] ] ] ]
    valid_classes, mapping = get_required_type_classes(nested * 100, True) # 10.6μs -> 11.0μs (3.53% slower)

def test_large_number_of_unique_model_simples():
    # Many unique ModelSimple subclasses
    models = []
    for i in range(500):
        models.append(type(f"MS{i}", (ModelSimple,), {}))
    valid_classes, mapping = get_required_type_classes(models, True) # 158μs -> 83.2μs (90.5% faster)

# Edge: Check that order is preserved for non-container types
def test_order_preservation_for_non_container_types():
    types = [str, int, float, bool]
    valid_classes, mapping = get_required_type_classes(types, True) # 3.65μs -> 2.92μs (24.9% faster)

# Edge: Dict with multiple keys (should only use str key)
def test_dict_with_multiple_keys():
    # Even if dict has multiple keys, only str key is used for mapping
    d = {str: int, int: float}
    valid_classes, mapping = get_required_type_classes([d], True) # 2.51μs -> 1.94μs (29.4% faster)

# Edge: List with mixed types inside (should not flatten)
def test_list_with_mixed_types_inside():
    l = [int, str, float]
    valid_classes, mapping = get_required_type_classes([l], True) # 1.82μs -> 1.85μs (1.25% slower)

# Edge: Tuple with mixed types inside (should not flatten)
def test_tuple_with_mixed_types_inside():
    t = (int, str, float)
    valid_classes, mapping = get_required_type_classes([t], True) # 1.85μs -> 1.84μs (0.651% faster)

# Edge: Dict with non-type keys (should not fail, but mapping will be None)

#------------------------------------------------
import io
from datetime import date, datetime
from types import MappingProxyType
from uuid import UUID

# imports
import pytest
from src.datadog_api_client.model_utils import get_required_type_classes

# function to test
# Unless explicitly stated otherwise all files in this repository are licensed under the Apache-2.0 License.
# This product includes software developed at Datadog (https://www.datadoghq.com/).
# Copyright 2019-Present Datadog, Inc.

none_type = type(None)
file_type = io.IOBase

# Minimal stubs for Model* classes for testing
class ModelSimple:
    pass

class ModelNormal:
    pass

class ModelComposed:
    _composed_schemas = {"oneOf": []}
from src.datadog_api_client.model_utils import get_required_type_classes

# --------------------------
# Unit Tests for get_required_type_classes
# --------------------------

# Helper classes for composed model testing
class SimpleA(ModelSimple):
    pass

class NormalA(ModelNormal):
    pass

class ComposedA(ModelComposed):
    # oneOf: [SimpleA, NormalA]
    _composed_schemas = {"oneOf": [SimpleA, NormalA]}

class ComposedEmpty(ModelComposed):
    # oneOf: []
    _composed_schemas = {"oneOf": []}

# 1. BASIC TEST CASES

def test_single_primitive_type():
    # Should just return that type, no child mapping
    codeflash_output = get_required_type_classes((int,), True); result = codeflash_output # 2.26μs -> 1.92μs (17.5% faster)

def test_multiple_primitive_types():
    # Should include all types, no child mapping
    codeflash_output = get_required_type_classes((int, str, bool), True); result = codeflash_output # 3.29μs -> 2.50μs (31.4% faster)

def test_single_model_simple():
    # Should just return the class, no child mapping
    codeflash_output = get_required_type_classes((SimpleA,), True); result = codeflash_output # 2.24μs -> 1.90μs (17.9% faster)

def test_single_model_normal():
    # Should just return the class, no child mapping
    codeflash_output = get_required_type_classes((NormalA,), True); result = codeflash_output # 2.43μs -> 1.92μs (26.5% faster)

def test_single_model_composed_server_context():
    # Should just return the class, no child mapping
    codeflash_output = get_required_type_classes((ComposedA,), True); result = codeflash_output # 2.29μs -> 1.97μs (16.1% faster)

def test_single_model_composed_client_context():
    # Should return the class and its oneOf classes
    codeflash_output = get_required_type_classes((ComposedA,), False); result = codeflash_output # 2.60μs -> 2.48μs (4.80% faster)

def test_list_type():
    # Should recognize list and map its child types
    codeflash_output = get_required_type_classes(([int, str],), True); result = codeflash_output # 1.84μs -> 1.69μs (8.75% faster)

def test_tuple_type():
    # Should recognize tuple and map its child types
    codeflash_output = get_required_type_classes(((int, str),), True); result = codeflash_output # 1.84μs -> 1.79μs (3.30% faster)

def test_dict_type():
    # Should recognize dict and map its child types (by string key)
    codeflash_output = get_required_type_classes(({str: int},), True); result = codeflash_output # 2.49μs -> 1.89μs (32.2% faster)

def test_mixed_types():
    # Mix of primitive, list, dict, model
    codeflash_output = get_required_type_classes((int, [str], {str: float}, SimpleA), True); result = codeflash_output # 4.24μs -> 3.39μs (25.0% faster)

def test_multiple_list_and_tuple_types():
    # Multiple lists and tuples
    codeflash_output = get_required_type_classes(([int], [str], (float, bool)), True); result = codeflash_output # 2.66μs -> 2.40μs (10.8% faster)

# 2. EDGE TEST CASES

def test_empty_required_types():
    # Should return empty tuple and dict
    codeflash_output = get_required_type_classes((), True); result = codeflash_output # 1.09μs -> 1.11μs (1.89% slower)

def test_empty_list_in_required_types():
    # Should handle empty list as child type
    codeflash_output = get_required_type_classes(([],), True); result = codeflash_output # 1.77μs -> 1.79μs (0.950% slower)

def test_empty_tuple_in_required_types():
    # Should handle empty tuple as child type
    codeflash_output = get_required_type_classes(((),), True); result = codeflash_output # 1.89μs -> 1.96μs (3.56% slower)


def test_composed_model_with_empty_oneof_client_context():
    # Should not add any extra classes
    codeflash_output = get_required_type_classes((ComposedEmpty,), False); result = codeflash_output # 2.81μs -> 2.42μs (16.2% faster)

def test_composed_model_with_nested_oneof_client_context():
    # Compose a composed model with another composed model
    class NestedComposed(ModelComposed):
        _composed_schemas = {"oneOf": [ComposedA, int]}
    codeflash_output = get_required_type_classes((NestedComposed,), False); result = codeflash_output # 2.49μs -> 2.25μs (10.5% faster)


def test_list_of_lists():
    # List of lists
    codeflash_output = get_required_type_classes(([[int]],), True); result = codeflash_output # 1.64μs -> 1.72μs (4.88% slower)

def test_tuple_of_dicts():
    # Tuple of dicts
    codeflash_output = get_required_type_classes((({str: int}, {str: str}),), True); result = codeflash_output # 2.00μs -> 1.76μs (13.5% faster)

def test_duplicate_types():
    # Duplicates in input should be preserved in output tuple
    codeflash_output = get_required_type_classes((int, int, str), True); result = codeflash_output # 3.24μs -> 2.59μs (25.1% faster)

def test_none_type():
    # NoneType should be just like any other type
    codeflash_output = get_required_type_classes((none_type,), True); result = codeflash_output # 2.31μs -> 2.00μs (15.4% faster)

def test_file_type():
    # IOBase should be recognized
    codeflash_output = get_required_type_classes((file_type,), True); result = codeflash_output # 2.28μs -> 1.91μs (19.7% faster)

def test_uuid_type():
    # UUID should be recognized
    codeflash_output = get_required_type_classes((UUID,), True); result = codeflash_output # 2.35μs -> 1.93μs (21.6% faster)

# 3. LARGE SCALE TEST CASES

def test_large_number_of_primitive_types():
    # 100 different types (use int, str, float, bool, etc. repeated)
    types = [int, str, float, bool] * 25  # 100 items
    codeflash_output = get_required_type_classes(tuple(types), True); result = codeflash_output # 32.5μs -> 18.5μs (75.3% faster)

def test_large_list_type():
    # List with 500 child types
    child_types = [int] * 500
    codeflash_output = get_required_type_classes((child_types,), True); result = codeflash_output # 1.68μs -> 1.76μs (4.54% slower)

def test_large_tuple_type():
    # Tuple with 500 child types
    child_types = tuple([str] * 500)
    codeflash_output = get_required_type_classes((child_types,), True); result = codeflash_output # 1.82μs -> 1.93μs (5.50% slower)

def test_large_mixed_types():
    # Mix of 200 ints, 200 [str], 200 {str: float}
    types = [int] * 200 + [[str]] * 200 + [{str: float}] * 200
    codeflash_output = get_required_type_classes(tuple(types), True); result = codeflash_output # 111μs -> 69.4μs (61.2% faster)

def test_large_composed_model_client_context():
    # Compose a model with 20 oneOfs, each a different ModelSimple subclass
    simple_models = []
    for i in range(20):
        cls = type(f"SimpleModel_{i}", (ModelSimple,), {})
        simple_models.append(cls)
    class LargeComposed(ModelComposed):
        _composed_schemas = {"oneOf": simple_models}
    codeflash_output = get_required_type_classes((LargeComposed,), False); result = codeflash_output # 2.72μs -> 2.31μs (17.7% faster)

def test_large_nested_lists():
    # List of lists of lists, 10 levels deep, each with one int
    nested = [int]
    for _ in range(9):
        nested = [nested]
    codeflash_output = get_required_type_classes((nested,), True); result = codeflash_output # 1.66μs -> 1.79μs (7.53% slower)
    # Unwrap 10 levels to get to int
    curr = result[1][list]
    for _ in range(9):
        curr = curr[0]

def test_large_number_of_dict_types():
    # 500 dicts, each with str: int
    types = [{str: int}] * 500
    codeflash_output = get_required_type_classes(tuple(types), True); result = codeflash_output # 97.7μs -> 58.5μs (66.9% faster)

def test_large_composed_model_with_nested_composed():
    # Compose a model with 10 nested composed models, each oneOf the previous
    prev = SimpleA
    for i in range(10):
        cls = type(f"ComposedNest_{i}", (ModelComposed,), {"_composed_schemas": {"oneOf": [prev]}})
        prev = cls
    codeflash_output = get_required_type_classes((prev,), False); result = codeflash_output # 3.11μs -> 2.31μs (34.5% faster)
    # Should include all composed classes and SimpleA
    # Collect all classes
    classes = set()
    curr = prev
    for i in range(10):
        classes.add(curr)
        curr = curr._composed_schemas["oneOf"][0]
    classes.add(SimpleA)
# codeflash_output is used to check that the output of the original code is the same as that of the optimized code.
⏪ Replay Tests and Runtime
Test File::Test Function Original ⏱️ Optimized ⏱️ Speedup
test_pytest_testsv2test_scenarios_py_teststest_thread_py_teststest_version_py_teststest_deserialization_p__replay_test_0.py::test_src_datadog_api_client_model_utils_get_required_type_classes 329μs 249μs 32.3%✅

To edit these changes git checkout codeflash/optimize-get_required_type_classes-mgcu724k and push.

Codeflash

The optimized code achieves a **53% speedup** through two key optimizations:

**1. Faster type checking in `get_required_type_classes`:**
- Replaced `isinstance(required_type, list/tuple/dict)` with `type(required_type) is list/tuple/dict`
- This eliminates the overhead of `isinstance()` which checks inheritance hierarchies, since we only care about exact type matches
- Line profiler shows this reduces time spent on type checking from ~16.9s to ~13.5s total across all `isinstance` calls

**2. Early return optimization in `get_possible_classes`:**
- Moved the `from_server_context` check to the very beginning, avoiding unnecessary list allocation when `from_server_context=True` (which happens in 58% of calls based on profiler data)
- When `from_server_context=False`, uses list concatenation `[cls] + composed_model_input_classes(cls)` instead of `extend()` to avoid an extra list creation and resize operation

**Performance characteristics:**
- **Best gains** on workloads with many primitive types (75-92% faster on large primitive type tests)
- **Significant gains** on dict-heavy workloads (67-74% faster) due to faster type checking
- **Minimal impact** on container types like nested lists/tuples (within 5% either way)
- The optimization is most effective when `from_server_context=True` or when processing many non-container types, which aligns with typical API client usage patterns
@codeflash-ai codeflash-ai bot requested a review from aseembits93 October 4, 2025 22:20
@codeflash-ai codeflash-ai bot added the ⚡️ codeflash Optimization PR opened by Codeflash AI label Oct 4, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

⚡️ codeflash Optimization PR opened by Codeflash AI

Projects

None yet

Development

Successfully merging this pull request may close these issues.

0 participants