Skip to content

Conversation

@codeflash-ai
Copy link

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

📄 41% (0.41x) speedup for build_default_config in stanza/pipeline/core.py

⏱️ Runtime : 1.56 milliseconds 1.11 milliseconds (best of 65 runs)

📝 Explanation and details

The optimization achieves a 40% speedup through several targeted performance improvements:

Key Optimizations:

  1. Localized os.path.join: Stored os.path.join as a local variable (join_path) to avoid repeated global name lookups, reducing overhead in the hot path where path construction happens frequently.

  2. Path prefix caching: Pre-computed the model directory prefix (mpfx = join_path(model_dir, lang, processor)) and reused it with f-string formatting (f"{mpfx}/{ms.package}.pt") instead of calling os.path.join repeatedly for each model path.

  3. Optimized dictionary lookup in build_default_config_option: Used PROCESSOR_VARIANTS.get() with a null check instead of direct indexing, avoiding potential KeyError handling overhead and making the variant check more efficient.

  4. Reduced function call overhead: Replaced os.path.join() calls with f-string concatenation where path prefixes are already constructed, eliminating function call overhead in tight loops.

  5. Variable caching: Cached dependencies[0] as deps_0 and model_specs[0] as model_spec_0 to avoid repeated list indexing.

Performance Impact by Test Type:

  • Large scale tests (many processors/dependencies) see the most benefit from reduced path construction overhead
  • NER processing benefits from optimized dependency path construction in nested loops
  • Basic tests show moderate improvement from reduced global lookups and function call overhead

The line profiler shows the most significant time reductions in path construction operations (lines with os.path.join calls), which were the primary bottlenecks consuming 15-35% of total execution time.

Correctness verification report:

Test Status
⚙️ Existing Unit Tests 🔘 None Found
🌀 Generated Regression Tests 32 Passed
⏪ Replay Tests 7 Passed
🔎 Concolic Coverage Tests 🔘 None Found
📊 Tests Coverage 92.1%
🌀 Generated Regression Tests and Runtime
import os

# imports
import pytest
from stanza.pipeline.core import build_default_config

NER = "ner"

# Custom exception for package errors
class IllegalPackageError(Exception):
    pass

# ModelSpec class for test inputs
class ModelSpec:
    def __init__(self, processor, package, dependencies=None):
        self.processor = processor
        self.package = package
        self.dependencies = dependencies if dependencies else []
from stanza.pipeline.core import build_default_config

# --- Unit Tests ---

# Basic Test Cases

def test_basic_single_processor_single_package_no_dependencies():
    # Test with one processor, one package, no dependencies
    resources = {}
    lang = "en"
    model_dir = "/models"
    load_list = [
        ("pos", [ModelSpec("pos", "default")])
    ]
    codeflash_output = build_default_config(resources, lang, model_dir, load_list); config = codeflash_output

def test_basic_single_processor_single_package_with_dependencies():
    # Test with one processor, one package, with dependencies
    resources = {}
    lang = "en"
    model_dir = "/models"
    load_list = [
        ("pos", [ModelSpec("pos", "default", dependencies=[("lemma", "default")])])
    ]
    codeflash_output = build_default_config(resources, lang, model_dir, load_list); config = codeflash_output

def test_basic_multiple_processors_each_one_package():
    # Test with two processors, each with one package, no dependencies
    resources = {}
    lang = "en"
    model_dir = "/models"
    load_list = [
        ("pos", [ModelSpec("pos", "default")]),
        ("lemma", [ModelSpec("lemma", "default")])
    ]
    codeflash_output = build_default_config(resources, lang, model_dir, load_list); config = codeflash_output

def test_basic_ner_processor_multiple_models_and_dependencies():
    # Test NER processor with two model packages and dependencies
    resources = {}
    lang = "en"
    model_dir = "/models"
    load_list = [
        ("ner", [
            ModelSpec("ner", "conll03", dependencies=[("forward_charlm", "default")]),
            ModelSpec("ner", "ontonotes", dependencies=[("backward_charlm", "default")])
        ])
    ]
    codeflash_output = build_default_config(resources, lang, model_dir, load_list); config = codeflash_output

def test_basic_variant_processor_option():
    # Test processor variant triggers special config option
    resources = {}
    lang = "en"
    model_dir = "/models"
    load_list = [
        ("pos", [ModelSpec("pos", "xpos")])
    ]
    codeflash_output = build_default_config(resources, lang, model_dir, load_list); config = codeflash_output

