Add perf test
authorJoseph Tremoulet <jotrem@microsoft.com>
Tue, 8 Aug 2017 21:09:25 +0000 (17:09 -0400)
committerJoseph Tremoulet <jotrem@microsoft.com>
Thu, 17 Aug 2017 23:36:41 +0000 (19:36 -0400)
Commit migrated from https://github.com/dotnet/coreclr/commit/8127531109f5137290f40eebbfbf4294e4034c89

src/coreclr/tests/src/JIT/Performance/CodeQuality/Layout/SearchLoops.cs [new file with mode: 0644]
src/coreclr/tests/src/JIT/Performance/CodeQuality/Layout/SearchLoops.csproj [new file with mode: 0644]

diff --git a/src/coreclr/tests/src/JIT/Performance/CodeQuality/Layout/SearchLoops.cs b/src/coreclr/tests/src/JIT/Performance/CodeQuality/Layout/SearchLoops.cs
new file mode 100644 (file)
index 0000000..c08e314
--- /dev/null
@@ -0,0 +1,120 @@
+// 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 Microsoft.Xunit.Performance;
+using Microsoft.Xunit.Performance.Api;
+using System;
+using System.Reflection;
+using Xunit;
+
+[assembly: OptimizeForBenchmarks]
+
+// Test code taken directly from GitHub issue #9692 (https://github.com/dotnet/coreclr/issues/9692)
+// Laying the loop's early return path in-line can cost 30% on this micro-benchmark.
+
+namespace Layout
+{
+    public unsafe class SearchLoops
+    {
+        public static int Main(string[] args)
+        {
+            // Make sure equal strings compare as such
+            if (!LoopReturn("hello", "hello") || !LoopGoto("goodbye", "goodbye"))
+            {
+                return -1;
+            }
+
+            // Make sure not-equal strings compare as such
+            if (LoopReturn("hello", "goodbye") || LoopGoto("goodbye", "hello"))
+            {
+                return -1;
+            }
+
+            // Success
+            return 100;
+        }
+
+        public int length = 100;
+
+        private string test1;
+        private string test2;
+
+        public SearchLoops()
+        {
+            test1 = new string('A', length);
+            test2 = new string('A', length);
+        }
+
+        [Benchmark]
+        public void LoopReturn()
+        {
+            Benchmark.Iterate(() => LoopReturn(test1, test2));
+        }
+
+        [Benchmark]
+        public void LoopGoto()
+        {
+            Benchmark.Iterate(() => LoopGoto(test1, test2));
+        }
+
+        // Variant with code written naturally -- need JIT to lay this out
+        // with return path out of loop for best performance.
+        public static bool LoopReturn(String strA, String strB)
+        {
+            int length = strA.Length;
+
+            fixed (char* ap = strA) fixed (char* bp = strB)
+            {
+                char* a = ap;
+                char* b = bp;
+
+                while (length != 0)
+                {
+                    int charA = *a;
+                    int charB = *b;
+
+                    if (charA != charB)
+                        return false;  // placement of prolog for this return is the issue
+
+                    a++;
+                    b++;
+                    length--;
+                }
+
+                return true;
+            }
+        }
+
+        // Variant with code written awkwardly but which acheives the desired
+        // performance if JIT simply lays out code in source order.
+        public static bool LoopGoto(String strA, String strB)
+        {
+            int length = strA.Length;
+
+            fixed (char* ap = strA) fixed (char* bp = strB)
+            {
+                char* a = ap;
+                char* b = bp;
+
+                while (length != 0)
+                {
+                    int charA = *a;
+                    int charB = *b;
+
+                    if (charA != charB)
+                        goto ReturnFalse;  // placement of prolog for this return is the issue
+
+                    a++;
+                    b++;
+                    length--;
+                }
+
+                return true;
+
+                ReturnFalse:
+                return false;
+            }
+        }
+    }
+}
diff --git a/src/coreclr/tests/src/JIT/Performance/CodeQuality/Layout/SearchLoops.csproj b/src/coreclr/tests/src/JIT/Performance/CodeQuality/Layout/SearchLoops.csproj
new file mode 100644 (file)
index 0000000..2826c93
--- /dev/null
@@ -0,0 +1,39 @@
+<?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>{C1BFD48A-A83F-4767-8EB2-3E2C50906681}</ProjectGuid>
+    <OutputType>Exe</OutputType>
+    <ProjectTypeGuids>{786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
+    <SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\..\</SolutionDir>
+    <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
+  </PropertyGroup>
+  <!-- Default configurations to help VS understand the configurations -->
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
+    <DebugType>pdbonly</DebugType>
+    <Optimize>true</Optimize>
+  </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="SearchLoops.cs" />
+  </ItemGroup>
+  <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.targets))\dir.targets" />
+  <PropertyGroup Condition=" '$(MsBuildProjectDirOverride)' != '' "></PropertyGroup>
+  <PropertyGroup>
+    <ProjectAssetsFile>$(JitPackagesConfigFileDirectory)benchmark\obj\project.assets.json</ProjectAssetsFile>
+  </PropertyGroup>
+</Project>