Talk:Code Racer

From Second Life Wiki
Revision as of 08:17, 25 October 2007 by Ppaatt Lynagh (talk | contribs) (share the unexplained nonsense results I see when I try racing substantive code)
Jump to navigation Jump to search

I don't think this Code Racer instrument works well yet.

I'm not sure why. Maybe next I'll try substituting llGetTimestamp for llGetTime by calling the script's getTime function in place of llGetTime.

So far the nonsense results I see are:


Click Running = No to stop this script after you've seen enough ...
2007-10-25T13:03:56.514828Z

~1.867728 ms elapsed in version 4 to place at 1 by 5 of 10 votes
~2.583531 ms elapsed in version 2 to place at 2 by 5 of 10 votes
~3.103991 ms elapsed in version 3 to place at 3 by 5 of 10 votes
~4.185323 ms elapsed in version 5 to place at 4 by 4 of 10 votes
~4.185323 ms elapsed in version 5 to place at 5 by 3 of 10 votes
0.988894 dilation @ 2007-10-25T13:04:14.761490Z

~4.413073 ms elapsed in version 2 to place at 1 by 26 of 100 votes
~4.002655 ms elapsed in version 3 to place at 2 by 25 of 100 votes
~5.015790 ms elapsed in version 1 to place at 3 by 26 of 100 votes
~4.643958 ms elapsed in version 4 to place at 4 by 25 of 100 votes
~5.015790 ms elapsed in version 1 to place at 5 by 25 of 100 votes
~4.413073 ms elapsed in version 2 to place at 5 by 25 of 100 votes
1.000000 dilation @ 2007-10-25T13:07:12.138661Z

~2.137260 ms elapsed in version 3 to place at 1 by 31 of 100 votes
~2.262673 ms elapsed in version 2 to place at 2 by 21 of 100 votes
~2.247390 ms elapsed in version 4 to place at 2 by 21 of 100 votes
~2.227553 ms elapsed in version 5 to place at 3 by 28 of 100 votes
~2.880072 ms elapsed in version 1 to place at 4 by 23 of 100 votes
~2.880072 ms elapsed in version 1 to place at 5 by 35 of 100 votes
0.997669 dilation @ 2007-10-25T13:10:15.778282Z

~1.444843 ms elapsed in version 2 to place at 1 by 26 of 100 votes
~1.444843 ms elapsed in version 2 to place at 2 by 27 of 100 votes
~1.872943 ms elapsed in version 1 to place at 3 by 27 of 100 votes
~1.872943 ms elapsed in version 1 to place at 4 by 25 of 100 votes
~1.872943 ms elapsed in version 1 to place at 5 by 30 of 100 votes
1.000000 dilation @ 2007-10-25T13:13:15.847526Z

The substantive code I was racing was the five versions of hex found in that article and in its discussion tab. At a glance we can believe that the concise & conventional hex1 version should run slow. No way should it beat out any of the other versions in any measure of run time.

// Race a few version of code in dilated script time as measured by llGetTime.
// http://wiki.secondlife.com/wiki/Code_Racer

// List every runnable version.

list theRunners = [0, 1, 2, 3, 4, 5];

// Count runnable versions.

integer theRunnable;

// Count votes to place.

list theVotes;

// Sum the run times observed.

list theSums;

// Count the races that ran without time running backwards.

integer theAverageable;

// Race no code alongside the other code in order to measure overhead.

runner0()
{
}

// Race a few versions of code.
// http://wiki.secondlife.com/wiki/hex

string XDIGITS1 = "0123456789abcdef"; // could be "0123456789ABCDEF"

string hexes1(integer bits)
{
    string nybbles = "";
    while (bits)
    {
        integer lsn = bits & 0xF; // least significant nybble
        string nybble = llGetSubString(XDIGITS1, lsn, lsn);
        nybbles = nybble + nybbles;
        bits = bits >> 4; // discard the least significant bits at right
        bits = bits & 0xfffFFFF; // discard the sign bits at left
    }
    return nybbles;
}

string hex1(integer value)
{
    if (value < 0)
    {
        return "-0x" + hexes1(-value);
    }
    else if (value == 0)
    {
        return "0x0"; // hexes(value) == "" when (value == 0)
    }
    else // if (0 < value)
    {
        return "0x" + hexes1(value);
    }
}