def test_basic_lemma_identity_option():
    # Test lemma identity triggers special config option
    resources = {}
    lang = "en"
    model_dir = "/models"
    load_list = [
        ("lemma", [ModelSpec("lemma", "identity")])
    ]
    codeflash_output = build_default_config(resources, lang, model_dir, load_list); config = codeflash_output

# Edge Test Cases




def test_edge_ner_with_no_dependencies():
    # Test NER processor with multiple models, no dependencies
    resources = {}
    lang = "en"
    model_dir = "/models"
    load_list = [
        ("ner", [
            ModelSpec("ner", "conll03"),
            ModelSpec("ner", "ontonotes")
        ])
    ]
    codeflash_output = build_default_config(resources, lang, model_dir, load_list); config = codeflash_output

def test_edge_dependency_is_empty_list():
    # Test processor with dependency as empty list
    resources = {}
    lang = "en"
    model_dir = "/models"
    load_list = [
        ("pos", [ModelSpec("pos", "default", dependencies=[])])
    ]
    codeflash_output = build_default_config(resources, lang, model_dir, load_list); config = codeflash_output

def test_edge_dependency_is_none():
    # Test processor with dependency as None
    resources = {}
    lang = "en"
    model_dir = "/models"
    load_list = [
        ("pos", [ModelSpec("pos", "default", dependencies=None)])
    ]
    codeflash_output = build_default_config(resources, lang, model_dir, load_list); config = codeflash_output

def test_edge_dependency_multiple_dependencies():
    # Test processor with multiple dependencies
    resources = {}
    lang = "en"
    model_dir = "/models"
    load_list = [
        ("pos", [ModelSpec("pos", "default", dependencies=[("lemma", "default"), ("mwt", "default")])])
    ]
    codeflash_output = build_default_config(resources, lang, model_dir, load_list); config = codeflash_output

def test_edge_empty_load_list():
    # Test with empty load_list
    resources = {}
    lang = "en"
    model_dir = "/models"
    load_list = []
    codeflash_output = build_default_config(resources, lang, model_dir, load_list); config = codeflash_output

def test_edge_unknown_processor_variant():
    # Test with unknown processor variant (should not trigger option)
    resources = {}
    lang = "en"
    model_dir = "/models"
    load_list = [
        ("tokenize", [ModelSpec("tokenize", "default")])
    ]
    codeflash_output = build_default_config(resources, lang, model_dir, load_list); config = codeflash_output

# Large Scale Test Cases

def test_large_scale_many_processors():
    # Test with many processors (up to 50), each with one package, no dependencies
    resources = {}
    lang = "en"
    model_dir = "/models"
    num_processors = 50
    load_list = [
        (f"proc{i}", [ModelSpec(f"proc{i}", "default")])
        for i in range(num_processors)
    ]
    codeflash_output = build_default_config(resources, lang, model_dir, load_list); config = codeflash_output
    for i in range(num_processors):
        key = f"proc{i}_model_path"

def test_large_scale_ner_many_models_and_dependencies():
    # Test NER processor with many models and dependencies (up to 100)
    resources = {}
    lang = "en"
    model_dir = "/models"
    num_models = 100
    load_list = [
        ("ner", [
            ModelSpec("ner", f"model{i}", dependencies=[("dep", f"dep{i}")])
            for i in range(num_models)
        ])
    ]
    codeflash_output = build_default_config(resources, lang, model_dir, load_list); config = codeflash_output
    for i in range(num_models):
        pass

def test_large_scale_many_dependencies():
    # Test processor with one package, but many dependencies (up to 200)
    resources = {}
    lang = "en"
    model_dir = "/models"
    num_deps = 200
    dependencies = [(f"dep{i}", "default") for i in range(num_deps)]
    load_list = [
        ("pos", [ModelSpec("pos", "default", dependencies=dependencies)])
    ]
    codeflash_output = build_default_config(resources, lang, model_dir, load_list); config = codeflash_output
    for i in range(num_deps):
        key = f"pos_dep{i}_path"

def test_large_scale_combined_processors_and_ner():
    # Test with several processors and a large NER block
    resources = {}
    lang = "en"
    model_dir = "/models"
    num_ner_models = 50
    num_other_procs = 10
    load_list = [
        ("ner", [
            ModelSpec("ner", f"model{i}", dependencies=[("dep", f"dep{i}")])
            for i in range(num_ner_models)
        ])
    ] + [
        (f"proc{i}", [ModelSpec(f"proc{i}", "default")])
        for i in range(num_other_procs)
    ]
    codeflash_output = build_default_config(resources, lang, model_dir, load_list); config = codeflash_output
    for i in range(num_ner_models):
        pass
    for i in range(num_other_procs):
        key = f"proc{i}_model_path"
