Skip to content
Open in Anthropic

Performance Characterization

This document provides asymptotic analysis, benchmark results, and performance recommendations for TeXpr. Understanding these characteristics is essential when evaluating user-supplied expressions.

Asymptotic Complexity

Parsing

OperationTime ComplexitySpace Complexity
TokenizationO(n)O(n)
ParsingO(n)O(d)
TotalO(n)O(n + d)

Where:

  • n = input string length
  • d = maximum nesting depth

The parser is a single-pass recursive descent with no backtracking, ensuring linear time complexity.

Evaluation

Expression TypeTime ComplexityNotes
ArithmeticO(nodes)Linear in AST size
Function callsO(nodes)Single function call per node
Summation O(iterations × body)Iteration count dominates
Integration O(10,000 × integrand)Fixed 10k Simpson intervals
DifferentiationO(nodes)Symbolic, creates new AST
Matrix operationsO(n³) for inverse/detStandard matrix algorithms

Worst-Case Scenarios

ScenarioComplexityMitigation
Deep nesting (((...)))O(d) stack spaceRecursion depth limit
Large summation ∑_{1}^{n}O(n × body)Iteration limit (100k)
Repeated evaluationO(n) per callUse caching
Complex derivativesO(n × order)AST grows with order

Benchmark Results

Test Environment

Benchmarks run on:

  • Language: Dart VM (release mode)
  • Tool: benchmark_harness package
  • Cache: Disabled to measure raw performance

Parse + Evaluate (Cold Path)

These benchmarks measure the full cycle of parsing and evaluating an expression:

ExpressionTime (µs)Category
1 + 2 + 3 + 4 + 5~2-5Basic arithmetic
x * y * z~3-6Multiplication chain
\sin{x} + \cos{x}~5-10Trigonometry
\sqrt{x^2 + y^2}~5-10Power and root
\int_{0}^{1} x^2 dx~2,000-3,000Definite integral
\frac{d}{dx}(x^{10})~10-20Derivative
\lim_{x \to \infty} \frac{2x+1}{x+3}~50-100Limit
2×2 matrix parse~10-20Matrix
3×3 matrix power~30-50Matrix operations
Normal distribution PDF~15-25Academic
Lorentz factor~8-15Physics

Evaluate Only (Hot Path)

Pre-parsed expressions with repeated evaluation:

ExpressionTime (µs)Speedup vs Cold
\sin{x} + \cos{x}~1-2~5x
\frac{d}{dx}(x^{10})~3-5~4x
Normal distribution PDF~3-5~5x

Caching Effects

ScenarioThroughput
Cold (no cache)~500 evaluations/ms
Warm (L1 parse cache)~5,000 evaluations/ms
Hot (L2 eval cache)~15,000 evaluations/ms

Cross-Language Comparison

TeXpr includes benchmarks for comparison against Python (SymPy) and JavaScript (mathjs).

Methodology

  • Each language uses native benchmarking tools
  • Dart: benchmark_harness
  • Python: pytest-benchmark
  • JavaScript: benchmark.js

Representative Results

ExpressionDart (TeXpr)Python (SymPy)JavaScript (mathjs)
Simple arithmetic~3 µs~100 µs~5 µs
Trigonometry~8 µs~150 µs~10 µs
Normal PDF~20 µs~300 µs~25 µs
Matrix 2×2~15 µs~200 µs~20 µs

NOTE

SymPy is a full CAS with symbolic capabilities; direct comparison is not apples-to-apples.

Running Benchmarks

bash
# Dart
dart run benchmark/advanced_benchmark.dart

# Python
cd benchmark/comparison/python
pip install pytest pytest-benchmark sympy
pytest benchmark_pytest.py --benchmark-only -v

# JavaScript
cd benchmark/comparison/js
npm install
npm run benchmark

Memory Characteristics

Per-Expression Memory

