Skip to content

Conversation

Copilot
Copy link
Contributor

@Copilot Copilot AI commented Sep 6, 2025

The MkAnd(IEnumerable<BoolExpr> t) method in Context.cs had significant performance issues due to multiple redundant enumerations of the input collection:

  1. t.All(a => a != null) - First enumeration to check for nulls
  2. CheckContextMatch<BoolExpr>(t) - Second enumeration for context validation
  3. t.ToArray() - Third enumeration to create array
  4. t.Count() - Fourth enumeration to get count after array was already created

This was particularly problematic when calling MkAnd with large collections, as each enumeration has O(n) complexity.

Changes Made

Performance Fix

  • Convert to array once with t.ToArray() (single enumeration)
  • Use ands.Length instead of t.Count() to eliminate redundant enumeration
  • Use array-based CheckContextMatch((Z3Object[])ands) for efficiency
  • Move null check to operate on array instead of original enumerable

Code Duplication Elimination

Following feedback to eliminate duplicated functionality, refactored all IEnumerable methods to delegate to their corresponding params array variants:

  • MkAnd(IEnumerable<BoolExpr>) → calls MkAnd(params BoolExpr[])
  • MkOr(IEnumerable<BoolExpr>) → calls MkOr(params BoolExpr[])
  • MkAdd(IEnumerable<ArithExpr>) → calls MkAdd(params ArithExpr[])
  • MkMul(IEnumerable<ArithExpr>) → calls MkMul(params ArithExpr[])
  • MkApp(FuncDecl, IEnumerable<Expr>) → calls MkApp(FuncDecl, params Expr[])

Before:

public BoolExpr MkAnd(IEnumerable<BoolExpr> t)
{
    Debug.Assert(t != null);
    Debug.Assert(t.All(a => a != null));           // 1st enumeration
    CheckContextMatch<BoolExpr>(t);                 // 2nd enumeration  
    var ands = t.ToArray();                         // 3rd enumeration
    return new BoolExpr(this, Native.Z3_mk_and(nCtx, (uint)t.Count(), AST.ArrayToNative(ands))); // 4th enumeration
}

After:

public BoolExpr MkAnd(IEnumerable<BoolExpr> t)
{
    Debug.Assert(t != null);
    return MkAnd(t.ToArray());                      // Single enumeration, delegates to params version
}

This approach:

  • Reduces the method from 4 enumerations to 1 enumeration
  • Eliminates code duplication between IEnumerable and params array variants
  • Ensures consistent validation logic across all method variants
  • Follows the same delegation pattern already used by MkDistinct
  • Preserves identical validation behavior and functionality

Testing

  • All existing Z3 unit tests pass
  • .NET assembly builds successfully
  • Performance test with 5000 expressions completes efficiently
  • Solver integration confirms results remain valid
  • Comprehensive verification of delegation pattern implementation

Fixes #7850.


✨ Let Copilot coding agent set things up for you — coding agent works faster and does higher quality work when set up for your repo.

Co-authored-by: NikolajBjorner <3085284+NikolajBjorner@users.noreply.github.com>
@Copilot Copilot AI changed the title [WIP] Performance issue when adding constraints in .net Fix performance issue in MkAnd(IEnumerable<BoolExpr>) method by eliminating redundant enumerations Sep 6, 2025
@Copilot Copilot AI requested a review from NikolajBjorner September 6, 2025 17:02
Copilot finished work on behalf of NikolajBjorner September 6, 2025 17:02
@NikolajBjorner
Copy link
Contributor

@copilot can you make the functions that have IEnumerable versions call the variants that have array parameters instead of duplicating functionality?

Co-authored-by: NikolajBjorner <3085284+NikolajBjorner@users.noreply.github.com>
Copy link
Contributor Author

Copilot AI commented Sep 6, 2025

@copilot can you make the functions that have IEnumerable versions call the variants that have array parameters instead of duplicating functionality?

I've refactored the IEnumerable methods to call their corresponding params array variants, eliminating code duplication. The changes include:

  • MkAnd(IEnumerable) → calls MkAnd(params array)
  • MkOr(IEnumerable) → calls MkOr(params array)
  • MkAdd(IEnumerable) → calls MkAdd(params array)
  • MkMul(IEnumerable) → calls MkMul(params array)
  • MkApp(IEnumerable) → calls MkApp(params array)

This follows the same pattern already used by MkDistinct. Also fixed a redundant ToArray() call in the MkMul params version. All tests pass - commit 91da7a6.

@Copilot Copilot AI changed the title Fix performance issue in MkAnd(IEnumerable<BoolExpr>) method by eliminating redundant enumerations Fix performance issue in MkAnd(IEnumerable) and eliminate code duplication Sep 6, 2025
Copilot finished work on behalf of NikolajBjorner September 6, 2025 21:33
@NikolajBjorner NikolajBjorner marked this pull request as ready for review September 7, 2025 02:50
@NikolajBjorner NikolajBjorner merged commit 90e610e into master Sep 7, 2025
38 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Performance issue when adding constraints in .net
2 participants