From 684b648be36dbc2f9b538b32c352b52418e69759 Mon Sep 17 00:00:00 2001 From: Andy Ayers Date: Fri, 8 Jan 2016 17:20:59 -0800 Subject: Add the bytemark benchmarks These are C# versions of benchmarks that originally appeared in BYTE magazine in 1995. See file headers for attribution. I've added [Benchmark] entry points for xunit-performance, and tuned each portion to run for about 1 second per xunit-performance iteration. Note a couple of the tests have variant implementations in C# -- either jagged vs MD arrays, or class vs struct, so there are more numbers reported than in the original benchmark. When run under xunit the work per test is fixed. When run as a command line app this will auto-tune (adjust iterations to try and hit some running time threshold). We may want to reduce the running time here some; I'll see what it ends up being in the lab. I've also tried to enable validation code where possible, both to ensure that the compiler gets the right answers, and also so it can't cheat and remove computation. The two assign benchmarks seem to report varying times in xunit mode, and may need to be investigated further. --- .../Performance/CodeQuality/Bytemark/ByteMark.cs | 1543 +++++++++++++++++++ .../CodeQuality/Bytemark/Bytemark.csproj | 60 + .../Performance/CodeQuality/Bytemark/Huffman.cs | 567 +++++++ .../Performance/CodeQuality/Bytemark/StringSort.cs | 345 +++++ .../CodeQuality/Bytemark/assign_jagged.cs | 548 +++++++ .../CodeQuality/Bytemark/assign_rect.cs | 542 +++++++ .../JIT/Performance/CodeQuality/Bytemark/bitops.cs | 276 ++++ .../CodeQuality/Bytemark/commands/assignjagged.dat | 2 + .../CodeQuality/Bytemark/commands/assignrect.dat | 2 + .../CodeQuality/Bytemark/commands/bitfield.dat | 2 + .../CodeQuality/Bytemark/commands/emfclass.dat | 2 + .../CodeQuality/Bytemark/commands/emfstruct.dat | 2 + .../CodeQuality/Bytemark/commands/four.dat | 2 + .../CodeQuality/Bytemark/commands/huff.dat | 2 + .../CodeQuality/Bytemark/commands/idea.dat | 2 + .../CodeQuality/Bytemark/commands/lu.dat | 2 + .../CodeQuality/Bytemark/commands/nnetjagged.dat | 2 + .../CodeQuality/Bytemark/commands/nnetrect.dat | 2 + .../Bytemark/commands/numsortjagged.dat | 2 + .../CodeQuality/Bytemark/commands/numsortrect.dat | 2 + .../CodeQuality/Bytemark/commands/stringsort.dat | 2 + .../Performance/CodeQuality/Bytemark/emfloat.cs | 1574 ++++++++++++++++++++ .../CodeQuality/Bytemark/emfloatclass.cs | 1563 +++++++++++++++++++ .../Performance/CodeQuality/Bytemark/fourier.cs | 227 +++ .../JIT/Performance/CodeQuality/Bytemark/idea.cs | 448 ++++++ .../Performance/CodeQuality/Bytemark/ludecomp.cs | 337 +++++ .../Performance/CodeQuality/Bytemark/neural-dat.cs | 228 +++ .../JIT/Performance/CodeQuality/Bytemark/neural.cs | 880 +++++++++++ .../CodeQuality/Bytemark/neuraljagged.cs | 879 +++++++++++ .../JIT/Performance/CodeQuality/Bytemark/nnet.dat | 210 +++ .../CodeQuality/Bytemark/numericsort.cs | 536 +++++++ .../Performance/CodeQuality/Bytemark/utility.cs | 102 ++ 32 files changed, 10893 insertions(+) create mode 100644 tests/src/JIT/Performance/CodeQuality/Bytemark/ByteMark.cs create mode 100644 tests/src/JIT/Performance/CodeQuality/Bytemark/Bytemark.csproj create mode 100644 tests/src/JIT/Performance/CodeQuality/Bytemark/Huffman.cs create mode 100644 tests/src/JIT/Performance/CodeQuality/Bytemark/StringSort.cs create mode 100644 tests/src/JIT/Performance/CodeQuality/Bytemark/assign_jagged.cs create mode 100644 tests/src/JIT/Performance/CodeQuality/Bytemark/assign_rect.cs create mode 100644 tests/src/JIT/Performance/CodeQuality/Bytemark/bitops.cs create mode 100644 tests/src/JIT/Performance/CodeQuality/Bytemark/commands/assignjagged.dat create mode 100644 tests/src/JIT/Performance/CodeQuality/Bytemark/commands/assignrect.dat create mode 100644 tests/src/JIT/Performance/CodeQuality/Bytemark/commands/bitfield.dat create mode 100644 tests/src/JIT/Performance/CodeQuality/Bytemark/commands/emfclass.dat create mode 100644 tests/src/JIT/Performance/CodeQuality/Bytemark/commands/emfstruct.dat create mode 100644 tests/src/JIT/Performance/CodeQuality/Bytemark/commands/four.dat create mode 100644 tests/src/JIT/Performance/CodeQuality/Bytemark/commands/huff.dat create mode 100644 tests/src/JIT/Performance/CodeQuality/Bytemark/commands/idea.dat create mode 100644 tests/src/JIT/Performance/CodeQuality/Bytemark/commands/lu.dat create mode 100644 tests/src/JIT/Performance/CodeQuality/Bytemark/commands/nnetjagged.dat create mode 100644 tests/src/JIT/Performance/CodeQuality/Bytemark/commands/nnetrect.dat create mode 100644 tests/src/JIT/Performance/CodeQuality/Bytemark/commands/numsortjagged.dat create mode 100644 tests/src/JIT/Performance/CodeQuality/Bytemark/commands/numsortrect.dat create mode 100644 tests/src/JIT/Performance/CodeQuality/Bytemark/commands/stringsort.dat create mode 100644 tests/src/JIT/Performance/CodeQuality/Bytemark/emfloat.cs create mode 100644 tests/src/JIT/Performance/CodeQuality/Bytemark/emfloatclass.cs create mode 100644 tests/src/JIT/Performance/CodeQuality/Bytemark/fourier.cs create mode 100644 tests/src/JIT/Performance/CodeQuality/Bytemark/idea.cs create mode 100644 tests/src/JIT/Performance/CodeQuality/Bytemark/ludecomp.cs create mode 100644 tests/src/JIT/Performance/CodeQuality/Bytemark/neural-dat.cs create mode 100644 tests/src/JIT/Performance/CodeQuality/Bytemark/neural.cs create mode 100644 tests/src/JIT/Performance/CodeQuality/Bytemark/neuraljagged.cs create mode 100644 tests/src/JIT/Performance/CodeQuality/Bytemark/nnet.dat create mode 100644 tests/src/JIT/Performance/CodeQuality/Bytemark/numericsort.cs create mode 100644 tests/src/JIT/Performance/CodeQuality/Bytemark/utility.cs diff --git a/tests/src/JIT/Performance/CodeQuality/Bytemark/ByteMark.cs b/tests/src/JIT/Performance/CodeQuality/Bytemark/ByteMark.cs new file mode 100644 index 0000000000..191e3cfc25 --- /dev/null +++ b/tests/src/JIT/Performance/CodeQuality/Bytemark/ByteMark.cs @@ -0,0 +1,1543 @@ +/* +** Copyright (c) Microsoft. All rights reserved. +** Licensed under the MIT license. +** See LICENSE file in the project root for full license information. +** +** This program was translated to C# and adapted for xunit-performance. +** New variants of several tests were added to compare class versus +** struct and to compare jagged arrays vs multi-dimensional arrays. +*/ + +/* +** BYTEmark (tm) +** BYTE Magazine's Native Mode benchmarks +** Rick Grehan, BYTE Magazine +** +** Create: +** Revision: 3/95 +** +** DISCLAIMER +** The source, executable, and documentation files that comprise +** the BYTEmark benchmarks are made available on an "as is" basis. +** This means that we at BYTE Magazine have made every reasonable +** effort to verify that the there are no errors in the source and +** executable code. We cannot, however, guarantee that the programs +** are error-free. Consequently, McGraw-HIll and BYTE Magazine make +** no claims in regard to the fitness of the source code, executable +** code, and documentation of the BYTEmark. +** +** Furthermore, BYTE Magazine, McGraw-Hill, and all employees +** of McGraw-Hill cannot be held responsible for any damages resulting +** from the use of this code or the results obtained from using +** this code. +*/ + +using Microsoft.Xunit.Performance; +using System; +using System.IO; + +[assembly: OptimizeForBenchmarks] +[assembly: MeasureInstructionsRetired] + +internal class global +{ + // Following should be modified accordingly per each compilation. + public const String SysName = "Generic 486/Pentium"; + public const String CompilerName = "CoreCLR"; + public const String CompilerVersion = "ver 0.0"; + + public static long min_ticks; + public static int min_secs; + public static bool allstats; + public static String ofile_name; // Output file name + public static StreamWriter ofile; // Output file + public static bool custrun; // Custom run flag + public static bool write_to_file; // Write output to file + public static int align; // Memory alignment + + /* + ** Following are global structures, one built for + ** each of the tests. + */ + public static SortStruct numsortstruct_jagged; // For numeric sort + public static SortStruct numsortstruct_rect; // For numeric sort + public static StringSort strsortstruct; // For string sort + public static BitOpStruct bitopstruct; // For bitfield ops + public static EmFloatStruct emfloatstruct_struct; // For emul. float. pt. + public static EmFloatStruct emfloatstruct_class; // For emul. float. pt. + public static FourierStruct fourierstruct; // For fourier test + public static AssignStruct assignstruct_jagged; // For assignment algs + public static AssignStruct assignstruct_rect; // For assignment algs + public static IDEAStruct ideastruct; // For IDEA encryption + public static HuffStruct huffstruct; // For Huffman compression + public static NNetStruct nnetstruct_jagged; // For Neural Net + public static NNetStruct nnetstruct_rect; // For Neural Net + public static LUStruct lustruct; // For LU decomposition + + public const long TICKS_PER_SEC = 1000; + public const long MINIMUM_TICKS = 60; // 60 msecs + +#if DEBUG + public const int MINIMUM_SECONDS = 1; +#else + public const int MINIMUM_SECONDS = 1; +#endif + + public const int NUMNUMARRAYS = 1000; + public const int NUMARRAYSIZE = 8111; + public const int STRINGARRAYSIZE = 8111; + // This is the upper limit of number of string arrays to sort in one + // iteration. If we can sort more than this number of arrays in less + // than MINIMUM_TICKS an exception is thrown. + public const int NUMSTRARRAYS = 100; + public const int HUFFARRAYSIZE = 5000; + public const int MAXHUFFLOOPS = 50000; + + // Assignment constants + public const int ASSIGNROWS = 101; + public const int ASSIGNCOLS = 101; + public const int MAXPOSLONG = 0x7FFFFFFF; + + // BitOps constants +#if LONG64 + public const int BITFARRAYSIZE = 16384; +#else + public const int BITFARRAYSIZE = 32768; +#endif + + // IDEA constants + public const int MAXIDEALOOPS = 5000; + public const int IDEAARRAYSIZE = 4000; + public const int IDEAKEYSIZE = 16; + public const int IDEABLOCKSIZE = 8; + public const int ROUNDS = 8; + public const int KEYLEN = (6 * ROUNDS + 4); + + // LUComp constants + public const int LUARRAYROWS = 101; + public const int LUARRAYCOLS = 101; + + + // EMFLOAT constants + public const int CPUEMFLOATLOOPMAX = 50000; + public const int EMFARRAYSIZE = 3000; +} + +/* +** TYPEDEFS +*/ + +public abstract class HarnessTest +{ + public bool bRunTest = true; + public double score; + public int adjust; /* Set adjust code */ + public int request_secs; /* # of seconds requested */ + + public abstract string Name(); + public abstract void ShowStats(); + public abstract double Run(); +} + +public abstract class SortStruct : HarnessTest +{ + public short numarrays = global.NUMNUMARRAYS; /* # of arrays */ + public int arraysize = global.NUMARRAYSIZE; /* # of elements in array */ + public override void ShowStats() + { + ByteMark.OutputString( + string.Format(" Number of arrays: {0}", numarrays)); + ByteMark.OutputString( + string.Format(" Array size: {0}", arraysize)); + } +} + +public abstract class StringSortStruct : HarnessTest +{ + public short numarrays = global.NUMNUMARRAYS; /* # of arrays */ + public int arraysize = global.STRINGARRAYSIZE; /* # of elements in array */ + public override void ShowStats() + { + ByteMark.OutputString( + string.Format(" Number of arrays: {0}", numarrays)); + ByteMark.OutputString( + string.Format(" Array size: {0}", arraysize)); + } +} + +public abstract class HuffStruct : HarnessTest +{ + public int arraysize = global.HUFFARRAYSIZE; + public int loops = 0; + public override void ShowStats() + { + ByteMark.OutputString( + string.Format(" Array size: {0}", arraysize)); + ByteMark.OutputString( + string.Format(" Number of loops: {0}", loops)); + } +} + +public abstract class FourierStruct : HarnessTest +{ + public int arraysize; /* Size of coeff. arrays */ + public override void ShowStats() + { + ByteMark.OutputString( + string.Format(" Number of coefficients: {0}", arraysize)); + } +} + +public abstract class AssignStruct : HarnessTest +{ + public short numarrays = global.NUMNUMARRAYS; /* # of elements in array */ + public override void ShowStats() + { + ByteMark.OutputString( + string.Format(" Number of arrays: {0}", numarrays)); + } +} + +public abstract class BitOpStruct : HarnessTest +{ + public int bitoparraysize; /* Total # of bitfield ops */ + public int bitfieldarraysize = global.BITFARRAYSIZE; /* Bit field array size */ + public override void ShowStats() + { + ByteMark.OutputString( + string.Format(" Operations array size: {0}", bitoparraysize)); + ByteMark.OutputString( + string.Format(" Bitfield array size: {0}", bitfieldarraysize)); + } +} + +public abstract class IDEAStruct : HarnessTest +{ + public int arraysize = global.IDEAARRAYSIZE; /* Size of array */ + public int loops; /* # of times to convert */ + public override void ShowStats() + { + ByteMark.OutputString( + string.Format(" Array size: {0}", arraysize)); + ByteMark.OutputString( + string.Format(" Number of loops: {0}", loops)); + } +} + +public abstract class LUStruct : HarnessTest +{ + public int numarrays; + public override void ShowStats() + { + ByteMark.OutputString( + string.Format(" Number of arrays: {0}", numarrays)); + } +} + +public abstract class NNetStruct : HarnessTest +{ + public int loops; /* # of times to learn */ + public double iterspersec; /* Results */ + public override void ShowStats() + { + ByteMark.OutputString( + string.Format(" Number of loops: {0}", loops)); + } +} + +public abstract class EmFloatStruct : HarnessTest +{ + public int arraysize = global.EMFARRAYSIZE; /* Size of array */ + public int loops; /* Loops per iterations */ + public override void ShowStats() + { + ByteMark.OutputString( + string.Format(" Number of loops: {0}", loops)); + ByteMark.OutputString( + string.Format(" Array size: {0}", arraysize)); + } +} + +public class ByteMark +{ + private static int[] s_randw; + private static double[] s_bindex; + private static HarnessTest[] s_tests; + + public static int Main(string[] args) + { + ByteMark app = new ByteMark(); + int result = app.ExecuteCore(args); + return result; + } + + public int ExecuteCore(string[] argv) + { + s_randw = new int[2] { 13, 117 }; + + global.min_ticks = global.MINIMUM_TICKS; + global.min_secs = global.MINIMUM_SECONDS; + global.allstats = false; + global.custrun = false; + global.align = 8; + global.write_to_file = false; + + /* + ** Indexes -- Baseline is DELL Pentium XP90 + ** 11/28/94 + */ + // JTR: Should make member of HarnessTest, but left + // this way to keep similar to original test. + s_bindex = new double[14] { + 38.993, /* Numeric sort */ + 38.993, /* Numeric sort */ + 2.238, /* String sort */ + 5829704, /* Bitfield */ + 2.084, /* FP Emulation */ + 2.084, /* FP Emulation */ + 879.278, /* Fourier */ + .2628, /* Assignment */ + .2628, /* Assignment */ + 65.382, /* IDEA */ + 36.062, /* Huffman */ + .6225, /* Neural Net */ + .6225, /* Neural Net */ + 19.3031 /* LU Decomposition */ + }; + + s_tests = new HarnessTest[14] + { + global.numsortstruct_jagged = new NumericSortJagged(), + global.numsortstruct_rect = new NumericSortRect(), + global.strsortstruct = new StringSort(), + global.bitopstruct = new BitOps(), + global.emfloatstruct_struct = new EMFloat(), + global.emfloatstruct_class = new EMFloatClass(), + global.fourierstruct = new Fourier(), + global.assignstruct_jagged = new AssignJagged(), + global.assignstruct_rect = new AssignRect(), + global.ideastruct = new IDEAEncryption(), + global.huffstruct = new Huffman(), + global.nnetstruct_jagged = new NeuralJagged(), + global.nnetstruct_rect = new Neural(), + global.lustruct = new LUDecomp(), + }; + + SetRequestSecs(); + + /* + ** Handle any command-line arguments. + */ + int argc = argv.Length; + if (argc > 0) + { + for (int i = 0; i < argc; i++) + { + if (parse_arg(argv[i]) == -1) + { + display_help("Bytemark"); + return -1; + } + } + } + + /* + ** Output header + */ + OutputString("BBBBBB YYY Y TTTTTTT EEEEEEE"); + OutputString("BBB B YYY Y TTT EEE"); + OutputString("BBB B YYY Y TTT EEE"); + OutputString("BBBBBB YYY Y TTT EEEEEEE"); + OutputString("BBB B YYY TTT EEE"); + OutputString("BBB B YYY TTT EEE"); + OutputString("BBBBBB YYY TTT EEEEEEE"); + OutputString(""); + OutputString("BYTEmark (tm) C# Mode Benchmark ver. 2 (06/99)"); + + if (global.allstats) + { + OutputString("========== ALL STATISTICS =========="); + DateTime time_and_date = DateTime.Now; + OutputString("**" + + time_and_date.ToString("ddd MMM dd HH:mm:ss yyyy")); + OutputString("**" + global.SysName); + OutputString("**" + global.CompilerName); + OutputString("**" + global.CompilerVersion); + OutputString("**Sizeof: int:4 short:2 long:8"); + OutputString("===================================="); + OutputString(""); + } + + try + { + /* + ** Execute the tests. + */ + int fpcount = 0; + int intcount = 0; + double intindex = 1.0; /* Integer index */ + double fpindex = 1.0; /* Floating-point index */ + for (int i = 0; i < s_tests.Length; i++) + { + if (s_tests[i].bRunTest) + { + double bmean; /* Benchmark mean */ + double bstdev; /* Benchmark stdev */ + int bnumrun; /* # of runs */ + OutputStringPart( + string.Format("{0}:", s_tests[i].Name())); + bench_with_confidence(i, + out bmean, + out bstdev, + out bnumrun); + OutputString( + string.Format(" Iterations/sec: {0:F5} Index: {1:F5}", + bmean, + bmean / s_bindex[i])); + + /* + ** Gather integer or FP indexes + */ + // JTR: indexes all have 1 added to them to compensate + // for splitting int sort into 2 tests + if ((i == 6) || (i == 12) || (i == 13) || (i == 11)) + { + /* FP index */ + fpindex = fpindex * (bmean / s_bindex[i]); + fpcount++; + } + else + { + /* Integer index */ + intindex = intindex * (bmean / s_bindex[i]); + intcount++; + } + + if (global.allstats) + { + OutputString( + string.Format(" Standard Deviation: {0}", bstdev)); + OutputString( + string.Format(" Number of runs: {0}", bnumrun)); + s_tests[i].ShowStats(); + } + } + } + /* + ** Output the total indexes + */ + if (!global.custrun) + { + OutputString("===========OVERALL============"); + OutputString( + string.Format("INTEGER INDEX: {0:F5}", Math.Pow(intindex, (double)1.0 / (double)intcount))); + OutputString( + string.Format("FLOATING-POINT INDEX: {0:F5}", Math.Pow(fpindex, (double)1.0 / (double)fpcount))); + OutputString(" (90 MHz Dell Pentium = 1.00)"); + OutputString("=============================="); + } + } + catch (Exception e) + { + Console.WriteLine("ExecuteCore - Exception {0}", e.ToString()); + if (global.ofile != null) + { + global.ofile.Flush(); + } + Console.WriteLine("Exception: {0}", e.ToString()); + return -1; + } + + return 100; + } + + /************** + ** parse_arg ** + *************** + ** Given a pointer to a string, we assume that's an argument. + ** Parse that argument and act accordingly. + ** Return 0 if ok, else return -1. + */ + private int parse_arg(String arg) + { + int i = 0; /* Index */ + StreamReader cfile = null; /* Command file identifier */ + + /* + ** First character has got to be a hyphen. + */ + if (arg[i++] != '-') return (-1); + + /* + ** Convert the rest of the argument to upper case + ** so there's little chance of confusion. + */ + arg = arg.ToUpper(); + + /* + ** Next character picks the action. + */ + switch (arg[i++]) + { + case '?': return (-1); /* Will display help */ + + case 'C': /* Command file name */ + /* + ** First try to open the file for reading. + */ + String inputFileName = arg.Substring(i); + + try + { + cfile = File.OpenText(inputFileName); + } + catch (IOException) + { + Console.WriteLine("**Error opening file: {0}", inputFileName); + return (-1); + } + + read_comfile(cfile); /* Read commands */ + break; + default: + return (-1); + } + return (0); + } + + /******************* + ** display_help() ** + ******************** + ** Display a help message showing argument requirements and such. + ** Exit when you're done...I mean, REALLY exit. + */ + private static void display_help(String progname) + { + Console.WriteLine("Usage: {0} [-c]", progname); + Console.WriteLine(" -c = Input parameters thru command file "); + } + + private enum PF + { // parameter flags + GMTICKS = 0, /* GLOBALMINTICKS */ + MINSECONDS = 1, /* MINSECONDS */ + ALLSTATS = 2, /* ALLSTATS */ + OUTFILE = 3, /* OUTFILE */ + CUSTOMRUN = 4, /* CUSTOMRUN */ + DONUM = 5, /* DONUMSORT */ + NUMNUMA = 6, /* NUMNUMARRAYS */ + NUMASIZE = 7, /* NUMARRAYSIZE */ + NUMMINS = 8, /* NUMMINSECONDS */ + DOSTR = 9, /* DOSTRINGSORT */ + STRASIZE = 10, /* STRARRAYSIZE */ + NUMSTRA = 11, /* NUMSTRARRAYS */ + STRMINS = 12, /* STRMINSECONDS */ + DOBITF = 13, /* DOBITFIELD */ + NUMBITOPS = 14, /* NUMBITOPS */ + BITFSIZE = 15, /* BITFIELDSIZE */ + BITMINS = 16, /* BITMINSECONDS */ + DOEMF = 17, /* DOEMF */ + EMFASIZE = 18, /* EMFARRAYSIZE */ + EMFLOOPS = 19, /* EMFLOOPS */ + EMFMINS = 20, /* EMFMINSECOND */ + DOFOUR = 21, /* DOFOUR */ + FOURASIZE = 22, /* FOURASIZE */ + FOURMINS = 23, /* FOURMINSECONDS */ + DOASSIGN = 24, /* DOASSIGN */ + AARRAYS = 25, /* ASSIGNARRAYS */ + ASSIGNMINS = 26, /* ASSIGNMINSECONDS */ + DOIDEA = 27, /* DOIDEA */ + IDEAASIZE = 28, /* IDEAARRAYSIZE */ + IDEALOOPS = 29, /* IDEALOOPS */ + IDEAMINS = 30, /* IDEAMINSECONDS */ + DOHUFF = 31, /* DOHUFF */ + HUFFASIZE = 32, /* HUFFARRAYSIZE */ + HUFFLOOPS = 33, /* HUFFLOOPS */ + HUFFMINS = 34, /* HUFFMINSECONDS */ + DONNET = 35, /* DONNET */ + NNETLOOPS = 36, /* NNETLOOPS */ + NNETMINS = 37, /* NNETMINSECONDS */ + DOLU = 38, /* DOLU */ + LUNARRAYS = 39, /* LUNUMARRAYS */ + LUMINS = 40, /* LUMINSECONDS */ + ALIGN = 41, /* ALIGN */ + + // Added for control of new C# rect/jagged struct/class tests + DONUMJAGGED = 42, /* DONUMSORTJAGGED */ + DONUMRECT = 43, /* DONUMSORTRECT */ + DOEMFSTRUCT = 44, /* DOEMFSTRUCT */ + DOEMFCLASS = 45, /* DOEMFCLASS */ + DOASSIGNJAGGED = 46, /* DOASSIGNJAGGED */ + DOASSIGNRECT = 47, /* DOASSIGNRECT */ + DONNETJAGGED = 48, /* DONNETJAGGED */ + DONNETRECT = 49, /* DONNETRECT */ + + MAXPARAM = 49 + } + + /* Parameter names */ + private static String[] s_paramnames = { + "GLOBALMINTICKS", + "MINSECONDS", + "ALLSTATS", + "OUTFILE", + "CUSTOMRUN", + "DONUMSORT", + "NUMNUMARRAYS", + "NUMARRAYSIZE", + "NUMMINSECONDS", + "DOSTRINGSORT", + "STRARRAYSIZE", + "NUMSTRARRAYS", + "STRMINSECONDS", + "DOBITFIELD", + "NUMBITOPS", + "BITFIELDSIZE", + "BITMINSECONDS", + "DOEMF", + "EMFARRAYSIZE", + "EMFLOOPS", + "EMFMINSECONDS", + "DOFOUR", + "FOURSIZE", + "FOURMINSECONDS", + "DOASSIGN", + "ASSIGNARRAYS", + "ASSIGNMINSECONDS", + "DOIDEA", + "IDEARRAYSIZE", + "IDEALOOPS", + "IDEAMINSECONDS", + "DOHUFF", + "HUFARRAYSIZE", + "HUFFLOOPS", + "HUFFMINSECONDS", + "DONNET", + "NNETLOOPS", + "NNETMINSECONDS", + "DOLU", + "LUNUMARRAYS", + "LUMINSECONDS", + "ALIGN", + + // Added for control of new C# rect/jagged struct/class tests + "DONUMSORTJAGGED", + "DONUMSORTRECT", + "DOEMFSTRUCT", + "DOEMFCLASS", + "DOASSIGNJAGGED", + "DOASSIGNRECT", + "DONNETJAGGED", + "DONNETRECT" + }; + + /***************** + ** read_comfile ** + ****************** + ** Read the command file. Set global parameters as + ** specified. This routine assumes that the command file + ** is already open. + */ + private static void read_comfile(StreamReader cfile) + { + String inbuf; + + String eptr; /* Offset to "=" sign */ + /* markples: now the value half of the key=value pair */ + + int eIndex; /* markples: now this is the "=" offset */ + + PF i; /* Index */ + + /* + ** Sit in a big loop, reading a line from the file at each + ** pass. Terminate on EOF. + */ + while ((inbuf = cfile.ReadLine()) != null) + { + /* + ** Parse up to the "=" sign. If we don't find an + ** "=", then flag an error. + */ + if ((eIndex = inbuf.IndexOf('=')) == -1) + { + Console.WriteLine("**COMMAND FILE ERROR at LINE:"); + Console.WriteLine(" " + inbuf); + goto skipswitch; /* A GOTO!!!! */ + } + + /* + ** Insert a null where the "=" was, then convert + ** the substring to uppercase. That will enable + ** us to perform the match. + */ + String name = inbuf.Substring(0, eIndex); + eptr = inbuf.Substring(eIndex + 1); + name = name.ToUpper(); + i = PF.MAXPARAM; + do + { + if (name == s_paramnames[(int)i]) + { + break; + } + } while (--i >= 0); + + if (i < 0) + { + Console.WriteLine("**COMMAND FILE ERROR -- UNKNOWN PARAM: " + + name); + goto skipswitch; + } + + /* + ** Advance eptr to the next field...which should be + ** the value assigned to the parameter. + */ + switch (i) + { + case PF.GMTICKS: /* GLOBALMINTICKS */ + global.min_ticks = Int64.Parse(eptr); + break; + + case PF.MINSECONDS: /* MINSECONDS */ + global.min_secs = Int32.Parse(eptr); + SetRequestSecs(); + break; + + case PF.ALLSTATS: /* ALLSTATS */ + global.allstats = getflag(eptr); + break; + + case PF.OUTFILE: /* OUTFILE */ + global.ofile_name = eptr; + try + { + global.ofile = File.AppendText(global.ofile_name); + global.write_to_file = true; + /* + ** Open the output file. + */ + } + catch (IOException) + { + Console.WriteLine("**Error opening output file: {0}", global.ofile_name); + global.write_to_file = false; + } + break; + + case PF.CUSTOMRUN: /* CUSTOMRUN */ + global.custrun = getflag(eptr); + for (i = 0; (int)i < s_tests.Length; i++) + { + s_tests[(int)i].bRunTest = !global.custrun; + } + break; + + case PF.DONUM: /* DONUMSORT */ + global.numsortstruct_jagged.bRunTest = + global.numsortstruct_rect.bRunTest = getflag(eptr); + break; + + case PF.NUMNUMA: /* NUMNUMARRAYS */ + global.numsortstruct_rect.numarrays = Int16.Parse(eptr); + global.numsortstruct_jagged.numarrays = global.numsortstruct_rect.numarrays; + global.numsortstruct_jagged.adjust = + global.numsortstruct_rect.adjust = 1; + break; + + case PF.NUMASIZE: /* NUMARRAYSIZE */ + global.numsortstruct_rect.arraysize = Int32.Parse(eptr); + global.numsortstruct_jagged.arraysize = global.numsortstruct_rect.arraysize; + break; + + case PF.NUMMINS: /* NUMMINSECONDS */ + global.numsortstruct_rect.request_secs = Int32.Parse(eptr); + global.numsortstruct_jagged.request_secs = global.numsortstruct_rect.request_secs; + break; + + case PF.DOSTR: /* DOSTRINGSORT */ + global.strsortstruct.bRunTest = getflag(eptr); + break; + + case PF.STRASIZE: /* STRARRAYSIZE */ + global.strsortstruct.arraysize = Int32.Parse(eptr); + break; + + case PF.NUMSTRA: /* NUMSTRARRAYS */ + global.strsortstruct.numarrays = Int16.Parse(eptr); + global.strsortstruct.adjust = 1; + break; + + case PF.STRMINS: /* STRMINSECONDS */ + global.strsortstruct.request_secs = Int32.Parse(eptr); + break; + + case PF.DOBITF: /* DOBITFIELD */ + global.bitopstruct.bRunTest = getflag(eptr); + break; + + case PF.NUMBITOPS: /* NUMBITOPS */ + global.bitopstruct.bitoparraysize = Int32.Parse(eptr); + global.bitopstruct.adjust = 1; + break; + + case PF.BITFSIZE: /* BITFIELDSIZE */ + global.bitopstruct.bitfieldarraysize = Int32.Parse(eptr); + break; + + case PF.BITMINS: /* BITMINSECONDS */ + global.bitopstruct.request_secs = Int32.Parse(eptr); + break; + + case PF.DOEMF: /* DOEMF */ + global.emfloatstruct_struct.bRunTest = + global.emfloatstruct_class.bRunTest = getflag(eptr); + break; + + case PF.EMFASIZE: /* EMFARRAYSIZE */ + global.emfloatstruct_class.arraysize = Int32.Parse(eptr); + global.emfloatstruct_struct.arraysize = global.emfloatstruct_class.arraysize; + break; + + case PF.EMFLOOPS: /* EMFLOOPS */ + global.emfloatstruct_class.loops = Int32.Parse(eptr); + break; + + case PF.EMFMINS: /* EMFMINSECOND */ + global.emfloatstruct_class.request_secs = Int32.Parse(eptr); + global.emfloatstruct_struct.request_secs = global.emfloatstruct_class.request_secs; + break; + + case PF.DOFOUR: /* DOFOUR */ + global.fourierstruct.bRunTest = getflag(eptr); + break; + + case PF.FOURASIZE: /* FOURASIZE */ + global.fourierstruct.arraysize = Int32.Parse(eptr); + global.fourierstruct.adjust = 1; + break; + + case PF.FOURMINS: /* FOURMINSECONDS */ + global.fourierstruct.request_secs = Int32.Parse(eptr); + break; + + case PF.DOASSIGN: /* DOASSIGN */ + global.assignstruct_jagged.bRunTest = + global.assignstruct_rect.bRunTest = getflag(eptr); + break; + + case PF.AARRAYS: /* ASSIGNARRAYS */ + global.assignstruct_rect.numarrays = Int16.Parse(eptr); + global.assignstruct_jagged.numarrays = global.assignstruct_rect.numarrays; + break; + + case PF.ASSIGNMINS: /* ASSIGNMINSECONDS */ + global.assignstruct_rect.request_secs = Int32.Parse(eptr); + global.assignstruct_jagged.request_secs = global.assignstruct_rect.request_secs; + break; + + case PF.DOIDEA: /* DOIDEA */ + global.ideastruct.bRunTest = getflag(eptr); + break; + + case PF.IDEAASIZE: /* IDEAARRAYSIZE */ + global.ideastruct.arraysize = Int32.Parse(eptr); + break; + + case PF.IDEALOOPS: /* IDEALOOPS */ + global.ideastruct.loops = Int32.Parse(eptr); + break; + + case PF.IDEAMINS: /* IDEAMINSECONDS */ + global.ideastruct.request_secs = Int32.Parse(eptr); + break; + + case PF.DOHUFF: /* DOHUFF */ + global.huffstruct.bRunTest = getflag(eptr); + break; + + case PF.HUFFASIZE: /* HUFFARRAYSIZE */ + global.huffstruct.arraysize = Int32.Parse(eptr); + break; + + case PF.HUFFLOOPS: /* HUFFLOOPS */ + global.huffstruct.loops = Int32.Parse(eptr); + global.huffstruct.adjust = 1; + break; + + case PF.HUFFMINS: /* HUFFMINSECONDS */ + global.huffstruct.request_secs = Int32.Parse(eptr); + break; + + case PF.DONNET: /* DONNET */ + global.nnetstruct_jagged.bRunTest = + global.nnetstruct_rect.bRunTest = getflag(eptr); + break; + + case PF.NNETLOOPS: /* NNETLOOPS */ + global.nnetstruct_rect.loops = Int32.Parse(eptr); + global.nnetstruct_jagged.loops = global.nnetstruct_rect.loops; + global.nnetstruct_jagged.adjust = + global.nnetstruct_rect.adjust = 1; + break; + + case PF.NNETMINS: /* NNETMINSECONDS */ + global.nnetstruct_rect.request_secs = Int32.Parse(eptr); + global.nnetstruct_jagged.request_secs = global.nnetstruct_rect.request_secs; + break; + + case PF.DOLU: /* DOLU */ + global.lustruct.bRunTest = getflag(eptr); + break; + + case PF.LUNARRAYS: /* LUNUMARRAYS */ + global.lustruct.numarrays = Int32.Parse(eptr); + global.lustruct.adjust = 1; + break; + + case PF.LUMINS: /* LUMINSECONDS */ + global.lustruct.request_secs = Int32.Parse(eptr); + break; + + case PF.ALIGN: /* ALIGN */ + global.align = Int32.Parse(eptr); + break; + + case PF.DONUMJAGGED: /* DONUMSORTJAGGED */ + global.numsortstruct_jagged.bRunTest = getflag(eptr); + break; + + case PF.DONUMRECT: /* DONUMSORTRECT */ + global.numsortstruct_rect.bRunTest = getflag(eptr); + break; + + case PF.DOEMFSTRUCT: /* DOEMFSTRUCT */ + global.emfloatstruct_struct.bRunTest = getflag(eptr); + break; + + case PF.DOEMFCLASS: /* DOEMFCLASS */ + global.emfloatstruct_class.bRunTest = getflag(eptr); + break; + + case PF.DOASSIGNJAGGED: /* DOASSIGNJAGGED */ + global.assignstruct_jagged.bRunTest = getflag(eptr); + break; + + case PF.DOASSIGNRECT: /* DOASSIGNRECT */ + global.assignstruct_rect.bRunTest = getflag(eptr); + break; + + case PF.DONNETJAGGED: /* DONNETJAGGED */ + global.nnetstruct_jagged.bRunTest = getflag(eptr); + break; + + case PF.DONNETRECT: /* DONNETRECT */ + global.nnetstruct_rect.bRunTest = getflag(eptr); + break; + } + skipswitch: + continue; + } /* End while */ + + return; + } + + /************ + ** getflag ** + ************* + ** Return 1 if cptr points to "T"; 0 otherwise. + */ + private static bool getflag(String cptr) + { + return cptr[0] == 'T' || cptr[0] == 't'; + } + + /********************* + ** set_request_secs ** + ********************** + ** Set everyone's "request_secs" entry to whatever + ** value is in global.min_secs. This is done + ** at the beginning, and possibly later if the + ** user redefines global.min_secs in the command file. + */ + private static void SetRequestSecs() + { + foreach (HarnessTest ht in s_tests) + { + ht.request_secs = global.min_secs; + } + return; + } + + /************************** + ** bench_with_confidence ** + *************************** + ** Given a benchmark id that indicates a function, this + ** routine repeatedly calls that benchmark, seeking + ** to collect enough scores to get 5 that meet the confidence + ** criteria. Return 0 if ok, -1 if failure. + ** Returns mean ans std. deviation of results if successful. + */ + private static + int bench_with_confidence(int fid, /* Function id */ + out double mean, /* Mean of scores */ + out double stdev, /* Standard deviation */ + out int numtries) /* # of attempts */ + { + double[] myscores = new double[5]; /* Need at least 5 scores */ + double c_half_interval; /* Confidence half interval */ + int i; /* Index */ + double newscore; /* For improving confidence interval */ + + /* + ** Get first 5 scores. Then begin confidence testing. + */ + for (i = 0; i < 5; i++) + { + myscores[i] = s_tests[fid].Run(); + } + numtries = 5; /* Show 5 attempts */ + + /* + ** The system allows a maximum of 10 tries before it gives + ** up. Since we've done 5 already, we'll allow 5 more. + */ + + /* + ** Enter loop to test for confidence criteria. + */ + while (true) + { + /* + ** Calculate confidence. + */ + calc_confidence(myscores, + out c_half_interval, + out mean, + out stdev); + + /* + ** Is half interval 5% or less of mean? + ** If so, we can go home. Otherwise, + ** we have to continue. + */ + if (c_half_interval / mean <= (double)0.05) + break; + + /* + ** Go get a new score and see if it + ** improves existing scores. + */ + do + { + if (numtries == 10) + return (-1); + newscore = s_tests[fid].Run(); + numtries += 1; + } while (seek_confidence(myscores, ref newscore, + out c_half_interval, out mean, out stdev) == 0); + } + + return (0); + } + + /******************** + ** seek_confidence ** + ********************* + ** Pass this routine an array of 5 scores PLUS a new score. + ** This routine tries the new score in place of each of + ** the other five scores to determine if the new score, + ** when replacing one of the others, improves the confidence + ** half-interval. + ** Return 0 if failure. Original 5 scores unchanged. + ** Return -1 if success. Also returns new half-interval, + ** mean, and stand. dev. + */ + private static int seek_confidence(double[] scores, + ref double newscore, + out double c_half_interval, + out double smean, + out double sdev) + { + double sdev_to_beat; /* Original sdev to be beaten */ + double temp; /* For doing a swap */ + int is_beaten; /* Indicates original was beaten */ + int i; /* Index */ + + /* + ** First calculate original standard deviation + */ + calc_confidence(scores, out c_half_interval, out smean, out sdev); + sdev_to_beat = sdev; + is_beaten = -1; + + /* + ** Try to beat original score. We'll come out of this + ** loop with a flag. + */ + for (i = 0; i < 5; i++) + { + temp = scores[i]; + scores[i] = newscore; + calc_confidence(scores, out c_half_interval, out smean, out sdev); + scores[i] = temp; + if (sdev_to_beat > sdev) + { + is_beaten = i; + sdev_to_beat = sdev; + } + } + + if (is_beaten != -1) + { + scores[is_beaten] = newscore; + return (-1); + } + return (0); + } + + /******************** + ** calc_confidence ** + ********************* + ** Given a set of 5 scores, calculate the confidence + ** half-interval. We'l also return the sample mean and sample + ** standard deviation. + ** NOTE: This routines presumes a confidence of 95% and + ** a confidence coefficient of .95 + */ + private static void calc_confidence(double[] scores, /* Array of scores */ + out double c_half_interval, /* Confidence half-int */ + out double smean, /* Standard mean */ + out double sdev) /* Sample stand dev */ + { + int i; /* Index */ + /* + ** First calculate mean. + */ + smean = (scores[0] + scores[1] + scores[2] + scores[3] + scores[4]) / + (double)5.0; + + /* + ** Get standard deviation - first get variance + */ + sdev = (double)0.0; + for (i = 0; i < 5; i++) + { + sdev += (scores[i] - smean) * (scores[i] - smean); + } + sdev /= (double)4.0; + sdev = Math.Sqrt(sdev) / Math.Sqrt(5.0); + + /* + ** Now calculate the confidence half-interval. + ** For a confidence level of 95% our confidence coefficient + ** gives us a multiplying factor of 2.776 + ** (The upper .025 quartile of a t distribution with 4 degrees + ** of freedom.) + */ + c_half_interval = (double)2.776 * sdev; + return; + } + + public static void OutputStringPart(String s) + { + Console.Write(s); + if (global.write_to_file) + { + global.ofile.Write(s); + } + } + + public static void OutputString(String s) + { + Console.WriteLine(s); + if (global.write_to_file) + { + global.ofile.WriteLine(s); + global.ofile.Flush(); + } + } + + /**************************** + ** TicksToSecs + ** Converts ticks to seconds. Converts ticks to integer + ** seconds, discarding any fractional amount. + */ + public static int TicksToSecs(long tickamount) + { + return ((int)(tickamount / global.TICKS_PER_SEC)); + } + + /**************************** + ** TicksToFracSecs + ** Converts ticks to fractional seconds. In other words, + ** this returns the exact conversion from ticks to + ** seconds. + */ + public static double TicksToFracSecs(long tickamount) + { + return ((double)tickamount / (double)global.TICKS_PER_SEC); + } + + public static long StartStopwatch() + { + //DateTime t = DateTime.Now; + //return(t.Ticks); + return Environment.TickCount; + } + + public static long StopStopwatch(long start) + { + //DateTime t = DateTime.Now; + //Console.WriteLine(t.Ticks - start); + //return(t.Ticks-start); + long x = Environment.TickCount - start; + //Console.WriteLine(x); + return x; + } + + /**************************** + * randwc() * + ***************************** + ** Returns int random modulo num. + */ + public static int randwc(int num) + { + return (randnum(0) % num); + } + + /*************************** + ** abs_randwc() ** + **************************** + ** Same as randwc(), only this routine returns only + ** positive numbers. + */ + public static int abs_randwc(int num) + { + int temp; /* Temporary storage */ + + temp = randwc(num); + if (temp < 0) temp = 0 - temp; + + return temp; + } + + /**************************** + * randnum() * + ***************************** + ** Second order linear congruential generator. + ** Constants suggested by J. G. Skellam. + ** If val==0, returns next member of sequence. + ** val!=0, restart generator. + */ + public static int randnum(int lngval) + { + int interm; + + if (lngval != 0L) + { s_randw[0] = 13; s_randw[1] = 117; } + + unchecked + { + interm = (s_randw[0] * 254754 + s_randw[1] * 529562) % 999563; + } + s_randw[1] = s_randw[0]; + s_randw[0] = interm; + return (interm); + } + + static void Setup() + { + s_randw = new int[2] { 13, 117 }; + global.min_ticks = global.MINIMUM_TICKS; + global.min_secs = global.MINIMUM_SECONDS; + global.allstats = false; + global.custrun = false; + global.align = 8; + global.write_to_file = false; + } + + const int NumericSortJaggedIterations = 20; + + [Benchmark] + public static void BenchNumericSortJagged() + { + Setup(); + NumericSortJagged t = new NumericSortJagged(); + t.numarrays = 1000; + t.adjust = 0; + + foreach (var iteration in Benchmark.Iterations) + { + using (iteration.StartMeasurement()) + { + for (int i = 0; i < NumericSortJaggedIterations; i++) + { + t.Run(); + } + } + } + } + + const int NumericSortRectangularIterations = 20; + + [Benchmark] + public static void BenchNumericSortRectangular() + { + Setup(); + NumericSortRect t = new NumericSortRect(); + t.numarrays = 1000; + t.adjust = 0; + + foreach (var iteration in Benchmark.Iterations) + { + using (iteration.StartMeasurement()) + { + for (int i = 0; i < NumericSortRectangularIterations; i++) + { + t.Run(); + } + } + } + } + + const int StringSortIterations = 15; + + [Benchmark] + public static void BenchStringSort() + { + Setup(); + StringSort t = new StringSort(); + t.numarrays = 1000; + t.adjust = 0; + + foreach (var iteration in Benchmark.Iterations) + { + using (iteration.StartMeasurement()) + { + for (int i = 0; i < StringSortIterations; i++) + { + t.Run(); + } + } + } + } + const int BitOpsIterations = 15; + + [Benchmark] + public static void BenchBitOps() + { + Setup(); + BitOps t = new BitOps(); + t.adjust = 0; + + foreach (var iteration in Benchmark.Iterations) + { + using (iteration.StartMeasurement()) + { + for (int i = 0; i < BitOpsIterations; i++) + { + t.Run(); + } + } + } + } + + const int EmFloatIterations = 8; + + [Benchmark] + public static void BenchEmFloat() + { + Setup(); + EmFloatStruct t = new EMFloat(); + t.loops = 100; + t.adjust = 0; + + foreach (var iteration in Benchmark.Iterations) + { + using (iteration.StartMeasurement()) + { + for (int i = 0; i < EmFloatIterations; i++) + { + t.Run(); + } + } + } + } + + const int EmFloatClassIterations = 8; + + [Benchmark] + public static void BenchEmFloatClass() + { + Setup(); + EmFloatStruct t = new EMFloatClass(); + t.loops = 100; + t.adjust = 0; + + foreach (var iteration in Benchmark.Iterations) + { + using (iteration.StartMeasurement()) + { + for (int i = 0; i < EmFloatClassIterations; i++) + { + t.Run(); + } + } + } + } + + const int FourierIterations = 20; + + [Benchmark] + public static void BenchFourier() + { + Setup(); + FourierStruct t = new Fourier(); + t.adjust = 0; + + foreach (var iteration in Benchmark.Iterations) + { + using (iteration.StartMeasurement()) + { + for (int i = 0; i < FourierIterations; i++) + { + t.Run(); + } + } + } + } + + const int AssignJaggedIterations = 15; + + [Benchmark] + public static void BenchAssignJagged() + { + Setup(); + AssignStruct t = new AssignJagged(); + t.numarrays = 1000; + t.adjust = 0; + + foreach (var iteration in Benchmark.Iterations) + { + using (iteration.StartMeasurement()) + { + for (int i = 0; i < AssignJaggedIterations; i++) + { + t.Run(); + } + } + } + } + + const int AssignRectangularIterations = 20; + + [Benchmark] + public static void BenchAssignRectangular() + { + Setup(); + AssignStruct t = new AssignRect(); + t.numarrays = 1000; + t.adjust = 0; + + foreach (var iteration in Benchmark.Iterations) + { + using (iteration.StartMeasurement()) + { + for (int i = 0; i < AssignRectangularIterations; i++) + { + t.Run(); + } + } + } + } + + const int IDEAEncryptionIterations = 20; + + [Benchmark] + public static void BenchIDEAEncryption() + { + Setup(); + IDEAStruct t = new IDEAEncryption(); + t.loops = 100; + t.adjust = 0; + + foreach (var iteration in Benchmark.Iterations) + { + using (iteration.StartMeasurement()) + { + for (int i = 0; i < IDEAEncryptionIterations; i++) + { + t.Run(); + } + } + } + } + + const int NeuralJaggedIterations = 20; + + [Benchmark] + public static void BenchNeuralJagged() + { + Setup(); + NNetStruct t = new NeuralJagged(); + t.loops = 100; + t.adjust = 0; + + foreach (var iteration in Benchmark.Iterations) + { + using (iteration.StartMeasurement()) + { + for (int i = 0; i < NeuralJaggedIterations; i++) + { + t.Run(); + } + } + } + } + + const int NeuralIterations = 12; + + [Benchmark] + public static void BenchNeural() + { + Setup(); + NNetStruct t = new Neural(); + t.loops = 100; + t.adjust = 0; + + foreach (var iteration in Benchmark.Iterations) + { + using (iteration.StartMeasurement()) + { + for (int i = 0; i < NeuralIterations; i++) + { + t.Run(); + } + } + } + } + + const int LUDecompIterations = 20; + + [Benchmark] + public static void BenchLUDecomp() + { + Setup(); + LUStruct t = new LUDecomp(); + t.numarrays = 1000; + t.adjust = 0; + + foreach (var iteration in Benchmark.Iterations) + { + using (iteration.StartMeasurement()) + { + for (int i = 0; i < LUDecompIterations; i++) + { + t.Run(); + } + } + } + } +} diff --git a/tests/src/JIT/Performance/CodeQuality/Bytemark/Bytemark.csproj b/tests/src/JIT/Performance/CodeQuality/Bytemark/Bytemark.csproj new file mode 100644 index 0000000000..5c2fb1e8ca --- /dev/null +++ b/tests/src/JIT/Performance/CodeQuality/Bytemark/Bytemark.csproj @@ -0,0 +1,60 @@ + + + + + Debug + AnyCPU + 2.0 + {95DFC527-4DC1-495E-97D7-E94EE1F7140D} + Exe + Properties + 512 + {786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + $(ProgramFiles)\Common Files\microsoft shared\VSTT\11.0\UITestExtensionPackages + ..\..\ + 7a9bfb7d + + + + + + pdbonly + true + + + + False + + + + + + + + + + + + + + + + + + + + + + + + + + + + $(JitPackagesConfigFileDirectory)benchmark\project.json + $(JitPackagesConfigFileDirectory)benchmark\project.lock.json + + + + + diff --git a/tests/src/JIT/Performance/CodeQuality/Bytemark/Huffman.cs b/tests/src/JIT/Performance/CodeQuality/Bytemark/Huffman.cs new file mode 100644 index 0000000000..52afc731d1 --- /dev/null +++ b/tests/src/JIT/Performance/CodeQuality/Bytemark/Huffman.cs @@ -0,0 +1,567 @@ +/* +** Copyright (c) Microsoft. All rights reserved. +** Licensed under the MIT license. +** See LICENSE file in the project root for full license information. +** +** This program was translated to C# and adapted for xunit-performance. +** New variants of several tests were added to compare class versus +** struct and to compare jagged arrays vs multi-dimensional arrays. +*/ + +/* +** BYTEmark (tm) +** BYTE Magazine's Native Mode benchmarks +** Rick Grehan, BYTE Magazine +** +** Create: +** Revision: 3/95 +** +** DISCLAIMER +** The source, executable, and documentation files that comprise +** the BYTEmark benchmarks are made available on an "as is" basis. +** This means that we at BYTE Magazine have made every reasonable +** effort to verify that the there are no errors in the source and +** executable code. We cannot, however, guarantee that the programs +** are error-free. Consequently, McGraw-HIll and BYTE Magazine make +** no claims in regard to the fitness of the source code, executable +** code, and documentation of the BYTEmark. +** +** Furthermore, BYTE Magazine, McGraw-Hill, and all employees +** of McGraw-Hill cannot be held responsible for any damages resulting +** from the use of this code or the results obtained from using +** this code. +*/ + +using System; + +/* +** TYPEDEFS +*/ +internal struct huff_node +{ + public byte c; /* Byte value */ + public float freq; /* Frequency */ + public int parent; /* Parent node */ + public int left; /* Left pointer = 0 */ + public int right; /* Right pointer = 1 */ +}; + +/************************ +** HUFFMAN COMPRESSION ** +************************/ +public class Huffman : HuffStruct +{ + public override string Name() + { + return "HUFFMAN"; + } + + /************** + ** DoHuffman ** + *************** + ** Execute a huffman compression on a block of plaintext. + ** Note that (as with IDEA encryption) an iteration of the + ** Huffman test includes a compression AND a decompression. + ** Also, the compression cycle includes building the + ** Huffman tree. + */ + public override double Run() + { + huff_node[] hufftree; + long accumtime; + double iterations; + byte[] comparray; + byte[] decomparray; + byte[] plaintext; + + InitWords(); + + /* + ** Allocate memory for the plaintext and the compressed text. + ** We'll be really pessimistic here, and allocate equal amounts + ** for both (though we know...well, we PRESUME) the compressed + ** stuff will take less than the plain stuff. + ** Also note that we'll build a 3rd buffer to decompress + ** into, and we preallocate space for the huffman tree. + ** (We presume that the Huffman tree will grow no larger + ** than 512 bytes. This is actually a super-conservative + ** estimate...but, who cares?) + */ + plaintext = new byte[this.arraysize]; + comparray = new byte[this.arraysize]; + decomparray = new byte[this.arraysize]; + + hufftree = new huff_node[512]; + + /* + ** Build the plaintext buffer. Since we want this to + ** actually be able to compress, we'll use the + ** wordcatalog to build the plaintext stuff. + */ + create_text_block(plaintext, this.arraysize - 1, 500); + // for (int i = 0; i < this.arraysize-1; i++) { + // Console.Write((char)plaintext[i]); + // } + plaintext[this.arraysize - 1] = (byte)'\0'; + // plaintextlen=this.arraysize; + + /* + ** See if we need to perform self adjustment loop. + */ + if (this.adjust == 0) + { + /* + ** Do self-adjustment. This involves initializing the + ** # of loops and increasing the loop count until we + ** get a number of loops that we can use. + */ + + for (this.loops = 100; + this.loops < global.MAXHUFFLOOPS; + this.loops += 10) + { + if (DoHuffIteration(plaintext, + comparray, + decomparray, + this.arraysize, + this.loops, + hufftree) > global.min_ticks) + break; + } + } + + /* + ** All's well if we get here. Do the test. + */ + accumtime = 0L; + iterations = (double)0.0; + + do + { + accumtime += DoHuffIteration(plaintext, + comparray, + decomparray, + this.arraysize, + this.loops, + hufftree); + iterations += (double)this.loops; + } while (ByteMark.TicksToSecs(accumtime) < this.request_secs); + + /* + ** Clean up, calculate results, and go home. Be sure to + ** show that we don't have to rerun adjustment code. + */ + //this.iterspersec=iterations / TicksToFracSecs(accumtime); + + if (this.adjust == 0) + this.adjust = 1; + + return (iterations / ByteMark.TicksToFracSecs(accumtime)); + } + + + /********************* + ** create_text_line ** + ********************** + ** Create a random line of text, stored at *dt. The line may be + ** no more than nchars long. + */ + private static void create_text_line(byte[] dt, int nchars, int lower) + { + int charssofar; /* # of characters so far */ + int tomove; /* # of characters to move */ + string myword; /* Local buffer for words */ + + int index = 0; + + charssofar = 0; + + do + { + /* + ** Grab a random word from the wordcatalog + */ + myword = wordcatarray[ByteMark.abs_randwc(Huffman.WORDCATSIZE)]; + + /* + ** Append a blank. + */ + myword += " "; + tomove = myword.Length; + + /* + ** See how long it is. If its length+charssofar > nchars, we have + ** to trim it. + */ + if ((tomove + charssofar) > nchars) + tomove = nchars - charssofar; + /* + ** Attach the word to the current line. Increment counter. + */ + for (int i = 0; i < tomove; i++) + { + dt[lower + index++] = (byte)myword[i]; + } + charssofar += tomove; + + /* + ** If we're done, bail out. Otherwise, go get another word. + */ + } while (charssofar < nchars); + + return; + } + + /********************** + ** create_text_block ** + *********************** + ** Build a block of text randomly loaded with words. The words + ** come from the wordcatalog (which must be loaded before you + ** call this). + ** *tb points to the memory where the text is to be built. + ** tblen is the # of bytes to put into the text block + ** maxlinlen is the maximum length of any line (line end indicated + ** by a carriage return). + */ + private static void create_text_block(byte[] tb, + int tblen, + short maxlinlen) + { + int bytessofar; /* # of bytes so far */ + int linelen; /* Line length */ + + bytessofar = 0; + do + { + /* + ** Pick a random length for a line and fill the line. + ** Make sure the line can fit (haven't exceeded tablen) and also + ** make sure you leave room to append a carriage return. + */ + linelen = ByteMark.abs_randwc(maxlinlen - 6) + 6; + if ((linelen + bytessofar) > tblen) + linelen = tblen - bytessofar; + + if (linelen > 1) + { + create_text_line(tb, linelen, bytessofar); + } + tb[linelen] = (byte)'\n'; /* Add the carriage return */ + + bytessofar += linelen; + } while (bytessofar < tblen); + } + + /******************** + ** DoHuffIteration ** + ********************* + ** Perform the huffman benchmark. This routine + ** (a) Builds the huffman tree + ** (b) Compresses the text + ** (c) Decompresses the text and verifies correct decompression + */ + private static long DoHuffIteration(byte[] plaintext, + byte[] comparray, + byte[] decomparray, + int arraysize, + int nloops, + huff_node[] hufftree) + { + int i; /* Index */ + int j; /* Bigger index */ + int root; /* Pointer to huffman tree root */ + float lowfreq1, lowfreq2; /* Low frequency counters */ + int lowidx1, lowidx2; /* Indexes of low freq. elements */ + int bitoffset; /* Bit offset into text */ + int textoffset; /* Char offset into text */ + int maxbitoffset; /* Holds limit of bit offset */ + int bitstringlen; /* Length of bitstring */ + int c; /* Character from plaintext */ + byte[] bitstring = new byte[30]; /* Holds bitstring */ + long elapsed; /* For stopwatch */ + + /* + ** Start the stopwatch + */ + elapsed = ByteMark.StartStopwatch(); + + /* + ** Do everything for nloops + */ + while (nloops-- != 0) + { + /* + ** Calculate the frequency of each byte value. Store the + ** results in what will become the "leaves" of the + ** Huffman tree. Interior nodes will be built in those + ** nodes greater than node #255. + */ + for (i = 0; i < 256; i++) + { + hufftree[i].freq = (float)0.0; + hufftree[i].c = (byte)i; + } + + for (j = 0; j < arraysize; j++) + hufftree[plaintext[j]].freq += (float)1.0; + + for (i = 0; i < 256; i++) + if (hufftree[i].freq != (float)0.0) + hufftree[i].freq /= (float)arraysize; + + /* + ** Build the huffman tree. First clear all the parent + ** pointers and left/right pointers. Also, discard all + ** nodes that have a frequency of true 0. + */ + for (i = 0; i < 512; i++) + { + if (hufftree[i].freq == (float)0.0) + hufftree[i].parent = EXCLUDED; + else + hufftree[i].parent = hufftree[i].left = hufftree[i].right = -1; + } + + /* + ** Go through the tree. Finding nodes of really low + ** frequency. + */ + root = 255; /* Starting root node-1 */ + while (true) + { + lowfreq1 = (float)2.0; lowfreq2 = (float)2.0; + lowidx1 = -1; lowidx2 = -1; + /* + ** Find first lowest frequency. + */ + for (i = 0; i <= root; i++) + if (hufftree[i].parent < 0) + if (hufftree[i].freq < lowfreq1) + { + lowfreq1 = hufftree[i].freq; + lowidx1 = i; + } + + /* + ** Did we find a lowest value? If not, the + ** tree is done. + */ + if (lowidx1 == -1) break; + + /* + ** Find next lowest frequency + */ + for (i = 0; i <= root; i++) + if ((hufftree[i].parent < 0) && (i != lowidx1)) + if (hufftree[i].freq < lowfreq2) + { + lowfreq2 = hufftree[i].freq; + lowidx2 = i; + } + + /* + ** If we could only find one item, then that + ** item is surely the root, and (as above) the + ** tree is done. + */ + if (lowidx2 == -1) break; + + /* + ** Attach the two new nodes to the current root, and + ** advance the current root. + */ + root++; /* New root */ + hufftree[lowidx1].parent = root; + hufftree[lowidx2].parent = root; + hufftree[root].freq = lowfreq1 + lowfreq2; + hufftree[root].left = lowidx1; + hufftree[root].right = lowidx2; + hufftree[root].parent = -2; /* Show root */ + } + + /* + ** Huffman tree built...compress the plaintext + */ + bitoffset = 0; /* Initialize bit offset */ + for (i = 0; i < arraysize; i++) + { + c = (int)plaintext[i]; /* Fetch character */ + /* + ** Build a bit string for byte c + */ + bitstringlen = 0; + while (hufftree[c].parent != -2) + { + if (hufftree[hufftree[c].parent].left == c) + bitstring[bitstringlen] = (byte)'0'; + else + bitstring[bitstringlen] = (byte)'1'; + c = hufftree[c].parent; + bitstringlen++; + } + + /* + ** Step backwards through the bit string, setting + ** bits in the compressed array as you go. + */ + while (bitstringlen-- != 0) + { + SetCompBit(comparray, bitoffset, bitstring[bitstringlen]); + bitoffset++; + } + } + + /* + ** Compression done. Perform de-compression. + */ + maxbitoffset = bitoffset; + bitoffset = 0; + textoffset = 0; + do + { + i = root; + while (hufftree[i].left != -1) + { + if (GetCompBit(comparray, bitoffset) == 0) + i = hufftree[i].left; + else + i = hufftree[i].right; + bitoffset++; + } + decomparray[textoffset] = hufftree[i].c; + +#if DEBUG + if (hufftree[i].c != plaintext[textoffset]) + { + /* Show error */ + string error = String.Format("Huffman: error at textoffset {0}", textoffset); + throw new Exception(error); + } +#endif + textoffset++; + } while (bitoffset < maxbitoffset); + } /* End the big while(nloops--) from above */ + + /* + ** All done + */ + return (ByteMark.StopStopwatch(elapsed)); + } + + /*************** + ** SetCompBit ** + **************** + ** Set a bit in the compression array. The value of the + ** bit is set according to char bitchar. + */ + private static void SetCompBit(byte[] comparray, + int bitoffset, + byte bitchar) + { + int byteoffset; + int bitnumb; + + /* + ** First calculate which element in the comparray to + ** alter. and the bitnumber. + */ + byteoffset = bitoffset >> 3; + bitnumb = bitoffset % 8; + + /* + ** Set or clear + */ + if (bitchar == '1') + comparray[byteoffset] |= ((byte)(1 << bitnumb)); + else + { + // JTR: Work around compiler bug: (byte)~(1<> 3; + bitnumb = bitoffset % 8; + + /* + ** Fetch + */ + return ((1 << bitnumb) & comparray[byteoffset]); + } + + protected const int WORDCATSIZE = 50; + protected const int EXCLUDED = 32000; /* Big positive value */ + protected static string[] wordcatarray; + protected static void InitWords() + { + wordcatarray = new string[] + { "Hello", + "He", + "Him", + "the", + "this", + "that", + "though", + "rough", + "cough", + "obviously", + "But", + "but", + "bye", + "begin", + "beginning", + "beginnings", + "of", + "our", + "ourselves", + "yourselves", + "to", + "together", + "togetherness", + "from", + "either", + "I", + "A", + "return", + "However", + "that", + "example", + "yet", + "quickly", + "all", + "if", + "were", + "includes", + "always", + "never", + "not", + "small", + "returns", + "set", + "basic", + "Entered", + "with", + "used", + "shown", + "you", + "know" }; + } +} diff --git a/tests/src/JIT/Performance/CodeQuality/Bytemark/StringSort.cs b/tests/src/JIT/Performance/CodeQuality/Bytemark/StringSort.cs new file mode 100644 index 0000000000..37efe551ac --- /dev/null +++ b/tests/src/JIT/Performance/CodeQuality/Bytemark/StringSort.cs @@ -0,0 +1,345 @@ +/* +** Copyright (c) Microsoft. All rights reserved. +** Licensed under the MIT license. +** See LICENSE file in the project root for full license information. +** +** This program was translated to C# and adapted for xunit-performance. +** New variants of several tests were added to compare class versus +** struct and to compare jagged arrays vs multi-dimensional arrays. +*/ + +/* +** BYTEmark (tm) +** BYTE Magazine's Native Mode benchmarks +** Rick Grehan, BYTE Magazine +** +** Create: +** Revision: 3/95 +** +** DISCLAIMER +** The source, executable, and documentation files that comprise +** the BYTEmark benchmarks are made available on an "as is" basis. +** This means that we at BYTE Magazine have made every reasonable +** effort to verify that the there are no errors in the source and +** executable code. We cannot, however, guarantee that the programs +** are error-free. Consequently, McGraw-HIll and BYTE Magazine make +** no claims in regard to the fitness of the source code, executable +** code, and documentation of the BYTEmark. +** +** Furthermore, BYTE Magazine, McGraw-Hill, and all employees +** of McGraw-Hill cannot be held responsible for any damages resulting +** from the use of this code or the results obtained from using +** this code. +** +*/ + +using System; +using System.Text; + +/******************** +** STRING HEAPSORT ** +********************/ + +/***************** +** DoStringSort ** +****************** +** This routine performs the CPU string sort test. +** Arguments: +** requested_secs = # of seconds to execute test +** stringspersec = # of strings per second sorted (RETURNED) +*/ + +internal static class StringOrdinalComparer +{ + public static int Compare(String left, String right) + { + return String.CompareOrdinal(left, right); + } +} + +public class StringSort : StringSortStruct +{ + public override string Name() + { + return "STRING SORT"; + } + public override double Run() + { + string[][] arraybase; /* Base pointers of array */ + long accumtime; /* Accumulated time */ + double iterations; /* Iteration counter */ + + /* + ** See if we need to do self adjustment code. + */ + if (this.adjust == 0) + { + /* + ** Self-adjustment code. The system begins by sorting 1 + ** array. If it does that in no time, then two arrays + ** are built and sorted. This process continues until + ** enough arrays are built to handle the tolerance. + */ + this.numarrays = 1; + while (true) + { + /* + ** Allocate space for arrays + */ + arraybase = new string[this.numarrays][]; + for (int i = 0; i < this.numarrays; i++) + arraybase[i] = new string[this.arraysize]; + + /* + ** Do an iteration of the string sort. If the + ** elapsed time is less than or equal to the permitted + ** minimum, then allocate for more arrays and + ** try again. + */ + if (DoStringSortIteration(arraybase, + this.numarrays, + this.arraysize) > global.min_ticks) + break; /* We're ok...exit */ + + if (this.numarrays++ > global.NUMSTRARRAYS) + { + throw new Exception("CPU:SSORT -- NUMSTRARRAYS hit."); + } + } + } + else + { + /* + ** Allocate space for arrays + */ + arraybase = new string[this.numarrays][]; + for (int i = 0; i < this.numarrays; i++) + arraybase[i] = new string[this.arraysize]; + } + + /* + ** All's well if we get here. Repeatedly perform sorts until the + ** accumulated elapsed time is greater than # of seconds requested. + */ + accumtime = 0L; + iterations = (double)0.0; + + do + { + accumtime += DoStringSortIteration(arraybase, + this.numarrays, + this.arraysize); + iterations += (double)this.numarrays; + } while (ByteMark.TicksToSecs(accumtime) < this.request_secs); + + if (this.adjust == 0) + this.adjust = 1; + + /* + ** Clean up, calculate results, and go home. + ** Set flag to show we don't need to rerun adjustment code. + */ + + return (iterations * (double)this.numarrays / ByteMark.TicksToFracSecs(accumtime)); + } + + /************************** + ** DoStringSortIteration ** + *************************** + ** This routine executes one iteration of the string + ** sort benchmark. It returns the number of ticks + ** Note that this routine also builds the offset pointer + ** array. + */ + + private static int DoStringSortIteration(string[][] arraybase, int numarrays, int arraysize) + { + long elapsed; /* Elapsed ticks */ + int i; + + /* + ** Load up the array(s) with random numbers + */ + LoadStringArray(arraybase, arraysize, numarrays); + + /* + ** Start the stopwatch + */ + elapsed = ByteMark.StartStopwatch(); + + /* + ** Execute heapsorts + */ + for (i = 0; i < numarrays; i++) + { + // StrHeapSort(tempobase,tempsbase,nstrings,0L,nstrings-1); + StrHeapSort(arraybase[i], 0, arraysize - 1); + } + + /* + ** Record elapsed time + */ + elapsed = ByteMark.StopStopwatch(elapsed); + +#if DEBUG + for (i = 0; i < arraysize - 1; i++) + { + /* + ** Compare strings to check for proper + ** sort. + */ + if (StringOrdinalComparer.Compare(arraybase[0][i + 1], arraybase[0][i]) < 0) + { + Console.Write("Error in StringSort! arraybase[0][{0}]='{1}', arraybase[0][{2}]='{3}\n", i, arraybase[0][i], i + 1, arraybase[0][i + 1]); + break; + } + } +#endif + + return ((int)elapsed); + } + + + /******************** + ** LoadStringArray ** + ********************* + ** Initialize the string array with random strings of + ** varying sizes. + ** Returns the pointer to the offset pointer array. + ** Note that since we're creating a number of arrays, this + ** routine builds one array, then copies it into the others. + */ + private static void LoadStringArray(string[][] array, /* String array */ + int arraysize, /* Size of array */ + int numarrays) /* # of arrays */ + { + /* + ** Initialize random number generator. + */ + ByteMark.randnum(13); + + /* + ** Load up the first array with randoms + */ + + int i; + for (i = 0; i < arraysize; i++) + { + int length; + + length = 4 + ByteMark.abs_randwc(76); + array[0][i] = ""; + + /* + ** Fill up the string with random bytes. + */ + StringBuilder builder = new StringBuilder(length); + + int add; + for (add = 0; add < length; add++) + { + char myChar = (char)(ByteMark.abs_randwc(96) + 32); + builder.Append(myChar); + } + array[0][i] = builder.ToString(); + } + + /* + ** We now have initialized a single full array. If there + ** is more than one array, copy the original into the + ** others. + */ + int k; + for (k = 1; k < numarrays; k++) + { + for (i = 0; i < arraysize; i++) + { + array[k][i] = array[0][i]; + } + } + } + + + /**************** + ** strheapsort ** + ***************** + ** Pass this routine a pointer to an array of unsigned char. + ** The array is presumed to hold strings occupying at most + ** 80 bytes (counts a byte count). + ** This routine also needs a pointer to an array of offsets + ** which represent string locations in the array, and + ** an unsigned long indicating the number of strings + ** in the array. + */ + private static void StrHeapSort(string[] array, + int bottom, /* lower bound */ + int top) /* upper bound */ + { + int i; + string temp; + + /* + ** Build a heap in the array + */ + for (i = (top / 2); i > 0; --i) + strsift(array, i, top); + + /* + ** Repeatedly extract maximum from heap and place it at the + ** end of the array. When we get done, we'll have a sorted + ** array. + */ + for (i = top; i > 0; --i) + { + strsift(array, bottom, i); + temp = array[0]; + array[0] = array[i]; /* perform exchange */ + array[i] = temp; + } + return; + } + + + /************ + ** strsift ** + ************* + ** Pass this function: + ** 1) A pointer to an array of offset pointers + ** 2) A pointer to a string array + ** 3) The number of elements in the string array + ** 4) Offset within which to sort. + ** Sift the array within the bounds of those offsets (thus + ** building a heap). + */ + private static void strsift(string[] array, + int i, + int j) + { + int k; + string temp; + + while ((i + i) <= j) + { + k = i + i; + if (k < j) + { + //array[k].CompareTo(array[k+1]); + if (StringOrdinalComparer.Compare(array[k], array[k + 1]) < 0) + ++k; + } + + //if(array[i] global.min_ticks) + break; /* We're ok...exit */ + + this.numarrays++; + } + } + else + { /* + ** Allocate space for arrays + */ + arraybase = new int[this.numarrays][][]; + for (int i = 0; i < this.numarrays; i++) + { + arraybase[i] = new int[global.ASSIGNROWS][]; + for (int j = 0; j < global.ASSIGNROWS; j++) + arraybase[i][j] = new int[global.ASSIGNCOLS]; + } + } + + /* + ** All's well if we get here. Do the tests. + */ + accumtime = 0; + iterations = (double)0.0; + + do + { + accumtime += DoAssignIteration(arraybase, this.numarrays); + iterations += (double)1.0; + } while (ByteMark.TicksToSecs(accumtime) < this.request_secs); + + if (this.adjust == 0) + this.adjust = 1; + + return (iterations * (double)this.numarrays + / ByteMark.TicksToFracSecs(accumtime)); + } + + /********************** + ** DoAssignIteration ** + *********************** + ** This routine executes one iteration of the assignment test. + ** It returns the number of ticks elapsed in the iteration. + */ + private static long DoAssignIteration(int[][][] arraybase, int numarrays) + { + long elapsed; /* Elapsed ticks */ + int i; + + /* + ** Load up the arrays with a random table. + */ + LoadAssignArrayWithRand(arraybase, numarrays); + + /* + ** Start the stopwatch + */ + elapsed = ByteMark.StartStopwatch(); + + /* + ** Execute assignment algorithms + */ + for (i = 0; i < numarrays; i++) + { + Assignment(arraybase[i]); + } + + /* + ** Get elapsed time + */ + return (ByteMark.StopStopwatch(elapsed)); + } + + /**************************** + ** LoadAssignArrayWithRand ** + ***************************** + ** Load the assignment arrays with random numbers. All positive. + ** These numbers represent costs. + */ + private static void LoadAssignArrayWithRand(int[][][] arraybase, int numarrays) + { + int i; + + /* + ** Set up the first array. Then just copy it into the + ** others. + */ + LoadAssign(arraybase[0]); + if (numarrays > 1) + for (i = 1; i < numarrays; i++) + { + CopyToAssign(arraybase[0], arraybase[i]); + } + + return; + } + + /*************** + ** LoadAssign ** + **************** + ** The array given by arraybase is loaded with positive random + ** numbers. Elements in the array are capped at 5,000,000. + */ + private static void LoadAssign(int[][] arraybase) + { + short i, j; + + /* + ** Reset random number generator so things repeat. + */ + ByteMark.randnum(13); + + for (i = 0; i < global.ASSIGNROWS; i++) + for (j = 0; j < global.ASSIGNCOLS; j++) + arraybase[i][j] = ByteMark.abs_randwc(5000000); + return; + } + + /***************** + ** CopyToAssign ** + ****************** + ** Copy the contents of one array to another. This is called by + ** the routine that builds the initial array, and is used to copy + ** the contents of the intial array into all following arrays. + */ + private static void CopyToAssign(int[][] arrayfrom, + int[][] arrayto) + { + short i, j; + + for (i = 0; i < global.ASSIGNROWS; i++) + for (j = 0; j < global.ASSIGNCOLS; j++) + arrayto[i][j] = arrayfrom[i][j]; + + return; + } + + /*************** + ** Assignment ** + ***************/ + private static void Assignment(int[][] arraybase) + { + short[][] assignedtableau = new short[global.ASSIGNROWS][]; + for (int z = 0; z < global.ASSIGNROWS; z++) + assignedtableau[z] = new short[global.ASSIGNCOLS]; + + /* + ** First, calculate minimum costs + */ + calc_minimum_costs(arraybase); + + /* + ** Repeat following until the number of rows selected + ** equals the number of rows in the tableau. + */ + while (first_assignments(arraybase, assignedtableau) != global.ASSIGNROWS) + { + second_assignments(arraybase, assignedtableau); + } + + return; + } + + /*********************** + ** calc_minimum_costs ** + ************************ + ** Revise the tableau by calculating the minimum costs on a + ** row and column basis. These minima are subtracted from + ** their rows and columns, creating a new tableau. + */ + private static void calc_minimum_costs(int[][] tableau) + { + short i, j; /* Index variables */ + int currentmin; /* Current minimum */ + /* + ** Determine minimum costs on row basis. This is done by + ** subtracting -- on a row-per-row basis -- the minum value + ** for that row. + */ + for (i = 0; i < global.ASSIGNROWS; i++) + { + currentmin = global.MAXPOSLONG; /* Initialize minimum */ + for (j = 0; j < global.ASSIGNCOLS; j++) + if (tableau[i][j] < currentmin) + currentmin = tableau[i][j]; + + for (j = 0; j < global.ASSIGNCOLS; j++) + tableau[i][j] -= currentmin; + } + + /* + ** Determine minimum cost on a column basis. This works + ** just as above, only now we step through the array + ** column-wise + */ + for (j = 0; j < global.ASSIGNCOLS; j++) + { + currentmin = global.MAXPOSLONG; /* Initialize minimum */ + for (i = 0; i < global.ASSIGNROWS; i++) + if (tableau[i][j] < currentmin) + currentmin = tableau[i][j]; + + /* + ** Here, we'll take the trouble to see if the current + ** minimum is zero. This is likely worth it, since the + ** preceding loop will have created at least one zero in + ** each row. We can save ourselves a few iterations. + */ + if (currentmin != 0) + for (i = 0; i < global.ASSIGNROWS; i++) + tableau[i][j] -= currentmin; + } + + return; + } + + /********************** + ** first_assignments ** + *********************** + ** Do first assignments. + ** The assignedtableau[] array holds a set of values that + ** indicate the assignment of a value, or its elimination. + ** The values are: + ** 0 = Item is neither assigned nor eliminated. + ** 1 = Item is assigned + ** 2 = Item is eliminated + ** Returns the number of selections made. If this equals + ** the number of rows, then an optimum has been determined. + */ + private static int first_assignments(int[][] tableau, short[][] assignedtableau) + { + short i, j, k; /* Index variables */ + short numassigns; /* # of assignments */ + short totnumassigns; /* Total # of assignments */ + short numzeros; /* # of zeros in row */ + int selected = 0; /* Flag used to indicate selection */ + + /* + ** Clear the assignedtableau, setting all members to show that + ** no one is yet assigned, eliminated, or anything. + */ + for (i = 0; i < global.ASSIGNROWS; i++) + for (j = 0; j < global.ASSIGNCOLS; j++) + assignedtableau[i][j] = 0; + + totnumassigns = 0; + do + { + numassigns = 0; + /* + ** Step through rows. For each one that is not currently + ** assigned, see if the row has only one zero in it. If so, + ** mark that as an assigned row/col. Eliminate other zeros + ** in the same column. + */ + for (i = 0; i < global.ASSIGNROWS; i++) + { + numzeros = 0; + for (j = 0; j < global.ASSIGNCOLS; j++) + if (tableau[i][j] == 0) + if (assignedtableau[i][j] == 0) + { + numzeros++; + selected = j; + } + if (numzeros == 1) + { + numassigns++; + totnumassigns++; + assignedtableau[i][selected] = 1; + for (k = 0; k < global.ASSIGNROWS; k++) + if ((k != i) && + (tableau[k][selected] == 0)) + assignedtableau[k][selected] = 2; + } + } + /* + ** Step through columns, doing same as above. Now, be careful + ** of items in the other rows of a selected column. + */ + for (j = 0; j < global.ASSIGNCOLS; j++) + { + numzeros = 0; + for (i = 0; i < global.ASSIGNROWS; i++) + if (tableau[i][j] == 0) + if (assignedtableau[i][j] == 0) + { + numzeros++; + selected = i; + } + if (numzeros == 1) + { + numassigns++; + totnumassigns++; + assignedtableau[selected][j] = 1; + for (k = 0; k < global.ASSIGNCOLS; k++) + if ((k != j) && + (tableau[selected][k] == 0)) + assignedtableau[selected][k] = 2; + } + } + /* + ** Repeat until no more assignments to be made. + */ + } while (numassigns != 0); + + /* + ** See if we can leave at this point. + */ + if (totnumassigns == global.ASSIGNROWS) return (totnumassigns); + + /* + ** Now step through the array by row. If you find any unassigned + ** zeros, pick the first in the row. Eliminate all zeros from + ** that same row & column. This occurs if there are multiple optima... + ** possibly. + */ + for (i = 0; i < global.ASSIGNROWS; i++) + { + selected = -1; + for (j = 0; j < global.ASSIGNCOLS; j++) + if ((tableau[i][j] == 0) && + (assignedtableau[i][j] == 0)) + { + selected = j; + break; + } + if (selected != -1) + { + assignedtableau[i][selected] = 1; + totnumassigns++; + for (k = 0; k < global.ASSIGNCOLS; k++) + if ((k != selected) && + (tableau[i][k] == 0)) + assignedtableau[i][k] = 2; + for (k = 0; k < global.ASSIGNROWS; k++) + if ((k != i) && + (tableau[k][selected] == 0)) + assignedtableau[k][selected] = 2; + } + } + + return (totnumassigns); + } + + /*********************** + ** second_assignments ** + ************************ + ** This section of the algorithm creates the revised + ** tableau, and is difficult to explain. I suggest you + ** refer to the algorithm's source, mentioned in comments + ** toward the beginning of the program. + */ + private static void second_assignments(int[][] tableau, short[][] assignedtableau) + { + int i, j; /* Indexes */ + short[] linesrow = new short[global.ASSIGNROWS]; + short[] linescol = new short[global.ASSIGNCOLS]; + int smallest; /* Holds smallest value */ + short numassigns; /* Number of assignments */ + short newrows; /* New rows to be considered */ + /* + ** Clear the linesrow and linescol arrays. + */ + for (i = 0; i < global.ASSIGNROWS; i++) + linesrow[i] = 0; + for (i = 0; i < global.ASSIGNCOLS; i++) + linescol[i] = 0; + + /* + ** Scan rows, flag each row that has no assignment in it. + */ + for (i = 0; i < global.ASSIGNROWS; i++) + { + numassigns = 0; + for (j = 0; j < global.ASSIGNCOLS; j++) + if (assignedtableau[i][j] == 1) + { + numassigns++; + break; + } + if (numassigns == 0) linesrow[i] = 1; + } + + do + { + newrows = 0; + /* + ** For each row checked above, scan for any zeros. If found, + ** check the associated column. + */ + for (i = 0; i < global.ASSIGNROWS; i++) + { + if (linesrow[i] == 1) + for (j = 0; j < global.ASSIGNCOLS; j++) + if (tableau[i][j] == 0) + linescol[j] = 1; + } + + /* + ** Now scan checked columns. If any contain assigned zeros, check + ** the associated row. + */ + for (j = 0; j < global.ASSIGNCOLS; j++) + if (linescol[j] == 1) + for (i = 0; i < global.ASSIGNROWS; i++) + if ((assignedtableau[i][j] == 1) && + (linesrow[i] != 1)) + { + linesrow[i] = 1; + newrows++; + } + } while (newrows != 0); + + /* + ** linesrow[n]==0 indicate rows covered by imaginary line + ** linescol[n]==1 indicate cols covered by imaginary line + ** For all cells not covered by imaginary lines, determine smallest + ** value. + */ + smallest = global.MAXPOSLONG; + for (i = 0; i < global.ASSIGNROWS; i++) + if (linesrow[i] != 0) + for (j = 0; j < global.ASSIGNCOLS; j++) + if (linescol[j] != 1) + if (tableau[i][j] < smallest) + smallest = tableau[i][j]; + + /* + ** Subtract smallest from all cells in the above set. + */ + for (i = 0; i < global.ASSIGNROWS; i++) + if (linesrow[i] != 0) + for (j = 0; j < global.ASSIGNCOLS; j++) + if (linescol[j] != 1) + tableau[i][j] -= smallest; + + /* + ** Add smallest to all cells covered by two lines. + */ + for (i = 0; i < global.ASSIGNROWS; i++) + if (linesrow[i] == 0) + for (j = 0; j < global.ASSIGNCOLS; j++) + if (linescol[j] == 1) + tableau[i][j] += smallest; + + return; + } +} diff --git a/tests/src/JIT/Performance/CodeQuality/Bytemark/assign_rect.cs b/tests/src/JIT/Performance/CodeQuality/Bytemark/assign_rect.cs new file mode 100644 index 0000000000..c93d033af8 --- /dev/null +++ b/tests/src/JIT/Performance/CodeQuality/Bytemark/assign_rect.cs @@ -0,0 +1,542 @@ +/* +** Copyright (c) Microsoft. All rights reserved. +** Licensed under the MIT license. +** See LICENSE file in the project root for full license information. +** +** This program was translated to C# and adapted for xunit-performance. +** New variants of several tests were added to compare class versus +** struct and to compare jagged arrays vs multi-dimensional arrays. +*/ + +/* +** BYTEmark (tm) +** BYTE Magazine's Native Mode benchmarks +** Rick Grehan, BYTE Magazine +** +** Create: +** Revision: 3/95 +** +** DISCLAIMER +** The source, executable, and documentation files that comprise +** the BYTEmark benchmarks are made available on an "as is" basis. +** This means that we at BYTE Magazine have made every reasonable +** effort to verify that the there are no errors in the source and +** executable code. We cannot, however, guarantee that the programs +** are error-free. Consequently, McGraw-HIll and BYTE Magazine make +** no claims in regard to the fitness of the source code, executable +** code, and documentation of the BYTEmark. +** +** Furthermore, BYTE Magazine, McGraw-Hill, and all employees +** of McGraw-Hill cannot be held responsible for any damages resulting +** from the use of this code or the results obtained from using +** this code. +*/ + +/************* +** DoAssign ** +************** +** Perform an assignment algorithm. +** The algorithm was adapted from the step by step guide found +** in "Quantitative Decision Making for Business" (Gordon, +** Pressman, and Cohn; Prentice-Hall) +** +** +** NOTES: +** 1. Even though the algorithm distinguishes between +** ASSIGNROWS and ASSIGNCOLS, as though the two might +** be different, it does presume a square matrix. +** I.E., ASSIGNROWS and ASSIGNCOLS must be the same. +** This makes for some algorithmically-correct but +** probably non-optimal constructs. +** +*/ + +using System; + +public class AssignRect : AssignStruct +{ + public override string Name() + { + return "ASSIGNMENT(rectangle)"; + } + + public override double Run() + { + int[][,] arraybase; + long accumtime; + double iterations; + + /* + ** See if we need to do self adjustment code. + */ + if (this.adjust == 0) + { + /* + ** Self-adjustment code. The system begins by working on 1 + ** array. If it does that in no time, then two arrays + ** are built. This process continues until + ** enough arrays are built to handle the tolerance. + */ + this.numarrays = 1; + while (true) + { + /* + ** Allocate space for arrays + */ + arraybase = new int[this.numarrays][,]; + for (int i = 0; i < this.numarrays; i++) + arraybase[i] = new int[global.ASSIGNROWS, global.ASSIGNCOLS]; + + /* + ** Do an iteration of the assignment alg. If the + ** elapsed time is less than or equal to the permitted + ** minimum, then allocate for more arrays and + ** try again. + */ + if (DoAssignIteration(arraybase, + this.numarrays) > global.min_ticks) + break; /* We're ok...exit */ + + this.numarrays++; + } + } + else + { /* + ** Allocate space for arrays + */ + arraybase = new int[this.numarrays][,]; + for (int i = 0; i < this.numarrays; i++) + arraybase[i] = new int[global.ASSIGNROWS, global.ASSIGNCOLS]; + } + + + /* + ** All's well if we get here. Do the tests. + */ + accumtime = 0; + iterations = (double)0.0; + + do + { + accumtime += DoAssignIteration(arraybase, this.numarrays); + iterations += (double)1.0; + } while (ByteMark.TicksToSecs(accumtime) < this.request_secs); + + if (this.adjust == 0) + this.adjust = 1; + + + + return (iterations * (double)this.numarrays + / ByteMark.TicksToFracSecs(accumtime)); + } + + /********************** + ** DoAssignIteration ** + *********************** + ** This routine executes one iteration of the assignment test. + ** It returns the number of ticks elapsed in the iteration. + */ + private static long DoAssignIteration(int[][,] arraybase, int numarrays) + { + long elapsed; /* Elapsed ticks */ + int i; + + /* + ** Load up the arrays with a random table. + */ + LoadAssignArrayWithRand(arraybase, numarrays); + + /* + ** Start the stopwatch + */ + elapsed = ByteMark.StartStopwatch(); + + /* + ** Execute assignment algorithms + */ + for (i = 0; i < numarrays; i++) + { + Assignment(arraybase[i]); + } + + /* + ** Get elapsed time + */ + return (ByteMark.StopStopwatch(elapsed)); + } + + /**************************** + ** LoadAssignArrayWithRand ** + ***************************** + ** Load the assignment arrays with random numbers. All positive. + ** These numbers represent costs. + */ + private static void LoadAssignArrayWithRand(int[][,] arraybase, int numarrays) + { + int i; + + /* + ** Set up the first array. Then just copy it into the + ** others. + */ + LoadAssign(arraybase[0]); + if (numarrays > 1) + for (i = 1; i < numarrays; i++) + { + CopyToAssign(arraybase[0], arraybase[i]); + } + + return; + } + + /*************** + ** LoadAssign ** + **************** + ** The array given by arraybase is loaded with positive random + ** numbers. Elements in the array are capped at 5,000,000. + */ + private static void LoadAssign(int[,] arraybase) + { + short i, j; + + /* + ** Reset random number generator so things repeat. + */ + ByteMark.randnum(13); + + for (i = 0; i < global.ASSIGNROWS; i++) + for (j = 0; j < global.ASSIGNROWS; j++) + arraybase[i, j] = ByteMark.abs_randwc(5000000); + return; + } + + /***************** + ** CopyToAssign ** + ****************** + ** Copy the contents of one array to another. This is called by + ** the routine that builds the initial array, and is used to copy + ** the contents of the intial array into all following arrays. + */ + private static void CopyToAssign(int[,] arrayfrom, + int[,] arrayto) + { + short i, j; + + for (i = 0; i < global.ASSIGNROWS; i++) + for (j = 0; j < global.ASSIGNCOLS; j++) + arrayto[i, j] = arrayfrom[i, j]; + + return; + } + + /*************** + ** Assignment ** + ***************/ + private static void Assignment(int[,] arraybase) + { + short[,] assignedtableau = new short[global.ASSIGNROWS, global.ASSIGNCOLS]; + + /* + ** First, calculate minimum costs + */ + calc_minimum_costs(arraybase); + + /* + ** Repeat following until the number of rows selected + ** equals the number of rows in the tableau. + */ + while (first_assignments(arraybase, assignedtableau) != global.ASSIGNROWS) + { + second_assignments(arraybase, assignedtableau); + } + + return; + } + + /*********************** + ** calc_minimum_costs ** + ************************ + ** Revise the tableau by calculating the minimum costs on a + ** row and column basis. These minima are subtracted from + ** their rows and columns, creating a new tableau. + */ + private static void calc_minimum_costs(int[,] tableau) + { + short i, j; /* Index variables */ + int currentmin; /* Current minimum */ + /* + ** Determine minimum costs on row basis. This is done by + ** subtracting -- on a row-per-row basis -- the minum value + ** for that row. + */ + for (i = 0; i < global.ASSIGNROWS; i++) + { + currentmin = global.MAXPOSLONG; /* Initialize minimum */ + for (j = 0; j < global.ASSIGNCOLS; j++) + if (tableau[i, j] < currentmin) + currentmin = tableau[i, j]; + + for (j = 0; j < global.ASSIGNCOLS; j++) + tableau[i, j] -= currentmin; + } + + /* + ** Determine minimum cost on a column basis. This works + ** just as above, only now we step through the array + ** column-wise + */ + for (j = 0; j < global.ASSIGNCOLS; j++) + { + currentmin = global.MAXPOSLONG; /* Initialize minimum */ + for (i = 0; i < global.ASSIGNROWS; i++) + if (tableau[i, j] < currentmin) + currentmin = tableau[i, j]; + + /* + ** Here, we'll take the trouble to see if the current + ** minimum is zero. This is likely worth it, since the + ** preceding loop will have created at least one zero in + ** each row. We can save ourselves a few iterations. + */ + if (currentmin != 0) + for (i = 0; i < global.ASSIGNROWS; i++) + tableau[i, j] -= currentmin; + } + + return; + } + + /********************** + ** first_assignments ** + *********************** + ** Do first assignments. + ** The assignedtableau[] array holds a set of values that + ** indicate the assignment of a value, or its elimination. + ** The values are: + ** 0 = Item is neither assigned nor eliminated. + ** 1 = Item is assigned + ** 2 = Item is eliminated + ** Returns the number of selections made. If this equals + ** the number of rows, then an optimum has been determined. + */ + private static int first_assignments(int[,] tableau, short[,] assignedtableau) + { + short i, j, k; /* Index variables */ + short numassigns; /* # of assignments */ + short totnumassigns; /* Total # of assignments */ + short numzeros; /* # of zeros in row */ + int selected = 0; /* Flag used to indicate selection */ + + /* + ** Clear the assignedtableau, setting all members to show that + ** no one is yet assigned, eliminated, or anything. + */ + for (i = 0; i < global.ASSIGNROWS; i++) + for (j = 0; j < global.ASSIGNCOLS; j++) + assignedtableau[i, j] = 0; + + totnumassigns = 0; + do + { + numassigns = 0; + /* + ** Step through rows. For each one that is not currently + ** assigned, see if the row has only one zero in it. If so, + ** mark that as an assigned row/col. Eliminate other zeros + ** in the same column. + */ + for (i = 0; i < global.ASSIGNROWS; i++) + { + numzeros = 0; + for (j = 0; j < global.ASSIGNCOLS; j++) + if (tableau[i, j] == 0) + if (assignedtableau[i, j] == 0) + { + numzeros++; + selected = j; + } + if (numzeros == 1) + { + numassigns++; + totnumassigns++; + assignedtableau[i, selected] = 1; + for (k = 0; k < global.ASSIGNROWS; k++) + if ((k != i) && + (tableau[k, selected] == 0)) + assignedtableau[k, selected] = 2; + } + } + /* + ** Step through columns, doing same as above. Now, be careful + ** of items in the other rows of a selected column. + */ + for (j = 0; j < global.ASSIGNCOLS; j++) + { + numzeros = 0; + for (i = 0; i < global.ASSIGNROWS; i++) + if (tableau[i, j] == 0) + if (assignedtableau[i, j] == 0) + { + numzeros++; + selected = i; + } + if (numzeros == 1) + { + numassigns++; + totnumassigns++; + assignedtableau[selected, j] = 1; + for (k = 0; k < global.ASSIGNCOLS; k++) + if ((k != j) && + (tableau[selected, k] == 0)) + assignedtableau[selected, k] = 2; + } + } + /* + ** Repeat until no more assignments to be made. + */ + } while (numassigns != 0); + + /* + ** See if we can leave at this point. + */ + if (totnumassigns == global.ASSIGNROWS) return (totnumassigns); + + /* + ** Now step through the array by row. If you find any unassigned + ** zeros, pick the first in the row. Eliminate all zeros from + ** that same row & column. This occurs if there are multiple optima... + ** possibly. + */ + for (i = 0; i < global.ASSIGNROWS; i++) + { + selected = -1; + for (j = 0; j < global.ASSIGNCOLS; j++) + if ((tableau[i, j] == 0) && + (assignedtableau[i, j] == 0)) + { + selected = j; + break; + } + if (selected != -1) + { + assignedtableau[i, selected] = 1; + totnumassigns++; + for (k = 0; k < global.ASSIGNCOLS; k++) + if ((k != selected) && + (tableau[i, k] == 0)) + assignedtableau[i, k] = 2; + for (k = 0; k < global.ASSIGNROWS; k++) + if ((k != i) && + (tableau[k, selected] == 0)) + assignedtableau[k, selected] = 2; + } + } + + return (totnumassigns); + } + + /*********************** + ** second_assignments ** + ************************ + ** This section of the algorithm creates the revised + ** tableau, and is difficult to explain. I suggest you + ** refer to the algorithm's source, mentioned in comments + ** toward the beginning of the program. + */ + private static void second_assignments(int[,] tableau, short[,] assignedtableau) + { + int i, j; /* Indexes */ + short[] linesrow = new short[global.ASSIGNROWS]; + short[] linescol = new short[global.ASSIGNCOLS]; + int smallest; /* Holds smallest value */ + short numassigns; /* Number of assignments */ + short newrows; /* New rows to be considered */ + /* + ** Clear the linesrow and linescol arrays. + */ + for (i = 0; i < global.ASSIGNROWS; i++) + linesrow[i] = 0; + for (i = 0; i < global.ASSIGNCOLS; i++) + linescol[i] = 0; + + /* + ** Scan rows, flag each row that has no assignment in it. + */ + for (i = 0; i < global.ASSIGNROWS; i++) + { + numassigns = 0; + for (j = 0; j < global.ASSIGNCOLS; j++) + if (assignedtableau[i, j] == 1) + { + numassigns++; + break; + } + if (numassigns == 0) linesrow[i] = 1; + } + + do + { + newrows = 0; + /* + ** For each row checked above, scan for any zeros. If found, + ** check the associated column. + */ + for (i = 0; i < global.ASSIGNROWS; i++) + { + if (linesrow[i] == 1) + for (j = 0; j < global.ASSIGNCOLS; j++) + if (tableau[i, j] == 0) + linescol[j] = 1; + } + + /* + ** Now scan checked columns. If any contain assigned zeros, check + ** the associated row. + */ + for (j = 0; j < global.ASSIGNCOLS; j++) + if (linescol[j] == 1) + for (i = 0; i < global.ASSIGNROWS; i++) + if ((assignedtableau[i, j] == 1) && + (linesrow[i] != 1)) + { + linesrow[i] = 1; + newrows++; + } + } while (newrows != 0); + + /* + ** linesrow[n]==0 indicate rows covered by imaginary line + ** linescol[n]==1 indicate cols covered by imaginary line + ** For all cells not covered by imaginary lines, determine smallest + ** value. + */ + smallest = global.MAXPOSLONG; + for (i = 0; i < global.ASSIGNROWS; i++) + if (linesrow[i] != 0) + for (j = 0; j < global.ASSIGNCOLS; j++) + if (linescol[j] != 1) + if (tableau[i, j] < smallest) + smallest = tableau[i, j]; + + /* + ** Subtract smallest from all cells in the above set. + */ + for (i = 0; i < global.ASSIGNROWS; i++) + if (linesrow[i] != 0) + for (j = 0; j < global.ASSIGNCOLS; j++) + if (linescol[j] != 1) + tableau[i, j] -= smallest; + + /* + ** Add smallest to all cells covered by two lines. + */ + for (i = 0; i < global.ASSIGNROWS; i++) + if (linesrow[i] == 0) + for (j = 0; j < global.ASSIGNCOLS; j++) + if (linescol[j] == 1) + tableau[i, j] += smallest; + + return; + } +} diff --git a/tests/src/JIT/Performance/CodeQuality/Bytemark/bitops.cs b/tests/src/JIT/Performance/CodeQuality/Bytemark/bitops.cs new file mode 100644 index 0000000000..c3d4653629 --- /dev/null +++ b/tests/src/JIT/Performance/CodeQuality/Bytemark/bitops.cs @@ -0,0 +1,276 @@ +/* +** Copyright (c) Microsoft. All rights reserved. +** Licensed under the MIT license. +** See LICENSE file in the project root for full license information. +** +** This program was translated to C# and adapted for xunit-performance. +** New variants of several tests were added to compare class versus +** struct and to compare jagged arrays vs multi-dimensional arrays. +*/ + +/* +** BYTEmark (tm) +** BYTE Magazine's Native Mode benchmarks +** Rick Grehan, BYTE Magazine +** +** Create: +** Revision: 3/95 +** +** DISCLAIMER +** The source, executable, and documentation files that comprise +** the BYTEmark benchmarks are made available on an "as is" basis. +** This means that we at BYTE Magazine have made every reasonable +** effort to verify that the there are no errors in the source and +** executable code. We cannot, however, guarantee that the programs +** are error-free. Consequently, McGraw-HIll and BYTE Magazine make +** no claims in regard to the fitness of the source code, executable +** code, and documentation of the BYTEmark. +** +** Furthermore, BYTE Magazine, McGraw-Hill, and all employees +** of McGraw-Hill cannot be held responsible for any damages resulting +** from the use of this code or the results obtained from using +** this code. +*/ + +/************************ +** BITFIELD OPERATIONS ** +*************************/ + +/************* +** DoBitops ** +************** +** Perform the bit operations test portion of the CPU +** benchmark. Returns the iterations per second. +*/ + +using System; + +public class BitOps : BitOpStruct +{ + public override string Name() + { + return "BITFIELD"; + } + + public override double Run() + { + int[] bitarraybase; /* Base of bitmap array */ + int[] bitoparraybase; /* Base of bitmap operations array */ + int nbitops = 0; /* # of bitfield operations */ + long accumtime; /* Accumulated time in ticks */ + double iterations; /* # of iterations */ + + /* + ** See if we need to run adjustment code. + */ + if (this.adjust == 0) + { + bitarraybase = new int[this.bitfieldarraysize]; + + /* + ** Initialize bitfield operations array to [2,30] elements + */ + this.bitoparraysize = 30; + + while (true) + { + /* + ** Allocate space for operations array + */ + bitoparraybase = new int[this.bitoparraysize * 2]; + + /* + ** Do an iteration of the bitmap test. If the + ** elapsed time is less than or equal to the permitted + ** minimum, then de-allocate the array, reallocate a + ** larger version, and try again. + */ + if (DoBitfieldIteration(bitarraybase, + bitoparraybase, + this.bitoparraysize, + ref nbitops) > global.min_ticks) + break; /* We're ok...exit */ + + this.bitoparraysize += 100; + } + } + else + { + /* + ** Don't need to do self adjustment, just allocate + ** the array space. + */ + bitarraybase = new int[this.bitfieldarraysize]; + bitoparraybase = new int[this.bitoparraysize * 2]; + } + + /* + ** All's well if we get here. Repeatedly perform bitops until the + ** accumulated elapsed time is greater than # of seconds requested. + */ + accumtime = 0; + iterations = (double)0.0; + + do + { + accumtime += DoBitfieldIteration(bitarraybase, + bitoparraybase, + this.bitoparraysize, + ref nbitops); + iterations += (double)nbitops; + } while (ByteMark.TicksToSecs(accumtime) < this.request_secs); + + /* + ** Clean up, calculate results, and go home. + ** Also, set adjustment flag to show that we don't have + ** to do self adjusting in the future. + */ + if (this.adjust == 0) + this.adjust = 1; + + return (iterations / ByteMark.TicksToFracSecs(accumtime)); + } + + /************************ + ** DoBitfieldIteration ** + ************************* + ** Perform a single iteration of the bitfield benchmark. + ** Return the # of ticks accumulated by the operation. + */ + private static long DoBitfieldIteration(int[] bitarraybase, + int[] bitoparraybase, + int bitoparraysize, + ref int nbitops) + { + int i; /* Index */ + int bitoffset; /* Offset into bitmap */ + long elapsed; /* Time to execute */ + + /* + ** Clear # bitops counter + */ + nbitops = 0; + + /* + ** Construct a set of bitmap offsets and run lengths. + ** The offset can be any random number from 0 to the + ** size of the bitmap (in bits). The run length can + ** be any random number from 1 to the number of bits + ** between the offset and the end of the bitmap. + ** Note that the bitmap has 8192 * 32 bits in it. + ** (262,144 bits) + */ + for (i = 0; i < bitoparraysize; i++) + { + /* First item is offset */ + bitoparraybase[i + i] = bitoffset = ByteMark.abs_randwc(262140); + + /* Next item is run length */ + nbitops += bitoparraybase[i + i + 1] = ByteMark.abs_randwc(262140 - bitoffset); + } + + /* + ** Array of offset and lengths built...do an iteration of + ** the test. + ** Start the stopwatch. + */ + elapsed = ByteMark.StartStopwatch(); + + /* + ** Loop through array off offset/run length pairs. + ** Execute operation based on modulus of index. + */ + for (i = 0; i < bitoparraysize; i++) + { + switch (i % 3) + { + case 0: /* Set run of bits */ + ToggleBitRun(bitarraybase, + bitoparraybase[i + i], + bitoparraybase[i + i + 1], + 1); + break; + + case 1: /* Clear run of bits */ + ToggleBitRun(bitarraybase, + bitoparraybase[i + i], + bitoparraybase[i + i + 1], + 0); + break; + + case 2: /* Complement run of bits */ + FlipBitRun(bitarraybase, + bitoparraybase[i + i], + bitoparraybase[i + i + 1]); + break; + } + } + + /* + ** Return elapsed time + */ + return (ByteMark.StopStopwatch(elapsed)); + } + + + /***************************** + ** ToggleBitRun * + ****************************** + ** Set or clear a run of nbits starting at + ** bit_addr in bitmap. + */ + private static void ToggleBitRun(int[] bitmap, /* Bitmap */ + int bit_addr, /* Address of bits to set */ + int nbits, /* # of bits to set/clr */ + int val) /* 1 or 0 */ + { + int bindex; /* Index into array */ + int bitnumb; /* Bit number */ + + while (nbits-- > 0) + { +#if LONG64 + bindex=bit_addr>>>6; /* Index is number /64 */ + bindex=bit_addr % 64; /* Bit number in word */ +#else + bindex = (int)((uint)bit_addr) >> 5; /* Index is number /32 */ + bitnumb = bit_addr % 32; /* bit number in word */ +#endif + + if (val != 0) + bitmap[bindex] |= (1 << bitnumb); + else + bitmap[bindex] &= ~(1 << bitnumb); + bit_addr++; + } + return; + } + + /*************** + ** FlipBitRun ** + **************** + ** Complements a run of bits. + */ + private static void FlipBitRun(int[] bitmap, /* Bit map */ + int bit_addr, /* Bit address */ + int nbits) /* # of bits to flip */ + { + int bindex; /* Index into array */ + int bitnumb; /* Bit number */ + + while (nbits-- > 0) + { +#if LONG64 + bindex=bit_addr>>6; /* Index is number /64 */ + bitnumb=bit_addr % 32; /* Bit number in longword */ +#else + bindex = bit_addr >> 5; /* Index is number /32 */ + bitnumb = bit_addr % 32; /* Bit number in longword */ +#endif + bitmap[bindex] ^= (1 << bitnumb); + bit_addr++; + } + + return; + } +} diff --git a/tests/src/JIT/Performance/CodeQuality/Bytemark/commands/assignjagged.dat b/tests/src/JIT/Performance/CodeQuality/Bytemark/commands/assignjagged.dat new file mode 100644 index 0000000000..52537e4558 --- /dev/null +++ b/tests/src/JIT/Performance/CodeQuality/Bytemark/commands/assignjagged.dat @@ -0,0 +1,2 @@ +CUSTOMRUN=T +DOASSIGNJAGGED=T diff --git a/tests/src/JIT/Performance/CodeQuality/Bytemark/commands/assignrect.dat b/tests/src/JIT/Performance/CodeQuality/Bytemark/commands/assignrect.dat new file mode 100644 index 0000000000..67ef988420 --- /dev/null +++ b/tests/src/JIT/Performance/CodeQuality/Bytemark/commands/assignrect.dat @@ -0,0 +1,2 @@ +CUSTOMRUN=T +DOASSIGNRECT=T diff --git a/tests/src/JIT/Performance/CodeQuality/Bytemark/commands/bitfield.dat b/tests/src/JIT/Performance/CodeQuality/Bytemark/commands/bitfield.dat new file mode 100644 index 0000000000..1577f86bfb --- /dev/null +++ b/tests/src/JIT/Performance/CodeQuality/Bytemark/commands/bitfield.dat @@ -0,0 +1,2 @@ +CUSTOMRUN=T +DOBITFIELD=T diff --git a/tests/src/JIT/Performance/CodeQuality/Bytemark/commands/emfclass.dat b/tests/src/JIT/Performance/CodeQuality/Bytemark/commands/emfclass.dat new file mode 100644 index 0000000000..421f4a44a5 --- /dev/null +++ b/tests/src/JIT/Performance/CodeQuality/Bytemark/commands/emfclass.dat @@ -0,0 +1,2 @@ +CUSTOMRUN=T +DOEMFCLASS=T diff --git a/tests/src/JIT/Performance/CodeQuality/Bytemark/commands/emfstruct.dat b/tests/src/JIT/Performance/CodeQuality/Bytemark/commands/emfstruct.dat new file mode 100644 index 0000000000..aca1caf27d --- /dev/null +++ b/tests/src/JIT/Performance/CodeQuality/Bytemark/commands/emfstruct.dat @@ -0,0 +1,2 @@ +CUSTOMRUN=T +DOEMFSTRUCT=T diff --git a/tests/src/JIT/Performance/CodeQuality/Bytemark/commands/four.dat b/tests/src/JIT/Performance/CodeQuality/Bytemark/commands/four.dat new file mode 100644 index 0000000000..6c7a13376b --- /dev/null +++ b/tests/src/JIT/Performance/CodeQuality/Bytemark/commands/four.dat @@ -0,0 +1,2 @@ +CUSTOMRUN=T +DOFOUR=T diff --git a/tests/src/JIT/Performance/CodeQuality/Bytemark/commands/huff.dat b/tests/src/JIT/Performance/CodeQuality/Bytemark/commands/huff.dat new file mode 100644 index 0000000000..d498cc008f --- /dev/null +++ b/tests/src/JIT/Performance/CodeQuality/Bytemark/commands/huff.dat @@ -0,0 +1,2 @@ +CUSTOMRUN=T +DOHUFF=T diff --git a/tests/src/JIT/Performance/CodeQuality/Bytemark/commands/idea.dat b/tests/src/JIT/Performance/CodeQuality/Bytemark/commands/idea.dat new file mode 100644 index 0000000000..f19f9ee28b --- /dev/null +++ b/tests/src/JIT/Performance/CodeQuality/Bytemark/commands/idea.dat @@ -0,0 +1,2 @@ +CUSTOMRUN=T +DOIDEA=T diff --git a/tests/src/JIT/Performance/CodeQuality/Bytemark/commands/lu.dat b/tests/src/JIT/Performance/CodeQuality/Bytemark/commands/lu.dat new file mode 100644 index 0000000000..73d3b730c5 --- /dev/null +++ b/tests/src/JIT/Performance/CodeQuality/Bytemark/commands/lu.dat @@ -0,0 +1,2 @@ +CUSTOMRUN=T +DOLU=T diff --git a/tests/src/JIT/Performance/CodeQuality/Bytemark/commands/nnetjagged.dat b/tests/src/JIT/Performance/CodeQuality/Bytemark/commands/nnetjagged.dat new file mode 100644 index 0000000000..d7c2baab28 --- /dev/null +++ b/tests/src/JIT/Performance/CodeQuality/Bytemark/commands/nnetjagged.dat @@ -0,0 +1,2 @@ +CUSTOMRUN=T +DONNETJAGGED=T diff --git a/tests/src/JIT/Performance/CodeQuality/Bytemark/commands/nnetrect.dat b/tests/src/JIT/Performance/CodeQuality/Bytemark/commands/nnetrect.dat new file mode 100644 index 0000000000..5c74684b23 --- /dev/null +++ b/tests/src/JIT/Performance/CodeQuality/Bytemark/commands/nnetrect.dat @@ -0,0 +1,2 @@ +CUSTOMRUN=T +DONNETRECT=T diff --git a/tests/src/JIT/Performance/CodeQuality/Bytemark/commands/numsortjagged.dat b/tests/src/JIT/Performance/CodeQuality/Bytemark/commands/numsortjagged.dat new file mode 100644 index 0000000000..3ce1748daf --- /dev/null +++ b/tests/src/JIT/Performance/CodeQuality/Bytemark/commands/numsortjagged.dat @@ -0,0 +1,2 @@ +CUSTOMRUN=T +DONUMSORTJAGGED=T diff --git a/tests/src/JIT/Performance/CodeQuality/Bytemark/commands/numsortrect.dat b/tests/src/JIT/Performance/CodeQuality/Bytemark/commands/numsortrect.dat new file mode 100644 index 0000000000..035f90c9f1 --- /dev/null +++ b/tests/src/JIT/Performance/CodeQuality/Bytemark/commands/numsortrect.dat @@ -0,0 +1,2 @@ +CUSTOMRUN=T +DONUMSORTRECT=T diff --git a/tests/src/JIT/Performance/CodeQuality/Bytemark/commands/stringsort.dat b/tests/src/JIT/Performance/CodeQuality/Bytemark/commands/stringsort.dat new file mode 100644 index 0000000000..74b0a2fc2f --- /dev/null +++ b/tests/src/JIT/Performance/CodeQuality/Bytemark/commands/stringsort.dat @@ -0,0 +1,2 @@ +CUSTOMRUN=T +DOSTRINGSORT=T diff --git a/tests/src/JIT/Performance/CodeQuality/Bytemark/emfloat.cs b/tests/src/JIT/Performance/CodeQuality/Bytemark/emfloat.cs new file mode 100644 index 0000000000..f03f25cfd4 --- /dev/null +++ b/tests/src/JIT/Performance/CodeQuality/Bytemark/emfloat.cs @@ -0,0 +1,1574 @@ +/* +** Copyright (c) Microsoft. All rights reserved. +** Licensed under the MIT license. +** See LICENSE file in the project root for full license information. +** +** This program was translated to C# and adapted for xunit-performance. +** New variants of several tests were added to compare class versus +** struct and to compare jagged arrays vs multi-dimensional arrays. +*/ + +/* +** BYTEmark (tm) +** BYTE Magazine's Native Mode benchmarks +** Rick Grehan, BYTE Magazine +** +** Create: +** Revision: 3/95 +** +** DISCLAIMER +** The source, executable, and documentation files that comprise +** the BYTEmark benchmarks are made available on an "as is" basis. +** This means that we at BYTE Magazine have made every reasonable +** effort to verify that the there are no errors in the source and +** executable code. We cannot, however, guarantee that the programs +** are error-free. Consequently, McGraw-HIll and BYTE Magazine make +** no claims in regard to the fitness of the source code, executable +** code, and documentation of the BYTEmark. +** +** Furthermore, BYTE Magazine, McGraw-Hill, and all employees +** of McGraw-Hill cannot be held responsible for any damages resulting +** from the use of this code or the results obtained from using +** this code. +*/ + +using System; + +/* +** DEFINES +*/ +public class EMFloat : EmFloatStruct +{ + public override string Name() + { + return "FP EMULATION(struct)"; + } + + private const int MAX_EXP = 32767; + private const int MIN_EXP = (-32767); + + private enum IFPF : byte + { + IFPF_IS_ZERO = 0, + IFPF_IS_SUBNORMAL = 1, + IFPF_IS_NORMAL = 2, + IFPF_IS_INFINITY = 3, + IFPF_IS_NAN = 4, + IFPF_TYPE_COUNT = 5, + }; + + private enum STATE + { + ZERO_ZERO = 0, + ZERO_SUBNORMAL = 1, + ZERO_NORMAL = 2, + ZERO_INFINITY = 3, + ZERO_NAN = 4, + + SUBNORMAL_ZERO = 5, + SUBNORMAL_SUBNORMAL = 6, + SUBNORMAL_NORMAL = 7, + SUBNORMAL_INFINITY = 8, + SUBNORMAL_NAN = 9, + + NORMAL_ZERO = 10, + NORMAL_SUBNORMAL = 11, + NORMAL_NORMAL = 12, + NORMAL_INFINITY = 13, + NORMAL_NAN = 14, + + INFINITY_ZERO = 15, + INFINITY_SUBNORMAL = 16, + INFINITY_NORMAL = 17, + INFINITY_INFINITY = 18, + INFINITY_NAN = 19, + + NAN_ZERO = 20, + NAN_SUBNORMAL = 21, + NAN_NORMAL = 22, + NAN_INFINITY = 23, + NAN_NAN = 24, + }; + + private enum OPERAND + { + OPERAND_ZERO = 0, + OPERAND_SUBNORMAL = 1, + OPERAND_NORMAL = 2, + OPERAND_INFINITY = 3, + OPERAND_NAN = 4, + }; + + /* + ** Following was already defined in NMGLOBAL.H + ** + */ + private const int INTERNAL_FPF_PRECISION = 4; + + /* + ** TYPEDEFS + */ + + private struct InternalFPF + { + public InternalFPF(int len) + { + type = IFPF.IFPF_IS_ZERO; sign = (byte)0; + exp = (short)0; mantissa = new char[len]; + } + + public IFPF type; /* Indicates, NORMAL, SUBNORMAL, etc. */ + public byte sign; /* Mantissa sign */ + public short exp; /* Signed exponent...no bias */ + public char[] mantissa; // [INTERNAL_FPF_PRECISION] + }; + + + /* + ** emfloat.c + ** Source for emulated floating-point routines. + ** BYTEmark (tm) + ** BYTE's Native Mode Benchmarks + ** Rick Grehan, BYTE Magazine. + ** + ** Created: + ** Last update: 3/95 + ** + ** DISCLAIMER + ** The source, executable, and documentation files that comprise + ** the BYTEmark benchmarks are made available on an "as is" basis. + ** This means that we at BYTE Magazine have made every reasonable + ** effort to verify that the there are no errors in the source and + ** executable code. We cannot, however, guarantee that the programs + ** are error-free. Consequently, McGraw-HIll and BYTE Magazine make + ** no claims in regard to the fitness of the source code, executable + ** code, and documentation of the BYTEmark. + ** Furthermore, BYTE Magazine, McGraw-Hill, and all employees + ** of McGraw-Hill cannot be held responsible for any damages resulting + ** from the use of this code or the results obtained from using + ** this code. + */ + + /* + ** Floating-point emulator. + ** These routines are only "sort of" IEEE-compliant. All work is + ** done using an internal representation. Also, the routines do + ** not check for many of the exceptions that might occur. + ** Still, the external formats produced are IEEE-compatible, + ** with the restriction that they presume a low-endian machine + ** (though the endianism will not effect the performance). + ** + ** Some code here was based on work done by Steve Snelgrove of + ** Orem, UT. Other code comes from routines presented in + ** the long-ago book: "Microprocessor Programming for + ** Computer Hobbyists" by Neill Graham. + */ + + /***************************** + ** FLOATING-POINT EMULATION ** + *****************************/ + + /************** + ** DoEmFloat ** + *************** + ** Perform the floating-point emulation routines portion of the + ** CPU benchmark. Returns the operations per second. + */ + public override double Run() + { + InternalFPF[] abase; /* Base of A array */ + InternalFPF[] bbase; /* Base of B array */ + InternalFPF[] cbase; /* Base of C array */ + long accumtime; /* Accumulated time in ticks */ + double iterations; /* # of iterations */ + long tickcount; /* # of ticks */ + int loops; /* # of loops */ + + /* + ** Test the emulation routines. + */ + + abase = new InternalFPF[this.arraysize]; + bbase = new InternalFPF[this.arraysize]; + cbase = new InternalFPF[this.arraysize]; + + + for (int i = 0; i < this.arraysize; i++) + { + abase[i] = new InternalFPF(INTERNAL_FPF_PRECISION); + bbase[i] = new InternalFPF(INTERNAL_FPF_PRECISION); + cbase[i] = new InternalFPF(INTERNAL_FPF_PRECISION); + } + + + /* + for (int i = 0; i < this.arraysize; i++) + { + abase[i].type = IFPF.IFPF_IS_ZERO; + abase[i].sign = (byte)0; + abase[i].exp = (short)0; + abase[i].mantissa = new char[INTERNAL_FPF_PRECISION]; + + bbase[i].type = IFPF.IFPF_IS_ZERO; + bbase[i].sign = (byte)0; + bbase[i].exp = (short)0; + bbase[i].mantissa = new char[INTERNAL_FPF_PRECISION]; + + cbase[i].type = IFPF.IFPF_IS_ZERO; + cbase[i].sign = (byte)0; + cbase[i].exp = (short)0; + cbase[i].mantissa = new char[INTERNAL_FPF_PRECISION]; + } + */ + + + /* + ** Set up the arrays + */ + SetupCPUEmFloatArrays(abase, bbase, cbase, this.arraysize); + + /* + ** See if we need to do self-adjusting code. + */ + if (this.adjust == 0) + { + this.loops = 0; + + /* + ** Do an iteration of the tests. If the elapsed time is + ** less than minimum, increase the loop count and try + ** again. + */ + for (loops = 1; loops < global.CPUEMFLOATLOOPMAX; loops += loops) + { + tickcount = DoEmFloatIteration(abase, bbase, cbase, + this.arraysize, + loops); + if (tickcount > global.min_ticks) + { + this.loops = loops; + break; + } + } + } + + /* + ** Verify that selft adjustment code worked. + */ + if (this.loops == 0) + { + throw new Exception("CPU:EMFPU -- CMPUEMFLOATLOOPMAX limit hit\n"); + } + + /* + ** All's well if we get here. Repeatedly perform floating + ** tests until the accumulated time is greater than the + ** # of seconds requested. + ** Each iteration performs arraysize * 3 operations. + */ + accumtime = 0L; + iterations = (double)0.0; + do + { + accumtime += DoEmFloatIteration(abase, bbase, cbase, + this.arraysize, + this.loops); + iterations += (double)1.0; + } while (ByteMark.TicksToSecs(accumtime) < this.request_secs); + + /* + ** Clean up, calculate results, and go home. + ** Also, indicate that adjustment is done. + */ + + if (this.adjust == 0) + this.adjust = 1; + double emflops = (iterations * (double)this.loops) / + (double)ByteMark.TicksToFracSecs(accumtime); + + return (emflops); + } + + + + + + /************************** + ** SetupCPUEmFloatArrays ** + *************************** + ** Set up the arrays that will be used in the emulated + ** floating-point tests. + ** This is done by loading abase and bbase elements with + ** random numbers. We use our long-to-floating point + ** routine to set them up. + ** NOTE: We really don't need the pointer to cbase...cbase + ** is overwritten in the benchmark. + */ + private static + void SetupCPUEmFloatArrays(InternalFPF[] abase, + InternalFPF[] bbase, + InternalFPF[] cbase, + int arraysize) + { + int i; + InternalFPF locFPF1, locFPF2; + locFPF1 = new InternalFPF(INTERNAL_FPF_PRECISION); + locFPF2 = new InternalFPF(INTERNAL_FPF_PRECISION); + + for (i = 0; i < arraysize; i++) + { + LongToInternalFPF(ByteMark.randwc(50000), ref locFPF1); + LongToInternalFPF(ByteMark.randwc(50000) + 1, ref locFPF2); + DivideInternalFPF(ref locFPF1, ref locFPF2, ref abase[i]); + LongToInternalFPF(ByteMark.randwc(50000) + 1, ref locFPF2); + DivideInternalFPF(ref locFPF1, ref locFPF2, ref bbase[i]); + } + return; + } + + /*********************** + ** DoEmFloatIteration ** + ************************ + ** Perform an iteration of the emulated floating-point + ** benchmark. Note that "an iteration" can involve multiple + ** loops through the benchmark. + */ + private static + long DoEmFloatIteration(InternalFPF[] abase, + InternalFPF[] bbase, + InternalFPF[] cbase, + int arraysize, int loops) + { + long elapsed; /* For the stopwatch */ + byte[] jtable = new byte[] { 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 2, 3, 3, 3 }; + int i; + + /* + ** Begin timing + */ + elapsed = ByteMark.StartStopwatch(); + + /* + ** Each pass through the array performs operations in + ** the followingratios: + ** 4 adds, 4 subtracts, 5 multiplies, 3 divides + ** (adds and subtracts being nearly the same operation) + */ + while (loops-- > 0) + { + for (i = 0; i < arraysize; i++) + switch (jtable[i % 16]) + { + case 0: /* Add */ + AddSubInternalFPF(0, ref abase[i], + ref bbase[i], + ref cbase[i]); + break; + case 1: /* Subtract */ + AddSubInternalFPF(1, ref abase[i], + ref bbase[i], + ref cbase[i]); + break; + case 2: /* Multiply */ + MultiplyInternalFPF(ref abase[i], + ref bbase[i], + ref cbase[i]); + break; + case 3: /* Divide */ + DivideInternalFPF(ref abase[i], + ref bbase[i], + ref cbase[i]); + break; + } + } + + return (ByteMark.StopStopwatch(elapsed)); + } + + /*********************** + ** SetInternalFPFZero ** + ************************ + ** Set an internal floating-point-format number to zero. + ** sign determines the sign of the zero. + */ + private static void SetInternalFPFZero(ref InternalFPF dest, + byte sign) + { + int i; /* Index */ + + dest.type = IFPF.IFPF_IS_ZERO; + dest.sign = sign; + dest.exp = MIN_EXP; + + for (i = 0; i < INTERNAL_FPF_PRECISION; i++) + dest.mantissa[i] = (char)0; + return; + } + + /*************************** + ** SetInternalFPFInfinity ** + **************************** + ** Set an internal floating-point-format number to infinity. + ** This can happen if the exponent exceeds MAX_EXP. + ** As above, sign picks the sign of infinity. + */ + private static void SetInternalFPFInfinity(ref InternalFPF dest, + byte sign) + { + int i; /* Index */ + + dest.type = IFPF.IFPF_IS_INFINITY; + dest.sign = sign; + dest.exp = MIN_EXP; + + for (i = 0; i < INTERNAL_FPF_PRECISION; i++) + dest.mantissa[i] = (char)0; + return; + } + + /********************** + ** SetInternalFPFNaN ** + *********************** + ** Set an internal floating-point-format number to Nan + ** (not a number). Note that we "emulate" an 80x87 as far + ** as the mantissa bits go. + */ + private static void SetInternalFPFNaN(ref InternalFPF dest) + { + int i; /* Index */ + + dest.type = IFPF.IFPF_IS_NAN; + dest.exp = MAX_EXP; + dest.sign = 1; + + dest.mantissa[0] = (char)0x4000; + for (i = 1; i < INTERNAL_FPF_PRECISION; i++) + dest.mantissa[i] = (char)0; + + return; + } + + /******************* + ** IsMantissaZero ** + ******************** + ** Pass this routine a pointer to an internal floating point format + ** number's mantissa. It checks for an all-zero mantissa. + ** Returns 0 if it is NOT all zeros, !=0 otherwise. + */ + private static bool IsMantissaZero(char[] mant) + { + int i; /* Index */ + int n; /* Return value */ + + n = 0; + for (i = 0; i < INTERNAL_FPF_PRECISION; i++) + n |= mant[i]; + + return (n == 0); + } + + /************** + ** Add16Bits ** + *************** + ** Add b, c, and carry. Retult in a. New carry in carry. + */ + private static void Add16Bits(ref char carry, + out char a, + char b, + char c) + { + int accum; /* Accumulator */ + + /* + ** Do the work in the 32-bit accumulator so we can return + ** the carry. + */ + accum = b; + accum += c; + accum += carry; + carry = (char)(((accum & 0x00010000) != 0) ? 1 : 0); /* New carry */ + a = (char)(accum & 0xFFFF); /* Result is lo 16 bits */ + return; + } + + /************** + ** Sub16Bits ** + *************** + ** Additive inverse of above. + */ + private static void Sub16Bits(ref char borrow, + out char a, + char b, + char c) + { + int accum; /* Accumulator */ + + accum = b; + accum -= c; + accum -= borrow; + borrow = (char)(((accum & 0x00010000) != 0) ? 1 : 0); /* New borrow */ + a = (char)(accum & 0xFFFF); + return; + } + + /******************* + ** ShiftMantLeft1 ** + ******************** + ** Shift a vector of 16-bit numbers left 1 bit. Also provides + ** a carry bit, which is shifted in at the beginning, and + ** shifted out at the end. + */ + private static void ShiftMantLeft1(ref char carry, + char[] mantissa) + { + int i; /* Index */ + int new_carry; + char accum; /* Temporary holding placed */ + + for (i = INTERNAL_FPF_PRECISION - 1; i >= 0; i--) + { + accum = mantissa[i]; + new_carry = accum & 0x8000; /* Get new carry */ + accum = unchecked((char)(accum << 1)); /* Do the shift */ + if (carry != 0) + accum |= (char)1; /* Insert previous carry */ + carry = (char)new_carry; + mantissa[i] = accum; /* Return shifted value */ + } + return; + } + + /******************** + ** ShiftMantRight1 ** + ********************* + ** Shift a mantissa right by 1 bit. Provides carry, as + ** above + */ + private static void ShiftMantRight1(ref char carry, + char[] mantissa) + { + int i; /* Index */ + int new_carry; + char accum; + + for (i = 0; i < INTERNAL_FPF_PRECISION; i++) + { + accum = mantissa[i]; + new_carry = accum & 1; /* Get new carry */ + accum = (char)(accum >> 1); + if (carry != 0) + accum |= (char)0x8000; + carry = (char)new_carry; + mantissa[i] = accum; + } + return; + } + + + /***************************** + ** StickyShiftMantRight ** + ****************************** + ** This is a shift right of the mantissa with a "sticky bit". + ** I.E., if a carry of 1 is shifted out of the least significant + ** bit, the least significant bit is set to 1. + */ + private static void StickyShiftRightMant(ref InternalFPF ptr, + int amount) + { + int i; /* Index */ + char carry; /* Self-explanatory */ + + if (ptr.type != IFPF.IFPF_IS_ZERO) /* Don't bother shifting a zero */ + { + /* + ** If the amount of shifting will shift everyting + ** out of existence, then just clear the whole mantissa + ** and set the lowmost bit to 1. + */ + if (amount >= INTERNAL_FPF_PRECISION * 16) + { + for (i = 0; i < INTERNAL_FPF_PRECISION - 1; i++) + ptr.mantissa[i] = (char)0; + ptr.mantissa[INTERNAL_FPF_PRECISION - 1] = (char)1; + } + else + for (i = 0; i < amount; i++) + { + carry = (char)0; + ShiftMantRight1(ref carry, ptr.mantissa); + if (carry != 0) + ptr.mantissa[INTERNAL_FPF_PRECISION - 1] |= (char)1; + } + } + return; + } + + + /************************************************** + ** POST ARITHMETIC PROCESSING ** + ** (NORMALIZE, ROUND, OVERFLOW, AND UNDERFLOW) ** + **************************************************/ + + /************** + ** normalize ** + *************** + ** Normalize an internal-representation number. Normalization + ** discards empty most-significant bits. + */ + private static void normalize(ref InternalFPF ptr) + { + char carry; + + /* + ** As long as there's a highmost 0 bit, shift the significand + ** left 1 bit. Each time you do this, though, you've + ** gotta decrement the exponent. + */ + while ((ptr.mantissa[0] & 0x8000) == 0) + { + carry = (char)0; + ShiftMantLeft1(ref carry, ptr.mantissa); + ptr.exp--; + } + return; + } + + /**************** + ** denormalize ** + ***************** + ** Denormalize an internal-representation number. This means + ** shifting it right until its exponent is equivalent to + ** minimum_exponent. (You have to do this often in order + ** to perform additions and subtractions). + */ + private static void denormalize(ref InternalFPF ptr, + int minimum_exponent) + { + int exponent_difference; + + if (IsMantissaZero(ptr.mantissa)) + { + throw new Exception("Error: zero significand in denormalize"); + } + + exponent_difference = ptr.exp - minimum_exponent; + if (exponent_difference < 0) + { + /* + ** The number is subnormal + */ + exponent_difference = -exponent_difference; + if (exponent_difference >= (INTERNAL_FPF_PRECISION * 16)) + { + /* Underflow */ + SetInternalFPFZero(ref ptr, ptr.sign); + } + else + { + ptr.exp += (short)exponent_difference; + StickyShiftRightMant(ref ptr, exponent_difference); + } + } + return; + } + + + /********************* + ** RoundInternalFPF ** + ********************** + ** Round an internal-representation number. + ** The kind of rounding we do here is simplest...referred to as + ** "chop". "Extraneous" rightmost bits are simply hacked off. + */ + private static + void RoundInternalFPF(ref InternalFPF ptr) + { + /* int i; */ + + if (ptr.type == IFPF.IFPF_IS_NORMAL || + ptr.type == IFPF.IFPF_IS_SUBNORMAL) + { + denormalize(ref ptr, MIN_EXP); + if (ptr.type != IFPF.IFPF_IS_ZERO) + { + /* clear the extraneous bits */ + ptr.mantissa[3] &= (char)0xfff8; + /* for (i=4; imantissa[i] = 0; + } + */ + /* + ** Check for overflow + */ + if (ptr.exp > MAX_EXP) + { + SetInternalFPFInfinity(ref ptr, ptr.sign); + } + } + } + return; + } + + /******************************************************* + ** ARITHMETIC OPERATIONS ON INTERNAL REPRESENTATION ** + *******************************************************/ + + private static void memmove(ref InternalFPF dest, ref InternalFPF src) + { + dest.type = src.type; + dest.sign = src.sign; + dest.exp = src.exp; + for (int i = 0; i < INTERNAL_FPF_PRECISION; i++) + { + dest.mantissa[i] = src.mantissa[i]; + } + /* This implementation only loses about .1 on the rating. Surprising. + dest = src; + dest.mantissa = new char[INTERNAL_FPF_PRECISION]; + for (int i = 0; i < INTERNAL_FPF_PRECISION; i++) + { + dest.mantissa[i] = src.mantissa[i]; + } + */ + } + + /*************** + ** choose_nan ** + **************** + ** Called by routines that are forced to perform math on + ** a pair of NaN's. This routine "selects" which NaN is + ** to be returned. + */ + private static void choose_nan(ref InternalFPF x, + ref InternalFPF y, + ref InternalFPF z, + int intel_flag) + { + int i; + + /* + ** Compare the two mantissas, + ** return the larger. Note that we will be emulating + ** an 80387 in this operation. + */ + for (i = 0; i < INTERNAL_FPF_PRECISION; i++) + { + if (x.mantissa[i] > y.mantissa[i]) + { + memmove(ref z, ref x); + return; + } + if (x.mantissa[i] < y.mantissa[i]) + { + memmove(ref z, ref y); + return; + } + } + + /* + ** They are equal + */ + if (intel_flag == 0) + /* if the operation is addition */ + memmove(ref z, ref x); + else + /* if the operation is multiplication */ + memmove(ref z, ref y); + return; + } + + + /********************** + ** AddSubInternalFPF ** + *********************** + ** Adding or subtracting internal-representation numbers. + ** Internal-representation numbers pointed to by x and y are + ** added/subtracted and the result returned in z. + */ + private static void AddSubInternalFPF(byte operation, + ref InternalFPF x, + ref InternalFPF y, + ref InternalFPF z) + { + int exponent_difference; + char borrow; + char carry; + int i; + InternalFPF locx, locy; /* Needed since we alter them */ + /* + ** Following big switch statement handles the + ** various combinations of operand types. + */ + int count = (int)IFPF.IFPF_TYPE_COUNT; + switch ((STATE)(((int)x.type * count) + (int)y.type)) + { + case STATE.ZERO_ZERO: + memmove(ref z, ref x); + if ((x.sign ^ y.sign ^ operation) != 0) + { + z.sign = 0; /* positive */ + } + break; + + case STATE.NAN_ZERO: + case STATE.NAN_SUBNORMAL: + case STATE.NAN_NORMAL: + case STATE.NAN_INFINITY: + case STATE.SUBNORMAL_ZERO: + case STATE.NORMAL_ZERO: + case STATE.INFINITY_ZERO: + case STATE.INFINITY_SUBNORMAL: + case STATE.INFINITY_NORMAL: + memmove(ref z, ref x); + break; + + + case STATE.ZERO_NAN: + case STATE.SUBNORMAL_NAN: + case STATE.NORMAL_NAN: + case STATE.INFINITY_NAN: + memmove(ref z, ref y); + break; + + case STATE.ZERO_SUBNORMAL: + case STATE.ZERO_NORMAL: + case STATE.ZERO_INFINITY: + case STATE.SUBNORMAL_INFINITY: + case STATE.NORMAL_INFINITY: + memmove(ref z, ref x); + z.sign ^= operation; + break; + + case STATE.SUBNORMAL_SUBNORMAL: + case STATE.SUBNORMAL_NORMAL: + case STATE.NORMAL_SUBNORMAL: + case STATE.NORMAL_NORMAL: + /* + ** Copy x and y to locals, since we may have + ** to alter them. + */ + locx = new InternalFPF(INTERNAL_FPF_PRECISION); + locy = new InternalFPF(INTERNAL_FPF_PRECISION); + memmove(ref locx, ref x); + memmove(ref locy, ref y); + + /* compute sum/difference */ + exponent_difference = locx.exp - locy.exp; + if (exponent_difference == 0) + { + /* + ** locx.exp == locy.exp + ** so, no shifting required + */ + if (locx.type == IFPF.IFPF_IS_SUBNORMAL || + locy.type == IFPF.IFPF_IS_SUBNORMAL) + z.type = IFPF.IFPF_IS_SUBNORMAL; + else + z.type = IFPF.IFPF_IS_NORMAL; + + /* + ** Assume that locx.mantissa > locy.mantissa + */ + z.sign = locx.sign; + z.exp = locx.exp; + } + else + if (exponent_difference > 0) + { + /* + ** locx.exp > locy.exp + */ + StickyShiftRightMant(ref locy, + exponent_difference); + z.type = locx.type; + z.sign = locx.sign; + z.exp = locx.exp; + } + else /* if (exponent_difference < 0) */ + { + /* + ** locx.exp < locy.exp + */ + StickyShiftRightMant(ref locx, + -exponent_difference); + z.type = locy.type; + z.sign = (byte)(locy.sign ^ operation); + z.exp = locy.exp; + } + + if ((locx.sign ^ locy.sign ^ operation) != 0) + { + /* + ** Signs are different, subtract mantissas + */ + borrow = (char)0; + for (i = (INTERNAL_FPF_PRECISION - 1); i >= 0; i--) + Sub16Bits(ref borrow, + out z.mantissa[i], + locx.mantissa[i], + locy.mantissa[i]); + + if (borrow != 0) + { + /* The y->mantissa was larger than the + ** x->mantissa leaving a negative + ** result. Change the result back to + ** an unsigned number and flip the + ** sign flag. + */ + z.sign = (byte)(locy.sign ^ operation); + borrow = (char)0; + for (i = (INTERNAL_FPF_PRECISION - 1); i >= 0; i--) + { + Sub16Bits(ref borrow, + out z.mantissa[i], + (char)0, + z.mantissa[i]); + } + } + else + { + /* The assumption made above + ** (i.e. x->mantissa >= y->mantissa) + ** was correct. Therefore, do nothing. + ** z->sign = x->sign; + */ + } + + if (IsMantissaZero(z.mantissa)) + { + z.type = IFPF.IFPF_IS_ZERO; + z.sign = 0; /* positive */ + } + else + if (locx.type == IFPF.IFPF_IS_NORMAL || + locy.type == IFPF.IFPF_IS_NORMAL) + { + normalize(ref z); + } + } + else + { + /* signs are the same, add mantissas */ + carry = (char)0; + for (i = (INTERNAL_FPF_PRECISION - 1); i >= 0; i--) + { + Add16Bits(ref carry, + out z.mantissa[i], + locx.mantissa[i], + locy.mantissa[i]); + } + + if (carry != 0) + { + z.exp++; + carry = (char)0; + ShiftMantRight1(ref carry, z.mantissa); + z.mantissa[0] |= (char)0x8000; + z.type = IFPF.IFPF_IS_NORMAL; + } + else + if ((z.mantissa[0] & 0x8000) != 0) + z.type = IFPF.IFPF_IS_NORMAL; + } + break; + + case STATE.INFINITY_INFINITY: + SetInternalFPFNaN(ref z); + break; + + case STATE.NAN_NAN: + choose_nan(ref x, ref y, ref z, 1); + break; + } + + /* + ** All the math is done; time to round. + */ + RoundInternalFPF(ref z); + return; + } + + + /************************ + ** MultiplyInternalFPF ** + ************************* + ** Two internal-representation numbers x and y are multiplied; the + ** result is returned in z. + */ + private static void MultiplyInternalFPF( + ref InternalFPF x, + ref InternalFPF y, + ref InternalFPF z) + { + int i; + int j; + char carry; + char[] extra_bits = new char[INTERNAL_FPF_PRECISION]; + InternalFPF locy; /* Needed since this will be altered */ + /* + ** As in the preceding function, this large switch + ** statement selects among the many combinations + ** of operands. + */ + int count = (int)IFPF.IFPF_TYPE_COUNT; + switch ((STATE)(((int)x.type * count) + (int)y.type)) + { + case STATE.INFINITY_SUBNORMAL: + case STATE.INFINITY_NORMAL: + case STATE.INFINITY_INFINITY: + case STATE.ZERO_ZERO: + case STATE.ZERO_SUBNORMAL: + case STATE.ZERO_NORMAL: + memmove(ref z, ref x); + z.sign ^= y.sign; + break; + + case STATE.SUBNORMAL_INFINITY: + case STATE.NORMAL_INFINITY: + case STATE.SUBNORMAL_ZERO: + case STATE.NORMAL_ZERO: + memmove(ref z, ref y); + z.sign ^= x.sign; + break; + + case STATE.ZERO_INFINITY: + case STATE.INFINITY_ZERO: + SetInternalFPFNaN(ref z); + break; + + case STATE.NAN_ZERO: + case STATE.NAN_SUBNORMAL: + case STATE.NAN_NORMAL: + case STATE.NAN_INFINITY: + memmove(ref z, ref x); + break; + + case STATE.ZERO_NAN: + case STATE.SUBNORMAL_NAN: + case STATE.NORMAL_NAN: + case STATE.INFINITY_NAN: + memmove(ref z, ref y); + break; + + + case STATE.SUBNORMAL_SUBNORMAL: + case STATE.SUBNORMAL_NORMAL: + case STATE.NORMAL_SUBNORMAL: + case STATE.NORMAL_NORMAL: + /* + ** Make a local copy of the y number, since we will be + ** altering it in the process of multiplying. + */ + locy = new InternalFPF(INTERNAL_FPF_PRECISION); + memmove(ref locy, ref y); + + /* + ** Check for unnormal zero arguments + */ + if (IsMantissaZero(x.mantissa) || IsMantissaZero(y.mantissa)) + { + SetInternalFPFInfinity(ref z, 0); + } + + /* + ** Initialize the result + */ + if (x.type == IFPF.IFPF_IS_SUBNORMAL || + y.type == IFPF.IFPF_IS_SUBNORMAL) + z.type = IFPF.IFPF_IS_SUBNORMAL; + else + z.type = IFPF.IFPF_IS_NORMAL; + + z.sign = (byte)(x.sign ^ y.sign); + z.exp = (short)(x.exp + y.exp); + for (i = 0; i < INTERNAL_FPF_PRECISION; i++) + { + z.mantissa[i] = (char)0; + extra_bits[i] = (char)0; + } + + for (i = 0; i < (INTERNAL_FPF_PRECISION * 16); i++) + { + /* + ** Get rightmost bit of the multiplier + */ + carry = (char)0; + ShiftMantRight1(ref carry, locy.mantissa); + if (carry != 0) + { + /* + ** Add the multiplicand to the product + */ + carry = (char)0; + for (j = (INTERNAL_FPF_PRECISION - 1); j >= 0; j--) + Add16Bits(ref carry, + out z.mantissa[j], + z.mantissa[j], + x.mantissa[j]); + } + else + { + carry = (char)0; + } + + /* + ** Shift the product right. Overflow bits get + ** shifted into extra_bits. We'll use it later + ** to help with the "sticky" bit. + */ + ShiftMantRight1(ref carry, z.mantissa); + ShiftMantRight1(ref carry, extra_bits); + } + + /* + ** Normalize + ** Note that we use a "special" normalization routine + ** because we need to use the extra bits. (These are + ** bits that may have been shifted off the bottom that + ** we want to reclaim...if we can. + */ + while ((z.mantissa[0] & 0x8000) == 0) + { + carry = (char)0; + ShiftMantLeft1(ref carry, extra_bits); + ShiftMantLeft1(ref carry, z.mantissa); + z.exp--; + } + + /* + ** Set the sticky bit if any bits set in extra bits. + */ + if (IsMantissaZero(extra_bits)) + { + z.mantissa[INTERNAL_FPF_PRECISION - 1] |= (char)1; + } + break; + + case STATE.NAN_NAN: + choose_nan(ref x, ref y, ref z, 0); + break; + } + + /* + ** All math done...do rounding. + */ + RoundInternalFPF(ref z); + return; + } + + + /********************** + ** DivideInternalFPF ** + *********************** + ** Divide internal FPF number x by y. Return result in z. + */ + private static void DivideInternalFPF( + ref InternalFPF x, + ref InternalFPF y, + ref InternalFPF z) + { + int i; + int j; + char carry; + char[] extra_bits = new char[INTERNAL_FPF_PRECISION]; + InternalFPF locx; /* Local for x number */ + + /* + ** As with preceding function, the following switch + ** statement selects among the various possible + ** operands. + */ + int count = (int)IFPF.IFPF_TYPE_COUNT; + switch ((STATE)(((int)x.type * count) + (int)y.type)) + { + case STATE.ZERO_ZERO: + case STATE.INFINITY_INFINITY: + SetInternalFPFNaN(ref z); + break; + + case STATE.ZERO_SUBNORMAL: + case STATE.ZERO_NORMAL: + if (IsMantissaZero(y.mantissa)) + { + SetInternalFPFNaN(ref z); + break; + } + goto case STATE.ZERO_INFINITY; + + case STATE.ZERO_INFINITY: + case STATE.SUBNORMAL_INFINITY: + case STATE.NORMAL_INFINITY: + SetInternalFPFZero(ref z, (byte)(x.sign ^ y.sign)); + break; + + case STATE.SUBNORMAL_ZERO: + case STATE.NORMAL_ZERO: + if (IsMantissaZero(x.mantissa)) + { + SetInternalFPFNaN(ref z); + break; + } + goto case STATE.INFINITY_ZERO; + + case STATE.INFINITY_ZERO: + case STATE.INFINITY_SUBNORMAL: + case STATE.INFINITY_NORMAL: + SetInternalFPFInfinity(ref z, 0); + z.sign = (byte)(x.sign ^ y.sign); + break; + + case STATE.NAN_ZERO: + case STATE.NAN_SUBNORMAL: + case STATE.NAN_NORMAL: + case STATE.NAN_INFINITY: + memmove(ref z, ref x); + break; + + case STATE.ZERO_NAN: + case STATE.SUBNORMAL_NAN: + case STATE.NORMAL_NAN: + case STATE.INFINITY_NAN: + memmove(ref z, ref y); + break; + + case STATE.SUBNORMAL_SUBNORMAL: + case STATE.NORMAL_SUBNORMAL: + case STATE.SUBNORMAL_NORMAL: + case STATE.NORMAL_NORMAL: + /* + ** Make local copy of x number, since we'll be + ** altering it in the process of dividing. + */ + locx = new InternalFPF(INTERNAL_FPF_PRECISION); + memmove(ref locx, ref x); + + /* + ** Check for unnormal zero arguments + */ + if (IsMantissaZero(locx.mantissa)) + { + if (IsMantissaZero(y.mantissa)) + SetInternalFPFNaN(ref z); + else + SetInternalFPFZero(ref z, 0); + break; + } + if (IsMantissaZero(y.mantissa)) + { + SetInternalFPFInfinity(ref z, 0); + break; + } + + /* + ** Initialize the result + */ + z.type = x.type; + z.sign = (byte)(x.sign ^ y.sign); + z.exp = (short)(x.exp - y.exp + + ((INTERNAL_FPF_PRECISION * 16 * 2))); + for (i = 0; i < INTERNAL_FPF_PRECISION; i++) + { + z.mantissa[i] = (char)0; + extra_bits[i] = (char)0; + } + + while ((z.mantissa[0] & 0x8000) == 0) + { + carry = (char)0; + ShiftMantLeft1(ref carry, locx.mantissa); + ShiftMantLeft1(ref carry, extra_bits); + + /* + ** Time to subtract yet? + */ + if (carry == 0) + for (j = 0; j < INTERNAL_FPF_PRECISION; j++) + { + if (y.mantissa[j] > extra_bits[j]) + { + carry = (char)0; + goto no_subtract; + } + if (y.mantissa[j] < extra_bits[j]) + break; + } + /* + ** Divisor (y) <= dividend (x), subtract + */ + carry = (char)0; + for (j = (INTERNAL_FPF_PRECISION - 1); j >= 0; j--) + Sub16Bits(ref carry, + out extra_bits[j], + extra_bits[j], + y.mantissa[j]); + carry = (char)1; /* 1 shifted into quotient */ + no_subtract: + ShiftMantLeft1(ref carry, z.mantissa); + z.exp--; + } + break; + + case STATE.NAN_NAN: + choose_nan(ref x, ref y, ref z, 0); + break; + } + + /* + ** Math complete...do rounding + */ + RoundInternalFPF(ref z); + } + + /********************** + ** LongToInternalFPF ** + *********************** + ** Convert a signed long integer into an internal FPF number. + */ + private static void LongToInternalFPF( + int mylong, + ref InternalFPF dest) + { + int i; /* Index */ + char myword; /* Used to hold converted stuff */ + /* + ** Save the sign and get the absolute value. This will help us + ** with 64-bit machines, since we use only the lower 32 + ** bits just in case. + */ + if (mylong < 0) + { + dest.sign = 1; + mylong = 0 - mylong; + } + else + dest.sign = 0; + /* + ** Prepare the destination floating point number + */ + dest.type = IFPF.IFPF_IS_NORMAL; + for (i = 0; i < INTERNAL_FPF_PRECISION; i++) + dest.mantissa[i] = (char)0; + + /* + ** See if we've got a zero. If so, make the resultant FP + ** number a true zero and go home. + */ + if (mylong == 0) + { + dest.type = IFPF.IFPF_IS_ZERO; + dest.exp = 0; + return; + } + + /* + ** Not a true zero. Set the exponent to 32 (internal FPFs have + ** no bias) and load the low and high words into their proper + ** locations in the mantissa. Then normalize. The action of + ** normalizing slides the mantissa bits into place and sets + ** up the exponent properly. + */ + dest.exp = 32; + myword = (char)((mylong >> 16) & 0xFFFFL); + dest.mantissa[0] = myword; + myword = (char)(mylong & 0xFFFFL); + dest.mantissa[1] = myword; + normalize(ref dest); + return; + } + + /************************ + ** InternalFPFToString ** + ************************* + ** FOR DEBUG PURPOSES + ** This routine converts an internal floating point representation + ** number to a string. Used in debugging the package. + ** Returns length of converted number. + ** NOTE: dest must point to a buffer big enough to hold the + ** result. Also, this routine does append a null (an effect + ** of using the sprintf() function). It also returns + ** a length count. + ** NOTE: This routine returns 5 significant digits. Thats + ** about all I feel safe with, given the method of + ** conversion. It should be more than enough for programmers + ** to determine whether the package is properly ported. + */ + private static int InternalFPFToString( + out string dest, + ref InternalFPF src) + { + InternalFPF locFPFNum; /* Local for src (will be altered) */ + InternalFPF IFPF10; /* Floating-point 10 */ + InternalFPF IFPFComp; /* For doing comparisons */ + int msign; /* Holding for mantissa sign */ + int expcount; /* Exponent counter */ + int ccount; /* Character counter */ + int i, j, k; /* Index */ + char carryaccum; /* Carry accumulator */ + char mycarry; /* Local for carry */ + + locFPFNum = new InternalFPF(INTERNAL_FPF_PRECISION); + IFPF10 = new InternalFPF(INTERNAL_FPF_PRECISION); + IFPFComp = new InternalFPF(INTERNAL_FPF_PRECISION); + dest = ""; + /* + ** Check first for the simple things...Nan, Infinity, Zero. + ** If found, copy the proper string in and go home. + */ + switch (src.type) + { + case IFPF.IFPF_IS_NAN: + dest = "NaN"; + return (3); + + case IFPF.IFPF_IS_INFINITY: + if (src.sign == 0) + dest = "+Inf"; + else + dest = "-Inf"; + return (4); + + case IFPF.IFPF_IS_ZERO: + if (src.sign == 0) + dest = "+0"; + else + dest = "-0"; + return (2); + } + + /* + ** Move the internal number into our local holding area, since + ** we'll be altering it to print it out. + */ + memmove(ref locFPFNum, ref src); + + /* + ** Set up a floating-point 10...which we'll use a lot in a minute. + */ + LongToInternalFPF(10, ref IFPF10); + + /* + ** Save the mantissa sign and make it positive. + */ + msign = src.sign; + src.sign = 0; + + expcount = 0; /* Init exponent counter */ + + /* + ** See if the number is less than 10. If so, multiply + ** the number repeatedly by 10 until it's not. For each + ** multiplication, decrement a counter so we can keep track + ** of the exponent. + */ + while (true) + { + AddSubInternalFPF(1, ref locFPFNum, ref IFPF10, ref IFPFComp); + if (IFPFComp.sign == 0) + break; + MultiplyInternalFPF(ref locFPFNum, ref IFPF10, ref IFPFComp); + expcount--; + memmove(ref locFPFNum, ref IFPFComp); + } + + /* + ** Do the reverse of the above. As long as the number is + ** greater than or equal to 10, divide it by 10. Increment the + ** exponent counter for each multiplication. + */ + while (true) + { + AddSubInternalFPF(1, ref locFPFNum, ref IFPF10, ref IFPFComp); + if (IFPFComp.sign != 0) + break; + DivideInternalFPF(ref locFPFNum, ref IFPF10, ref IFPFComp); + expcount++; + memmove(ref locFPFNum, ref IFPFComp); + } + + /* + ** About time to start storing things. First, store the + ** mantissa sign. + */ + ccount = 1; /* Init character counter */ + if (msign == 0) + dest += "+"; + else + dest += "-"; + + /* + ** At this point we know that the number is in the range + ** 10 > n >=1. We need to "strip digits" out of the + ** mantissa. We do this by treating the mantissa as + ** an integer and multiplying by 10. (Not a floating-point + ** 10, but an integer 10. Since this is debug code and we + ** could care less about speed, we'll do it the stupid + ** way and simply add the number to itself 10 times. + ** Anything that makes it to the left of the implied binary point + ** gets stripped off and emitted. We'll do this for + ** 5 significant digits (which should be enough to + ** verify things). + */ + /* + ** Re-position radix point + */ + carryaccum = (char)0; + while (locFPFNum.exp > 0) + { + mycarry = (char)0; + ShiftMantLeft1(ref mycarry, locFPFNum.mantissa); + carryaccum = (char)(carryaccum << 1); + if (mycarry != 0) + carryaccum++; + locFPFNum.exp--; + } + + while (locFPFNum.exp < 0) + { + mycarry = (char)0; + ShiftMantRight1(ref mycarry, locFPFNum.mantissa); + locFPFNum.exp++; + } + + for (i = 0; i < 6; i++) + if (i == 1) + { /* Emit decimal point */ + dest += "."; + ccount++; + } + else + { /* Emit a digit */ + string s = "0"; // (char)('0'+carryaccum)); // this isn't ever called + dest += s; + ccount++; + + carryaccum = (char)0; + memmove(ref IFPF10, ref locFPFNum); + + /* Do multiply via repeated adds */ + for (j = 0; j < 9; j++) + { + mycarry = (char)0; + for (k = (INTERNAL_FPF_PRECISION - 1); k >= 0; k--) + Add16Bits(ref mycarry, out IFPFComp.mantissa[k], + locFPFNum.mantissa[k], + IFPF10.mantissa[k]); + carryaccum += (char)(mycarry != 0 ? 1 : 0); + memmove(ref locFPFNum, ref IFPFComp); + } + } + + /* + ** Now move the 'E', the exponent sign, and the exponent + ** into the string. + */ + dest += "E"; + dest += expcount.ToString(); + + /* + ** All done, go home. + */ + return (dest.Length); + } +} diff --git a/tests/src/JIT/Performance/CodeQuality/Bytemark/emfloatclass.cs b/tests/src/JIT/Performance/CodeQuality/Bytemark/emfloatclass.cs new file mode 100644 index 0000000000..1667231e3d --- /dev/null +++ b/tests/src/JIT/Performance/CodeQuality/Bytemark/emfloatclass.cs @@ -0,0 +1,1563 @@ +/* +** Copyright (c) Microsoft. All rights reserved. +** Licensed under the MIT license. +** See LICENSE file in the project root for full license information. +** +** This program was translated to C# and adapted for xunit-performance. +** New variants of several tests were added to compare class versus +** struct and to compare jagged arrays vs multi-dimensional arrays. +*/ + +/* +** BYTEmark (tm) +** BYTE Magazine's Native Mode benchmarks +** Rick Grehan, BYTE Magazine +** +** Create: +** Revision: 3/95 +** +** DISCLAIMER +** The source, executable, and documentation files that comprise +** the BYTEmark benchmarks are made available on an "as is" basis. +** This means that we at BYTE Magazine have made every reasonable +** effort to verify that the there are no errors in the source and +** executable code. We cannot, however, guarantee that the programs +** are error-free. Consequently, McGraw-HIll and BYTE Magazine make +** no claims in regard to the fitness of the source code, executable +** code, and documentation of the BYTEmark. +** +** Furthermore, BYTE Magazine, McGraw-Hill, and all employees +** of McGraw-Hill cannot be held responsible for any damages resulting +** from the use of this code or the results obtained from using +** this code. +*/ + +using System; + +/* +** DEFINES +*/ +public class EMFloatClass : EmFloatStruct +{ + public override string Name() + { + return "FP EMULATION(class)"; + } + + private const int MAX_EXP = 32767; + private const int MIN_EXP = (-32767); + + private enum IFPF : byte + { + IFPF_IS_ZERO = 0, + IFPF_IS_SUBNORMAL = 1, + IFPF_IS_NORMAL = 2, + IFPF_IS_INFINITY = 3, + IFPF_IS_NAN = 4, + IFPF_TYPE_COUNT = 5, + }; + + private enum STATE + { + ZERO_ZERO = 0, + ZERO_SUBNORMAL = 1, + ZERO_NORMAL = 2, + ZERO_INFINITY = 3, + ZERO_NAN = 4, + + SUBNORMAL_ZERO = 5, + SUBNORMAL_SUBNORMAL = 6, + SUBNORMAL_NORMAL = 7, + SUBNORMAL_INFINITY = 8, + SUBNORMAL_NAN = 9, + + NORMAL_ZERO = 10, + NORMAL_SUBNORMAL = 11, + NORMAL_NORMAL = 12, + NORMAL_INFINITY = 13, + NORMAL_NAN = 14, + + INFINITY_ZERO = 15, + INFINITY_SUBNORMAL = 16, + INFINITY_NORMAL = 17, + INFINITY_INFINITY = 18, + INFINITY_NAN = 19, + + NAN_ZERO = 20, + NAN_SUBNORMAL = 21, + NAN_NORMAL = 22, + NAN_INFINITY = 23, + NAN_NAN = 24, + }; + + private enum OPERAND + { + OPERAND_ZERO = 0, + OPERAND_SUBNORMAL = 1, + OPERAND_NORMAL = 2, + OPERAND_INFINITY = 3, + OPERAND_NAN = 4, + }; + + /* + ** Following was already defined in NMGLOBAL.H + ** + */ + private const int INTERNAL_FPF_PRECISION = 4; + + /* + ** TYPEDEFS + */ + + private class InternalFPF + { + public InternalFPF() + { + type = IFPF.IFPF_IS_ZERO; sign = (byte)0; + exp = (short)0; mantissa = new char[INTERNAL_FPF_PRECISION]; + } + public IFPF type; /* Indicates, NORMAL, SUBNORMAL, etc. */ + public byte sign; /* Mantissa sign */ + public short exp; /* Signed exponent...no bias */ + public char[] mantissa; // [INTERNAL_FPF_PRECISION] + }; + + /* + ** emfloat.c + ** Source for emulated floating-point routines. + ** BYTEmark (tm) + ** BYTE's Native Mode Benchmarks + ** Rick Grehan, BYTE Magazine. + ** + ** Created: + ** Last update: 3/95 + ** + ** DISCLAIMER + ** The source, executable, and documentation files that comprise + ** the BYTEmark benchmarks are made available on an "as is" basis. + ** This means that we at BYTE Magazine have made every reasonable + ** effort to verify that the there are no errors in the source and + ** executable code. We cannot, however, guarantee that the programs + ** are error-free. Consequently, McGraw-HIll and BYTE Magazine make + ** no claims in regard to the fitness of the source code, executable + ** code, and documentation of the BYTEmark. + ** Furthermore, BYTE Magazine, McGraw-Hill, and all employees + ** of McGraw-Hill cannot be held responsible for any damages resulting + ** from the use of this code or the results obtained from using + ** this code. + */ + + /* + ** Floating-point emulator. + ** These routines are only "sort of" IEEE-compliant. All work is + ** done using an internal representation. Also, the routines do + ** not check for many of the exceptions that might occur. + ** Still, the external formats produced are IEEE-compatible, + ** with the restriction that they presume a low-endian machine + ** (though the endianism will not effect the performance). + ** + ** Some code here was based on work done by Steve Snelgrove of + ** Orem, UT. Other code comes from routines presented in + ** the long-ago book: "Microprocessor Programming for + ** Computer Hobbyists" by Neill Graham. + */ + + /***************************** + ** FLOATING-POINT EMULATION ** + *****************************/ + + /************** + ** DoEmFloat ** + *************** + ** Perform the floating-point emulation routines portion of the + ** CPU benchmark. Returns the operations per second. + */ + public override double Run() + { + InternalFPF[] abase; /* Base of A array */ + InternalFPF[] bbase; /* Base of B array */ + InternalFPF[] cbase; /* Base of C array */ + long accumtime; /* Accumulated time in ticks */ + double iterations; /* # of iterations */ + long tickcount; /* # of ticks */ + int loops; /* # of loops */ + + /* + ** Test the emulation routines. + */ + + abase = new InternalFPF[this.arraysize]; + bbase = new InternalFPF[this.arraysize]; + cbase = new InternalFPF[this.arraysize]; + + for (int i = 0; i < this.arraysize; i++) + { + abase[i] = new InternalFPF(); + bbase[i] = new InternalFPF(); + cbase[i] = new InternalFPF(); + } + + /* + for (int i = 0; i < this.arraysize; i++) + { + abase[i].type = IFPF.IFPF_IS_ZERO; + abase[i].sign = (byte)0; + abase[i].exp = (short)0; + abase[i].mantissa = new char[INTERNAL_FPF_PRECISION]; + + bbase[i].type = IFPF.IFPF_IS_ZERO; + bbase[i].sign = (byte)0; + bbase[i].exp = (short)0; + bbase[i].mantissa = new char[INTERNAL_FPF_PRECISION]; + + cbase[i].type = IFPF.IFPF_IS_ZERO; + cbase[i].sign = (byte)0; + cbase[i].exp = (short)0; + cbase[i].mantissa = new char[INTERNAL_FPF_PRECISION]; + } + */ + + /* + ** Set up the arrays + */ + SetupCPUEmFloatArrays(abase, bbase, cbase, this.arraysize); + + /* + ** See if we need to do self-adjusting code. + */ + if (this.adjust == 0) + { + this.loops = 0; + + /* + ** Do an iteration of the tests. If the elapsed time is + ** less than minimum, increase the loop count and try + ** again. + */ + for (loops = 1; loops < global.CPUEMFLOATLOOPMAX; loops += loops) + { + tickcount = DoEmFloatIteration(abase, bbase, cbase, + this.arraysize, + loops); + if (tickcount > global.min_ticks) + { + this.loops = loops; + break; + } + } + } + + /* + ** Verify that selft adjustment code worked. + */ + if (this.loops == 0) + { + throw new Exception("CPU:EMFPU -- CMPUEMFLOATLOOPMAX limit hit\n"); + } + + /* + ** All's well if we get here. Repeatedly perform floating + ** tests until the accumulated time is greater than the + ** # of seconds requested. + ** Each iteration performs arraysize * 3 operations. + */ + accumtime = 0L; + iterations = (double)0.0; + do + { + accumtime += DoEmFloatIteration(abase, bbase, cbase, + this.arraysize, + this.loops); + iterations += (double)1.0; + } while (ByteMark.TicksToSecs(accumtime) < this.request_secs); + + /* + ** Clean up, calculate results, and go home. + ** Also, indicate that adjustment is done. + */ + + if (this.adjust == 0) + this.adjust = 1; + double emflops = (iterations * (double)this.loops) / + (double)ByteMark.TicksToFracSecs(accumtime); + + return (emflops); + } + + + + + + + /************************** + ** SetupCPUEmFloatArrays ** + *************************** + ** Set up the arrays that will be used in the emulated + ** floating-point tests. + ** This is done by loading abase and bbase elements with + ** random numbers. We use our long-to-floating point + ** routine to set them up. + ** NOTE: We really don't need the pointer to cbase...cbase + ** is overwritten in the benchmark. + */ + private static + void SetupCPUEmFloatArrays(InternalFPF[] abase, + InternalFPF[] bbase, + InternalFPF[] cbase, + int arraysize) + { + int i; + InternalFPF locFPF1, locFPF2; + locFPF1 = new InternalFPF(); + locFPF2 = new InternalFPF(); + + for (i = 0; i < arraysize; i++) + { + LongToInternalFPF(ByteMark.randwc(50000), locFPF1); + LongToInternalFPF(ByteMark.randwc(50000) + 1, locFPF2); + DivideInternalFPF(locFPF1, locFPF2, abase[i]); + LongToInternalFPF(ByteMark.randwc(50000) + 1, locFPF2); + DivideInternalFPF(locFPF1, locFPF2, bbase[i]); + } + return; + } + + /*********************** + ** DoEmFloatIteration ** + ************************ + ** Perform an iteration of the emulated floating-point + ** benchmark. Note that "an iteration" can involve multiple + ** loops through the benchmark. + */ + private static + long DoEmFloatIteration(InternalFPF[] abase, + InternalFPF[] bbase, + InternalFPF[] cbase, + int arraysize, int loops) + { + long elapsed; /* For the stopwatch */ + byte[] jtable = new byte[] { 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 2, 3, 3, 3 }; + int i; + + /* + ** Begin timing + */ + elapsed = ByteMark.StartStopwatch(); + + /* + ** Each pass through the array performs operations in + ** the followingratios: + ** 4 adds, 4 subtracts, 5 multiplies, 3 divides + ** (adds and subtracts being nearly the same operation) + */ + while (loops-- > 0) + { + for (i = 0; i < arraysize; i++) + switch (jtable[i % 16]) + { + case 0: /* Add */ + AddSubInternalFPF(0, abase[i], + bbase[i], + cbase[i]); + break; + case 1: /* Subtract */ + AddSubInternalFPF(1, abase[i], + bbase[i], + cbase[i]); + break; + case 2: /* Multiply */ + MultiplyInternalFPF(abase[i], + bbase[i], + cbase[i]); + break; + case 3: /* Divide */ + DivideInternalFPF(abase[i], + bbase[i], + cbase[i]); + break; + } + } + + return (ByteMark.StopStopwatch(elapsed)); + } + + /*********************** + ** SetInternalFPFZero ** + ************************ + ** Set an internal floating-point-format number to zero. + ** sign determines the sign of the zero. + */ + private static void SetInternalFPFZero(InternalFPF dest, + byte sign) + { + int i; /* Index */ + + dest.type = IFPF.IFPF_IS_ZERO; + dest.sign = sign; + dest.exp = MIN_EXP; + + for (i = 0; i < INTERNAL_FPF_PRECISION; i++) + dest.mantissa[i] = (char)0; + return; + } + + /*************************** + ** SetInternalFPFInfinity ** + **************************** + ** Set an internal floating-point-format number to infinity. + ** This can happen if the exponent exceeds MAX_EXP. + ** As above, sign picks the sign of infinity. + */ + private static void SetInternalFPFInfinity(InternalFPF dest, + byte sign) + { + int i; /* Index */ + + dest.type = IFPF.IFPF_IS_INFINITY; + dest.sign = sign; + dest.exp = MIN_EXP; + + for (i = 0; i < INTERNAL_FPF_PRECISION; i++) + dest.mantissa[i] = (char)0; + return; + } + + /********************** + ** SetInternalFPFNaN ** + *********************** + ** Set an internal floating-point-format number to Nan + ** (not a number). Note that we "emulate" an 80x87 as far + ** as the mantissa bits go. + */ + private static void SetInternalFPFNaN(InternalFPF dest) + { + int i; /* Index */ + + dest.type = IFPF.IFPF_IS_NAN; + dest.exp = MAX_EXP; + dest.sign = 1; + + dest.mantissa[0] = (char)0x4000; + for (i = 1; i < INTERNAL_FPF_PRECISION; i++) + dest.mantissa[i] = (char)0; + + return; + } + + /******************* + ** IsMantissaZero ** + ******************** + ** Pass this routine a pointer to an internal floating point format + ** number's mantissa. It checks for an all-zero mantissa. + ** Returns 0 if it is NOT all zeros, !=0 otherwise. + */ + private static bool IsMantissaZero(char[] mant) + { + int i; /* Index */ + int n; /* Return value */ + + n = 0; + for (i = 0; i < INTERNAL_FPF_PRECISION; i++) + n |= mant[i]; + + return (n == 0); + } + + /************** + ** Add16Bits ** + *************** + ** Add b, c, and carry. Retult in a. New carry in carry. + */ + private static void Add16Bits(ref char carry, + out char a, + char b, + char c) + { + int accum; /* Accumulator */ + + /* + ** Do the work in the 32-bit accumulator so we can return + ** the carry. + */ + accum = b; + accum += c; + accum += carry; + carry = (char)(((accum & 0x00010000) != 0) ? 1 : 0); /* New carry */ + a = (char)(accum & 0xFFFF); /* Result is lo 16 bits */ + return; + } + + /************** + ** Sub16Bits ** + *************** + ** Additive inverse of above. + */ + private static void Sub16Bits(ref char borrow, + out char a, + char b, + char c) + { + int accum; /* Accumulator */ + + accum = b; + accum -= c; + accum -= borrow; + borrow = (char)(((accum & 0x00010000) != 0) ? 1 : 0); /* New borrow */ + a = (char)(accum & 0xFFFF); + return; + } + + /******************* + ** ShiftMantLeft1 ** + ******************** + ** Shift a vector of 16-bit numbers left 1 bit. Also provides + ** a carry bit, which is shifted in at the beginning, and + ** shifted out at the end. + */ + private static void ShiftMantLeft1(ref char carry, + char[] mantissa) + { + int i; /* Index */ + int new_carry; + char accum; /* Temporary holding placed */ + + for (i = INTERNAL_FPF_PRECISION - 1; i >= 0; i--) + { + accum = mantissa[i]; + new_carry = accum & 0x8000; /* Get new carry */ + accum = unchecked((char)(accum << 1)); /* Do the shift */ + if (carry != 0) + accum |= (char)1; /* Insert previous carry */ + carry = (char)new_carry; + mantissa[i] = accum; /* Return shifted value */ + } + return; + } + + /******************** + ** ShiftMantRight1 ** + ********************* + ** Shift a mantissa right by 1 bit. Provides carry, as + ** above + */ + private static void ShiftMantRight1(ref char carry, + char[] mantissa) + { + int i; /* Index */ + int new_carry; + char accum; + + for (i = 0; i < INTERNAL_FPF_PRECISION; i++) + { + accum = mantissa[i]; + new_carry = accum & 1; /* Get new carry */ + accum = (char)(accum >> 1); + if (carry != 0) + accum |= (char)0x8000; + carry = (char)new_carry; + mantissa[i] = accum; + } + return; + } + + + /***************************** + ** StickyShiftMantRight ** + ****************************** + ** This is a shift right of the mantissa with a "sticky bit". + ** I.E., if a carry of 1 is shifted out of the least significant + ** bit, the least significant bit is set to 1. + */ + private static void StickyShiftRightMant(InternalFPF ptr, + int amount) + { + int i; /* Index */ + char carry; /* Self-explanatory */ + + if (ptr.type != IFPF.IFPF_IS_ZERO) /* Don't bother shifting a zero */ + { + /* + ** If the amount of shifting will shift everyting + ** out of existence, then just clear the whole mantissa + ** and set the lowmost bit to 1. + */ + if (amount >= INTERNAL_FPF_PRECISION * 16) + { + for (i = 0; i < INTERNAL_FPF_PRECISION - 1; i++) + ptr.mantissa[i] = (char)0; + ptr.mantissa[INTERNAL_FPF_PRECISION - 1] = (char)1; + } + else + for (i = 0; i < amount; i++) + { + carry = (char)0; + ShiftMantRight1(ref carry, ptr.mantissa); + if (carry != 0) + ptr.mantissa[INTERNAL_FPF_PRECISION - 1] |= (char)1; + } + } + return; + } + + + /************************************************** + ** POST ARITHMETIC PROCESSING ** + ** (NORMALIZE, ROUND, OVERFLOW, AND UNDERFLOW) ** + **************************************************/ + + /************** + ** normalize ** + *************** + ** Normalize an internal-representation number. Normalization + ** discards empty most-significant bits. + */ + private static void normalize(InternalFPF ptr) + { + char carry; + + /* + ** As long as there's a highmost 0 bit, shift the significand + ** left 1 bit. Each time you do this, though, you've + ** gotta decrement the exponent. + */ + while ((ptr.mantissa[0] & 0x8000) == 0) + { + carry = (char)0; + ShiftMantLeft1(ref carry, ptr.mantissa); + ptr.exp--; + } + return; + } + + /**************** + ** denormalize ** + ***************** + ** Denormalize an internal-representation number. This means + ** shifting it right until its exponent is equivalent to + ** minimum_exponent. (You have to do this often in order + ** to perform additions and subtractions). + */ + private static void denormalize(InternalFPF ptr, + int minimum_exponent) + { + int exponent_difference; + + if (IsMantissaZero(ptr.mantissa)) + { + throw new Exception("Error: zero significand in denormalize"); + } + + exponent_difference = ptr.exp - minimum_exponent; + if (exponent_difference < 0) + { + /* + ** The number is subnormal + */ + exponent_difference = -exponent_difference; + if (exponent_difference >= (INTERNAL_FPF_PRECISION * 16)) + { + /* Underflow */ + SetInternalFPFZero(ptr, ptr.sign); + } + else + { + ptr.exp += (short)exponent_difference; + StickyShiftRightMant(ptr, exponent_difference); + } + } + return; + } + + + /********************* + ** RoundInternalFPF ** + ********************** + ** Round an internal-representation number. + ** The kind of rounding we do here is simplest...referred to as + ** "chop". "Extraneous" rightmost bits are simply hacked off. + */ + private static + void RoundInternalFPF(InternalFPF ptr) + { + /* int i; */ + + if (ptr.type == IFPF.IFPF_IS_NORMAL || + ptr.type == IFPF.IFPF_IS_SUBNORMAL) + { + denormalize(ptr, MIN_EXP); + if (ptr.type != IFPF.IFPF_IS_ZERO) + { + /* clear the extraneous bits */ + ptr.mantissa[3] &= (char)0xfff8; + /* for (i=4; imantissa[i] = 0; + } + */ + /* + ** Check for overflow + */ + if (ptr.exp > MAX_EXP) + { + SetInternalFPFInfinity(ptr, ptr.sign); + } + } + } + return; + } + + /******************************************************* + ** ARITHMETIC OPERATIONS ON INTERNAL REPRESENTATION ** + *******************************************************/ + + private static void memmove(InternalFPF dest, InternalFPF src) + { + dest.type = src.type; + dest.sign = src.sign; + dest.exp = src.exp; + for (int i = 0; i < INTERNAL_FPF_PRECISION; i++) + { + dest.mantissa[i] = src.mantissa[i]; + } + } + + /*************** + ** choose_nan ** + **************** + ** Called by routines that are forced to perform math on + ** a pair of NaN's. This routine "selects" which NaN is + ** to be returned. + */ + private static void choose_nan(InternalFPF x, + InternalFPF y, + InternalFPF z, + int intel_flag) + { + int i; + + /* + ** Compare the two mantissas, + ** return the larger. Note that we will be emulating + ** an 80387 in this operation. + */ + for (i = 0; i < INTERNAL_FPF_PRECISION; i++) + { + if (x.mantissa[i] > y.mantissa[i]) + { + memmove(z, x); + return; + } + if (x.mantissa[i] < y.mantissa[i]) + { + memmove(z, y); + return; + } + } + + /* + ** They are equal + */ + if (intel_flag == 0) + /* if the operation is addition */ + memmove(z, x); + else + /* if the operation is multiplication */ + memmove(z, y); + return; + } + + + /********************** + ** AddSubInternalFPF ** + *********************** + ** Adding or subtracting internal-representation numbers. + ** Internal-representation numbers pointed to by x and y are + ** added/subtracted and the result returned in z. + */ + private static void AddSubInternalFPF(byte operation, + InternalFPF x, + InternalFPF y, + InternalFPF z) + { + int exponent_difference; + char borrow; + char carry; + int i; + InternalFPF locx, locy; /* Needed since we alter them */ + /* + ** Following big switch statement handles the + ** various combinations of operand types. + */ + int count = (int)IFPF.IFPF_TYPE_COUNT; + switch ((STATE)(((int)x.type * count) + (int)y.type)) + { + case STATE.ZERO_ZERO: + memmove(z, x); + if ((x.sign ^ y.sign ^ operation) != 0) + { + z.sign = 0; /* positive */ + } + break; + + case STATE.NAN_ZERO: + case STATE.NAN_SUBNORMAL: + case STATE.NAN_NORMAL: + case STATE.NAN_INFINITY: + case STATE.SUBNORMAL_ZERO: + case STATE.NORMAL_ZERO: + case STATE.INFINITY_ZERO: + case STATE.INFINITY_SUBNORMAL: + case STATE.INFINITY_NORMAL: + memmove(z, x); + break; + + + case STATE.ZERO_NAN: + case STATE.SUBNORMAL_NAN: + case STATE.NORMAL_NAN: + case STATE.INFINITY_NAN: + memmove(z, y); + break; + + case STATE.ZERO_SUBNORMAL: + case STATE.ZERO_NORMAL: + case STATE.ZERO_INFINITY: + case STATE.SUBNORMAL_INFINITY: + case STATE.NORMAL_INFINITY: + memmove(z, x); + z.sign ^= operation; + break; + + case STATE.SUBNORMAL_SUBNORMAL: + case STATE.SUBNORMAL_NORMAL: + case STATE.NORMAL_SUBNORMAL: + case STATE.NORMAL_NORMAL: + /* + ** Copy x and y to locals, since we may have + ** to alter them. + */ + locx = new InternalFPF(); + locy = new InternalFPF(); + memmove(locx, x); + memmove(locy, y); + + /* compute sum/difference */ + exponent_difference = locx.exp - locy.exp; + if (exponent_difference == 0) + { + /* + ** locx.exp == locy.exp + ** so, no shifting required + */ + if (locx.type == IFPF.IFPF_IS_SUBNORMAL || + locy.type == IFPF.IFPF_IS_SUBNORMAL) + z.type = IFPF.IFPF_IS_SUBNORMAL; + else + z.type = IFPF.IFPF_IS_NORMAL; + + /* + ** Assume that locx.mantissa > locy.mantissa + */ + z.sign = locx.sign; + z.exp = locx.exp; + } + else + if (exponent_difference > 0) + { + /* + ** locx.exp > locy.exp + */ + StickyShiftRightMant(locy, + exponent_difference); + z.type = locx.type; + z.sign = locx.sign; + z.exp = locx.exp; + } + else /* if (exponent_difference < 0) */ + { + /* + ** locx.exp < locy.exp + */ + StickyShiftRightMant(locx, + -exponent_difference); + z.type = locy.type; + z.sign = (byte)(locy.sign ^ operation); + z.exp = locy.exp; + } + + if ((locx.sign ^ locy.sign ^ operation) != 0) + { + /* + ** Signs are different, subtract mantissas + */ + borrow = (char)0; + for (i = (INTERNAL_FPF_PRECISION - 1); i >= 0; i--) + Sub16Bits(ref borrow, + out z.mantissa[i], + locx.mantissa[i], + locy.mantissa[i]); + + if (borrow != 0) + { + /* The y->mantissa was larger than the + ** x->mantissa leaving a negative + ** result. Change the result back to + ** an unsigned number and flip the + ** sign flag. + */ + z.sign = (byte)(locy.sign ^ operation); + borrow = (char)0; + for (i = (INTERNAL_FPF_PRECISION - 1); i >= 0; i--) + { + Sub16Bits(ref borrow, + out z.mantissa[i], + (char)0, + z.mantissa[i]); + } + } + else + { + /* The assumption made above + ** (i.e. x->mantissa >= y->mantissa) + ** was correct. Therefore, do nothing. + ** z->sign = x->sign; + */ + } + + if (IsMantissaZero(z.mantissa)) + { + z.type = IFPF.IFPF_IS_ZERO; + z.sign = 0; /* positive */ + } + else + if (locx.type == IFPF.IFPF_IS_NORMAL || + locy.type == IFPF.IFPF_IS_NORMAL) + { + normalize(z); + } + } + else + { + /* signs are the same, add mantissas */ + carry = (char)0; + for (i = (INTERNAL_FPF_PRECISION - 1); i >= 0; i--) + { + Add16Bits(ref carry, + out z.mantissa[i], + locx.mantissa[i], + locy.mantissa[i]); + } + + if (carry != 0) + { + z.exp++; + carry = (char)0; + ShiftMantRight1(ref carry, z.mantissa); + z.mantissa[0] |= (char)0x8000; + z.type = IFPF.IFPF_IS_NORMAL; + } + else + if ((z.mantissa[0] & 0x8000) != 0) + z.type = IFPF.IFPF_IS_NORMAL; + } + break; + + case STATE.INFINITY_INFINITY: + SetInternalFPFNaN(z); + break; + + case STATE.NAN_NAN: + choose_nan(x, y, z, 1); + break; + } + + /* + ** All the math is done; time to round. + */ + RoundInternalFPF(z); + return; + } + + + /************************ + ** MultiplyInternalFPF ** + ************************* + ** Two internal-representation numbers x and y are multiplied; the + ** result is returned in z. + */ + private static void MultiplyInternalFPF( + InternalFPF x, + InternalFPF y, + InternalFPF z) + { + int i; + int j; + char carry; + char[] extra_bits = new char[INTERNAL_FPF_PRECISION]; + InternalFPF locy; /* Needed since this will be altered */ + /* + ** As in the preceding function, this large switch + ** statement selects among the many combinations + ** of operands. + */ + int count = (int)IFPF.IFPF_TYPE_COUNT; + switch ((STATE)(((int)x.type * count) + (int)y.type)) + { + case STATE.INFINITY_SUBNORMAL: + case STATE.INFINITY_NORMAL: + case STATE.INFINITY_INFINITY: + case STATE.ZERO_ZERO: + case STATE.ZERO_SUBNORMAL: + case STATE.ZERO_NORMAL: + memmove(z, x); + z.sign ^= y.sign; + break; + + case STATE.SUBNORMAL_INFINITY: + case STATE.NORMAL_INFINITY: + case STATE.SUBNORMAL_ZERO: + case STATE.NORMAL_ZERO: + memmove(z, y); + z.sign ^= x.sign; + break; + + case STATE.ZERO_INFINITY: + case STATE.INFINITY_ZERO: + SetInternalFPFNaN(z); + break; + + case STATE.NAN_ZERO: + case STATE.NAN_SUBNORMAL: + case STATE.NAN_NORMAL: + case STATE.NAN_INFINITY: + memmove(z, x); + break; + + case STATE.ZERO_NAN: + case STATE.SUBNORMAL_NAN: + case STATE.NORMAL_NAN: + case STATE.INFINITY_NAN: + memmove(z, y); + break; + + + case STATE.SUBNORMAL_SUBNORMAL: + case STATE.SUBNORMAL_NORMAL: + case STATE.NORMAL_SUBNORMAL: + case STATE.NORMAL_NORMAL: + /* + ** Make a local copy of the y number, since we will be + ** altering it in the process of multiplying. + */ + locy = new InternalFPF(); + memmove(locy, y); + + /* + ** Check for unnormal zero arguments + */ + if (IsMantissaZero(x.mantissa) || IsMantissaZero(y.mantissa)) + { + SetInternalFPFInfinity(z, 0); + } + + /* + ** Initialize the result + */ + if (x.type == IFPF.IFPF_IS_SUBNORMAL || + y.type == IFPF.IFPF_IS_SUBNORMAL) + z.type = IFPF.IFPF_IS_SUBNORMAL; + else + z.type = IFPF.IFPF_IS_NORMAL; + + z.sign = (byte)(x.sign ^ y.sign); + z.exp = (short)(x.exp + y.exp); + for (i = 0; i < INTERNAL_FPF_PRECISION; i++) + { + z.mantissa[i] = (char)0; + extra_bits[i] = (char)0; + } + + for (i = 0; i < (INTERNAL_FPF_PRECISION * 16); i++) + { + /* + ** Get rightmost bit of the multiplier + */ + carry = (char)0; + ShiftMantRight1(ref carry, locy.mantissa); + if (carry != 0) + { + /* + ** Add the multiplicand to the product + */ + carry = (char)0; + for (j = (INTERNAL_FPF_PRECISION - 1); j >= 0; j--) + Add16Bits(ref carry, + out z.mantissa[j], + z.mantissa[j], + x.mantissa[j]); + } + else + { + carry = (char)0; + } + + /* + ** Shift the product right. Overflow bits get + ** shifted into extra_bits. We'll use it later + ** to help with the "sticky" bit. + */ + ShiftMantRight1(ref carry, z.mantissa); + ShiftMantRight1(ref carry, extra_bits); + } + + /* + ** Normalize + ** Note that we use a "special" normalization routine + ** because we need to use the extra bits. (These are + ** bits that may have been shifted off the bottom that + ** we want to reclaim...if we can. + */ + while ((z.mantissa[0] & 0x8000) == 0) + { + carry = (char)0; + ShiftMantLeft1(ref carry, extra_bits); + ShiftMantLeft1(ref carry, z.mantissa); + z.exp--; + } + + /* + ** Set the sticky bit if any bits set in extra bits. + */ + if (IsMantissaZero(extra_bits)) + { + z.mantissa[INTERNAL_FPF_PRECISION - 1] |= (char)1; + } + break; + + case STATE.NAN_NAN: + choose_nan(x, y, z, 0); + break; + } + + /* + ** All math done...do rounding. + */ + RoundInternalFPF(z); + return; + } + + + /********************** + ** DivideInternalFPF ** + *********************** + ** Divide internal FPF number x by y. Return result in z. + */ + private static void DivideInternalFPF( + InternalFPF x, + InternalFPF y, + InternalFPF z) + { + int i; + int j; + char carry; + char[] extra_bits = new char[INTERNAL_FPF_PRECISION]; + InternalFPF locx; /* Local for x number */ + + /* + ** As with preceding function, the following switch + ** statement selects among the various possible + ** operands. + */ + int count = (int)IFPF.IFPF_TYPE_COUNT; + switch ((STATE)(((int)x.type * count) + (int)y.type)) + { + case STATE.ZERO_ZERO: + case STATE.INFINITY_INFINITY: + SetInternalFPFNaN(z); + break; + + case STATE.ZERO_SUBNORMAL: + case STATE.ZERO_NORMAL: + if (IsMantissaZero(y.mantissa)) + { + SetInternalFPFNaN(z); + break; + } + goto case STATE.ZERO_INFINITY; + + case STATE.ZERO_INFINITY: + case STATE.SUBNORMAL_INFINITY: + case STATE.NORMAL_INFINITY: + SetInternalFPFZero(z, (byte)(x.sign ^ y.sign)); + break; + + case STATE.SUBNORMAL_ZERO: + case STATE.NORMAL_ZERO: + if (IsMantissaZero(x.mantissa)) + { + SetInternalFPFNaN(z); + break; + } + goto case STATE.INFINITY_ZERO; + + case STATE.INFINITY_ZERO: + case STATE.INFINITY_SUBNORMAL: + case STATE.INFINITY_NORMAL: + SetInternalFPFInfinity(z, 0); + z.sign = (byte)(x.sign ^ y.sign); + break; + + case STATE.NAN_ZERO: + case STATE.NAN_SUBNORMAL: + case STATE.NAN_NORMAL: + case STATE.NAN_INFINITY: + memmove(z, x); + break; + + case STATE.ZERO_NAN: + case STATE.SUBNORMAL_NAN: + case STATE.NORMAL_NAN: + case STATE.INFINITY_NAN: + memmove(z, y); + break; + + case STATE.SUBNORMAL_SUBNORMAL: + case STATE.NORMAL_SUBNORMAL: + case STATE.SUBNORMAL_NORMAL: + case STATE.NORMAL_NORMAL: + /* + ** Make local copy of x number, since we'll be + ** altering it in the process of dividing. + */ + + locx = new InternalFPF(); + memmove(locx, x); + + /* + ** Check for unnormal zero arguments + */ + if (IsMantissaZero(locx.mantissa)) + { + if (IsMantissaZero(y.mantissa)) + SetInternalFPFNaN(z); + else + SetInternalFPFZero(z, 0); + break; + } + if (IsMantissaZero(y.mantissa)) + { + SetInternalFPFInfinity(z, 0); + break; + } + + /* + ** Initialize the result + */ + z.type = x.type; + z.sign = (byte)(x.sign ^ y.sign); + z.exp = (short)(x.exp - y.exp + + ((INTERNAL_FPF_PRECISION * 16 * 2))); + for (i = 0; i < INTERNAL_FPF_PRECISION; i++) + { + z.mantissa[i] = (char)0; + extra_bits[i] = (char)0; + } + + while ((z.mantissa[0] & 0x8000) == 0) + { + carry = (char)0; + ShiftMantLeft1(ref carry, locx.mantissa); + ShiftMantLeft1(ref carry, extra_bits); + + /* + ** Time to subtract yet? + */ + if (carry == 0) + for (j = 0; j < INTERNAL_FPF_PRECISION; j++) + { + if (y.mantissa[j] > extra_bits[j]) + { + carry = (char)0; + goto no_subtract; + } + if (y.mantissa[j] < extra_bits[j]) + break; + } + /* + ** Divisor (y) <= dividend (x), subtract + */ + carry = (char)0; + for (j = (INTERNAL_FPF_PRECISION - 1); j >= 0; j--) + Sub16Bits(ref carry, + out extra_bits[j], + extra_bits[j], + y.mantissa[j]); + carry = (char)1; /* 1 shifted into quotient */ + no_subtract: + ShiftMantLeft1(ref carry, z.mantissa); + z.exp--; + } + break; + + case STATE.NAN_NAN: + choose_nan(x, y, z, 0); + break; + } + + /* + ** Math complete...do rounding + */ + RoundInternalFPF(z); + } + + /********************** + ** LongToInternalFPF ** + *********************** + ** Convert a signed long integer into an internal FPF number. + */ + private static void LongToInternalFPF( + int mylong, + InternalFPF dest) + { + int i; /* Index */ + char myword; /* Used to hold converted stuff */ + /* + ** Save the sign and get the absolute value. This will help us + ** with 64-bit machines, since we use only the lower 32 + ** bits just in case. + */ + if (mylong < 0) + { + dest.sign = 1; + mylong = 0 - mylong; + } + else + dest.sign = 0; + /* + ** Prepare the destination floating point number + */ + dest.type = IFPF.IFPF_IS_NORMAL; + for (i = 0; i < INTERNAL_FPF_PRECISION; i++) + dest.mantissa[i] = (char)0; + + /* + ** See if we've got a zero. If so, make the resultant FP + ** number a true zero and go home. + */ + if (mylong == 0) + { + dest.type = IFPF.IFPF_IS_ZERO; + dest.exp = 0; + return; + } + + /* + ** Not a true zero. Set the exponent to 32 (internal FPFs have + ** no bias) and load the low and high words into their proper + ** locations in the mantissa. Then normalize. The action of + ** normalizing slides the mantissa bits into place and sets + ** up the exponent properly. + */ + dest.exp = 32; + myword = (char)((mylong >> 16) & 0xFFFFL); + dest.mantissa[0] = myword; + myword = (char)(mylong & 0xFFFFL); + dest.mantissa[1] = myword; + normalize(dest); + return; + } + + /************************ + ** InternalFPFToString ** + ************************* + ** FOR DEBUG PURPOSES + ** This routine converts an internal floating point representation + ** number to a string. Used in debugging the package. + ** Returns length of converted number. + ** NOTE: dest must point to a buffer big enough to hold the + ** result. Also, this routine does append a null (an effect + ** of using the sprintf() function). It also returns + ** a length count. + ** NOTE: This routine returns 5 significant digits. Thats + ** about all I feel safe with, given the method of + ** conversion. It should be more than enough for programmers + ** to determine whether the package is properly ported. + */ + private static int InternalFPFToString( + out string dest, + InternalFPF src) + { + InternalFPF locFPFNum; /* Local for src (will be altered) */ + InternalFPF IFPF10; /* Floating-point 10 */ + InternalFPF IFPFComp; /* For doing comparisons */ + int msign; /* Holding for mantissa sign */ + int expcount; /* Exponent counter */ + int ccount; /* Character counter */ + int i, j, k; /* Index */ + char carryaccum; /* Carry accumulator */ + char mycarry; /* Local for carry */ + + locFPFNum = new InternalFPF(); + IFPF10 = new InternalFPF(); + IFPFComp = new InternalFPF(); + dest = ""; + /* + ** Check first for the simple things...Nan, Infinity, Zero. + ** If found, copy the proper string in and go home. + */ + switch (src.type) + { + case IFPF.IFPF_IS_NAN: + dest = "NaN"; + return (3); + + case IFPF.IFPF_IS_INFINITY: + if (src.sign == 0) + dest = "+Inf"; + else + dest = "-Inf"; + return (4); + + case IFPF.IFPF_IS_ZERO: + if (src.sign == 0) + dest = "+0"; + else + dest = "-0"; + return (2); + } + + /* + ** Move the internal number into our local holding area, since + ** we'll be altering it to print it out. + */ + memmove(locFPFNum, src); + + /* + ** Set up a floating-point 10...which we'll use a lot in a minute. + */ + LongToInternalFPF(10, IFPF10); + + /* + ** Save the mantissa sign and make it positive. + */ + msign = src.sign; + src.sign = 0; + + expcount = 0; /* Init exponent counter */ + + /* + ** See if the number is less than 10. If so, multiply + ** the number repeatedly by 10 until it's not. For each + ** multiplication, decrement a counter so we can keep track + ** of the exponent. + */ + while (true) + { + AddSubInternalFPF(1, locFPFNum, IFPF10, IFPFComp); + if (IFPFComp.sign == 0) + break; + MultiplyInternalFPF(locFPFNum, IFPF10, IFPFComp); + expcount--; + memmove(locFPFNum, IFPFComp); + } + + /* + ** Do the reverse of the above. As long as the number is + ** greater than or equal to 10, divide it by 10. Increment the + ** exponent counter for each multiplication. + */ + while (true) + { + AddSubInternalFPF(1, locFPFNum, IFPF10, IFPFComp); + if (IFPFComp.sign != 0) + break; + DivideInternalFPF(locFPFNum, IFPF10, IFPFComp); + expcount++; + memmove(locFPFNum, IFPFComp); + } + + /* + ** About time to start storing things. First, store the + ** mantissa sign. + */ + ccount = 1; /* Init character counter */ + if (msign == 0) + dest += "+"; + else + dest += "-"; + + /* + ** At this point we know that the number is in the range + ** 10 > n >=1. We need to "strip digits" out of the + ** mantissa. We do this by treating the mantissa as + ** an integer and multiplying by 10. (Not a floating-point + ** 10, but an integer 10. Since this is debug code and we + ** could care less about speed, we'll do it the stupid + ** way and simply add the number to itself 10 times. + ** Anything that makes it to the left of the implied binary point + ** gets stripped off and emitted. We'll do this for + ** 5 significant digits (which should be enough to + ** verify things). + */ + /* + ** Re-position radix point + */ + carryaccum = (char)0; + while (locFPFNum.exp > 0) + { + mycarry = (char)0; + ShiftMantLeft1(ref mycarry, locFPFNum.mantissa); + carryaccum = (char)(carryaccum << 1); + if (mycarry != 0) + carryaccum++; + locFPFNum.exp--; + } + + while (locFPFNum.exp < 0) + { + mycarry = (char)0; + ShiftMantRight1(ref mycarry, locFPFNum.mantissa); + locFPFNum.exp++; + } + + for (i = 0; i < 6; i++) + if (i == 1) + { /* Emit decimal point */ + dest += "."; + ccount++; + } + else + { /* Emit a digit */ + string s = "0"; // ((char)('0'+carryaccum)); // never gets called. + dest += s; + ccount++; + + carryaccum = (char)0; + memmove(IFPF10, locFPFNum); + + /* Do multiply via repeated adds */ + for (j = 0; j < 9; j++) + { + mycarry = (char)0; + for (k = (INTERNAL_FPF_PRECISION - 1); k >= 0; k--) + Add16Bits(ref mycarry, out IFPFComp.mantissa[k], + locFPFNum.mantissa[k], + IFPF10.mantissa[k]); + carryaccum += (char)(mycarry != 0 ? 1 : 0); + memmove(locFPFNum, IFPFComp); + } + } + + /* + ** Now move the 'E', the exponent sign, and the exponent + ** into the string. + */ + dest += "E"; + dest += expcount.ToString(); + + /* + ** All done, go home. + */ + return (dest.Length); + } +} diff --git a/tests/src/JIT/Performance/CodeQuality/Bytemark/fourier.cs b/tests/src/JIT/Performance/CodeQuality/Bytemark/fourier.cs new file mode 100644 index 0000000000..ffcbda8e2c --- /dev/null +++ b/tests/src/JIT/Performance/CodeQuality/Bytemark/fourier.cs @@ -0,0 +1,227 @@ +/* +** Copyright (c) Microsoft. All rights reserved. +** Licensed under the MIT license. +** See LICENSE file in the project root for full license information. +** +** This program was translated to C# and adapted for xunit-performance. +** New variants of several tests were added to compare class versus +** struct and to compare jagged arrays vs multi-dimensional arrays. +*/ + +/* +** BYTEmark (tm) +** BYTE Magazine's Native Mode benchmarks +** Rick Grehan, BYTE Magazine +** +** Create: +** Revision: 3/95 +** +** DISCLAIMER +** The source, executable, and documentation files that comprise +** the BYTEmark benchmarks are made available on an "as is" basis. +** This means that we at BYTE Magazine have made every reasonable +** effort to verify that the there are no errors in the source and +** executable code. We cannot, however, guarantee that the programs +** are error-free. Consequently, McGraw-HIll and BYTE Magazine make +** no claims in regard to the fitness of the source code, executable +** code, and documentation of the BYTEmark. +** +** Furthermore, BYTE Magazine, McGraw-Hill, and all employees +** of McGraw-Hill cannot be held responsible for any damages resulting +** from the use of this code or the results obtained from using +** this code. +*/ + +using System; + +public class Fourier : FourierStruct +{ + public override string Name() + { + return "FOURIER"; + } + + /* + ** Perform the transcendental/trigonometric portion of the + ** benchmark. This benchmark calculates the first n + ** fourier coefficients of the function (x+1)^x defined + ** on the interval 0,2. + */ + public override double Run() + { + double[] abase; /* Base of A[] coefficients array */ + double[] bbase; /* Base of B[] coefficients array */ + long accumtime; /* Accumulated time in ticks */ + double iterations; /* # of iterations */ + + /* + ** See if we need to do self-adjustment code. + */ + if (this.adjust == 0) + { + this.arraysize = 100; /* Start at 100 elements */ + while (true) + { + abase = new double[this.arraysize]; + bbase = new double[this.arraysize]; + /* + ** Do an iteration of the tests. If the elapsed time is + ** less than or equal to the permitted minimum, re-allocate + ** larger arrays and try again. + */ + if (DoFPUTransIteration(abase, bbase, this.arraysize) > global.min_ticks) break; + this.arraysize += 50; + } + } + else + { + /* + ** Don't need self-adjustment. Just allocate the + ** arrays, and go. + */ + abase = new double[this.arraysize]; + bbase = new double[this.arraysize]; + } + + accumtime = 0L; + iterations = 0.0; + do + { + accumtime += DoFPUTransIteration(abase, bbase, this.arraysize); + iterations += (double)this.arraysize * (double)2.0 - (double)1.0; + } while (ByteMark.TicksToSecs(accumtime) < this.request_secs); + + if (this.adjust == 0) + this.adjust = 1; + + return iterations / (double)ByteMark.TicksToFracSecs(accumtime); + } + + /* + ** Perform an iteration of the FPU Transcendental/trigonometric + ** benchmark. Here, an iteration consists of calculating the + ** first n fourier coefficients of the function (x+1)^x on + ** the interval 0,2. n is given by arraysize. + ** NOTE: The # of integration steps is fixed at + ** 200. + */ + private static long DoFPUTransIteration(double[] abase, double[] bbase, int arraysize) + { + double omega; /* Fundamental frequency */ + int i; /* Index */ + long elapsed; /* Elapsed time */ + + elapsed = ByteMark.StartStopwatch(); + + /* + ** Calculate the fourier series. Begin by + ** calculating A[0]. + */ + + abase[0] = TrapezoidIntegrate(0.0, 2.0, 200, 0.0, 0) / 2.0; + + /* + ** Calculate the fundamental frequency. + ** ( 2 * pi ) / period...and since the period + ** is 2, omega is simply pi. + */ + omega = 3.1415926535897921; + + for (i = 1; i < arraysize; i++) + { + /* + ** Calculate A[i] terms. Note, once again, that we + ** can ignore the 2/period term outside the integral + ** since the period is 2 and the term cancels itself + ** out. + */ + abase[i] = TrapezoidIntegrate(0.0, 2.0, 200, omega * (double)i, 1); + + bbase[i] = TrapezoidIntegrate(0.0, 2.0, 200, omega * (double)i, 2); + } + /* + ** All done, stop the stopwatch + */ + return (ByteMark.StopStopwatch(elapsed)); + } + + /* + ** Perform a simple trapezoid integration on the + ** function (x+1)**x. + ** x0,x1 set the lower and upper bounds of the + ** integration. + ** nsteps indicates # of trapezoidal sections + ** omegan is the fundamental frequency times + ** the series member # + ** select = 0 for the A[0] term, 1 for cosine terms, and + ** 2 for sine terms. + ** Returns the value. + */ + private static double TrapezoidIntegrate(double x0, double x1, int nsteps, double omegan, int select) + { + double x; /* Independent variable */ + double dx; /* Stepsize */ + double rvalue; /* Return value */ + + /* + ** Initialize independent variable + */ + x = x0; + + /* + ** Calculate stepsize + */ + dx = (x1 - x0) / (double)nsteps; + + /* + ** Initialize the return value. + */ + rvalue = thefunction(x0, omegan, select) / (double)2.0; + + /* + ** Compute the other terms of the integral. + */ + if (nsteps != 1) + { + --nsteps; /* Already done 1 step */ + while (--nsteps != 0) + { + x += dx; + rvalue += thefunction(x, omegan, select); + } + } + /* + ** Finish computation + */ + rvalue = (rvalue + thefunction(x1, omegan, select) / (double)2.0) * dx; + + return (rvalue); + } + + /* + ** This routine selects the function to be used + ** in the Trapezoid integration. + ** x is the independent variable + ** omegan is omega * n + ** select chooses which of the sine/cosine functions + ** are used. note the special case for select=0. + */ + private static double thefunction(double x, double omegan, int select) + { + /* + ** Use select to pick which function we call. + */ + switch (select) + { + case 0: return Math.Pow(x + (double)1.0, x); + case 1: return Math.Pow(x + (double)1.0, x) * Math.Cos(omegan * x); + case 2: return Math.Pow(x + (double)1.0, x) * Math.Sin(omegan * x); + } + + /* + ** We should never reach this point, but the following + ** keeps compilers from issuing a warning message. + */ + return (0.0); + } +} diff --git a/tests/src/JIT/Performance/CodeQuality/Bytemark/idea.cs b/tests/src/JIT/Performance/CodeQuality/Bytemark/idea.cs new file mode 100644 index 0000000000..d57ca779ae --- /dev/null +++ b/tests/src/JIT/Performance/CodeQuality/Bytemark/idea.cs @@ -0,0 +1,448 @@ +/* +** Copyright (c) Microsoft. All rights reserved. +** Licensed under the MIT license. +** See LICENSE file in the project root for full license information. +** +** This program was translated to C# and adapted for xunit-performance. +** New variants of several tests were added to compare class versus +** struct and to compare jagged arrays vs multi-dimensional arrays. +*/ + +/* +** BYTEmark (tm) +** BYTE Magazine's Native Mode benchmarks +** Rick Grehan, BYTE Magazine +** +** Create: +** Revision: 3/95 +** +** DISCLAIMER +** The source, executable, and documentation files that comprise +** the BYTEmark benchmarks are made available on an "as is" basis. +** This means that we at BYTE Magazine have made every reasonable +** effort to verify that the there are no errors in the source and +** executable code. We cannot, however, guarantee that the programs +** are error-free. Consequently, McGraw-HIll and BYTE Magazine make +** no claims in regard to the fitness of the source code, executable +** code, and documentation of the BYTEmark. +** +** Furthermore, BYTE Magazine, McGraw-Hill, and all employees +** of McGraw-Hill cannot be held responsible for any damages resulting +** from the use of this code or the results obtained from using +** this code. +*/ + +/******************** +** IDEA Encryption ** +********************* +** IDEA - International Data Encryption Algorithm. +** Based on code presented in Applied Cryptography by Bruce Schneier. +** Which was based on code developed by Xuejia Lai and James L. Massey. +** Other modifications made by Colin Plumb. +** +*/ + +/*********** +** DoIDEA ** +************ +** Perform IDEA encryption. Note that we time encryption & decryption +** time as being a single loop. +*/ + +using System; + +public class IDEAEncryption : IDEAStruct +{ + public override string Name() + { + return "IDEA"; + } + + public override double Run() + { + int i; + char[] Z = new char[global.KEYLEN]; + char[] DK = new char[global.KEYLEN]; + char[] userkey = new char[8]; + long accumtime; + double iterations; + byte[] plain1; /* First plaintext buffer */ + byte[] crypt1; /* Encryption buffer */ + byte[] plain2; /* Second plaintext buffer */ + + /* + ** Re-init random-number generator. + */ + ByteMark.randnum(3); + + /* + ** Build an encryption/decryption key + */ + for (i = 0; i < 8; i++) + userkey[i] = (char)(ByteMark.abs_randwc(60000) & 0xFFFF); + for (i = 0; i < global.KEYLEN; i++) + Z[i] = (char)0; + + /* + ** Compute encryption/decryption subkeys + */ + en_key_idea(userkey, Z); + de_key_idea(Z, DK); + + /* + ** Allocate memory for buffers. We'll make 3, called plain1, + ** crypt1, and plain2. It works like this: + ** plain1 >>encrypt>> crypt1 >>decrypt>> plain2. + ** So, plain1 and plain2 should match. + ** Also, fill up plain1 with sample text. + */ + plain1 = new byte[this.arraysize]; + crypt1 = new byte[this.arraysize]; + plain2 = new byte[this.arraysize]; + + /* + ** Note that we build the "plaintext" by simply loading + ** the array up with random numbers. + */ + for (i = 0; i < this.arraysize; i++) + plain1[i] = (byte)(ByteMark.abs_randwc(255) & 0xFF); + + /* + ** See if we need to perform self adjustment loop. + */ + if (this.adjust == 0) + { + /* + ** Do self-adjustment. This involves initializing the + ** # of loops and increasing the loop count until we + ** get a number of loops that we can use. + */ + for (this.loops = 100; + this.loops < global.MAXIDEALOOPS; + this.loops += 10) + if (DoIDEAIteration(plain1, crypt1, plain2, + this.arraysize, + this.loops, + Z, DK) > global.min_ticks) + break; + } + + /* + ** All's well if we get here. Do the test. + */ + accumtime = 0; + iterations = (double)0.0; + + do + { + accumtime += DoIDEAIteration(plain1, crypt1, plain2, + this.arraysize, + this.loops, Z, DK); + iterations += (double)this.loops; + } while (ByteMark.TicksToSecs(accumtime) < this.request_secs); + + /* + ** Clean up, calculate results, and go home. Be sure to + ** show that we don't have to rerun adjustment code. + */ + + if (this.adjust == 0) + this.adjust = 1; + + return (iterations / ByteMark.TicksToFracSecs(accumtime)); + } + + /******************** + ** DoIDEAIteration ** + ********************* + ** Execute a single iteration of the IDEA encryption algorithm. + ** Actually, a single iteration is one encryption and one + ** decryption. + */ + private static long DoIDEAIteration(byte[] plain1, + byte[] crypt1, + byte[] plain2, + int arraysize, + int nloops, + char[] Z, + char[] DK) + { + int i; + int j; + long elapsed; + + /* + ** Start the stopwatch. + */ + elapsed = ByteMark.StartStopwatch(); + + /* + ** Do everything for nloops. + */ + + for (i = 0; i < nloops; i++) + { + for (j = 0; j < arraysize; j += 8) + cipher_idea(plain1, crypt1, j, Z); /* Encrypt */ + + for (j = 0; j < arraysize; j += 8) + cipher_idea(crypt1, plain2, j, DK); /* Decrypt */ + } + + // Validate output + for (j = 0; j < arraysize; j++) + if (plain1[j] != plain2[j]) + { + string error = String.Format("IDEA: error at index {0} ({1} <> {2})!", j, (int)plain1[j], (int)plain2[j]); + throw new Exception(error); + } + + /* + ** Get elapsed time. + */ + return (ByteMark.StopStopwatch(elapsed)); + } + + /******** + ** mul ** + ********* + ** Performs multiplication, modulo (2**16)+1. This code is structured + ** on the assumption that untaken branches are cheaper than taken + ** branches, and that the compiler doesn't schedule branches. + */ + private static char mul(char a, char b) + { + int p; + if (a != 0) + { + if (b != 0) + { + p = unchecked((int)(a * b)); + b = low16(p); + a = unchecked((char)(p >> 16)); + return unchecked((char)(b - a + (b < a ? 1 : 0))); + } + else + return unchecked((char)(1 - a)); + } + else + return unchecked((char)(1 - b)); + } + + /******** + ** inv ** + ********* + ** Compute multiplicative inverse of x, modulo (2**16)+1 + ** using Euclid's GCD algorithm. It is unrolled twice + ** to avoid swapping the meaning of the registers. And + ** some subtracts are changed to adds. + */ + private static char inv(char x) + { + char t0, t1; + char q, y; + + if (x <= 1) + return (x); /* 0 and 1 are self-inverse */ + + t1 = (char)(0x10001 / x); + y = (char)(0x10001 % x); + + if (y == 1) + return (low16(1 - t1)); + + t0 = (char)1; + + do + { + q = (char)(x / y); + x = (char)(x % y); + t0 += (char)(q * t1); + if (x == 1) + return (t0); + q = (char)(y / x); + y = (char)(y % x); + t1 += (char)(q * t0); + } while (y != 1); + return (low16(1 - t1)); + } + + /**************** + ** en_key_idea ** + ***************** + ** Compute IDEA encryption subkeys Z + */ + private static void en_key_idea(char[] userkey, char[] Z) + { + int i, j; + + // NOTE: The temp variables (tmp,idx) were not in original C code. + // It may affect numbers a bit. + int tmp = 0; + int idx = 0; + + /* + ** shifts + */ + for (j = 0; j < 8; j++) + Z[j + idx] = userkey[tmp++]; + for (i = 0; j < global.KEYLEN; j++) + { + i++; + Z[i + 7 + idx] = unchecked((char)((Z[(i & 7) + idx] << 9) | (Z[((i + 1) & 7) + idx] >> 7))); + idx += (i & 8); + i &= 7; + } + return; + } + + /**************** + ** de_key_idea ** + ***************** + ** Compute IDEA decryption subkeys DK from encryption + ** subkeys Z. + */ + private static void de_key_idea(char[] Z, char[] DK) + { + char[] TT = new char[global.KEYLEN]; + int j; + char t1, t2, t3; + + short p = (short)global.KEYLEN; + + // NOTE: Another local variable was needed here but was not in original C. + // May affect benchmark numbers. + int tmpZ = 0; + + t1 = inv(Z[tmpZ++]); + t2 = unchecked((char)(-Z[tmpZ++])); + t3 = unchecked((char)(-Z[tmpZ++])); + TT[--p] = inv(Z[tmpZ++]); + TT[--p] = t3; + TT[--p] = t2; + TT[--p] = t1; + + for (j = 1; j < global.ROUNDS; j++) + { + t1 = Z[tmpZ++]; + TT[--p] = Z[tmpZ++]; + TT[--p] = t1; + t1 = inv(Z[tmpZ++]); + t2 = unchecked((char)(-Z[tmpZ++])); + t3 = unchecked((char)(-Z[tmpZ++])); + TT[--p] = inv(Z[tmpZ++]); + TT[--p] = t2; + TT[--p] = t3; + TT[--p] = t1; + } + + t1 = Z[tmpZ++]; + TT[--p] = Z[tmpZ++]; + TT[--p] = t1; + t1 = inv(Z[tmpZ++]); + t2 = unchecked((char)(-Z[tmpZ++])); + t3 = unchecked((char)(-Z[tmpZ++])); + TT[--p] = inv(Z[tmpZ++]); + TT[--p] = t3; + TT[--p] = t2; + TT[--p] = t1; + + /* + ** Copy and destroy temp copy + */ + for (j = 0, p = 0; j < global.KEYLEN; j++) + { + DK[j] = TT[p]; + TT[p++] = (char)0; + } + + return; + } + + /* + ** MUL(x,y) + ** This #define creates a macro that computes x=x*y modulo 0x10001. + ** Requires temps t16 and t32. Also requires y to be strictly 16 + ** bits. Here, I am using the simplest form. May not be the + ** fastest. -- RG + */ + /* #define MUL(x,y) (x=mul(low16(x),y)) */ + + /**************** + ** cipher_idea ** + ***************** + ** IDEA encryption/decryption algorithm. + */ + + // NOTE: args in and out were renamed because in/out are reserved words + // in cool. + + private static void cipher_idea(byte[] xin, byte[] xout, int offset, char[] Z) + { + char x1, x2, x3, x4, t1, t2; + int r = global.ROUNDS; + + // NOTE: More local variables (AND AN ARG) were required by this + // function. The original C code did not need/have these. + int offset2 = offset; + int idx = 0; + + // NOTE: Because of big endian (and lack of pointers) I had to + // force two bytes into the chars instead of how original + // c code did it. + unchecked + { + x1 = (char)((xin[offset]) | (xin[offset + 1] << 8)); + x2 = (char)((xin[offset + 2]) | (xin[offset + 3] << 8)); + x3 = (char)((xin[offset + 4]) | (xin[offset + 5] << 8)); + x4 = (char)((xin[offset + 6]) | (xin[offset + 7] << 8)); + + do + { + MUL(ref x1, Z[idx++]); + x2 += Z[idx++]; + x3 += Z[idx++]; + MUL(ref x4, Z[idx++]); + + t2 = (char)(x1 ^ x3); + MUL(ref t2, Z[idx++]); + t1 = (char)(t2 + (x2 ^ x4)); + MUL(ref t1, Z[idx++]); + t2 = (char)(t1 + t2); + + x1 ^= t1; + x4 ^= t2; + + t2 ^= x2; + x2 = (char)(x3 ^ t1); + x3 = t2; + } while ((--r) != 0); + + MUL(ref x1, Z[idx++]); + xout[offset2] = (byte)(x1 & 0x00ff); + xout[offset2 + 1] = (byte)((x1 >> 8) & 0x00ff); + xout[offset2 + 2] = (byte)((x3 + Z[idx]) & 0x00ff); + xout[offset2 + 3] = (byte)(((x3 + Z[idx++]) >> 8) & 0x00ff); + xout[offset2 + 4] = (byte)((x2 + Z[idx]) & 0x00ff); + xout[offset2 + 5] = (byte)(((x2 + Z[idx++]) >> 8) & 0x00ff); + MUL(ref x4, Z[idx]); + xout[offset2 + 6] = (byte)(x4 & 0x00ff); + xout[offset2 + 7] = (byte)((x4 >> 8) & 0x00ff); + } + return; + } + + // These were macros in the original C code + + /* #define low16(x) ((x) & 0x0FFFF) */ + private static char low16(int x) + { + return (char)((x) & 0x0FFFF); + } + + /* #define MUL(x,y) (x=mul(low16(x),y)) */ + private static void MUL(ref char x, char y) + { + x = mul(low16(x), y); + } +} diff --git a/tests/src/JIT/Performance/CodeQuality/Bytemark/ludecomp.cs b/tests/src/JIT/Performance/CodeQuality/Bytemark/ludecomp.cs new file mode 100644 index 0000000000..006af82603 --- /dev/null +++ b/tests/src/JIT/Performance/CodeQuality/Bytemark/ludecomp.cs @@ -0,0 +1,337 @@ +/* +** Copyright (c) Microsoft. All rights reserved. +** Licensed under the MIT license. +** See LICENSE file in the project root for full license information. +** +** This program was translated to C# and adapted for xunit-performance. +** New variants of several tests were added to compare class versus +** struct and to compare jagged arrays vs multi-dimensional arrays. +*/ + +/* +** BYTEmark (tm) +** BYTE Magazine's Native Mode benchmarks +** Rick Grehan, BYTE Magazine +** +** Create: +** Revision: 3/95 +** +** DISCLAIMER +** The source, executable, and documentation files that comprise +** the BYTEmark benchmarks are made available on an "as is" basis. +** This means that we at BYTE Magazine have made every reasonable +** effort to verify that the there are no errors in the source and +** executable code. We cannot, however, guarantee that the programs +** are error-free. Consequently, McGraw-HIll and BYTE Magazine make +** no claims in regard to the fitness of the source code, executable +** code, and documentation of the BYTEmark. +** +** Furthermore, BYTE Magazine, McGraw-Hill, and all employees +** of McGraw-Hill cannot be held responsible for any damages resulting +** from the use of this code or the results obtained from using +** this code. +*/ + +using System; + +/*********************** +** LU DECOMPOSITION ** +** (Linear Equations) ** +************************ +** These routines come from "Numerical Recipes in Pascal". +** Note that, as in the assignment algorithm, though we +** separately define LUARRAYROWS and LUARRAYCOLS, the two +** must be the same value (this routine depends on a square +** matrix). +*/ + +internal class LUDecomp : LUStruct +{ + private const int MAXLUARRAYS = 1000; + + private static double[] s_LUtempvv; + + public override string Name() + { + return "LU DECOMPOSITION"; + } + + public override double Run() + { + double[][] a; + double[] b; + double[][][] abase = null; + double[][] bbase = null; + int n; + int i; + long accumtime; + double iterations; + + /* + ** Our first step is to build a "solvable" problem. This + ** will become the "seed" set that all others will be + ** derived from. (I.E., we'll simply copy these arrays + ** into the others. + */ + a = new double[global.LUARRAYROWS][]; + for (int j = 0; j < global.LUARRAYROWS; j++) + { + a[j] = new double[global.LUARRAYCOLS]; + } + b = new double[global.LUARRAYROWS]; + n = global.LUARRAYROWS; + + s_LUtempvv = new double[global.LUARRAYROWS]; + + build_problem(a, n, b); + + if (this.adjust == 0) + { + for (i = 1; i <= MAXLUARRAYS; i++) + { + abase = new double[i + 1][][]; + for (int j = 0; j < i + 1; j++) + { + abase[j] = new double[global.LUARRAYROWS][]; + for (int k = 0; k < global.LUARRAYROWS; k++) + { + abase[j][k] = new double[global.LUARRAYCOLS]; + } + } + + bbase = new double[i + 1][]; + for (int j = 0; j < i + 1; j++) + { + bbase[j] = new double[global.LUARRAYROWS]; + } + + if (DoLUIteration(a, b, abase, bbase, i) > global.min_ticks) + { + this.numarrays = i; + break; + } + } + + if (this.numarrays == 0) + { + throw new Exception("FPU:LU -- Array limit reached"); + } + } + else + { + abase = new double[this.numarrays][][]; + for (int j = 0; j < this.numarrays; j++) + { + abase[j] = new double[global.LUARRAYROWS][]; + for (int k = 0; k < global.LUARRAYROWS; k++) + { + abase[j][k] = new double[global.LUARRAYCOLS]; + } + } + bbase = new double[this.numarrays][]; + for (int j = 0; j < this.numarrays; j++) + { + bbase[j] = new double[global.LUARRAYROWS]; + } + } + + accumtime = 0; + iterations = 0.0; + + do + { + accumtime += DoLUIteration(a, b, abase, bbase, this.numarrays); + iterations += (double)this.numarrays; + } while (ByteMark.TicksToSecs(accumtime) < this.request_secs); + + if (this.adjust == 0) this.adjust = 1; + + return iterations / ByteMark.TicksToFracSecs(accumtime); + } + + private static long DoLUIteration(double[][] a, double[] b, double[][][] abase, double[][] bbase, int numarrays) + { + double[][] locabase; + double[] locbbase; + long elapsed; + int k, j, i; + + for (j = 0; j < numarrays; j++) + { + locabase = abase[j]; + locbbase = bbase[j]; + for (i = 0; i < global.LUARRAYROWS; i++) + for (k = 0; k < global.LUARRAYCOLS; k++) + locabase[i][k] = a[i][k]; + for (i = 0; i < global.LUARRAYROWS; i++) + locbbase[i] = b[i]; + } + + elapsed = ByteMark.StartStopwatch(); + + for (i = 0; i < numarrays; i++) + { + locabase = abase[i]; + locbbase = bbase[i]; + + lusolve(locabase, global.LUARRAYROWS, locbbase); + } + + return (ByteMark.StopStopwatch(elapsed)); + } + + private static void build_problem(double[][] a, int n, double[] b) + { + int i, j, k, k1; + double rcon; + + ByteMark.randnum(13); + + for (i = 0; i < n; i++) + { + b[i] = (double)(ByteMark.abs_randwc(100) + 1); + for (j = 0; j < n; j++) + if (i == j) + a[i][j] = (double)(ByteMark.abs_randwc(1000) + 1); + else + a[i][j] = (double)0.0; + } + + for (i = 0; i < 8 * n; i++) + { + k = ByteMark.abs_randwc(n); + k1 = ByteMark.abs_randwc(n); + if (k != k1) + { + if (k < k1) rcon = 1.0; + else rcon = -1.0; + for (j = 0; j < n; j++) + a[k][j] += a[k1][j] * rcon; + b[k] += b[k1] * rcon; + } + } + } + + private static int ludcmp(double[][] a, int n, int[] indx, out int d) + { + double big; + double sum; + double dum; + int i, j, k; + int imax = 0; + double tiny; + + tiny = 1.0e-20; + d = 1; + + for (i = 0; i < n; i++) + { + big = 0.0; + for (j = 0; j < n; j++) + if (Math.Abs(a[i][j]) > big) + big = Math.Abs(a[i][j]); + if (big == 0.0) return 0; + s_LUtempvv[i] = 1.0 / big; + } + + for (j = 0; j < n; j++) + { + if (j != 0) + for (i = 0; i < j; i++) + { + sum = a[i][j]; + if (i != 0) + for (k = 0; k < i; k++) + sum -= a[i][k] * a[k][j]; + a[i][j] = sum; + } + big = 0.0; + + for (i = j; i < n; i++) + { + sum = a[i][j]; + if (j != 0) + for (k = 0; k < j; k++) + sum -= a[i][k] * a[k][j]; + a[i][j] = sum; + dum = s_LUtempvv[i] * Math.Abs(sum); + if (dum >= big) + { + big = dum; + imax = i; + } + } + + if (j != imax) + { + for (k = 0; k < n; k++) + { + dum = a[imax][k]; + a[imax][k] = a[j][k]; + a[j][k] = dum; + } + d = -d; + dum = s_LUtempvv[imax]; + s_LUtempvv[imax] = s_LUtempvv[j]; + s_LUtempvv[j] = dum; + } + + indx[j] = imax; + if (a[j][j] == 0.0) + a[j][j] = tiny; + if (j != (n - 1)) + { + dum = 1.0 / a[j][j]; + for (i = j + 1; i < n; i++) + a[i][j] = a[i][j] * dum; + } + } + + return 1; + } + + private static void lubksb(double[][] a, int n, int[] indx, double[] b) + { + int i, j; + int ip; + int ii; + double sum; + + ii = -1; + + for (i = 0; i < n; i++) + { + ip = indx[i]; + sum = b[ip]; + b[ip] = b[i]; + if (ii != -1) + for (j = ii; j < i; j++) + sum = sum - a[i][j] * b[j]; + else + if (sum != (double)0.0) + ii = i; + b[i] = sum; + } + + for (i = (n - 1); i >= 0; i--) + { + sum = b[i]; + if (i != (n - 1)) + for (j = (i + 1); j < n; j++) + sum = sum - a[i][j] * b[j]; + b[i] = sum / a[i][i]; + } + } + + private static int lusolve(double[][] a, int n, double[] b) + { + int[] indx = new int[global.LUARRAYROWS]; + int d; + + if (ludcmp(a, n, indx, out d) == 0) return 0; + + lubksb(a, n, indx, b); + + return 1; + } +} diff --git a/tests/src/JIT/Performance/CodeQuality/Bytemark/neural-dat.cs b/tests/src/JIT/Performance/CodeQuality/Bytemark/neural-dat.cs new file mode 100644 index 0000000000..e0ffb4a6ac --- /dev/null +++ b/tests/src/JIT/Performance/CodeQuality/Bytemark/neural-dat.cs @@ -0,0 +1,228 @@ +/* +** Copyright (c) Microsoft. All rights reserved. +** Licensed under the MIT license. +** See LICENSE file in the project root for full license information. +** +** This program was translated to C# and adapted for xunit-performance. +** New variants of several tests were added to compare class versus +** struct and to compare jagged arrays vs multi-dimensional arrays. +*/ + +// Captures contents of NNET.DAT as a string +// to simplfy testing in CoreCLR. + +internal class NeuralData +{ + public static string Input = + "5 7 8 \n" + + "26 \n" + + "0 0 1 0 0 \n" + + "0 1 0 1 0 \n" + + "1 0 0 0 1 \n" + + "1 0 0 0 1 \n" + + "1 1 1 1 1 \n" + + "1 0 0 0 1 \n" + + "1 0 0 0 1 \n" + + "0 1 0 0 0 0 0 1 \n" + + "1 1 1 1 0 \n" + + "1 0 0 0 1 \n" + + "1 0 0 0 1 \n" + + "1 1 1 1 0 \n" + + "1 0 0 0 1 \n" + + "1 0 0 0 1 \n" + + "1 1 1 1 0 \n" + + "0 1 0 0 0 0 1 0 \n" + + "0 1 1 1 0 \n" + + "1 0 0 0 1 \n" + + "1 0 0 0 0 \n" + + "1 0 0 0 0 \n" + + "1 0 0 0 0 \n" + + "1 0 0 0 1 \n" + + "0 1 1 1 0 \n" + + "0 1 0 0 0 0 1 1 \n" + + "1 1 1 1 0 \n" + + "1 0 0 0 1 \n" + + "1 0 0 0 1 \n" + + "1 0 0 0 1 \n" + + "1 0 0 0 1 \n" + + "1 0 0 0 1 \n" + + "1 1 1 1 0 \n" + + "0 1 0 0 0 1 0 0 \n" + + "1 1 1 1 1 \n" + + "1 0 0 0 0 \n" + + "1 0 0 0 0 \n" + + "1 1 1 0 0 \n" + + "1 0 0 0 0 \n" + + "1 0 0 0 0 \n" + + "1 1 1 1 1 \n" + + "0 1 0 0 0 1 0 1 \n" + + "1 1 1 1 1 \n" + + "1 0 0 0 0 \n" + + "1 0 0 0 0 \n" + + "1 1 1 0 0 \n" + + "1 0 0 0 0 \n" + + "1 0 0 0 0 \n" + + "1 0 0 0 0 \n" + + "0 1 0 0 0 1 1 0 \n" + + "0 1 1 1 0 \n" + + "1 0 0 0 1 \n" + + "1 0 0 0 0 \n" + + "1 0 0 0 0 \n" + + "1 0 0 1 1 \n" + + "1 0 0 0 1 \n" + + "0 1 1 1 0 \n" + + "0 1 0 0 0 1 1 1 \n" + + "1 0 0 0 1 \n" + + "1 0 0 0 1 \n" + + "1 0 0 0 1 \n" + + "1 1 1 1 1 \n" + + "1 0 0 0 1 \n" + + "1 0 0 0 1 \n" + + "1 0 0 0 1 \n" + + "0 1 0 0 1 0 0 0 \n" + + "0 1 1 1 0 \n" + + "0 0 1 0 0 \n" + + "0 0 1 0 0 \n" + + "0 0 1 0 0 \n" + + "0 0 1 0 0 \n" + + "0 0 1 0 0 \n" + + "0 1 1 1 0 \n" + + "0 1 0 0 1 0 0 1 \n" + + "0 0 0 0 1 \n" + + "0 0 0 0 1 \n" + + "0 0 0 0 1 \n" + + "0 0 0 0 1 \n" + + "1 0 0 0 1 \n" + + "1 0 0 0 1 \n" + + "0 1 1 1 0 \n" + + "0 1 0 0 1 0 1 0 \n" + + "1 0 0 0 1 \n" + + "1 0 0 1 0 \n" + + "1 0 1 0 0 \n" + + "1 1 0 0 0 \n" + + "1 0 1 0 0 \n" + + "1 0 0 1 0 \n" + + "1 0 0 0 1 \n" + + "0 1 0 0 1 0 1 1 \n" + + "1 0 0 0 0 \n" + + "1 0 0 0 0 \n" + + "1 0 0 0 0 \n" + + "1 0 0 0 0 \n" + + "1 0 0 0 0 \n" + + "1 0 0 0 0 \n" + + "1 1 1 1 1 \n" + + "0 1 0 0 1 1 0 0 \n" + + "1 0 0 0 1 \n" + + "1 1 0 1 1 \n" + + "1 0 1 0 1 \n" + + "1 0 1 0 1 \n" + + "1 0 0 0 1 \n" + + "1 0 0 0 1 \n" + + "1 0 0 0 1 \n" + + "0 1 0 0 1 1 0 1 \n" + + "1 0 0 0 1 \n" + + "1 1 0 0 1 \n" + + "1 0 1 0 1 \n" + + "1 0 1 0 1 \n" + + "1 0 1 0 1 \n" + + "1 0 0 1 1 \n" + + "1 0 0 0 1 \n" + + "0 1 0 0 1 1 1 0 \n" + + "0 1 1 1 0 \n" + + "1 0 0 0 1 \n" + + "1 0 0 0 1 \n" + + "1 0 0 0 1 \n" + + "1 0 0 0 1 \n" + + "1 0 0 0 1 \n" + + "0 1 1 1 0 \n" + + "0 1 0 0 1 1 1 1 \n" + + "1 1 1 1 0 \n" + + "1 0 0 0 1 \n" + + "1 0 0 0 1 \n" + + "1 1 1 1 0 \n" + + "1 0 0 0 0 \n" + + "1 0 0 0 0 \n" + + "1 0 0 0 0 \n" + + "0 1 0 1 0 0 0 0 \n" + + "0 1 1 1 0 \n" + + "1 0 0 0 1 \n" + + "1 0 0 0 1 \n" + + "1 0 0 0 1 \n" + + "1 0 1 0 1 \n" + + "1 0 0 1 1 \n" + + "0 1 1 1 1 \n" + + "0 1 0 1 0 0 0 1 \n" + + "1 1 1 1 0 \n" + + "1 0 0 0 1 \n" + + "1 0 0 0 1 \n" + + "1 1 1 1 0 \n" + + "1 0 1 0 0 \n" + + "1 0 0 1 0 \n" + + "1 0 0 0 1 \n" + + "0 1 0 1 0 0 1 0 \n" + + "0 1 1 1 1 \n" + + "1 0 0 0 0 \n" + + "1 0 0 0 0 \n" + + "0 1 1 1 0 \n" + + "0 0 0 0 1 \n" + + "0 0 0 0 1 \n" + + "1 1 1 1 0 \n" + + "0 1 0 1 0 0 1 1 \n" + + "1 1 1 1 1 \n" + + "0 0 1 0 0 \n" + + "0 0 1 0 0 \n" + + "0 0 1 0 0 \n" + + "0 0 1 0 0 \n" + + "0 0 1 0 0 \n" + + "0 0 1 0 0 \n" + + "0 1 0 1 0 1 0 0 \n" + + "1 0 0 0 1 \n" + + "1 0 0 0 1 \n" + + "1 0 0 0 1 \n" + + "1 0 0 0 1 \n" + + "1 0 0 0 1 \n" + + "1 0 0 0 1 \n" + + "0 1 1 1 0 \n" + + "0 1 0 1 0 1 0 1 \n" + + "1 0 0 0 1 \n" + + "1 0 0 0 1 \n" + + "0 1 0 1 0 \n" + + "0 1 0 1 0 \n" + + "0 1 0 1 0 \n" + + "0 1 0 1 0 \n" + + "0 0 1 0 0 \n" + + "0 1 0 1 0 1 1 0 \n" + + "1 0 0 0 1 \n" + + "1 0 0 0 1 \n" + + "1 0 0 0 1 \n" + + "1 0 1 0 1 \n" + + "1 0 1 0 1 \n" + + "1 0 1 0 1 \n" + + "0 1 0 1 0 \n" + + "0 1 0 1 0 1 1 1 \n" + + "1 0 0 0 1 \n" + + "0 1 0 1 0 \n" + + "0 1 0 1 0 \n" + + "0 0 1 0 0 \n" + + "0 1 0 1 0 \n" + + "0 1 0 1 0 \n" + + "1 0 0 0 1 \n" + + "0 1 0 1 1 0 0 0 \n" + + "1 0 0 0 1 \n" + + "0 1 0 1 0 \n" + + "0 1 0 1 0 \n" + + "0 0 1 0 0 \n" + + "0 0 1 0 0 \n" + + "0 0 1 0 0 \n" + + "0 0 1 0 0 \n" + + "0 1 0 1 1 0 0 1 \n" + + "1 1 1 1 1 \n" + + "0 0 0 1 0 \n" + + "0 0 0 1 0 \n" + + "0 0 1 0 0 \n" + + "0 1 0 0 0 \n" + + "0 1 0 0 0 \n" + + "1 1 1 1 1 \n" + + "0 1 0 1 1 0 1 0 \n" + + " \n"; +} diff --git a/tests/src/JIT/Performance/CodeQuality/Bytemark/neural.cs b/tests/src/JIT/Performance/CodeQuality/Bytemark/neural.cs new file mode 100644 index 0000000000..e93e331cc7 --- /dev/null +++ b/tests/src/JIT/Performance/CodeQuality/Bytemark/neural.cs @@ -0,0 +1,880 @@ +/* +** Copyright (c) Microsoft. All rights reserved. +** Licensed under the MIT license. +** See LICENSE file in the project root for full license information. +** +** This program was translated to C# and adapted for xunit-performance. +** New variants of several tests were added to compare class versus +** struct and to compare jagged arrays vs multi-dimensional arrays. +*/ + +/* +** BYTEmark (tm) +** BYTE Magazine's Native Mode benchmarks +** Rick Grehan, BYTE Magazine +** +** Create: +** Revision: 3/95 +** +** DISCLAIMER +** The source, executable, and documentation files that comprise +** the BYTEmark benchmarks are made available on an "as is" basis. +** This means that we at BYTE Magazine have made every reasonable +** effort to verify that the there are no errors in the source and +** executable code. We cannot, however, guarantee that the programs +** are error-free. Consequently, McGraw-HIll and BYTE Magazine make +** no claims in regard to the fitness of the source code, executable +** code, and documentation of the BYTEmark. +** +** Furthermore, BYTE Magazine, McGraw-Hill, and all employees +** of McGraw-Hill cannot be held responsible for any damages resulting +** from the use of this code or the results obtained from using +** this code. +*/ + +/******************************** +** BACK PROPAGATION NEURAL NET ** +********************************* +** This code is a modified version of the code +** that was submitted to BYTE Magazine by +** Maureen Caudill. It accomanied an article +** that I CANNOT NOW RECALL. +** The author's original heading/comment was +** as follows: +** +** Backpropagation Network +** Written by Maureen Caudill +** in Think C 4.0 on a Macintosh +** +** (c) Maureen Caudill 1988-1991 +** This network will accept 5x7 input patterns +** and produce 8 bit output patterns. +** The source code may be copied or modified without restriction, +** but no fee may be charged for its use. +** +** ++++++++++++++ +** I have modified the code so that it will work +** on systems other than a Macintosh -- RG +*/ + +/*********** +** DoNNet ** +************ +** Perform the neural net benchmark. +** Note that this benchmark is one of the few that +** requires an input file. That file is "NNET.DAT" and +** should be on the local directory (from which the +** benchmark program in launched). +*/ + +using System; +using System.IO; + +public class Neural : NNetStruct +{ + public override string Name() + { + return "NEURAL NET(rectangle)"; + } + + /* + ** DEFINES + */ + public static int T = 1; /* TRUE */ + public static int F = 0; /* FALSE */ + public static int ERR = -1; + public static int MAXPATS = 10; /* max number of patterns in data file */ + public static int IN_X_SIZE = 5; /* number of neurodes/row of input layer */ + public static int IN_Y_SIZE = 7; /* number of neurodes/col of input layer */ + public static int IN_SIZE = 35; /* equals IN_X_SIZE*IN_Y_SIZE */ + public static int MID_SIZE = 8; /* number of neurodes in middle layer */ + public static int OUT_SIZE = 8; /* number of neurodes in output layer */ + public static double MARGIN = 0.1; /* how near to 1,0 do we have to come to stop? */ + public static double BETA = 0.09; /* beta learning constant */ + public static double ALPHA = 0.09; /* momentum term constant */ + public static double STOP = 0.1; /* when worst_error less than STOP, training is done */ + + /* + ** MAXNNETLOOPS + ** + ** This constant sets the max number of loops through the neural + ** net that the system will attempt before giving up. This + ** is not a critical constant. You can alter it if your system + ** has sufficient horsepower. + */ + public static int MAXNNETLOOPS = 50000; + + + /* + ** GLOBALS + */ + public static double[,] mid_wts = new double[MID_SIZE, IN_SIZE]; /* middle layer weights */ + public static double[,] out_wts = new double[OUT_SIZE, MID_SIZE]; + public static double[] mid_out = new double[MID_SIZE]; + public static double[] out_out = new double[OUT_SIZE]; + public static double[] mid_error = new double[MID_SIZE]; + public static double[] out_error = new double[OUT_SIZE]; + public static double[,] mid_wt_change = new double[MID_SIZE, IN_SIZE]; + public static double[,] out_wt_change = new double[OUT_SIZE, MID_SIZE]; + public static double[,] in_pats = new double[MAXPATS, IN_SIZE]; + public static double[,] out_pats = new double[MAXPATS, OUT_SIZE]; + public static double[] tot_out_error = new double[MAXPATS]; + public static double[,] out_wt_cum_change = new double[OUT_SIZE, MID_SIZE]; + public static double[,] mid_wt_cum_change = new double[MID_SIZE, IN_SIZE]; + public static double worst_error = 0.0; /* worst error each pass through the data */ + public static double average_error = 0.0; /* average error each pass through the data */ + public static double[] avg_out_error = new double[MAXPATS]; + + public static int iteration_count = 0; /* number of passes thru network so far */ + public static int numpats = 0; /* number of patterns in data file */ + public static int numpasses = 0; /* number of training passes through data file */ + public static int learned = 0; /* flag--if TRUE, network has learned all patterns */ + + /* + ** The Neural Net test requires an input data file. + ** The name is specified here. + */ + public static string inpath = "NNET.DAT"; + + public override + double Run() + { + return DoNNET(this); + } + + /********************* + ** read_data_file() ** + ********************** + ** Read in the input data file and store the patterns in + ** in_pats and out_pats. + ** The format for the data file is as follows: + ** + ** line# data expected + ** ----- ------------------------------ + ** 1 In-X-size,in-y-size,out-size + ** 2 number of patterns in file + ** 3 1st X row of 1st input pattern + ** 4.. following rows of 1st input pattern pattern + ** in-x+2 y-out pattern + ** 1st X row of 2nd pattern + ** etc. + ** + ** Each row of data is separated by commas or spaces. + ** The data is expected to be ascii text corresponding to + ** either a +1 or a 0. + ** + ** Sample input for a 1-pattern file (The comments to the + ** right may NOT be in the file unless more sophisticated + ** parsing of the input is done.): + ** + ** 5,7,8 input is 5x7 grid, output is 8 bits + ** 1 one pattern in file + ** 0,1,1,1,0 beginning of pattern for "O" + ** 1,0,0,0,1 + ** 1,0,0,0,1 + ** 1,0,0,0,1 + ** 1,0,0,0,1 + ** 1,0,0,0,0 + ** 0,1,1,1,0 + ** 0,1,0,0,1,1,1,1 ASCII code for "O" -- 0100 1111 + ** + ** Clearly, this simple scheme can be expanded or enhanced + ** any way you like. + ** + ** Returns -1 if any file error occurred, otherwise 0. + **/ + private + void read_data_file() + { + int xinsize = 0, yinsize = 0, youtsize = 0; + int patt = 0, element = 0, i = 0, row = 0; + int vals_read = 0; + int val1 = 0, val2 = 0, val3 = 0, val4 = 0, val5 = 0, val6 = 0, val7 = 0, val8 = 0; + Object[] results = new Object[8]; + + string input = NeuralData.Input; + + StringReader infile = new StringReader(input); + + vals_read = Utility.fscanf(infile, "%d %d %d", results); + xinsize = (int)results[0]; + yinsize = (int)results[1]; + youtsize = (int)results[2]; + + if (vals_read != 3) + { + throw new Exception("NNET: error reading input"); + } + vals_read = Utility.fscanf(infile, "%d", results); + numpats = (int)results[0]; + if (vals_read != 1) + { + throw new Exception("NNET: error reading input"); + } + if (numpats > MAXPATS) + numpats = MAXPATS; + + for (patt = 0; patt < numpats; patt++) + { + element = 0; + for (row = 0; row < yinsize; row++) + { + vals_read = Utility.fscanf(infile, "%d %d %d %d %d", results); + val1 = (int)results[0]; + val2 = (int)results[1]; + val3 = (int)results[2]; + val4 = (int)results[3]; + val5 = (int)results[4]; + if (vals_read != 5) + { + throw new Exception("NNET: error reading input"); + } + element = row * xinsize; + + in_pats[patt, element] = (double)val1; element++; + in_pats[patt, element] = (double)val2; element++; + in_pats[patt, element] = (double)val3; element++; + in_pats[patt, element] = (double)val4; element++; + in_pats[patt, element] = (double)val5; element++; + } + for (i = 0; i < IN_SIZE; i++) + { + if (in_pats[patt, i] >= 0.9) + in_pats[patt, i] = 0.9; + if (in_pats[patt, i] <= 0.1) + in_pats[patt, i] = 0.1; + } + element = 0; + vals_read = Utility.fscanf(infile, "%d %d %d %d %d %d %d %d", results); + val1 = (int)results[0]; + val2 = (int)results[1]; + val3 = (int)results[2]; + val4 = (int)results[3]; + val5 = (int)results[4]; + val6 = (int)results[5]; + val7 = (int)results[6]; + val8 = (int)results[7]; + + out_pats[patt, element] = (double)val1; element++; + out_pats[patt, element] = (double)val2; element++; + out_pats[patt, element] = (double)val3; element++; + out_pats[patt, element] = (double)val4; element++; + out_pats[patt, element] = (double)val5; element++; + out_pats[patt, element] = (double)val6; element++; + out_pats[patt, element] = (double)val7; element++; + out_pats[patt, element] = (double)val8; element++; + } + } + + + private + double DoNNET(NNetStruct locnnetstruct) + { + // string errorcontext = "CPU:NNET"; + // int systemerror = 0; + long accumtime = 0; + double iterations = 0.0; + + /* + ** Init random number generator. + ** NOTE: It is important that the random number generator + ** be re-initialized for every pass through this test. + ** The NNET algorithm uses the random number generator + ** to initialize the net. Results are sensitive to + ** the initial neural net state. + */ + ByteMark.randnum(3); + + /* + ** Read in the input and output patterns. We'll do this + ** only once here at the beginning. These values don't + ** change once loaded. + */ + read_data_file(); + + /* + ** See if we need to perform self adjustment loop. + */ + if (locnnetstruct.adjust == 0) + { + /* + ** Do self-adjustment. This involves initializing the + ** # of loops and increasing the loop count until we + ** get a number of loops that we can use. + */ + for (locnnetstruct.loops = 1; + locnnetstruct.loops < MAXNNETLOOPS; + locnnetstruct.loops++) + { + ByteMark.randnum(3); + if (DoNNetIteration(locnnetstruct.loops) > global.min_ticks) + break; + } + } + + /* + ** All's well if we get here. Do the test. + */ + accumtime = 0L; + iterations = (double)0.0; + + do + { + ByteMark.randnum(3); /* Gotta do this for Neural Net */ + accumtime += DoNNetIteration(locnnetstruct.loops); + iterations += (double)locnnetstruct.loops; + } while (ByteMark.TicksToSecs(accumtime) < locnnetstruct.request_secs); + + /* + ** Clean up, calculate results, and go home. Be sure to + ** show that we don't have to rerun adjustment code. + */ + locnnetstruct.iterspersec = iterations / ByteMark.TicksToFracSecs(accumtime); + + if (locnnetstruct.adjust == 0) + locnnetstruct.adjust = 1; + + + return locnnetstruct.iterspersec; + } + + /******************** + ** DoNNetIteration ** + ********************* + ** Do a single iteration of the neural net benchmark. + ** By iteration, we mean a "learning" pass. + */ + public static long DoNNetIteration(long nloops) + { + long elapsed; /* Elapsed time */ + int patt; + + /* + ** Run nloops learning cycles. Notice that, counted with + ** the learning cycle is the weight randomization and + ** zeroing of changes. This should reduce clock jitter, + ** since we don't have to stop and start the clock for + ** each iteration. + */ + elapsed = ByteMark.StartStopwatch(); + while (nloops-- != 0) + { + randomize_wts(); + zero_changes(); + iteration_count = 1; + learned = F; + numpasses = 0; + while (learned == F) + { + for (patt = 0; patt < numpats; patt++) + { + worst_error = 0.0; /* reset this every pass through data */ + move_wt_changes(); /* move last pass's wt changes to momentum array */ + do_forward_pass(patt); + do_back_pass(patt); + iteration_count++; + } + numpasses++; + learned = check_out_error(); + } + } + return (ByteMark.StopStopwatch(elapsed)); + } + + /************************* + ** do_mid_forward(patt) ** + ************************** + ** Process the middle layer's forward pass + ** The activation of middle layer's neurode is the weighted + ** sum of the inputs from the input pattern, with sigmoid + ** function applied to the inputs. + **/ + public static void do_mid_forward(int patt) + { + double sum; + int neurode, i; + + for (neurode = 0; neurode < MID_SIZE; neurode++) + { + sum = 0.0; + for (i = 0; i < IN_SIZE; i++) + { /* compute weighted sum of input signals */ + sum += mid_wts[neurode, i] * in_pats[patt, i]; + } + /* + ** apply sigmoid function f(x) = 1/(1+exp(-x)) to weighted sum + */ + sum = 1.0 / (1.0 + Math.Exp(-sum)); + mid_out[neurode] = sum; + } + return; + } + + /********************* + ** do_out_forward() ** + ********************** + ** process the forward pass through the output layer + ** The activation of the output layer is the weighted sum of + ** the inputs (outputs from middle layer), modified by the + ** sigmoid function. + **/ + public static void do_out_forward() + { + double sum; + int neurode, i; + + for (neurode = 0; neurode < OUT_SIZE; neurode++) + { + sum = 0.0; + for (i = 0; i < MID_SIZE; i++) + { /* + ** compute weighted sum of input signals + ** from middle layer + */ + sum += out_wts[neurode, i] * mid_out[i]; + } + /* + ** Apply f(x) = 1/(1+Math.Exp(-x)) to weighted input + */ + sum = 1.0 / (1.0 + Math.Exp(-sum)); + out_out[neurode] = sum; + } + return; + } + + /************************* + ** display_output(patt) ** + ************************** + ** Display the actual output vs. the desired output of the + ** network. + ** Once the training is complete, and the "learned" flag set + ** to TRUE, then display_output sends its output to both + ** the screen and to a text output file. + ** + ** NOTE: This routine has been disabled in the benchmark + ** version. -- RG + **/ + /* + public static void display_output(int patt) + { + int i; + + fprintf(outfile,"\n Iteration # %d",iteration_count); + fprintf(outfile,"\n Desired Output: "); + + for (i=0; i tot_error) + tot_error = -error; /* worst error this pattern */ + } + else + { + sum += error; + if (error > tot_error) + tot_error = error; /* worst error this pattern */ + } + } + avg_out_error[patt] = sum / OUT_SIZE; + tot_out_error[patt] = tot_error; + return; + } + + /*********************** + ** worst_pass_error() ** + ************************ + ** Find the worst and average error in the pass and save it + **/ + public static void worst_pass_error() + { + double error, sum; + + int i; + + error = 0.0; + sum = 0.0; + for (i = 0; i < numpats; i++) + { + if (tot_out_error[i] > error) error = tot_out_error[i]; + sum += avg_out_error[i]; + } + worst_error = error; + average_error = sum / numpats; + return; + } + + /******************* + ** do_mid_error() ** + ******************** + ** Compute the error for the middle layer neurodes + ** This is based on the output errors computed above. + ** Note that the derivative of the sigmoid f(x) is + ** f'(x) = f(x)(1 - f(x)) + ** Recall that f(x) is merely the output of the middle + ** layer neurode on the forward pass. + **/ + public static void do_mid_error() + { + double sum; + int neurode, i; + + for (neurode = 0; neurode < MID_SIZE; neurode++) + { + sum = 0.0; + for (i = 0; i < OUT_SIZE; i++) + sum += out_wts[i, neurode] * out_error[i]; + + /* + ** apply the derivative of the sigmoid here + ** Because of the choice of sigmoid f(I), the derivative + ** of the sigmoid is f'(I) = f(I)(1 - f(I)) + */ + mid_error[neurode] = mid_out[neurode] * (1 - mid_out[neurode]) * sum; + } + return; + } + + /********************* + ** adjust_out_wts() ** + ********************** + ** Adjust the weights of the output layer. The error for + ** the output layer has been previously propagated back to + ** the middle layer. + ** Use the Delta Rule with momentum term to adjust the weights. + **/ + public static void adjust_out_wts() + { + int weight, neurode; + double learn, delta, alph; + + learn = BETA; + alph = ALPHA; + for (neurode = 0; neurode < OUT_SIZE; neurode++) + { + for (weight = 0; weight < MID_SIZE; weight++) + { + /* standard delta rule */ + delta = learn * out_error[neurode] * mid_out[weight]; + + /* now the momentum term */ + delta += alph * out_wt_change[neurode, weight]; + out_wts[neurode, weight] += delta; + + /* keep track of this pass's cum wt changes for next pass's momentum */ + out_wt_cum_change[neurode, weight] += delta; + } + } + return; + } + + /************************* + ** adjust_mid_wts(patt) ** + ************************** + ** Adjust the middle layer weights using the previously computed + ** errors. + ** We use the Generalized Delta Rule with momentum term + **/ + public static void adjust_mid_wts(int patt) + { + int weight, neurode; + double learn, alph, delta; + + learn = BETA; + alph = ALPHA; + for (neurode = 0; neurode < MID_SIZE; neurode++) + { + for (weight = 0; weight < IN_SIZE; weight++) + { + /* first the basic delta rule */ + delta = learn * mid_error[neurode] * in_pats[patt, weight]; + + /* with the momentum term */ + delta += alph * mid_wt_change[neurode, weight]; + mid_wts[neurode, weight] += delta; + + /* keep track of this pass's cum wt changes for next pass's momentum */ + mid_wt_cum_change[neurode, weight] += delta; + } + } + return; + } + + /******************* + ** do_back_pass() ** + ******************** + ** Process the backward propagation of error through network. + **/ + public static void do_back_pass(int patt) + { + do_out_error(patt); + do_mid_error(); + adjust_out_wts(); + adjust_mid_wts(patt); + + return; + } + + + /********************** + ** move_wt_changes() ** + *********************** + ** Move the weight changes accumulated last pass into the wt-change + ** array for use by the momentum term in this pass. Also zero out + ** the accumulating arrays after the move. + **/ + public static void move_wt_changes() + { + int i, j; + + for (i = 0; i < MID_SIZE; i++) + for (j = 0; j < IN_SIZE; j++) + { + mid_wt_change[i, j] = mid_wt_cum_change[i, j]; + /* + ** Zero it out for next pass accumulation. + */ + mid_wt_cum_change[i, j] = 0.0; + } + + for (i = 0; i < OUT_SIZE; i++) + for (j = 0; j < MID_SIZE; j++) + { + out_wt_change[i, j] = out_wt_cum_change[i, j]; + out_wt_cum_change[i, j] = 0.0; + } + + return; + } + + /********************** + ** check_out_error() ** + *********************** + ** Check to see if the error in the output layer is below + ** MARGIN*OUT_SIZE for all output patterns. If so, then + ** assume the network has learned acceptably well. This + ** is simply an arbitrary measure of how well the network + ** has learned -- many other standards are possible. + **/ + public static int check_out_error() + { + int result, i, error; + + result = T; + error = F; + worst_pass_error(); /* identify the worst error in this pass */ + + /* + #if DEBUG + Console.WriteLine("\n Iteration # {0}",iteration_count); + #endif + */ + for (i = 0; i < numpats; i++) + { + /* printf("\n Error pattern %d: Worst: %8.3f; Average: %8.3f", + i+1,tot_out_error[i], avg_out_error[i]); + fprintf(outfile, + "\n Error pattern %d: Worst: %8.3f; Average: %8.3f", + i+1,tot_out_error[i]); + */ + + if (worst_error >= STOP) result = F; + if (tot_out_error[i] >= 16.0) error = T; + } + + if (error == T) result = ERR; + + +#if DEBUG + /* printf("\n Error this pass thru data: Worst: %8.3f; Average: %8.3f", + worst_error,average_error); + */ + /* fprintf(outfile, + "\n Error this pass thru data: Worst: %8.3f; Average: %8.3f", + worst_error, average_error); */ +#endif + + return (result); + } + + + /******************* + ** zero_changes() ** + ******************** + ** Zero out all the wt change arrays + **/ + public static void zero_changes() + { + int i, j; + + for (i = 0; i < MID_SIZE; i++) + { + for (j = 0; j < IN_SIZE; j++) + { + mid_wt_change[i, j] = 0.0; + mid_wt_cum_change[i, j] = 0.0; + } + } + + for (i = 0; i < OUT_SIZE; i++) + { + for (j = 0; j < MID_SIZE; j++) + { + out_wt_change[i, j] = 0.0; + out_wt_cum_change[i, j] = 0.0; + } + } + return; + } + + + /******************** + ** randomize_wts() ** + ********************* + ** Intialize the weights in the middle and output layers to + ** random values between -0.25..+0.25 + ** Function rand() returns a value between 0 and 32767. + ** + ** NOTE: Had to make alterations to how the random numbers were + ** created. -- RG. + **/ + public static void randomize_wts() + { + int neurode, i; + double value; + + /* + ** Following not used int benchmark version -- RG + ** + ** printf("\n Please enter a random number seed (1..32767): "); + ** scanf("%d", &i); + ** srand(i); + */ + + for (neurode = 0; neurode < MID_SIZE; neurode++) + { + for (i = 0; i < IN_SIZE; i++) + { + value = (double)ByteMark.abs_randwc(100000); + value = value / (double)100000.0 - (double)0.5; + mid_wts[neurode, i] = value / 2; + } + } + for (neurode = 0; neurode < OUT_SIZE; neurode++) + { + for (i = 0; i < MID_SIZE; i++) + { + value = (double)ByteMark.abs_randwc(100000); + value = value / (double)10000.0 - (double)0.5; + out_wts[neurode, i] = value / 2; + } + } + return; + } + + /********************** + ** display_mid_wts() ** + *********************** + ** Display the weights on the middle layer neurodes + ** NOTE: This routine is not used in the benchmark + ** test -- RG + **/ + /* static void display_mid_wts() + { + int neurode, weight, row, col; + + fprintf(outfile,"\n Weights of Middle Layer neurodes:"); + + for (neurode=0; neurode MAXPATS) + numpats = MAXPATS; + + for (patt = 0; patt < numpats; patt++) + { + element = 0; + for (row = 0; row < yinsize; row++) + { + vals_read = Utility.fscanf(infile, "%d %d %d %d %d", results); + val1 = (int)results[0]; + val2 = (int)results[1]; + val3 = (int)results[2]; + val4 = (int)results[3]; + val5 = (int)results[4]; + if (vals_read != 5) + { + throw new Exception("NNET: error reading input"); + } + element = row * xinsize; + + in_pats[patt][element] = (double)val1; element++; + in_pats[patt][element] = (double)val2; element++; + in_pats[patt][element] = (double)val3; element++; + in_pats[patt][element] = (double)val4; element++; + in_pats[patt][element] = (double)val5; element++; + } + for (i = 0; i < IN_SIZE; i++) + { + if (in_pats[patt][i] >= 0.9) + in_pats[patt][i] = 0.9; + if (in_pats[patt][i] <= 0.1) + in_pats[patt][i] = 0.1; + } + element = 0; + vals_read = Utility.fscanf(infile, "%d %d %d %d %d %d %d %d", results); + val1 = (int)results[0]; + val2 = (int)results[1]; + val3 = (int)results[2]; + val4 = (int)results[3]; + val5 = (int)results[4]; + val6 = (int)results[5]; + val7 = (int)results[6]; + val8 = (int)results[7]; + + out_pats[patt][element] = (double)val1; element++; + out_pats[patt][element] = (double)val2; element++; + out_pats[patt][element] = (double)val3; element++; + out_pats[patt][element] = (double)val4; element++; + out_pats[patt][element] = (double)val5; element++; + out_pats[patt][element] = (double)val6; element++; + out_pats[patt][element] = (double)val7; element++; + out_pats[patt][element] = (double)val8; element++; + } + } + + private + double DoNNET(NNetStruct locnnetstruct) + { + // string errorcontext = "CPU:NNET"; + // int systemerror = 0; + long accumtime = 0; + double iterations = 0.0; + + /* + ** Init random number generator. + ** NOTE: It is important that the random number generator + ** be re-initialized for every pass through this test. + ** The NNET algorithm uses the random number generator + ** to initialize the net. Results are sensitive to + ** the initial neural net state. + */ + ByteMark.randnum(3); + + /* + ** Read in the input and output patterns. We'll do this + ** only once here at the beginning. These values don't + ** change once loaded. + */ + read_data_file(); + + /* + ** See if we need to perform self adjustment loop. + */ + if (locnnetstruct.adjust == 0) + { + /* + ** Do self-adjustment. This involves initializing the + ** # of loops and increasing the loop count until we + ** get a number of loops that we can use. + */ + for (locnnetstruct.loops = 1; + locnnetstruct.loops < MAXNNETLOOPS; + locnnetstruct.loops++) + { + ByteMark.randnum(3); + if (DoNNetIteration(locnnetstruct.loops) > global.min_ticks) + break; + } + } + + /* + ** All's well if we get here. Do the test. + */ + accumtime = 0L; + iterations = (double)0.0; + + do + { + ByteMark.randnum(3); /* Gotta do this for Neural Net */ + accumtime += DoNNetIteration(locnnetstruct.loops); + iterations += (double)locnnetstruct.loops; + } while (ByteMark.TicksToSecs(accumtime) < locnnetstruct.request_secs); + + /* + ** Clean up, calculate results, and go home. Be sure to + ** show that we don't have to rerun adjustment code. + */ + locnnetstruct.iterspersec = iterations / ByteMark.TicksToFracSecs(accumtime); + + if (locnnetstruct.adjust == 0) + locnnetstruct.adjust = 1; + + return locnnetstruct.iterspersec; + } + + /******************** + ** DoNNetIteration ** + ********************* + ** Do a single iteration of the neural net benchmark. + ** By iteration, we mean a "learning" pass. + */ + public static long DoNNetIteration(long nloops) + { + long elapsed; /* Elapsed time */ + int patt; + + /* + ** Run nloops learning cycles. Notice that, counted with + ** the learning cycle is the weight randomization and + ** zeroing of changes. This should reduce clock jitter, + ** since we don't have to stop and start the clock for + ** each iteration. + */ + elapsed = ByteMark.StartStopwatch(); + while (nloops-- != 0) + { + randomize_wts(); + zero_changes(); + iteration_count = 1; + learned = F; + numpasses = 0; + while (learned == F) + { + for (patt = 0; patt < numpats; patt++) + { + worst_error = 0.0; /* reset this every pass through data */ + move_wt_changes(); /* move last pass's wt changes to momentum array */ + do_forward_pass(patt); + do_back_pass(patt); + iteration_count++; + } + numpasses++; + learned = check_out_error(); + } + } + + return (ByteMark.StopStopwatch(elapsed)); + } + + /************************* + ** do_mid_forward(patt) ** + ************************** + ** Process the middle layer's forward pass + ** The activation of middle layer's neurode is the weighted + ** sum of the inputs from the input pattern, with sigmoid + ** function applied to the inputs. + **/ + public static void do_mid_forward(int patt) + { + double sum; + int neurode, i; + + for (neurode = 0; neurode < MID_SIZE; neurode++) + { + sum = 0.0; + for (i = 0; i < IN_SIZE; i++) + { /* compute weighted sum of input signals */ + sum += mid_wts[neurode][i] * in_pats[patt][i]; + } + /* + ** apply sigmoid function f(x) = 1/(1+exp(-x)) to weighted sum + */ + sum = 1.0 / (1.0 + Math.Exp(-sum)); + mid_out[neurode] = sum; + } + return; + } + + /********************* + ** do_out_forward() ** + ********************** + ** process the forward pass through the output layer + ** The activation of the output layer is the weighted sum of + ** the inputs (outputs from middle layer), modified by the + ** sigmoid function. + **/ + public static void do_out_forward() + { + double sum; + int neurode, i; + + for (neurode = 0; neurode < OUT_SIZE; neurode++) + { + sum = 0.0; + for (i = 0; i < MID_SIZE; i++) + { /* + ** compute weighted sum of input signals + ** from middle layer + */ + sum += out_wts[neurode][i] * mid_out[i]; + } + /* + ** Apply f(x) = 1/(1+Math.Exp(-x)) to weighted input + */ + sum = 1.0 / (1.0 + Math.Exp(-sum)); + out_out[neurode] = sum; + } + return; + } + + /************************* + ** display_output(patt) ** + ************************** + ** Display the actual output vs. the desired output of the + ** network. + ** Once the training is complete, and the "learned" flag set + ** to TRUE, then display_output sends its output to both + ** the screen and to a text output file. + ** + ** NOTE: This routine has been disabled in the benchmark + ** version. -- RG + **/ + /* + public static void display_output(int patt) + { + int i; + + fprintf(outfile,"\n Iteration # %d",iteration_count); + fprintf(outfile,"\n Desired Output: "); + + for (i=0; i tot_error) + tot_error = -error; /* worst error this pattern */ + } + else + { + sum += error; + if (error > tot_error) + tot_error = error; /* worst error this pattern */ + } + } + avg_out_error[patt] = sum / OUT_SIZE; + tot_out_error[patt] = tot_error; + return; + } + + /*********************** + ** worst_pass_error() ** + ************************ + ** Find the worst and average error in the pass and save it + **/ + public static void worst_pass_error() + { + double error, sum; + + int i; + + error = 0.0; + sum = 0.0; + for (i = 0; i < numpats; i++) + { + if (tot_out_error[i] > error) error = tot_out_error[i]; + sum += avg_out_error[i]; + } + worst_error = error; + average_error = sum / numpats; + return; + } + + /******************* + ** do_mid_error() ** + ******************** + ** Compute the error for the middle layer neurodes + ** This is based on the output errors computed above. + ** Note that the derivative of the sigmoid f(x) is + ** f'(x) = f(x)(1 - f(x)) + ** Recall that f(x) is merely the output of the middle + ** layer neurode on the forward pass. + **/ + public static void do_mid_error() + { + double sum; + int neurode, i; + + for (neurode = 0; neurode < MID_SIZE; neurode++) + { + sum = 0.0; + for (i = 0; i < OUT_SIZE; i++) + sum += out_wts[i][neurode] * out_error[i]; + + /* + ** apply the derivative of the sigmoid here + ** Because of the choice of sigmoid f(I), the derivative + ** of the sigmoid is f'(I) = f(I)(1 - f(I)) + */ + mid_error[neurode] = mid_out[neurode] * (1 - mid_out[neurode]) * sum; + } + return; + } + + /********************* + ** adjust_out_wts() ** + ********************** + ** Adjust the weights of the output layer. The error for + ** the output layer has been previously propagated back to + ** the middle layer. + ** Use the Delta Rule with momentum term to adjust the weights. + **/ + public static void adjust_out_wts() + { + int weight, neurode; + double learn, delta, alph; + + learn = BETA; + alph = ALPHA; + for (neurode = 0; neurode < OUT_SIZE; neurode++) + { + for (weight = 0; weight < MID_SIZE; weight++) + { + /* standard delta rule */ + delta = learn * out_error[neurode] * mid_out[weight]; + + /* now the momentum term */ + delta += alph * out_wt_change[neurode][weight]; + out_wts[neurode][weight] += delta; + + /* keep track of this pass's cum wt changes for next pass's momentum */ + out_wt_cum_change[neurode][weight] += delta; + } + } + return; + } + + /************************* + ** adjust_mid_wts(patt) ** + ************************** + ** Adjust the middle layer weights using the previously computed + ** errors. + ** We use the Generalized Delta Rule with momentum term + **/ + public static void adjust_mid_wts(int patt) + { + int weight, neurode; + double learn, alph, delta; + + learn = BETA; + alph = ALPHA; + for (neurode = 0; neurode < MID_SIZE; neurode++) + { + for (weight = 0; weight < IN_SIZE; weight++) + { + /* first the basic delta rule */ + delta = learn * mid_error[neurode] * in_pats[patt][weight]; + + /* with the momentum term */ + delta += alph * mid_wt_change[neurode][weight]; + mid_wts[neurode][weight] += delta; + + /* keep track of this pass's cum wt changes for next pass's momentum */ + mid_wt_cum_change[neurode][weight] += delta; + } + } + return; + } + + /******************* + ** do_back_pass() ** + ******************** + ** Process the backward propagation of error through network. + **/ + public static void do_back_pass(int patt) + { + do_out_error(patt); + do_mid_error(); + adjust_out_wts(); + adjust_mid_wts(patt); + + return; + } + + + /********************** + ** move_wt_changes() ** + *********************** + ** Move the weight changes accumulated last pass into the wt-change + ** array for use by the momentum term in this pass. Also zero out + ** the accumulating arrays after the move. + **/ + public static void move_wt_changes() + { + int i, j; + + for (i = 0; i < MID_SIZE; i++) + for (j = 0; j < IN_SIZE; j++) + { + mid_wt_change[i][j] = mid_wt_cum_change[i][j]; + /* + ** Zero it out for next pass accumulation. + */ + mid_wt_cum_change[i][j] = 0.0; + } + + for (i = 0; i < OUT_SIZE; i++) + for (j = 0; j < MID_SIZE; j++) + { + out_wt_change[i][j] = out_wt_cum_change[i][j]; + out_wt_cum_change[i][j] = 0.0; + } + + return; + } + + /********************** + ** check_out_error() ** + *********************** + ** Check to see if the error in the output layer is below + ** MARGIN*OUT_SIZE for all output patterns. If so, then + ** assume the network has learned acceptably well. This + ** is simply an arbitrary measure of how well the network + ** has learned -- many other standards are possible. + **/ + public static int check_out_error() + { + int result, i, error; + + result = T; + error = F; + worst_pass_error(); /* identify the worst error in this pass */ + + for (i = 0; i < numpats; i++) + { + if (worst_error >= STOP) result = F; + if (tot_out_error[i] >= 16.0) error = T; + } + + if (error == T) result = ERR; + + return (result); + } + + /******************* + ** zero_changes() ** + ******************** + ** Zero out all the wt change arrays + **/ + public static void zero_changes() + { + int i, j; + + for (i = 0; i < MID_SIZE; i++) + { + for (j = 0; j < IN_SIZE; j++) + { + mid_wt_change[i][j] = 0.0; + mid_wt_cum_change[i][j] = 0.0; + } + } + + for (i = 0; i < OUT_SIZE; i++) + { + for (j = 0; j < MID_SIZE; j++) + { + out_wt_change[i][j] = 0.0; + out_wt_cum_change[i][j] = 0.0; + } + } + return; + } + + /******************** + ** randomize_wts() ** + ********************* + ** Intialize the weights in the middle and output layers to + ** random values between -0.25..+0.25 + ** Function rand() returns a value between 0 and 32767. + ** + ** NOTE: Had to make alterations to how the random numbers were + ** created. -- RG. + **/ + public static void randomize_wts() + { + int neurode, i; + double value; + + /* + ** Following not used int benchmark version -- RG + ** + ** printf("\n Please enter a random number seed (1..32767): "); + ** scanf("%d", &i); + ** srand(i); + */ + + for (neurode = 0; neurode < MID_SIZE; neurode++) + { + for (i = 0; i < IN_SIZE; i++) + { + value = (double)ByteMark.abs_randwc(100000); + value = value / (double)100000.0 - (double)0.5; + mid_wts[neurode][i] = value / 2; + } + } + for (neurode = 0; neurode < OUT_SIZE; neurode++) + { + for (i = 0; i < MID_SIZE; i++) + { + value = (double)ByteMark.abs_randwc(100000); + value = value / (double)10000.0 - (double)0.5; + out_wts[neurode][i] = value / 2; + } + } + return; + } + + /********************** + ** display_mid_wts() ** + *********************** + ** Display the weights on the middle layer neurodes + ** NOTE: This routine is not used in the benchmark + ** test -- RG + **/ + /* static void display_mid_wts() + { + int neurode, weight, row, col; + + fprintf(outfile,"\n Weights of Middle Layer neurodes:"); + + for (neurode=0; neurode global.min_ticks) + break; /* We're ok...exit */ + if (this.numarrays++ > global.NUMNUMARRAYS) + { + throw new Exception("CPU:NSORT -- NUMNUMARRAYS hit."); + } + } + } + else + { + /* + ** Allocate space for arrays + */ + arraybase = new int[this.numarrays][]; + for (int i = 0; i < this.numarrays; i++) + arraybase[i] = new int[this.arraysize]; + } + + /* + ** All's well if we get here. Repeatedly perform sorts until the + ** accumulated elapsed time is greater than # of seconds requested. + */ + accumtime = 0L; + iterations = (double)0.0; + + do + { + accumtime += DoNumSortIteration(arraybase, + this.arraysize, + this.numarrays); + iterations += (double)1.0; + } while (ByteMark.TicksToSecs(accumtime) < this.request_secs); + + if (this.adjust == 0) + this.adjust = 1; + + return (iterations * (double)this.numarrays / ByteMark.TicksToFracSecs(accumtime)); + } + + /*********************** + ** DoNumSortIteration ** + ************************ + ** This routine executes one iteration of the numeric + ** sort benchmark. It returns the number of ticks + ** elapsed for the iteration. + */ + + // JTR: The last 2 parms are no longer needed as they + // can be inferred from the arraybase. + private static int DoNumSortIteration(int[][] arraybase, int arraysize, int numarrays) + { + long elapsed; /* Elapsed ticks */ + int i; + /* + ** Load up the array with random numbers + */ + LoadNumArrayWithRand(arraybase, arraysize, numarrays); + + /* + ** Start the stopwatch + */ + elapsed = ByteMark.StartStopwatch(); + + /* + ** Execute a heap of heapsorts + */ + for (i = 0; i < numarrays; i++) + { + // NumHeapSort(arraybase+i*arraysize,0L,arraysize-1L); + NumHeapSort(arraybase[i], 0, arraysize - 1); + } + + /* + ** Get elapsed time + */ + elapsed = ByteMark.StopStopwatch(elapsed); +#if DEBUG + { + for (i = 0; i < arraysize - 1; i++) + { /* + ** Compare to check for proper + ** sort. + */ + if (arraybase[0][i + 1] < arraybase[0][i]) + { + Console.Write("Sort Error\n"); + break; + } + } + } +#endif + + return ((int)elapsed); + } + + /************************* + ** LoadNumArrayWithRand ** + ************************** + ** Load up an array with random longs. + */ + private static void LoadNumArrayWithRand(int[][] array, /* Pointer to arrays */ + int arraysize, + int numarrays) /* # of elements in array */ + { + int i; /* Used for index */ + + /* + ** Initialize the random number generator + */ + ByteMark.randnum(13); + + /* + ** Load up first array with randoms + */ + for (i = 0; i < arraysize; i++) + array[0][i] = ByteMark.randnum(0); + + /* + ** Now, if there's more than one array to load, copy the + ** first into each of the others. + */ + for (i = 1; i < numarrays; i++) + { + // the old code didn't do a memcpy, so I'm not doing + // an Array.Copy() + for (int j = 0; j < arraysize; j++) + array[i][j] = array[0][j]; + } + + return; + } + + /**************** + ** NumHeapSort ** + ***************** + ** Pass this routine a pointer to an array of long + ** integers. Also pass in minimum and maximum offsets. + ** This routine performs a heap sort on that array. + */ + private static void NumHeapSort(int[] array, + int bottom, /* Lower bound */ + int top) /* Upper bound */ + { + int temp; /* Used to exchange elements */ + int i; /* Loop index */ + + /* + ** First, build a heap in the array + */ + for (i = (top / 2); i > 0; --i) + NumSift(array, i, top); + + /* + ** Repeatedly extract maximum from heap and place it at the + ** end of the array. When we get done, we'll have a sorted + ** array. + */ + for (i = top; i > 0; --i) + { + NumSift(array, bottom, i); + temp = array[0]; /* Perform exchange */ + array[0] = array[i]; + array[i] = temp; + } + return; + } + + /************ + ** NumSift ** + ************* + ** Peforms the sift operation on a numeric array, + ** constructing a heap in the array. + */ + private static void NumSift(int[] array, /* Array of numbers */ + int i, /* Minimum of array */ + int j) /* Maximum of array */ + { + int k; + int temp; /* Used for exchange */ + + while ((i + i) <= j) + { + k = i + i; + if (k < j) + if (array[k] < array[k + 1]) + ++k; + if (array[i] < array[k]) + { + temp = array[k]; + array[k] = array[i]; + array[i] = temp; + i = k; + } + else + i = j + 1; + } + return; + } +} + +/////////////////////////////////////////////////////////////////////////////////////// +// New class +/////////////////////////////////////////////////////////////////////////////////////// +public class NumericSortRect : SortStruct +{ + public override string Name() + { + return "NUMERIC SORT(rectangle)"; + } + + public override double Run() + { + /* + ** Set the error context string. + */ + int[,] arraybase; /* Base pointers of array */ + long accumtime; /* Accumulated time */ + double iterations; /* Iteration counter */ + + /* + ** See if we need to do self adjustment code. + */ + if (this.adjust == 0) + { + /* + ** Self-adjustment code. The system begins by sorting 1 + ** array. If it does that in no time, then two arrays + ** are built and sorted. This process continues until + ** enough arrays are built to handle the tolerance. + */ + this.numarrays = 1; + while (true) + { + /* + ** Allocate space for arrays + */ + arraybase = new int[this.numarrays, this.arraysize]; + + /* + ** Do an iteration of the numeric sort. If the + ** elapsed time is less than or equal to the permitted + ** minimum, then allocate for more arrays and + ** try again. + */ + if (DoNumSortIteration(arraybase, + this.arraysize, + this.numarrays) > global.min_ticks) + break; /* We're ok...exit */ + if (this.numarrays++ > global.NUMNUMARRAYS) + { + throw new Exception("CPU:NSORT -- NUMNUMARRAYS hit."); + } + } + } + else + { + /* + ** Allocate space for arrays + */ + arraybase = new int[this.numarrays, this.arraysize]; + } + + /* + ** All's well if we get here. Repeatedly perform sorts until the + ** accumulated elapsed time is greater than # of seconds requested. + */ + accumtime = 0L; + iterations = (double)0.0; + + do + { + accumtime += DoNumSortIteration(arraybase, + this.arraysize, + this.numarrays); + iterations += (double)1.0; + } while (ByteMark.TicksToSecs(accumtime) < this.request_secs); + + if (this.adjust == 0) + this.adjust = 1; + + return (iterations * (double)this.numarrays / ByteMark.TicksToFracSecs(accumtime)); + } + + /*********************** + ** DoNumSortIteration ** + ************************ + ** This routine executes one iteration of the numeric + ** sort benchmark. It returns the number of ticks + ** elapsed for the iteration. + */ + + // JTR: The last 2 parms are no longer needed as they + // can be inferred from the arraybase. + private static int DoNumSortIteration(int[,] arraybase, int arraysize, int numarrays) + { + long elapsed; /* Elapsed ticks */ + int i; + /* + ** Load up the array with random numbers + */ + LoadNumArrayWithRand(arraybase, arraysize, numarrays); + + /* + ** Start the stopwatch + */ + elapsed = ByteMark.StartStopwatch(); + + /* + ** Execute a heap of heapsorts + */ + for (i = 0; i < numarrays; i++) + { + // NumHeapSort(arraybase+i*arraysize,0L,arraysize-1L); + NumHeapSort(arraybase, i, arraysize - 1); + } + + /* + ** Get elapsed time + */ + elapsed = ByteMark.StopStopwatch(elapsed); +#if DEBUG + { + for (i = 0; i < arraysize - 1; i++) + { /* + ** Compare to check for proper + ** sort. + */ + if (arraybase[0, i + 1] < arraybase[0, i]) + { + Console.Write("size: {0}, count: {1}, total: {2}\n", arraysize, numarrays, arraybase.Length); + Console.Write("Sort Error at index {0}\n", i); + break; + } + } + } +#endif + + return ((int)elapsed); + } + + /************************* + ** LoadNumArrayWithRand ** + ************************** + ** Load up an array with random longs. + */ + private static void LoadNumArrayWithRand(int[,] array, /* Pointer to arrays */ + int arraysize, + int numarrays) /* # of elements in array */ + { + int i; /* Used for index */ + + /* + ** Initialize the random number generator + */ + ByteMark.randnum(13); + + /* + ** Load up first array with randoms + */ + for (i = 0; i < arraysize; i++) + array[0, i] = ByteMark.randnum(0); + + /* + ** Now, if there's more than one array to load, copy the + ** first into each of the others. + */ + while (--numarrays > 0) + { + for (int j = 0; j < arraysize; j++, i++) + array[numarrays, j] = array[0, j]; + } + + return; + } + + /**************** + ** NumHeapSort ** + ***************** + ** Pass this routine a pointer to an array of long + ** integers. Also pass in minimum and maximum offsets. + ** This routine performs a heap sort on that array. + */ + private static void NumHeapSort(int[,] array, + int row, /* which row */ + int top) /* Upper bound */ + { + int temp; /* Used to exchange elements */ + int i; /* Loop index */ + + /* + ** First, build a heap in the array + */ + for (i = (top / 2); i > 0; --i) + NumSift(array, row, i, top); + + /* + ** Repeatedly extract maximum from heap and place it at the + ** end of the array. When we get done, we'll have a sorted + ** array. + */ + for (i = top; i > 0; --i) + { + NumSift(array, row, 0, i); + temp = array[row, 0]; /* Perform exchange */ + array[row, 0] = array[row, i]; + array[row, i] = temp; + } + return; + } + + /************ + ** NumSift ** + ************* + ** Peforms the sift operation on a numeric array, + ** constructing a heap in the array. + */ + private static void NumSift(int[,] array, /* Array of numbers */ + int row, + int i, /* Minimum of array */ + int j) /* Maximum of array */ + { + int k; + int temp; /* Used for exchange */ + + while ((i + i) <= j) + { + k = i + i; + if (k < j) + if (array[row, k] < array[row, k + 1]) + ++k; + if (array[row, i] < array[row, k]) + { + temp = array[row, k]; + array[row, k] = array[row, i]; + array[row, i] = temp; + i = k; + } + else + i = j + 1; + } + return; + } +} diff --git a/tests/src/JIT/Performance/CodeQuality/Bytemark/utility.cs b/tests/src/JIT/Performance/CodeQuality/Bytemark/utility.cs new file mode 100644 index 0000000000..7a1363c4ea --- /dev/null +++ b/tests/src/JIT/Performance/CodeQuality/Bytemark/utility.cs @@ -0,0 +1,102 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System; +using System.Text; +using System.IO; + +public class Utility +{ + static public int sscanf(String stream, String format, Object[] results) + { + int fieldsRead = 0; + int resultsIndex = 0; + int formatIndex = 0; + char fieldType = '\0'; + char charRead = '\0'; + bool readingField = false; + bool eatWhiteSpace = false; + StringReader srStream = new StringReader(stream); + + while (formatIndex < format.Length) + { + if (Char.IsWhiteSpace((char)format[formatIndex])) + { + eatWhiteSpace = true; + formatIndex++; + continue; + } + while (eatWhiteSpace) + { + if (!Char.IsWhiteSpace((char)srStream.Peek())) + { + eatWhiteSpace = false; + break; + } + srStream.Read(); + } + if ('%' == format[formatIndex]) //If we found a scan field type + { + StringBuilder sb = new StringBuilder(); + ++formatIndex; + fieldType = format[formatIndex++]; + readingField = true; + charRead = (char)srStream.Read(); + + while (readingField) + { + if (-1 == (short)charRead) + { + readingField = false; + } + + sb.Append(charRead); + + int intCharRead = srStream.Peek(); + unchecked + { + charRead = (char)intCharRead; + } + if (Char.IsWhiteSpace(charRead) || ('c' == fieldType) || (-1 == intCharRead)) + { + readingField = false; + fieldsRead++; + + switch (fieldType) + { + case 'c': + results[resultsIndex++] = sb.ToString()[0]; + break; + case 'd': + case 'i': + int parsedInt; + parsedInt = int.Parse(sb.ToString()); + results[resultsIndex++] = parsedInt; + break; + case 'f': + double parsedDouble; + parsedDouble = double.Parse(sb.ToString()); + results[resultsIndex++] = parsedDouble; + break; + case 's': + results[resultsIndex++] = sb.ToString(); + break; + } + continue; + } + charRead = (char)srStream.Read(); + } + } + } + + return fieldsRead; + } + + static public int fscanf(TextReader stream, String format, Object[] results) + { + String s = stream.ReadLine(); + if (null == s) + return 0; + return sscanf(s, format, results); + } +} -- cgit v1.2.3