diff options
author | Jiyoung Yun <jy910.yun@samsung.com> | 2016-11-23 19:09:09 +0900 |
---|---|---|
committer | Jiyoung Yun <jy910.yun@samsung.com> | 2016-11-23 19:09:09 +0900 |
commit | 4b4aad7217d3292650e77eec2cf4c198ea9c3b4b (patch) | |
tree | 98110734c91668dfdbb126fcc0e15ddbd93738ca /tests/src/JIT/Performance/CodeQuality/Bytemark | |
parent | fa45f57ed55137c75ac870356a1b8f76c84b229c (diff) | |
download | coreclr-4b4aad7217d3292650e77eec2cf4c198ea9c3b4b.tar.gz coreclr-4b4aad7217d3292650e77eec2cf4c198ea9c3b4b.tar.bz2 coreclr-4b4aad7217d3292650e77eec2cf4c198ea9c3b4b.zip |
Imported Upstream version 1.1.0upstream/1.1.0
Diffstat (limited to 'tests/src/JIT/Performance/CodeQuality/Bytemark')
32 files changed, 10938 insertions, 0 deletions
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..10699c67dc --- /dev/null +++ b/tests/src/JIT/Performance/CodeQuality/Bytemark/ByteMark.cs @@ -0,0 +1,1546 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. +/* +** 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<FILE>]", progname); + Console.WriteLine(" -c = Input parameters thru command file <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..160660c9df --- /dev/null +++ b/tests/src/JIT/Performance/CodeQuality/Bytemark/Bytemark.csproj @@ -0,0 +1,59 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.props))\dir.props" /> + <PropertyGroup> + <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> + <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform> + <SchemaVersion>2.0</SchemaVersion> + <ProjectGuid>{95DFC527-4DC1-495E-97D7-E94EE1F7140D}</ProjectGuid> + <OutputType>Exe</OutputType> + <AppDesignerFolder>Properties</AppDesignerFolder> + <FileAlignment>512</FileAlignment> + <ProjectTypeGuids>{786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids> + <ReferencePath>$(ProgramFiles)\Common Files\microsoft shared\VSTT\11.0\UITestExtensionPackages</ReferencePath> + <SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\..\</SolutionDir> + <NuGetPackageImportStamp>7a9bfb7d</NuGetPackageImportStamp> + <NuGetTargetMoniker>.NETStandard,Version=v1.4</NuGetTargetMoniker> + <GCStressIncompatible>true</GCStressIncompatible> + </PropertyGroup> + <!-- Default configurations to help VS understand the configurations --> + <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "> + </PropertyGroup> + <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "> + <DebugType>pdbonly</DebugType> + <Optimize>true</Optimize> + </PropertyGroup> + <ItemGroup> + <CodeAnalysisDependentAssemblyPaths Condition=" '$(VS100COMNTOOLS)' != '' " Include="$(VS100COMNTOOLS)..\IDE\PrivateAssemblies"> + <Visible>False</Visible> + </CodeAnalysisDependentAssemblyPaths> + </ItemGroup> + <ItemGroup> + <Service Include="{82A7F48D-3B50-4B1E-B82E-3ADA8210C358}" /> + </ItemGroup> + <ItemGroup> + <Compile Include="ByteMark.cs" /> + <Compile Include="Huffman.cs" /> + <Compile Include="StringSort.cs" /> + <Compile Include="assign_jagged.cs" /> + <Compile Include="assign_rect.cs" /> + <Compile Include="bitops.cs" /> + <Compile Include="emfloat.cs" /> + <Compile Include="emfloatclass.cs" /> + <Compile Include="fourier.cs" /> + <Compile Include="idea.cs" /> + <Compile Include="ludecomp.cs" /> + <Compile Include="neural.cs" /> + <Compile Include="neuraljagged.cs" /> + <Compile Include="neural-dat.cs" /> + <Compile Include="numericsort.cs" /> + <Compile Include="utility.cs" /> + </ItemGroup> + <PropertyGroup> + <ProjectJson>$(JitPackagesConfigFileDirectory)benchmark\project.json</ProjectJson> + <ProjectLockJson>$(JitPackagesConfigFileDirectory)benchmark\project.lock.json</ProjectLockJson> + </PropertyGroup> + <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.targets))\dir.targets" /> + <PropertyGroup Condition=" '$(MsBuildProjectDirOverride)' != '' "> + </PropertyGroup> +</Project> 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..bfe9965919 --- /dev/null +++ b/tests/src/JIT/Performance/CodeQuality/Bytemark/Huffman.cs @@ -0,0 +1,570 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. +/* +** 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<<bitnumb); + //int b = ~(1<<bitnumb); + comparray[byteoffset] &= unchecked((byte)(~(1 << bitnumb))); + } + + return; + } + + /*************** + ** GetCompBit ** + **************** + ** Return the bit value of a bit in the comparession array. + ** Returns 0 if the bit is clear, nonzero otherwise. + */ + private static int GetCompBit(byte[] comparray, + int bitoffset) + { + int byteoffset; + int bitnumb; + + /* + ** Calculate byte offset and bit number. + */ + byteoffset = bitoffset >> 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..e4888b2edd --- /dev/null +++ b/tests/src/JIT/Performance/CodeQuality/Bytemark/StringSort.cs @@ -0,0 +1,348 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. +/* +** 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]<array[k]) + if (StringOrdinalComparer.Compare(array[i], array[k]) < 0) + { + temp = array[k]; + array[k] = array[i]; + array[i] = temp; + i = k; + } + else + i = j + 1; + } + return; + } +} + diff --git a/tests/src/JIT/Performance/CodeQuality/Bytemark/assign_jagged.cs b/tests/src/JIT/Performance/CodeQuality/Bytemark/assign_jagged.cs new file mode 100644 index 0000000000..1ad69ff129 --- /dev/null +++ b/tests/src/JIT/Performance/CodeQuality/Bytemark/assign_jagged.cs @@ -0,0 +1,551 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. +/* +** 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 AssignJagged : AssignStruct +{ + public override string Name() + { + return "ASSIGNMENT(jagged)"; + } + 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][]; + for (int j = 0; j < global.ASSIGNROWS; j++) + arraybase[i][j] = new int[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][]; + 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..43cab8a0ea --- /dev/null +++ b/tests/src/JIT/Performance/CodeQuality/Bytemark/assign_rect.cs @@ -0,0 +1,545 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. +/* +** 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..d692eeea23 --- /dev/null +++ b/tests/src/JIT/Performance/CodeQuality/Bytemark/bitops.cs @@ -0,0 +1,279 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. +/* +** 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..e61b9380ab --- /dev/null +++ b/tests/src/JIT/Performance/CodeQuality/Bytemark/emfloat.cs @@ -0,0 +1,1577 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. +/* +** 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; i<INTERNAL_FPF_PRECISION; i++) + { + ptr->mantissa[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..0ae261c16b --- /dev/null +++ b/tests/src/JIT/Performance/CodeQuality/Bytemark/emfloatclass.cs @@ -0,0 +1,1566 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. +/* +** 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; i<INTERNAL_FPF_PRECISION; i++) + { + ptr->mantissa[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..e2e0a5d609 --- /dev/null +++ b/tests/src/JIT/Performance/CodeQuality/Bytemark/fourier.cs @@ -0,0 +1,230 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. +/* +** 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..732a8f0ec9 --- /dev/null +++ b/tests/src/JIT/Performance/CodeQuality/Bytemark/idea.cs @@ -0,0 +1,451 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. +/* +** 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..d45a51df59 --- /dev/null +++ b/tests/src/JIT/Performance/CodeQuality/Bytemark/ludecomp.cs @@ -0,0 +1,340 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. +/* +** 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..6e3ab36b94 --- /dev/null +++ b/tests/src/JIT/Performance/CodeQuality/Bytemark/neural-dat.cs @@ -0,0 +1,231 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. +/* +** 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..9e842c3910 --- /dev/null +++ b/tests/src/JIT/Performance/CodeQuality/Bytemark/neural.cs @@ -0,0 +1,883 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. +/* +** 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<OUT_SIZE; i++) + { + fprintf(outfile,"%6.3f ",out_pats[patt][i]); + } + fprintf(outfile,"\n Actual Output: "); + + for (i=0; i<OUT_SIZE; i++) + { + fprintf(outfile,"%6.3f ",out_out[i]); + } + fprintf(outfile,"\n"); + return; + } + */ + + /********************** + ** do_forward_pass() ** + *********************** + ** control function for the forward pass through the network + ** NOTE: I have disabled the call to display_output() in + ** the benchmark version -- RG. + **/ + public static void do_forward_pass(int patt) + { + do_mid_forward(patt); /* process forward pass, middle layer */ + do_out_forward(); /* process forward pass, output layer */ + /* display_output(patt); ** display results of forward pass */ + return; + } + + /*********************** + ** do_out_error(patt) ** + ************************ + ** Compute the error for the output layer neurodes. + ** This is simply Desired - Actual. + **/ + public static void do_out_error(int patt) + { + int neurode; + double error, tot_error, sum; + + tot_error = 0.0; + sum = 0.0; + for (neurode = 0; neurode < OUT_SIZE; neurode++) + { + out_error[neurode] = out_pats[patt, neurode] - out_out[neurode]; + /* + ** while we're here, also compute magnitude + ** of total error and worst error in this pass. + ** We use these to decide if we are done yet. + */ + error = out_error[neurode]; + if (error < 0.0) + { + sum += -error; + if (-error > 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<MID_SIZE; neurode++) + { + fprintf(outfile,"\n Mid Neurode # %d",neurode); + for (row=0; row<IN_Y_SIZE; row++) + { + fprintf(outfile,"\n "); + for (col=0; col<IN_X_SIZE; col++) + { + weight = IN_X_SIZE * row + col; + fprintf(outfile," %8.3f ", mid_wts[neurode,weight]); + } + } + } + return; + } + */ + /********************** + ** display_out_wts() ** + *********************** + ** Display the weights on the output layer neurodes + ** NOTE: This code is not used in the benchmark + ** test -- RG + */ + /* void display_out_wts() + { + int neurode, weight; + + fprintf(outfile,"\n Weights of Output Layer neurodes:"); + + for (neurode=0; neurode<OUT_SIZE; neurode++) + { + fprintf(outfile,"\n Out Neurode # %d \n",neurode); + for (weight=0; weight<MID_SIZE; weight++) + { + fprintf(outfile," %8.3f ", out_wts[neurode,weight]); + } + } + return; + } + */ +} + diff --git a/tests/src/JIT/Performance/CodeQuality/Bytemark/neuraljagged.cs b/tests/src/JIT/Performance/CodeQuality/Bytemark/neuraljagged.cs new file mode 100644 index 0000000000..d9dd199c98 --- /dev/null +++ b/tests/src/JIT/Performance/CodeQuality/Bytemark/neuraljagged.cs @@ -0,0 +1,882 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. +/* +** 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 NeuralJagged : NNetStruct +{ + public override string Name() + { + return "NEURAL NET(jagged)"; + } + + /* + ** 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][]; + public static double[][] out_wts = new double[OUT_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][]; + public static double[][] out_wt_change = new double[OUT_SIZE][]; + public static double[][] in_pats = new double[MAXPATS][]; + public static double[][] out_pats = new double[MAXPATS][]; + public static double[] tot_out_error = new double[MAXPATS]; + public static double[][] out_wt_cum_change = new double[OUT_SIZE][]; + public static double[][] mid_wt_cum_change = new double[MID_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 static void Init() + { + for (int i = 0; i < MID_SIZE; i++) + { + mid_wts[i] = new double[IN_SIZE]; + mid_wt_cum_change[i] = new double[IN_SIZE]; + mid_wt_change[i] = new double[IN_SIZE]; + } + for (int i = 0; i < OUT_SIZE; i++) + { + out_wt_change[i] = new double[MID_SIZE]; + out_wts[i] = new double[MID_SIZE]; + out_wt_cum_change[i] = new double[MID_SIZE]; + } + + for (int i = 0; i < MAXPATS; i++) + { + in_pats[i] = new double[IN_SIZE]; + out_pats[i] = new double[OUT_SIZE]; + } + } + + public override + double Run() + { + Init(); + 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<OUT_SIZE; i++) + { + fprintf(outfile,"%6.3f ",out_pats[patt][i]); + } + fprintf(outfile,"\n Actual Output: "); + + for (i=0; i<OUT_SIZE; i++) + { + fprintf(outfile,"%6.3f ",out_out[i]); + } + fprintf(outfile,"\n"); + return; + } + */ + + /********************** + ** do_forward_pass() ** + *********************** + ** control function for the forward pass through the network + ** NOTE: I have disabled the call to display_output() in + ** the benchmark version -- RG. + **/ + public static void do_forward_pass(int patt) + { + do_mid_forward(patt); /* process forward pass, middle layer */ + do_out_forward(); /* process forward pass, output layer */ + /* display_output(patt); ** display results of forward pass */ + return; + } + + /*********************** + ** do_out_error(patt) ** + ************************ + ** Compute the error for the output layer neurodes. + ** This is simply Desired - Actual. + **/ + public static void do_out_error(int patt) + { + int neurode; + double error, tot_error, sum; + + tot_error = 0.0; + sum = 0.0; + for (neurode = 0; neurode < OUT_SIZE; neurode++) + { + out_error[neurode] = out_pats[patt][neurode] - out_out[neurode]; + /* + ** while we're here, also compute magnitude + ** of total error and worst error in this pass. + ** We use these to decide if we are done yet. + */ + error = out_error[neurode]; + if (error < 0.0) + { + sum += -error; + if (-error > 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<MID_SIZE; neurode++) + { + fprintf(outfile,"\n Mid Neurode # %d",neurode); + for (row=0; row<IN_Y_SIZE; row++) + { + fprintf(outfile,"\n "); + for (col=0; col<IN_X_SIZE; col++) + { + weight = IN_X_SIZE * row + col; + fprintf(outfile," %8.3f ", mid_wts[neurode,weight]); + } + } + } + return; + } + */ + /********************** + ** display_out_wts() ** + *********************** + ** Display the weights on the output layer neurodes + ** NOTE: This code is not used in the benchmark + ** test -- RG + */ + /* void display_out_wts() + { + int neurode, weight; + + fprintf(outfile,"\n Weights of Output Layer neurodes:"); + + for (neurode=0; neurode<OUT_SIZE; neurode++) + { + fprintf(outfile,"\n Out Neurode # %d \n",neurode); + for (weight=0; weight<MID_SIZE; weight++) + { + fprintf(outfile," %8.3f ", out_wts[neurode,weight]); + } + } + return; + } + */ +} + diff --git a/tests/src/JIT/Performance/CodeQuality/Bytemark/nnet.dat b/tests/src/JIT/Performance/CodeQuality/Bytemark/nnet.dat new file mode 100644 index 0000000000..5711730d93 --- /dev/null +++ b/tests/src/JIT/Performance/CodeQuality/Bytemark/nnet.dat @@ -0,0 +1,210 @@ +5 7 8 +26 +0 0 1 0 0 +0 1 0 1 0 +1 0 0 0 1 +1 0 0 0 1 +1 1 1 1 1 +1 0 0 0 1 +1 0 0 0 1 +0 1 0 0 0 0 0 1 +1 1 1 1 0 +1 0 0 0 1 +1 0 0 0 1 +1 1 1 1 0 +1 0 0 0 1 +1 0 0 0 1 +1 1 1 1 0 +0 1 0 0 0 0 1 0 +0 1 1 1 0 +1 0 0 0 1 +1 0 0 0 0 +1 0 0 0 0 +1 0 0 0 0 +1 0 0 0 1 +0 1 1 1 0 +0 1 0 0 0 0 1 1 +1 1 1 1 0 +1 0 0 0 1 +1 0 0 0 1 +1 0 0 0 1 +1 0 0 0 1 +1 0 0 0 1 +1 1 1 1 0 +0 1 0 0 0 1 0 0 +1 1 1 1 1 +1 0 0 0 0 +1 0 0 0 0 +1 1 1 0 0 +1 0 0 0 0 +1 0 0 0 0 +1 1 1 1 1 +0 1 0 0 0 1 0 1 +1 1 1 1 1 +1 0 0 0 0 +1 0 0 0 0 +1 1 1 0 0 +1 0 0 0 0 +1 0 0 0 0 +1 0 0 0 0 +0 1 0 0 0 1 1 0 +0 1 1 1 0 +1 0 0 0 1 +1 0 0 0 0 +1 0 0 0 0 +1 0 0 1 1 +1 0 0 0 1 +0 1 1 1 0 +0 1 0 0 0 1 1 1 +1 0 0 0 1 +1 0 0 0 1 +1 0 0 0 1 +1 1 1 1 1 +1 0 0 0 1 +1 0 0 0 1 +1 0 0 0 1 +0 1 0 0 1 0 0 0 +0 1 1 1 0 +0 0 1 0 0 +0 0 1 0 0 +0 0 1 0 0 +0 0 1 0 0 +0 0 1 0 0 +0 1 1 1 0 +0 1 0 0 1 0 0 1 +0 0 0 0 1 +0 0 0 0 1 +0 0 0 0 1 +0 0 0 0 1 +1 0 0 0 1 +1 0 0 0 1 +0 1 1 1 0 +0 1 0 0 1 0 1 0 +1 0 0 0 1 +1 0 0 1 0 +1 0 1 0 0 +1 1 0 0 0 +1 0 1 0 0 +1 0 0 1 0 +1 0 0 0 1 +0 1 0 0 1 0 1 1 +1 0 0 0 0 +1 0 0 0 0 +1 0 0 0 0 +1 0 0 0 0 +1 0 0 0 0 +1 0 0 0 0 +1 1 1 1 1 +0 1 0 0 1 1 0 0 +1 0 0 0 1 +1 1 0 1 1 +1 0 1 0 1 +1 0 1 0 1 +1 0 0 0 1 +1 0 0 0 1 +1 0 0 0 1 +0 1 0 0 1 1 0 1 +1 0 0 0 1 +1 1 0 0 1 +1 0 1 0 1 +1 0 1 0 1 +1 0 1 0 1 +1 0 0 1 1 +1 0 0 0 1 +0 1 0 0 1 1 1 0 +0 1 1 1 0 +1 0 0 0 1 +1 0 0 0 1 +1 0 0 0 1 +1 0 0 0 1 +1 0 0 0 1 +0 1 1 1 0 +0 1 0 0 1 1 1 1 +1 1 1 1 0 +1 0 0 0 1 +1 0 0 0 1 +1 1 1 1 0 +1 0 0 0 0 +1 0 0 0 0 +1 0 0 0 0 +0 1 0 1 0 0 0 0 +0 1 1 1 0 +1 0 0 0 1 +1 0 0 0 1 +1 0 0 0 1 +1 0 1 0 1 +1 0 0 1 1 +0 1 1 1 1 +0 1 0 1 0 0 0 1 +1 1 1 1 0 +1 0 0 0 1 +1 0 0 0 1 +1 1 1 1 0 +1 0 1 0 0 +1 0 0 1 0 +1 0 0 0 1 +0 1 0 1 0 0 1 0 +0 1 1 1 1 +1 0 0 0 0 +1 0 0 0 0 +0 1 1 1 0 +0 0 0 0 1 +0 0 0 0 1 +1 1 1 1 0 +0 1 0 1 0 0 1 1 +1 1 1 1 1 +0 0 1 0 0 +0 0 1 0 0 +0 0 1 0 0 +0 0 1 0 0 +0 0 1 0 0 +0 0 1 0 0 +0 1 0 1 0 1 0 0 +1 0 0 0 1 +1 0 0 0 1 +1 0 0 0 1 +1 0 0 0 1 +1 0 0 0 1 +1 0 0 0 1 +0 1 1 1 0 +0 1 0 1 0 1 0 1 +1 0 0 0 1 +1 0 0 0 1 +0 1 0 1 0 +0 1 0 1 0 +0 1 0 1 0 +0 1 0 1 0 +0 0 1 0 0 +0 1 0 1 0 1 1 0 +1 0 0 0 1 +1 0 0 0 1 +1 0 0 0 1 +1 0 1 0 1 +1 0 1 0 1 +1 0 1 0 1 +0 1 0 1 0 +0 1 0 1 0 1 1 1 +1 0 0 0 1 +0 1 0 1 0 +0 1 0 1 0 +0 0 1 0 0 +0 1 0 1 0 +0 1 0 1 0 +1 0 0 0 1 +0 1 0 1 1 0 0 0 +1 0 0 0 1 +0 1 0 1 0 +0 1 0 1 0 +0 0 1 0 0 +0 0 1 0 0 +0 0 1 0 0 +0 0 1 0 0 +0 1 0 1 1 0 0 1 +1 1 1 1 1 +0 0 0 1 0 +0 0 0 1 0 +0 0 1 0 0 +0 1 0 0 0 +0 1 0 0 0 +1 1 1 1 1 +0 1 0 1 1 0 1 0 diff --git a/tests/src/JIT/Performance/CodeQuality/Bytemark/numericsort.cs b/tests/src/JIT/Performance/CodeQuality/Bytemark/numericsort.cs new file mode 100644 index 0000000000..44da6e3a71 --- /dev/null +++ b/tests/src/JIT/Performance/CodeQuality/Bytemark/numericsort.cs @@ -0,0 +1,539 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. +/* +** 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. +*/ + +/************** +** DoNumSort ** +*************** +** This routine performs the CPU numeric sort test. +** NOTE: Last version incorrectly stated that the routine +** returned result in # of longword sorted per second. +** Not so; the routine returns # of iterations per sec. +*/ + +using System; + +// #define DEBUG + +public class NumericSortJagged : SortStruct +{ + public override string Name() + { + return "NUMERIC SORT(jagged)"; + } + + 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][]; + for (int i = 0; i < this.numarrays; i++) + arraybase[i] = new int[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][]; + 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. <shrug> + 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. <shrug> + 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..c0bdbb656f --- /dev/null +++ b/tests/src/JIT/Performance/CodeQuality/Bytemark/utility.cs @@ -0,0 +1,103 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more 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); + } +} |