# codeflash_output is used to check that the output of the original code is the same as that of the optimized code.
#------------------------------------------------
import os

# imports
import pytest  # used for our unit tests
from stanza.pipeline.core import build_default_config

NER = 'ner'

class IllegalPackageError(Exception):
    pass

# Minimal ModelSpec class for testing
class ModelSpec:
    def __init__(self, processor, package, dependencies=None):
        self.processor = processor
        self.package = package
        self.dependencies = dependencies if dependencies is not None else []
from stanza.pipeline.core import build_default_config

# --- Unit tests ---

# Basic Test Cases

def test_basic_single_processor_no_dependencies():
    # Test a single processor with no dependencies
    resources = {}
    lang = 'en'
    model_dir = '/models'
    load_list = [
        ('pos', [ModelSpec('pos', 'default')])
    ]
    codeflash_output = build_default_config(resources, lang, model_dir, load_list); config = codeflash_output

def test_basic_single_processor_with_dependencies():
    # Test a single processor with one dependency
    resources = {}
    lang = 'en'
    model_dir = '/models'
    load_list = [
        ('depparse', [ModelSpec('depparse', 'default', dependencies=[('pos', 'default')])])
    ]
    codeflash_output = build_default_config(resources, lang, model_dir, load_list); config = codeflash_output

def test_basic_multiple_processors():
    # Test multiple processors, each with no dependencies
    resources = {}
    lang = 'en'
    model_dir = '/models'
    load_list = [
        ('pos', [ModelSpec('pos', 'default')]),
        ('depparse', [ModelSpec('depparse', 'default')])
    ]
    codeflash_output = build_default_config(resources, lang, model_dir, load_list); config = codeflash_output

def test_basic_ner_multiple_models():
    # Test NER processor with multiple model specs (should return list)
    resources = {}
    lang = 'en'
    model_dir = '/models'
    load_list = [
        ('ner', [
            ModelSpec('ner', 'default'),
            ModelSpec('ner', 'conll03')
        ])
    ]
    codeflash_output = build_default_config(resources, lang, model_dir, load_list); config = codeflash_output

def test_basic_ner_with_dependencies():
    # Test NER processor with dependencies
    resources = {}
    lang = 'en'
    model_dir = '/models'
    load_list = [
        ('ner', [
            ModelSpec('ner', 'default', dependencies=[('forward_charlm', 'default'), ('backward_charlm', 'default')])
        ])
    ]
    codeflash_output = build_default_config(resources, lang, model_dir, load_list); config = codeflash_output

def test_basic_variant_processor():
    # Test processor variant (e.g., pos:fast)
    resources = {}
    lang = 'en'
    model_dir = '/models'
    load_list = [
        ('pos', [ModelSpec('pos', 'fast')])
    ]
    codeflash_output = build_default_config(resources, lang, model_dir, load_list); config = codeflash_output

def test_basic_lemma_identity():
    # Test lemma=identity special case
    resources = {}
    lang = 'en'
    model_dir = '/models'
    load_list = [
        ('lemma', [ModelSpec('lemma', 'identity')])
    ]
    codeflash_output = build_default_config(resources, lang, model_dir, load_list); config = codeflash_output

# Edge Test Cases




def test_edge_empty_dependencies():
    # Dependencies list is empty; should not add dependency paths
    resources = {}
    lang = 'en'
    model_dir = '/models'
    load_list = [
        ('pos', [ModelSpec('pos', 'default', dependencies=[])])
    ]
    codeflash_output = build_default_config(resources, lang, model_dir, load_list); config = codeflash_output

def test_edge_none_dependencies():
    # Dependencies is None; should not add dependency paths
    resources = {}
    lang = 'en'
    model_dir = '/models'
    load_list = [
        ('pos', [ModelSpec('pos', 'default', dependencies=None)])
    ]
    codeflash_output = build_default_config(resources, lang, model_dir, load_list); config = codeflash_output

def test_edge_ner_mixed_dependencies():
    # NER with mixed dependencies and empty dependency blocks
    resources = {}
    lang = 'en'
    model_dir = '/models'
    load_list = [
        ('ner', [
            ModelSpec('ner', 'default', dependencies=[('forward_charlm', 'default')]),
            ModelSpec('ner', 'conll03', dependencies=[])
        ])
    ]
    codeflash_output = build_default_config(resources, lang, model_dir, load_list); config = codeflash_output