ComponentTypical Size
Token~40 bytes
AST node~50-100 bytes
Simple expression AST~500 bytes
Complex expression AST~2-5 KB

Cache Memory

Cache LayerDefault SizeMemory
L1 Parse cache128 entries~64 KB typical
L2 Eval cache256 entries~16 KB typical
L3 Differentiation64 entries~32 KB typical
L4 Sub-expressionTransientPer-evaluation

Memory Limits

Configure cache sizes to bound memory usage:

dart
final evaluator = Texpr(
  cacheConfig: CacheConfig(
    parsedExpressionCacheSize: 64,    // Smaller cache
    maxCacheInputLength: 2000,         // Reject very long inputs
  ),
);

Performance Recommendations

For High-Throughput Applications

  1. Parse once, evaluate many

    dart
    final ast = texpr.parse(expression);
    for (final vars in variableSets) {
      texpr.evaluateParsed(ast, vars);
    }
  2. Enable caching (default)

    dart
    final texpr = Texpr();  // Caching enabled by default
  3. Pre-warm cache for known expressions

    dart
    for (final expr in commonExpressions) {
      texpr.parse(expr);  // Populates L1 cache
    }

For User-Supplied Expressions

  1. Limit input length

    dart
    if (input.length > 5000) {
      throw ArgumentError('Expression too long');
    }
  2. Validate before evaluating

    dart
    final validation = texpr.validate(input);
    if (!validation.isValid) return handleError(validation);
  3. Consider timeouts

    dart
    await Future.value(texpr.evaluate(input))
        .timeout(Duration(milliseconds: 100));

For Real-Time Applications (60 FPS)

At 60 FPS, you have ~16ms per frame. Budget for expressions:

Expression ComplexityEvaluations per Frame
Simple arithmetic~5,000
Trigonometry~2,000
With derivatives~500
With integrals~5-10

Recommendations:

  • Pre-parse all expressions outside the render loop
  • Use evaluation-only caching
  • Avoid integration in per-frame calculations

For Mobile Devices

Mobile devices have slower CPUs. Apply a 2-4× slowdown factor:

DesktopMobile (estimated)
3 µs6-12 µs
20 µs40-80 µs
2,000 µs4,000-8,000 µs

Profiling Your Application

Dart DevTools

dart
import 'dart:developer';

Timeline.startSync('texpr_parse');
final ast = texpr.parse(expression);
Timeline.finishSync();

Timeline.startSync('texpr_evaluate');
final result = texpr.evaluateParsed(ast, variables);
Timeline.finishSync();

Manual Timing

dart
final sw = Stopwatch()..start();
for (var i = 0; i < 1000; i++) {
  texpr.evaluate(expression, variables);
}
sw.stop();
print('Average: ${sw.elapsedMicroseconds / 1000} µs');

Optimization Internals

L1-L4 Cache Layers

Expression String


┌──────────────────┐
│ L1 Parse Cache   │ ─── Hash(string) → AST
└────────┬─────────┘


┌──────────────────┐
│ L2 Eval Cache    │ ─── Hash(AST, vars) → Result
└────────┬─────────┘


┌──────────────────┐
│ L3 Diff Cache    │ ─── Hash(AST, var) → Derivative AST
└────────┬─────────┘


┌──────────────────┐
│ L4 Sub-expr      │ ─── Per-evaluation transient cache
└──────────────────┘

Cache Hit Rates

Typical hit rates in production use:

CacheExpected Hit Rate
L1 Parse90-99% (repeated expressions)
L2 Eval50-80% (same expression, same vars)
L3 Diff80-95% (calculus operations)
L4 Sub-exprVariable (depends on expression structure)

Summary

MetricTypical Value
Parse time2-20 µs
Eval time (simple)1-10 µs
Eval time (integral)2,000-3,000 µs
Cache speedup5-30×
Memory per expression0.5-5 KB
Throughput (hot cache)15,000 evals/ms

Made with ❤️ by TeXpr, Docs Inspired by ElysiaJS