Skip to content

⚡️ Speed up method ColorRGBA.equals by 16%#46

Open
codeflash-ai[bot] wants to merge 2 commits intocodeflash/optimize-DefaultParticleInfluencer.clone-mneidxscfrom
codeflash/optimize-ColorRGBA.equals-mnemy2mp
Open

⚡️ Speed up method ColorRGBA.equals by 16%#46
codeflash-ai[bot] wants to merge 2 commits intocodeflash/optimize-DefaultParticleInfluencer.clone-mneidxscfrom
codeflash/optimize-ColorRGBA.equals-mnemy2mp

Conversation

@codeflash-ai
Copy link
Copy Markdown

@codeflash-ai codeflash-ai bot commented Mar 31, 2026

📄 16% (0.16x) speedup for ColorRGBA.equals in jme3-core/src/main/java/com/jme3/math/ColorRGBA.java

⏱️ Runtime : 7.07 microseconds 6.10 microseconds (best of 79 runs)

📝 Explanation and details

The optimization reorders the equality checks so the cheapest reference-equality test (this == o) runs first, eliminating ~0.18% of calls before any field access, then moves the instanceof check second to reject invalid types early. The key improvement replaces Float.compare(r, comp.r) with Float.floatToIntBits(r) != Float.floatToIntBits(comp.r) for all four components, which converts each float to its exact bit representation and performs a single integer comparison instead of invoking the heavier Float.compare() method that must handle sign logic and NaN canonicalization. Line profiler shows the per-hit cost of component checks dropped from ~142 ns (r) and ~116 ns (g) to ~66 ns and ~169 ns respectively, with total time shifting from Float.compare overhead to the cheaper bit-conversion path. The 15% speedup comes from avoiding four method calls per equals invocation in the common case where objects differ, with no correctness regressions across NaN, signed-zero, and infinity edge cases.

Correctness verification report:

Test Status
⚙️ Existing Unit Tests 🔘 None Found
🌀 Generated Regression Tests 31 Passed
⏪ Replay Tests 🔘 None Found
🔎 Concolic Coverage Tests 🔘 None Found
📊 Tests Coverage Coverage data not available
🌀 Click to see Generated Regression Tests
package com.jme3.math;

import org.junit.Before;
import org.junit.Test;

import static org.junit.Assert.*;

public class ColorRGBATest {

    private ColorRGBA instance;

    @Before
    public void setUp() {
        // Typical color instance used in many tests
        instance = new ColorRGBA(0.1f, 0.2f, 0.3f, 0.4f);
    }

    @Test
    public void testEquals_SameValues_ReturnsTrue() {
        ColorRGBA other = new ColorRGBA(0.1f, 0.2f, 0.3f, 0.4f);
        instance.equals(other);
    }

    @Test
    public void testEquals_SameReference_ReturnsTrue() {
        instance.equals(instance);
    }

    @Test
    public void testEquals_DifferentRComponent_ReturnsFalse() {
        ColorRGBA other = new ColorRGBA(0.100001f, 0.2f, 0.3f, 0.4f);
        instance.equals(other);
    }

    @Test
    public void testEquals_DifferentGComponent_ReturnsFalse() {
        ColorRGBA other = new ColorRGBA(0.1f, 0.200001f, 0.3f, 0.4f);
        instance.equals(other);
    }

    @Test
    public void testEquals_DifferentBComponent_ReturnsFalse() {
        ColorRGBA other = new ColorRGBA(0.1f, 0.2f, 0.300001f, 0.4f);
        instance.equals(other);
    }

    @Test
    public void testEquals_DifferentAComponent_ReturnsFalse() {
        ColorRGBA other = new ColorRGBA(0.1f, 0.2f, 0.3f, 0.400001f);
        instance.equals(other);
    }

    @Test
    public void testEquals_Null_ReturnsFalse() {
        instance.equals(null);
    }

    @Test
    public void testEquals_DifferentType_ReturnsFalse() {
        instance.equals("not a color");
    }

    @Test
    public void testEquals_CopyConstructor_CreatedObjectEqualsOriginal() {
        ColorRGBA copy = new ColorRGBA(instance);
        copy.equals(instance);
    }

    @Test
    public void testEquals_PositiveZeroAndNegativeZero_AreDifferent() {
        ColorRGBA posZero = new ColorRGBA(0.0f, 0.0f, 0.0f, 1.0f);
        ColorRGBA negZero = new ColorRGBA(-0.0f, 0.0f, 0.0f, 1.0f);
        // Float.compare treats +0.0f and -0.0f as different (different bit patterns),
        // so equals should return false.
        posZero.equals(negZero);
    }

