Skip to content

Conversation

misrasaurabh1
Copy link
Contributor

Change Summary

📄 multiple_of_validator() in pydantic/_internal/_validators.py

📈 Performance improved by 31% (0.31x faster)

⏱️ Runtime went down from 33.8 microseconds to 25.8 microseconds

Explanation and details

The given function checks whether x is a multiple of multiple_of and raises an error if it isn't. One area of improvement could be avoiding double computation of the modulus operation.

This rewritten code avoids checking x % multiple_of == 0 explicitly. Instead, it simply checks if x % multiple_of is truthy, which means the remainder is non-zero, leading to a minor but effective optimization.

Correctness verification

The new optimized code was tested for correctness. The results are listed below.

🔘 (none found) − ⚙️ Existing Unit Tests

✅ 20 Passed − 🌀 Generated Regression Tests

(click to show generated tests)
# imports
from typing import Any

import pytest  # used for our unit tests


# Custom error class for demonstration purposes
class PydanticKnownError(Exception):
    def __init__(self, error_type, context):
        self.error_type = error_type
        self.context = context
from pydantic._internal._validators import multiple_of_validator

# unit tests

# Valid Multiples
def test_valid_multiples():
    assert multiple_of_validator(10, 2) == 10
    assert multiple_of_validator(15, 3) == 15
    assert multiple_of_validator(-10, 2) == -10
    assert multiple_of_validator(-15, -3) == -15
    assert multiple_of_validator(10.0, 2.0) == 10.0
    assert multiple_of_validator(15.0, 3.0) == 15.0
    assert multiple_of_validator(10, 2.0) == 10
    assert multiple_of_validator(15.0, 3) == 15.0

# Invalid Multiples
def test_invalid_multiples():
    with pytest.raises(PydanticKnownError):
        multiple_of_validator(10, 3)
    with pytest.raises(PydanticKnownError):
        multiple_of_validator(15, 4)
    with pytest.raises(PydanticKnownError):
        multiple_of_validator(-10, 3)
    with pytest.raises(PydanticKnownError):
        multiple_of_validator(-15, 4)
    with pytest.raises(PydanticKnownError):
        multiple_of_validator(10.0, 3.0)
    with pytest.raises(PydanticKnownError):
        multiple_of_validator(15.0, 4.0)
    with pytest.raises(PydanticKnownError):
        multiple_of_validator(10, 3.0)
    with pytest.raises(PydanticKnownError):
        multiple_of_validator(15.0, 4)

# Edge Cases
def test_edge_cases():
    assert multiple_of_validator(0, 1) == 0
    assert multiple_of_validator(0, -1) == 0
    assert multiple_of_validator(0, 2.0) == 0
    with pytest.raises(ValueError):
        multiple_of_validator(10, 0)
    with pytest.raises(ValueError):
        multiple_of_validator(0, 0)
    assert multiple_of_validator(10, -2) == 10
    assert multiple_of_validator(-10, -2) == -10
    assert multiple_of_validator(10.0, -2.0) == 10.0

# Non-Numeric Types
def test_non_numeric_types():
    with pytest.raises(TypeError):
        multiple_of_validator("10", 2)
    with pytest.raises(TypeError):
        multiple_of_validator(10, "2")
    with pytest.raises(TypeError):
        multiple_of_validator([10], 2)
    with pytest.raises(TypeError):
        multiple_of_validator(10, [2])
    with pytest.raises(TypeError):
        multiple_of_validator(None, 2)
    with pytest.raises(TypeError):
        multiple_of_validator(10, None)

# Large Scale Test Cases
def test_large_scale():
    assert multiple_of_validator(10**18, 10**9) == 10**18
    with pytest.raises(PydanticKnownError):
        multiple_of_validator(10**18, 10**8)
    assert multiple_of_validator(10**18 * 1.0, 10**9 * 1.0) == 10**18 * 1.0
    with pytest.raises(PydanticKnownError):
        multiple_of_validator(10**18 * 1.0, 10**8 * 1.0)

# Boundary Conditions
def test_boundary_conditions():
    assert multiple_of_validator(-2**31, 2) == -2**31
    with pytest.raises(PydanticKnownError):
        multiple_of_validator(-2**31, 3)
    assert multiple_of_validator(2**31 - 1, 1) == 2**31 - 1
    with pytest.raises(PydanticKnownError):
        multiple_of_validator(2**31 - 1, 2)

# Special Numerical Values
def test_special_numerical_values():
    with pytest.raises(TypeError):
        multiple_of_validator(float('inf'), 2)
    with pytest.raises(TypeError):
        multiple_of_validator(10, float('inf'))
    with pytest.raises(TypeError):
        multiple_of_validator(float('nan'), 2)
    with pytest.raises(TypeError):
        multiple_of_validator(10, float('nan'))

🔘 (none found) − ⏪ Replay Tests

Checklist

  • The pull request title is a good summary of the changes - it will be used in the changelog
  • Unit tests for the changes exist
  • Tests pass on CI
  • Documentation reflects the changes where applicable
  • My PR is ready to review, please add a comment including the phrase "please review" to assign reviewers

Certainly! The given function checks whether `x` is a multiple of `multiple_of` and raises an error if it isn't. One area of improvement could be avoiding double computation of the modulus operation.



This rewritten code avoids checking `x % multiple_of == 0` explicitly. Instead, it simply checks if `x % multiple_of` is truthy, which means the remainder is non-zero, leading to a minor but effective optimization.
@github-actions github-actions bot added the relnotes-fix Used for bugfixes. label Jul 4, 2024
Copy link

codspeed-hq bot commented Jul 4, 2024

CodSpeed Performance Report

Merging #9839 will not alter performance

Comparing misrasaurabh1:codeflash/optimize-multiple_of_validator-2024-06-06T15.35.15 (7e9ea62) with main (c1101a9)

Summary

✅ 13 untouched benchmarks

Copy link
Contributor

@sydney-runkle sydney-runkle left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Snazzy!

@sydney-runkle sydney-runkle merged commit 1f6a518 into pydantic:main Jul 17, 2024
@sydney-runkle sydney-runkle added relnotes-performance Used for performance improvements. and removed relnotes-fix Used for bugfixes. labels Jul 17, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
relnotes-performance Used for performance improvements.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants