samedi 22 décembre 2018

Why do I get a different result when I use an auxiliary variable before inserting a return value into an array?

I'm trying to brush up my Java so I'm implementing a double-array trie data structure. Now I'm trying to understand some strange behavior in my code.

I have a class with the following method that searches an array check, which is a non-static member of the same class, until it finds an element that satisfies certain conditions. The code does not modify anything apart from what you see in the method, where only local variables are updated.

private int findBase( final int[] characters )
{
    final int setLength = characters.length;
    final int max = characters[setLength - 1];
    final int firstCharacter = characters[0];

    int nextLink = getLinkAfter( FIRST_LINK, INIT_LAST_LINK + firstCharacter );

    while ( nextLink > 0 )
    {
        int count = 1;
        int newBase = nextLink - firstCharacter;

        ensureCapacity( newBase + max );

        while ( ( count < setLength ) && ( check[newBase + characters[count]] < 0 ) )
            count++;

        // IF all characters fit with newBase
        if ( count == setLength )
            return newBase;

        nextLink = getNextLink( nextLink );
    }

    // Should never get here, so we raise an exception
    throw new RuntimeException( "Error traversing the G-list." );
}

In another method I call this function and insert the result into the array base, which is also a non-static member.

As you can see, I use an auxiliary variable newBase to save the return value before I insert it into the array. Afterwards I added an assertion that checks that the value is "OK".

I have a few JUnit test cases including one that reads a million strings from a file and tries to insert them into the data structure (with no randomization involved at any point), and when I run this code everything works just fine.

...
else
{
    // Find a base that can branch both characters
    int[] characters = 
        { CharMap.byteToInt( suffixA[0] ), 
          CharMap.byteToInt( suffixB[0] ) };
    Arrays.sort( characters );

    // TODO: Sort out this nonsense
    int newBase = findBase( characters );
    base[state] = newBase;

    assert ( base[state] > START_STATE );

    insertNewLeaf( state, suffixA, 0, data );
    insertNewLeaf( state, suffixB, 0, oldData );
}
...

However, if I insert the return value without the auxiliary variable like below, the above assertion fails at some point.

// TODO: Sort out this nonsense
base[state] = findBase( characters );

The data structure itself should be entirely deterministic and I don't see how any external randomness (like execution order of the test cases) could affect the outcome of the code above.

Any idea why this is happening?

I use OpenJDK 1.8.0_181 and Maven 3.3.9 in Linux.

Aucun commentaire:

Enregistrer un commentaire