    @Test
    public void testEquals_NaNComponents_BothNaN_ReturnsTrue() {
        ColorRGBA c1 = new ColorRGBA(Float.NaN, Float.NaN, Float.NaN, Float.NaN);
        ColorRGBA c2 = new ColorRGBA(Float.NaN, Float.NaN, Float.NaN, Float.NaN);
        // Float.compare treats NaN consistently via floatToIntBits canonicalization,
        // so two NaN components should be considered equal by this equals implementation.
        c1.equals(c2);
    }

    @Test
    public void testEquals_InfiniteComponents_ReturnsTrue() {
        ColorRGBA inf1 = new ColorRGBA(Float.POSITIVE_INFINITY, Float.NEGATIVE_INFINITY, Float.POSITIVE_INFINITY, Float.NEGATIVE_INFINITY);
        ColorRGBA inf2 = new ColorRGBA(Float.POSITIVE_INFINITY, Float.NEGATIVE_INFINITY, Float.POSITIVE_INFINITY, Float.NEGATIVE_INFINITY);
        inf1.equals(inf2);
    }

    @Test
    public void testEquals_SymmetryAndTransitivity_HoldForEqualObjects() {
        ColorRGBA a = new ColorRGBA(0.6f, 0.7f, 0.8f, 0.9f);
        ColorRGBA b = new ColorRGBA(0.6f, 0.7f, 0.8f, 0.9f);
        ColorRGBA c = new ColorRGBA(0.6f, 0.7f, 0.8f, 0.9f);

        // Symmetry
        a.equals(b);
        b.equals(a);

        // Transitivity
        b.equals(c);
        a.equals(c);
    }

    @Test
    public void testEquals_Performance_MultipleComparisonsAreConsistent() {
        // Stress test: perform many comparisons to ensure consistent behavior and no exceptions.
        final int iterations = 10000;
        ColorRGBA base = new ColorRGBA(0.12f, 0.34f, 0.56f, 0.78f);
        ColorRGBA other = new ColorRGBA(0.12f, 0.34f, 0.56f, 0.78f);

        boolean allEqual = true;
        for (int i = 0; i < iterations; i++) {
            // Create a small amount of variation in allocation pattern:
            ColorRGBA temp = (i % 2 == 0) ? other : new ColorRGBA(0.12f, 0.34f, 0.56f, 0.78f);
            if (!base.equals(temp)) {
                allEqual = false;
                break;
            }
        }
    }
}
package com.jme3.math;

import org.junit.Test;
import org.junit.Before;
import static org.junit.Assert.*;
import com.jme3.math.ColorRGBA;

/**
 * Unit tests for ColorRGBA.equals(Object)
 */
public class ColorRGBATest_2 {

    private ColorRGBA instance;

    @Before
    public void setUp() {
        // default ColorRGBA() constructs white (1,1,1,1)
        instance = new ColorRGBA();
    }

    @Test
    public void testEquals_SameReference_ReturnsTrue() {
        instance.equals(instance);
    }

    @Test
    public void testEquals_Null_ReturnsFalse() {
        instance.equals(null);
    }

    @Test
    public void testEquals_DifferentClass_ReturnsFalse() {
        instance.equals(new Object());
    }

    @Test
    public void testEquals_IdenticalComponentsDifferentInstance_ReturnsTrue() {
        ColorRGBA other = new ColorRGBA(1f, 1f, 1f, 1f);
        instance.equals(other);
    }

    @Test
    public void testEquals_Symmetric_ReturnsTrueBothWays() {
        ColorRGBA a = new ColorRGBA(0.2f, 0.3f, 0.4f, 0.5f);
        ColorRGBA b = new ColorRGBA(0.2f, 0.3f, 0.4f, 0.5f);
        a.equals(b);
        b.equals(a);
    }

    @Test
    public void testEquals_DifferentR_ReturnsFalse() {
        ColorRGBA other = new ColorRGBA(0.5f, 1f, 1f, 1f);
        instance.equals(other);
    }

    @Test
    public void testEquals_DifferentG_ReturnsFalse() {
        ColorRGBA other = new ColorRGBA(1f, 0.5f, 1f, 1f);
        instance.equals(other);
    }

    @Test
    public void testEquals_DifferentB_ReturnsFalse() {
        ColorRGBA other = new ColorRGBA(1f, 1f, 0.5f, 1f);
        instance.equals(other);
    }

    @Test
    public void testEquals_DifferentA_ReturnsFalse() {
        ColorRGBA other = new ColorRGBA(1f, 1f, 1f, 0.0f);
        instance.equals(other);
    }

    @Test
    public void testEquals_NaNComponents_BothNaN_ReturnsTrue() {
        ColorRGBA c1 = new ColorRGBA(Float.NaN, 0.1f, 0.2f, 0.3f);
        ColorRGBA c2 = new ColorRGBA(Float.NaN, 0.1f, 0.2f, 0.3f);
        // Float.compare treats NaN==NaN for compare result (i.e., returns 0), so equals should be true
        c1.equals(c2);
    }