runner1()
{
    hex1(0x7fffFFFF);
}

// http://wiki.secondlife.com/wiki/hex

string hex2(integer value)
{
    string lead = "0x";
    if (value & 0x80000000) // means (integer < 0) but is smaller and faster
    {
        lead = "-0x";
        value = -value; // unnecessary when value == -0x80000000
    }

    string nybbles = "";
    do
    {
        integer lsn = value & 0xF; // least significant nybble
        nybbles = llGetSubString("0123456789abcdef", lsn, lsn) + nybbles;
    } while ((value = (0xfffFFFF & (value >> 4))));

    return lead + nybbles;
}

runner2()
{
    hex2(0x7fffFFFF);
}

string hex3(integer value)
{
    string lead = "0x";
    if (value & 0x80000000) // means (integer < 0) but is smaller and faster
    {
        lead = "-0x";
        value = -value; // unnecessary when value == -0x80000000
    }

    integer lsn; // least significant nybble
    string nybbles = "";
    do
    {
        nybbles = llGetSubString("0123456789abcdef", lsn = (value & 0xF), lsn) + nybbles;
    }
    while ((value = (0xfffFFFF & (value >> 4))));
    
    return lead + nybbles;
}

runner3()
{
    hex3(0x7fffFFFF);
}

// http://wiki.secondlife.com/wiki/hex

string bits2nybbles4(integer bits)
{
    string nybbles = "";
    do
    {
        integer lsn = bits & 0xF; // least significant nybble
        nybbles = llGetSubString("0123456789ABCDEF", lsn, lsn) + nybbles;
    } while (bits = (0xfffFFFF & (bits >> 4)));
    return nybbles;
}

runner4()
{
    bits2nybbles4(0x7fffFFFF);
}

// http://wiki.secondlife.com/wiki/Talk:hex

string bits2nybbles5(integer bits)
{
    integer lsn; // least significant nybble
    string nybbles = "";
    do
    {
        integer lsn = bits & 0xF; // least significant nybble
        nybbles = llGetSubString("0123456789ABCDEF", lsn = (bits & 0xF), lsn) + nybbles;
    } while (bits = (0xfffFFFF & (bits >> 4)));
    return nybbles;
}

runner5()
{
    bits2nybbles5(0x7fffFFFF);
}

// Run the chosen runner once.
// Run thru an equal time while choosing any runner.

run(integer runner)
{
    if (runner == 0) { runner0(); }
    if (runner == 1) { runner1(); }
    if (runner == 2) { runner2(); }
    if (runner == 3) { runner3(); }
    if (runner == 4) { runner4(); }
    if (runner == 5) { runner5(); }
}

// Start up.

startup()
{
    llOwnerSay("");
    llOwnerSay("Click Running = No to stop this script after you've seen enough ...");
    llOwnerSay(llGetTimestamp());
}

// Measure the race in calendar time elapsed since the minute began,
// if called in place of llGetTime.
//
// Note: "YYYY-MM-DDThh:mm:ss.ff..fZ" = llGetTimestamp();
// Note: 17 = 0 + llStringLength("YYYY-MM-DDThh:mm:")
// Note: -2 = -1 - llStringLength("Z")

float getTime()
{
    return (float) llGetSubString(llGetTimestamp(), 17, -2); // "ss.ff..f"
}

// Race the runners and return the times when each ran.
// Run in llGetTime dilated time or in getTime calendar time.

list eachRunnerRun()
{
    list befores = [];
    integer runnablePlus = theRunnable + 1;
    integer running;
    for (running = 0; running < runnablePlus; ++running)
    {
        integer running = llList2Integer(theRunners, running);
        befores += llGetTime();
        run(running);
    }
    return befores;
}

// Return elapsed run time per runner,
// else return [] if time ran backwards.

list getRuntimesElseNone(list befores)
{
    list runtimes = [];
    float before = llList2Float(befores, 0);
    integer timing;
    for (timing = 0; timing < theRunnable; ++timing)
    {
        float after = llList2Float(befores, timing + 1);
        float runtime = after - before;
        if (runtime < 0.0) return [];
        runtimes += runtime;
        before = after;
    }
    return runtimes;
}