def test_edge_unknown_processor_variant():
    # Processor not in PROCESSOR_VARIANTS should not trigger variant logic
    resources = {}
    lang = 'en'
    model_dir = '/models'
    load_list = [
        ('xyz', [ModelSpec('xyz', 'special')])
    ]
    codeflash_output = build_default_config(resources, lang, model_dir, load_list); config = codeflash_output

def test_edge_no_model_specs():
    # Processor with empty model_specs list should not crash, but will skip
    resources = {}
    lang = 'en'
    model_dir = '/models'
    load_list = [
        ('pos', [])
    ]
    # Should raise IndexError due to model_specs[0] access
    with pytest.raises(IndexError):
        build_default_config(resources, lang, model_dir, load_list)

# Large Scale Test Cases

def test_large_many_processors():
    # Test with many processors (up to 1000)
    resources = {}
    lang = 'en'
    model_dir = '/models'
    load_list = [
        (f'proc{i}', [ModelSpec(f'proc{i}', 'default')]) for i in range(100)
    ]
    codeflash_output = build_default_config(resources, lang, model_dir, load_list); config = codeflash_output
    for i in range(100):
        key = f'proc{i}_model_path'

def test_large_many_ner_models():
    # Test NER with many models (up to 1000)
    resources = {}
    lang = 'en'
    model_dir = '/models'
    ner_model_specs = [ModelSpec('ner', f'pkg{i}') for i in range(100)]
    load_list = [
        ('ner', ner_model_specs)
    ]
    codeflash_output = build_default_config(resources, lang, model_dir, load_list); config = codeflash_output

def test_large_many_dependencies():
    # Test a processor with many dependencies
    resources = {}
    lang = 'en'
    model_dir = '/models'
    dependencies = [(f'dep{i}', 'default') for i in range(50)]
    load_list = [
        ('depparse', [ModelSpec('depparse', 'default', dependencies=dependencies)])
    ]
    codeflash_output = build_default_config(resources, lang, model_dir, load_list); config = codeflash_output
    for i in range(50):
        key = f'depparse_dep{i}_path'

def test_large_mixed_processors_and_ner():
    # Test a mix of many processors and a large NER processor
    resources = {}
    lang = 'en'
    model_dir = '/models'
    load_list = [
        (f'proc{i}', [ModelSpec(f'proc{i}', 'default')]) for i in range(50)
    ]
    ner_model_specs = [ModelSpec('ner', f'pkg{i}') for i in range(50)]
    load_list.append(('ner', ner_model_specs))
    codeflash_output = build_default_config(resources, lang, model_dir, load_list); config = codeflash_output
    for i in range(50):
        key = f'proc{i}_model_path'
# 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

To edit these changes git checkout codeflash/optimize-build_default_config-mh4exzdl and push.

Codeflash

The optimization achieves a **40% speedup** through several targeted performance improvements:

**Key Optimizations:**

1. **Localized `os.path.join`**: Stored `os.path.join` as a local variable (`join_path`) to avoid repeated global name lookups, reducing overhead in the hot path where path construction happens frequently.

2. **Path prefix caching**: Pre-computed the model directory prefix (`mpfx = join_path(model_dir, lang, processor)`) and reused it with f-string formatting (`f"{mpfx}/{ms.package}.pt"`) instead of calling `os.path.join` repeatedly for each model path.

3. **Optimized dictionary lookup in `build_default_config_option`**: Used `PROCESSOR_VARIANTS.get()` with a null check instead of direct indexing, avoiding potential KeyError handling overhead and making the variant check more efficient.

4. **Reduced function call overhead**: Replaced `os.path.join()` calls with f-string concatenation where path prefixes are already constructed, eliminating function call overhead in tight loops.

5. **Variable caching**: Cached `dependencies[0]` as `deps_0` and `model_specs[0]` as `model_spec_0` to avoid repeated list indexing.

**Performance Impact by Test Type:**
- **Large scale tests** (many processors/dependencies) see the most benefit from reduced path construction overhead
- **NER processing** benefits from optimized dependency path construction in nested loops
- **Basic tests** show moderate improvement from reduced global lookups and function call overhead

The line profiler shows the most significant time reductions in path construction operations (lines with `os.path.join` calls), which were the primary bottlenecks consuming 15-35% of total execution time.
@codeflash-ai codeflash-ai bot requested a review from mashraf-222 October 24, 2025 05:31
@codeflash-ai codeflash-ai bot added the ⚡️ codeflash Optimization PR opened by Codeflash AI label Oct 24, 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.

1 participant