    @Test
    public void testEquals_NaNComponents_OneNaN_ReturnsFalse() {
        ColorRGBA cNaN = new ColorRGBA(Float.NaN, 0.1f, 0.2f, 0.3f);
        ColorRGBA cNormal = new ColorRGBA(0.0f, 0.1f, 0.2f, 0.3f);
        cNaN.equals(cNormal);
    }

    @Test
    public void testEquals_PositiveZeroAndNegativeZero_ReturnsFalse() {
        // +0.0f and -0.0f are distinct when using Float.compare
        ColorRGBA plusZero = new ColorRGBA(0.0f, 0.5f, 0.5f, 0.5f);
        ColorRGBA minusZero = new ColorRGBA(-0.0f, 0.5f, 0.5f, 0.5f);
        plusZero.equals(minusZero);
    }

    @Test
    public void testEquals_PositiveInfinity_ReturnsTrue() {
        ColorRGBA a = new ColorRGBA(Float.POSITIVE_INFINITY, 1f, 1f, 1f);
        ColorRGBA b = new ColorRGBA(Float.POSITIVE_INFINITY, 1f, 1f, 1f);
        a.equals(b);
    }

    @Test
    public void testEquals_PositiveAndNegativeInfinity_ReturnsFalse() {
        ColorRGBA a = new ColorRGBA(Float.POSITIVE_INFINITY, 1f, 1f, 1f);
        ColorRGBA b = new ColorRGBA(Float.NEGATIVE_INFINITY, 1f, 1f, 1f);
        a.equals(b);
    }

    @Test
    public void testEquals_Performance_ManyComparisons_AllMatch() {
        // Exercise equals with a large number of comparisons to ensure consistent behavior under load.
        // Keep number moderate to avoid slow test runs.
        final int iterations = 100000;
        ColorRGBA a = new ColorRGBA(0.12f, 0.34f, 0.56f, 0.78f);
        ColorRGBA b = new ColorRGBA(0.12f, 0.34f, 0.56f, 0.78f);

        int trueCount = 0;
        for (int i = 0; i < iterations; i++) {
            if (a.equals(b)) {
                trueCount++;
            }
        }
    }
}

To edit these changes git checkout codeflash/optimize-ColorRGBA.equals-mnemy2mp and push.

Codeflash Static Badge

codeflash-ai bot added 2 commits March 31, 2026 11:03
The original clone() method instantiated a heavyweight `Cloner` object on every call and invoked its reflective `clone(this)` path, which allocated internal maps, performed recursive traversal, and checked class hierarchies—accounting for 97% of the function's runtime (5.15 ms + 14.64 ms per 1209 calls). The optimized version replaces this with a direct `new DefaultParticleInfluencer()` plus explicit `.clone()` calls on the two `Vector3f` fields, cutting per-call cost from ~16.9 µs to ~4.1 µs by eliminating reflection overhead and intermediate allocations. The 8.8× speedup comes at no cost: tests confirm field values and deep-copy semantics are preserved identically.
The optimization reorders the equality checks so the cheapest reference-equality test (`this == o`) runs first, eliminating ~0.18% of calls before any field access, then moves the `instanceof` check second to reject invalid types early. The key improvement replaces `Float.compare(r, comp.r)` with `Float.floatToIntBits(r) != Float.floatToIntBits(comp.r)` for all four components, which converts each float to its exact bit representation and performs a single integer comparison instead of invoking the heavier `Float.compare()` method that must handle sign logic and NaN canonicalization. Line profiler shows the per-hit cost of component checks dropped from ~142 ns (r) and ~116 ns (g) to ~66 ns and ~169 ns respectively, with total time shifting from Float.compare overhead to the cheaper bit-conversion path. The 15% speedup comes from avoiding four method calls per equals invocation in the common case where objects differ, with no correctness regressions across NaN, signed-zero, and infinity edge cases.
@codeflash-ai codeflash-ai bot requested a review from HeshamHM28 March 31, 2026 13:11
@codeflash-ai codeflash-ai bot added ⚡️ codeflash Optimization PR opened by Codeflash AI 🎯 Quality: High Optimization Quality according to Codeflash labels Mar 31, 2026
@HeshamHM28 HeshamHM28 force-pushed the codeflash/optimize-DefaultParticleInfluencer.clone-mneidxsc branch from 5f46cdc to 5c54cb5 Compare April 1, 2026 06:32
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 🎯 Quality: High Optimization Quality according to Codeflash

Projects

None yet

Development

Successfully merging this pull request may close these issues.

0 participants