// Add to a listed float.

list addFloat(list sums, integer index, float addition)
{
    list results = [];
    integer summable = llGetListLength(sums);
    integer summing;
    for (summing = 0; summing < summable; ++summing)
    {
        float result = llList2Float(sums, summing);
        if (index == summing)
        {
            result += addition;
        }
        results += result;
    }
    return results;
}

// Add to a listed integer.

list addInteger(list sums, integer index, integer addition)
{
    list results = [];
    integer summable = llGetListLength(sums);
    integer summing;
    for (summing = 0; summing < summable; ++summing)
    {
        integer result = llList2Integer(sums, summing);
        if (index == summing)
        {
            result += addition;
        }
        results += result;
    }
    return results;
}

// Race the runners once, vote to place, and sum run time per runner.

runRace()
{

    // Race the runners once.

    list runtimes = getRuntimesElseNone(eachRunnerRun());
//  llOwnerSay("[" + llDumpList2String(runtimes, ", ") + "] == runtimes");

    // Sort the runtimes into places.

    list places = llListSort(runtimes, 1, TRUE); // sort least to most
    integer placeable = llGetListLength(places); // 0 or theRunnable

    // Sum run time per runner.

    integer adding;
    for (adding = 0; adding < placeable; ++adding)
    {
        integer running = llList2Integer(theRunners, adding);
        float runtime = llList2Float(runtimes, adding);
        theSums = addFloat(theSums, running, runtime);
        ++theAverageable;

        // Vote to place at each equal runtime.

        integer placing;
        for (placing = 0; placing < placeable; ++placing)
        {
            float placed = llList2Float(places, placing);
            if (runtime == placed)
            {
                integer flat = (running * placeable) + placing;
                theVotes = addInteger(theVotes, flat, 1);
            }
        }
    }
}

// Start up a burst of races.

zeroBurst()
{
    theSums = theVotes = [];
    theRunnable = llGetListLength(theRunners);
    integer placing;
    for (placing = 0; placing < theRunnable; ++placing)
    {
        theSums += 0.0;
        integer running;
        for (running = 0; running < theRunnable; ++running)
        {
            theVotes += 0;
        }
    }
}

// Report a burst of races.

reportBurst(integer scaling)
{

    // Consider each place.

    integer placing;
    for (placing = 0; placing < theRunnable; ++placing)
    {
        list votes = llList2ListStrided(llList2List(theVotes, placing, -1), 0, -1, theRunnable);
        integer winner = llList2Integer(llListSort(votes, 1, TRUE), -1);

        // Find the winner (or the winners) of that place.

        integer running;
        for (running = 1; running < theRunnable; ++running)
        {
            integer vote4place = llList2Integer(votes, running); 
            if (vote4place == winner)
            {

                // Describe a winner.

                float summed = llList2Float(theSums, running) - llList2Float(theSums, 0);
                float average = 1000.0 * (summed / theAverageable);

                llOwnerSay("~" +
                    (string) average + " ms elapsed in version " + (string) running +
                    " to place at " + (string) placing +
                    " by " + (string) vote4place + " of " + (string) scaling + " votes"
                );
            }
        }
    }
}

// Run a few bursts of races at each scale, for indefinitely increasing scale.

runRaceRepeatedly()
{
    integer scaleable = 10;
    integer repeatable = 1; // don't bother to repeat the first burst
    while (TRUE)
    {
        integer repeating;
        for (repeating = 0; repeating < repeatable; ++repeating)
        {
            zeroBurst();

            integer scaling;
            for (scaling = 0; scaling < scaleable; ++scaling)
            {
                runRace();
                theRunners = llList2List(theRunners, -1, -1) + llList2List(theRunners, 0, -2);
            }

            llOwnerSay("");
            reportBurst(scaling);
            llOwnerSay((string) llGetRegionTimeDilation() + " dilation @ " + llGetTimestamp());
        }
        
        scaleable *= 10;
        repeatable = 3; // repeat most bursts several times
    }
}

// Race to measure relative run-time cost of multiple versions of code.
// Produce unreasonable answers when the run-time cost measured equals or exceeds 60 seconds.

default
{
    state_entry()
    {
        startup();
        runRaceRepeatedly();
    }
}