Difference between revisions of "Talk:Efficiency Tester"
(Talk:Hex suggests that we should tweak this instrument to call llGetTime rather than llGetTimestamp, in order to more accurately measure elapsed run time when time dilates) |
(→Measuring no-op loops is actually a "serious" benchmark!: new section) |
||
(3 intermediate revisions by 2 users not shown) | |||
Line 1: | Line 1: | ||
[[ | = The Elusive Run-Time of [[Hex]] = | ||
I'm next trying runs of the following instance of an llGetTime [[Efficiency Tester]]. You see five source lines commented out near the string "test once or more" ... I'm measuring the near zero cost of running with all five commented out, the max cost of none of the five commented out, and the individual costs of commenting in one line at a time ... We'll see if those results make sense, when they eventually appear. -- [[User:Ppaatt Lynagh|Ppaatt Lynagh]] 10:57, 27 October 2007 (PDT) | |||
<syntaxhighlight lang="lsl2"> | |||
// 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); | |||
} | |||
} | |||
// 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; | |||
} | |||
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; | |||
} | |||
// 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; | |||
} | |||
// 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; | |||
} | |||
// IMPORTANT: | |||
// Only perform tests in an empty region. | |||
// To reduce contamination and be sure to wearing no attachments. | |||
// Preferably do tests in a private sim with one on it. | |||
// Don't move while performing the test. | |||
// There is a margin of error so run the tests multiple times to determine it. | |||
// (16384 - (15267 - 18)) was the well-known byte code size of this llGetTime/ llGetTimestamp harness | |||
// Measure the race instead | |||
// in calendar milliseconds elapsed since the day began, | |||
// if called in place of llGetTime. | |||
integer getTime() // count milliseconds since the day began | |||
{ | |||
string stamp = llGetTimestamp(); // "YYYY-MM-DDThh:mm:ss.ff..fZ" | |||
return (integer) llGetSubString(stamp, 11, 12) * 3600000 + // hh | |||
(integer) llGetSubString(stamp, 14, 15) * 60000 + // mm | |||
llRound((float)llGetSubString(stamp, 17, -2) * 1000000.0)/1000; // ss.ff..f | |||
} | |||
default | |||
{ | |||
state_entry() | |||
{ | |||
// always measure how small, not only how fast | |||
llOwnerSay((string) llGetFreeMemory() + " free bytes of code at default.state_entry"); | |||
// always take more than one measurement | |||
integer repeateds; | |||
for (repeateds = 0; repeateds < 3; ++repeateds) | |||
{ | |||
// declare test variables | |||
float counter; | |||
// declare framework variables | |||
float i = 0; | |||
float j = 0; | |||
integer max = 10000; // 2ms of work takes 20 seconds to repeat 10,000 times, plus overhead | |||
// begin | |||
float t0 = llGetTime(); | |||
// loop to measure elapsed time to run sample code | |||
do | |||
{ | |||
// test once or more | |||
// hex1(0x7fffFFFF); | |||
// hex2(0x7fffFFFF); | |||
// hex3(0x7fffFFFF); | |||
// bits2nybbles4(0x7fffFFFF); | |||
// bits2nybbles5(0x7fffFFFF); | |||
} while (++i < max); | |||
float t1 = llGetTime(); | |||
// loop to measure elapsed time to run no code | |||
do ; while (++j < max); | |||
float t2 = llGetTime(); | |||
// complain if time ran backwards | |||
if (!((t0 <= t1) && (t1 <= t2))) | |||
{ | |||
llOwnerSay("MEANINGLESS RESULT -- SIMULATED TIME RAN BACKWARDS -- TRY AGAIN"); | |||
} | |||
// report average time elapsed per run | |||
float elapsedms = 1000.0 * (((t1 - t0) - (t2 - t1)) / max); | |||
llOwnerSay((string) elapsedms + "+-??% ms may have elapsed on average in each of"); | |||
llOwnerSay((string) max + " trials of running the code in the loop"); | |||
} | |||
} | |||
} | |||
</syntaxhighlight> | |||
== Measuring no-op loops is actually a "serious" benchmark! == | |||
The main text quotes: | |||
{{blockquote|1=Try the empty test of deleting the <code>{ counter += 1; }</code> source line to see the astonishing inaccuracy of this instrument. The time cost of no code, as measured here, isn't always zero!}} | |||
Note that measuring "no code" (or {{Wikipedia|NOP_(Code)|NOP}} — "no operation") ''is'' a perfectly valid and established method of doing crude (but nevertheless useful!) performance comparisons. One of the most famous benchmarks in the history of computing is definitely the {{Wikipedia|BogoMIPS}} benchmark, developed by Linus Torvalds for the Linux kernel to perform a calibration test of the CPU. The actual unit of processing — one BogoMIPS (the acronym for "bogus million instructions per second") — is roughly defined as "the number of million times per second a processor can do absolutely nothing".<ref name=quote>{{Wikipedia|Eric S Raymond}}, and {{Wikipedia|Geoff Mackenzie}}, published on the {{Wikipedia|Internet}} in the early 1990s, untraceable origin; quoted via Wikipedia</ref><ref name=esr>{{cite web | url = http://www.catb.org/jargon/html/B/BogoMIPS.html | first = Eric S. | last = Raymond | author-link = Eric S Raymond | title = Hackers Jargon File}}</ref>. | |||
While this so-called "benchmark" hardly deserves that title, it spurred lots of discussions in the later 1990s regarding certain claims that processor X from manufacturer Y was actually "better and faster" than processor W from manufacturer Z — because Linux, when booted, would calculate the BogoMIPS for each, and this allowed a sort-of-independent way for ''anyone'' to do their own pseudo-benchmarking — and claim whatever they wished about X vs. Z 😄 | |||
Linus and the most senior kernel core developers had long ago explained that this measurement was absolutely meaningless and not worthy being used as any form of categorising CPUs according to their perceived speed, solely based on their BogoMIPS "rating" — a practice that ''was'' used (for a while!) by server manufacturers targeting the emerging market of co-locating Linux servers. Several more modern CPU architectures made the BogoMIPS "rating" absurd — the CPU could see before-hand that there were an insane lot of NOPs coming next, and skip over all of them until the next non-NOP instruction, thus making its BogoMIPS rating approach infinity (or at least a huge number, far beyond the capabilities of human technology). At the same time, CPUs started incorporating other, much better, methods for doing the required calibration tests needed by the Linux kernel when booting up, so these — and ''not'' the BogoMIPS "do-nothing-loop" — are currently used to establish the baseline. Nevertheless, probably for historic reasons, and as of 2023, Linux ''still'' calculates the BogoMIPS for each processor core, even though it doesn't need to (except perhaps if someone is booting up their ancient and venerable 386DX and hopes to install modern-day Linux on it). | |||
Thus, your own measurements are not exactly "astonishingly inaccurate". They are, in fact, as good a measurement as what happens every moment that a Linux kernel — the most widely used operating system on Earth — boots up. Thus, you rest on the shoulders of giants, and there is no shame in claiming otherwise! 🤣 | |||
— [[User:Gwyneth Llewelyn|Gwyneth Llewelyn]] ([[User talk:Gwyneth Llewelyn|talk]]) 11:24, 12 September 2023 (PDT) |
Latest revision as of 10:24, 12 September 2023
The Elusive Run-Time of Hex
I'm next trying runs of the following instance of an llGetTime Efficiency Tester. You see five source lines commented out near the string "test once or more" ... I'm measuring the near zero cost of running with all five commented out, the max cost of none of the five commented out, and the individual costs of commenting in one line at a time ... We'll see if those results make sense, when they eventually appear. -- Ppaatt Lynagh 10:57, 27 October 2007 (PDT)
// 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);
}
}
// 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;
}
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;
}
// 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;
}
// 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;
}
// IMPORTANT:
// Only perform tests in an empty region.
// To reduce contamination and be sure to wearing no attachments.
// Preferably do tests in a private sim with one on it.
// Don't move while performing the test.
// There is a margin of error so run the tests multiple times to determine it.
// (16384 - (15267 - 18)) was the well-known byte code size of this llGetTime/ llGetTimestamp harness
// Measure the race instead
// in calendar milliseconds elapsed since the day began,
// if called in place of llGetTime.
integer getTime() // count milliseconds since the day began
{
string stamp = llGetTimestamp(); // "YYYY-MM-DDThh:mm:ss.ff..fZ"
return (integer) llGetSubString(stamp, 11, 12) * 3600000 + // hh
(integer) llGetSubString(stamp, 14, 15) * 60000 + // mm
llRound((float)llGetSubString(stamp, 17, -2) * 1000000.0)/1000; // ss.ff..f
}
default
{
state_entry()
{
// always measure how small, not only how fast
llOwnerSay((string) llGetFreeMemory() + " free bytes of code at default.state_entry");
// always take more than one measurement
integer repeateds;
for (repeateds = 0; repeateds < 3; ++repeateds)
{
// declare test variables
float counter;
// declare framework variables
float i = 0;
float j = 0;
integer max = 10000; // 2ms of work takes 20 seconds to repeat 10,000 times, plus overhead
// begin
float t0 = llGetTime();
// loop to measure elapsed time to run sample code
do
{
// test once or more
// hex1(0x7fffFFFF);
// hex2(0x7fffFFFF);
// hex3(0x7fffFFFF);
// bits2nybbles4(0x7fffFFFF);
// bits2nybbles5(0x7fffFFFF);
} while (++i < max);
float t1 = llGetTime();
// loop to measure elapsed time to run no code
do ; while (++j < max);
float t2 = llGetTime();
// complain if time ran backwards
if (!((t0 <= t1) && (t1 <= t2)))
{
llOwnerSay("MEANINGLESS RESULT -- SIMULATED TIME RAN BACKWARDS -- TRY AGAIN");
}
// report average time elapsed per run
float elapsedms = 1000.0 * (((t1 - t0) - (t2 - t1)) / max);
llOwnerSay((string) elapsedms + "+-??% ms may have elapsed on average in each of");
llOwnerSay((string) max + " trials of running the code in the loop");
}
}
}
Measuring no-op loops is actually a "serious" benchmark!
The main text quotes:
Try the empty test of deleting the
{ counter += 1; }
source line to see the astonishing inaccuracy of this instrument. The time cost of no code, as measured here, isn't always zero!
Note that measuring "no code" (or NOP — "no operation") is a perfectly valid and established method of doing crude (but nevertheless useful!) performance comparisons. One of the most famous benchmarks in the history of computing is definitely the BogoMIPS benchmark, developed by Linus Torvalds for the Linux kernel to perform a calibration test of the CPU. The actual unit of processing — one BogoMIPS (the acronym for "bogus million instructions per second") — is roughly defined as "the number of million times per second a processor can do absolutely nothing".[1][2].
While this so-called "benchmark" hardly deserves that title, it spurred lots of discussions in the later 1990s regarding certain claims that processor X from manufacturer Y was actually "better and faster" than processor W from manufacturer Z — because Linux, when booted, would calculate the BogoMIPS for each, and this allowed a sort-of-independent way for anyone to do their own pseudo-benchmarking — and claim whatever they wished about X vs. Z 😄
Linus and the most senior kernel core developers had long ago explained that this measurement was absolutely meaningless and not worthy being used as any form of categorising CPUs according to their perceived speed, solely based on their BogoMIPS "rating" — a practice that was used (for a while!) by server manufacturers targeting the emerging market of co-locating Linux servers. Several more modern CPU architectures made the BogoMIPS "rating" absurd — the CPU could see before-hand that there were an insane lot of NOPs coming next, and skip over all of them until the next non-NOP instruction, thus making its BogoMIPS rating approach infinity (or at least a huge number, far beyond the capabilities of human technology). At the same time, CPUs started incorporating other, much better, methods for doing the required calibration tests needed by the Linux kernel when booting up, so these — and not the BogoMIPS "do-nothing-loop" — are currently used to establish the baseline. Nevertheless, probably for historic reasons, and as of 2023, Linux still calculates the BogoMIPS for each processor core, even though it doesn't need to (except perhaps if someone is booting up their ancient and venerable 386DX and hopes to install modern-day Linux on it).
Thus, your own measurements are not exactly "astonishingly inaccurate". They are, in fact, as good a measurement as what happens every moment that a Linux kernel — the most widely used operating system on Earth — boots up. Thus, you rest on the shoulders of giants, and there is no shame in claiming otherwise! 🤣
— Gwyneth Llewelyn (talk) 11:24, 12 September 2023 (PDT)
- ↑ Eric S Raymond, and Geoff Mackenzie, published on the Internet in the early 1990s, untraceable origin; quoted via Wikipedia
- ↑ Raymond, Eric S.. Hackers Jargon File.