summaryrefslogtreecommitdiff
path: root/tests/src/JIT/Performance/CodeQuality/Bytemark
diff options
context:
space:
mode:
authorJiyoung Yun <jy910.yun@samsung.com>2016-11-23 19:09:09 +0900
committerJiyoung Yun <jy910.yun@samsung.com>2016-11-23 19:09:09 +0900
commit4b4aad7217d3292650e77eec2cf4c198ea9c3b4b (patch)
tree98110734c91668dfdbb126fcc0e15ddbd93738ca /tests/src/JIT/Performance/CodeQuality/Bytemark
parentfa45f57ed55137c75ac870356a1b8f76c84b229c (diff)
downloadcoreclr-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')
-rw-r--r--tests/src/JIT/Performance/CodeQuality/Bytemark/ByteMark.cs1546
-rw-r--r--tests/src/JIT/Performance/CodeQuality/Bytemark/Bytemark.csproj59
-rw-r--r--tests/src/JIT/Performance/CodeQuality/Bytemark/Huffman.cs570
-rw-r--r--tests/src/JIT/Performance/CodeQuality/Bytemark/StringSort.cs348
-rw-r--r--tests/src/JIT/Performance/CodeQuality/Bytemark/assign_jagged.cs551
-rw-r--r--tests/src/JIT/Performance/CodeQuality/Bytemark/assign_rect.cs545
-rw-r--r--tests/src/JIT/Performance/CodeQuality/Bytemark/bitops.cs279
-rw-r--r--tests/src/JIT/Performance/CodeQuality/Bytemark/commands/assignjagged.dat2
-rw-r--r--tests/src/JIT/Performance/CodeQuality/Bytemark/commands/assignrect.dat2
-rw-r--r--tests/src/JIT/Performance/CodeQuality/Bytemark/commands/bitfield.dat2
-rw-r--r--tests/src/JIT/Performance/CodeQuality/Bytemark/commands/emfclass.dat2
-rw-r--r--tests/src/JIT/Performance/CodeQuality/Bytemark/commands/emfstruct.dat2
-rw-r--r--tests/src/JIT/Performance/CodeQuality/Bytemark/commands/four.dat2
-rw-r--r--tests/src/JIT/Performance/CodeQuality/Bytemark/commands/huff.dat2
-rw-r--r--tests/src/JIT/Performance/CodeQuality/Bytemark/commands/idea.dat2
-rw-r--r--tests/src/JIT/Performance/CodeQuality/Bytemark/commands/lu.dat2
-rw-r--r--tests/src/JIT/Performance/CodeQuality/Bytemark/commands/nnetjagged.dat2
-rw-r--r--tests/src/JIT/Performance/CodeQuality/Bytemark/commands/nnetrect.dat2
-rw-r--r--tests/src/JIT/Performance/CodeQuality/Bytemark/commands/numsortjagged.dat2
-rw-r--r--tests/src/JIT/Performance/CodeQuality/Bytemark/commands/numsortrect.dat2
-rw-r--r--tests/src/JIT/Performance/CodeQuality/Bytemark/commands/stringsort.dat2
-rw-r--r--tests/src/JIT/Performance/CodeQuality/Bytemark/emfloat.cs1577
-rw-r--r--tests/src/JIT/Performance/CodeQuality/Bytemark/emfloatclass.cs1566
-rw-r--r--tests/src/JIT/Performance/CodeQuality/Bytemark/fourier.cs230
-rw-r--r--tests/src/JIT/Performance/CodeQuality/Bytemark/idea.cs451
-rw-r--r--tests/src/JIT/Performance/CodeQuality/Bytemark/ludecomp.cs340
-rw-r--r--tests/src/JIT/Performance/CodeQuality/Bytemark/neural-dat.cs231
-rw-r--r--tests/src/JIT/Performance/CodeQuality/Bytemark/neural.cs883
-rw-r--r--tests/src/JIT/Performance/CodeQuality/Bytemark/neuraljagged.cs882
-rw-r--r--tests/src/JIT/Performance/CodeQuality/Bytemark/nnet.dat210
-rw-r--r--tests/src/JIT/Performance/CodeQuality/Bytemark/numericsort.cs539
-rw-r--r--tests/src/JIT/Performance/CodeQuality/Bytemark/utility.cs103
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);
+ }
+}