{"action":"created","issue":{"url":"https://api.github.com/repos/dotnet/coreclr/issues/1678","labels_url":"https://api.github.com/repos/dotnet/coreclr/issues/1678/labels{/name}","comments_url":"https://api.github.com/repos/dotnet/coreclr/issues/1678/comments","events_url":"https://api.github.com/repos/dotnet/coreclr/issues/1678/events","html_url":"https://github.com/dotnet/coreclr/issues/1678","id":109663023,"number":1678,"title":"Performance oddity: Vector3 * Matrix3 slower than Vector4 * Matrix4","user":{"login":"RossNordby","id":657209,"avatar_url":"https://avatars.githubusercontent.com/u/657209?v=3","gravatar_id":"","url":"https://api.github.com/users/RossNordby","html_url":"https://github.com/RossNordby","followers_url":"https://api.github.com/users/RossNordby/followers","following_url":"https://api.github.com/users/RossNordby/following{/other_user}","gists_url":"https://api.github.com/users/RossNordby/gists{/gist_id}","starred_url":"https://api.github.com/users/RossNordby/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/RossNordby/subscriptions","organizations_url":"https://api.github.com/users/RossNordby/orgs","repos_url":"https://api.github.com/users/RossNordby/repos","events_url":"https://api.github.com/users/RossNordby/events{/privacy}","received_events_url":"https://api.github.com/users/RossNordby/received_events","type":"User","site_admin":false},"labels":[],"state":"open","locked":false,"assignee":null,"milestone":null,"comments":2,"created_at":"2015-10-04T04:50:04Z","updated_at":"2015-10-04T08:24:24Z","closed_at":null,"body":"The Vector3 case seems to generate unnecessary instructions. Here's the code used to generate the results:\r\n```C#\r\nusing System;\r\nusing System.Diagnostics;\r\nusing System.Numerics;\r\nusing System.Runtime.CompilerServices;\r\n\r\nnamespace Repro\r\n{\r\n    class Repro\r\n    {\r\n        public struct Matrix4\r\n        {\r\n            public Vector4 X;\r\n            public Vector4 Y;\r\n            public Vector4 Z;\r\n            public Vector4 W;\r\n\r\n            [MethodImpl(MethodImplOptions.AggressiveInlining)]\r\n            public static void Transform(ref Vector4 v, ref Matrix4 m, out Vector4 result)\r\n            {\r\n                var x = new Vector4(v.X);\r\n                var y = new Vector4(v.Y);\r\n                var z = new Vector4(v.Z);\r\n                var w = new Vector4(v.W);\r\n                result = m.X * x + m.Y * y + m.Z * z + m.W * w;\r\n            }\r\n        }\r\n\r\n        public struct Matrix3\r\n        {\r\n            public Vector3 X;\r\n            public Vector3 Y;\r\n            public Vector3 Z;\r\n\r\n            [MethodImpl(MethodImplOptions.AggressiveInlining)]\r\n            public static void Transform(ref Vector3 v, ref Matrix3 m, out Vector3 result)\r\n            {\r\n                var x = new Vector3(v.X);\r\n                var y = new Vector3(v.Y);\r\n                var z = new Vector3(v.Z);\r\n                result = m.X * x + m.Y * y + m.Z * z;\r\n            }\r\n        }\r\n\r\n        static void Main(string[] args)\r\n        {\r\n            const int iterationCount = 10000000;\r\n\r\n            //MATRIX4x4\r\n            {\r\n                var v = new Vector4(1, 2, 3, 4);\r\n                var m = new Matrix4\r\n                {\r\n                    X = new Vector4(1, 0, 0, 0),\r\n                    Y = new Vector4(0, 1, 0, 0),\r\n                    Z = new Vector4(0, 0, 1, 0),\r\n                    W = new Vector4(0, 0, 0, 1)\r\n                };\r\n\r\n                //Warmup\r\n                {\r\n                    Vector4 result;\r\n                    Matrix4.Transform(ref v, ref m, out result);\r\n                }\r\n                Vector4 accumulator = new Vector4();\r\n                var startTime = Stopwatch.GetTimestamp();\r\n                for (int i = 0; i < iterationCount; ++i)\r\n                {\r\n                    Vector4 result;\r\n                    Matrix4.Transform(ref v, ref m, out result);\r\n                    Matrix4.Transform(ref result, ref m, out result);\r\n                    Matrix4.Transform(ref result, ref m, out result);\r\n                    Matrix4.Transform(ref result, ref m, out result);\r\n                    Matrix4.Transform(ref result, ref m, out result);\r\n                    Matrix4.Transform(ref result, ref m, out result);\r\n                    Matrix4.Transform(ref result, ref m, out result);\r\n                    Matrix4.Transform(ref result, ref m, out result);\r\n                    Matrix4.Transform(ref result, ref m, out result);\r\n                    Matrix4.Transform(ref result, ref m, out result);\r\n                    accumulator += result; //Avoid optimizing out the loop.\r\n                }\r\n                var endTime = Stopwatch.GetTimestamp();\r\n                Console.WriteLine($\"4x4 Time: {(endTime - startTime) / (double)Stopwatch.Frequency}\");\r\n            }\r\n\r\n            //MATRIX3x3\r\n            {\r\n                var v = new Vector3(1, 2, 3);\r\n                var m = new Matrix3 \r\n                {\r\n                    X = new Vector3(1, 0, 0),\r\n                    Y = new Vector3(0, 1, 0),\r\n                    Z = new Vector3(0, 0, 1),\r\n                };\r\n                //Warmup\r\n                {\r\n                    Vector3 result;\r\n                    Matrix3.Transform(ref v, ref m, out result);\r\n                }\r\n                Vector3 accumulator = new Vector3();\r\n                var startTime = Stopwatch.GetTimestamp();\r\n                for (int i = 0; i < iterationCount; ++i)\r\n                {\r\n                    Vector3 result;\r\n                    Matrix3.Transform(ref v, ref m, out result);\r\n                    Matrix3.Transform(ref result, ref m, out result);\r\n                    Matrix3.Transform(ref result, ref m, out result);\r\n                    Matrix3.Transform(ref result, ref m, out result);\r\n                    Matrix3.Transform(ref result, ref m, out result);\r\n                    Matrix3.Transform(ref result, ref m, out result);\r\n                    Matrix3.Transform(ref result, ref m, out result);\r\n                    Matrix3.Transform(ref result, ref m, out result);\r\n                    Matrix3.Transform(ref result, ref m, out result);\r\n                    Matrix3.Transform(ref result, ref m, out result);\r\n                    accumulator += result; //Avoid optimizing out the loop.\r\n                }\r\n                var endTime = Stopwatch.GetTimestamp();\r\n                Console.WriteLine($\"3x3 Time: {(endTime - startTime) / (double)Stopwatch.Frequency}\");\r\n            }\r\n        }\r\n    }\r\n}\r\n\r\n```\r\n\r\n\r\nRunning this code on my machine yields the following results:\r\n4x4 Time: 0.319555539691772\r\n3x3 Time: 0.345366045755573\r\n\r\nThe instructions generated for the Vector4 case are as follows:\r\n```\r\n00007FFA84DB472E  movaps      xmm1,xmm0  \r\n00007FFA84DB4731  shufps      xmm1,xmm1,0  \r\n00007FFA84DB4735  movaps      xmm2,xmm0  \r\n00007FFA84DB4738  psrldq      xmm2,4  \r\n00007FFA84DB473D  shufps      xmm2,xmm2,0  \r\n00007FFA84DB4741  movaps      xmm3,xmm0  \r\n00007FFA84DB4744  psrldq      xmm3,8  \r\n00007FFA84DB4749  shufps      xmm3,xmm3,0  \r\n00007FFA84DB474D  psrldq      xmm0,0Ch  \r\n00007FFA84DB4752  shufps      xmm0,xmm0,0  \r\n00007FFA84DB4756  movups      xmm4,xmmword ptr [rsp+88h]  \r\n00007FFA84DB475E  mulps       xmm1,xmm4  \r\n00007FFA84DB4761  movups      xmm4,xmmword ptr [rsp+78h]  \r\n00007FFA84DB4766  mulps       xmm2,xmm4  \r\n00007FFA84DB4769  addps       xmm1,xmm2  \r\n00007FFA84DB476C  movups      xmm2,xmmword ptr [rsp+68h]  \r\n00007FFA84DB4771  mulps       xmm2,xmm3  \r\n00007FFA84DB4774  addps       xmm1,xmm2  \r\n00007FFA84DB4777  movups      xmm2,xmmword ptr [rsp+58h]  \r\n00007FFA84DB477C  mulps       xmm0,xmm2  \r\n00007FFA84DB477F  addps       xmm0,xmm1  \r\n```\r\nIn contrast, the Vector3 case inserts shift-clearing prior to each broadcast shuffle:\r\n```\r\n00007FFA84DC4D11  movaps      xmm1,xmm0  \r\n00007FFA84DC4D14  pslldq      xmm1,0Ch  \r\n00007FFA84DC4D19  psrldq      xmm1,0Ch  \r\n00007FFA84DC4D1E  shufps      xmm1,xmm1,40h  \r\n00007FFA84DC4D22  movaps      xmm2,xmm0  \r\n00007FFA84DC4D25  psrldq      xmm2,4  \r\n00007FFA84DC4D2A  pslldq      xmm2,0Ch  \r\n00007FFA84DC4D2F  psrldq      xmm2,0Ch  \r\n00007FFA84DC4D34  shufps      xmm2,xmm2,40h  \r\n00007FFA84DC4D38  psrldq      xmm0,8  \r\n00007FFA84DC4D3D  pslldq      xmm0,0Ch  \r\n00007FFA84DC4D42  psrldq      xmm0,0Ch  \r\n00007FFA84DC4D47  shufps      xmm0,xmm0,40h  \r\n00007FFA84DC4D4B  movups      xmm3,xmmword ptr [rsp+48h]  \r\n00007FFA84DC4D50  mulps       xmm1,xmm3  \r\n00007FFA84DC4D53  movups      xmm3,xmmword ptr [rsp+38h]  \r\n00007FFA84DC4D58  mulps       xmm2,xmm3  \r\n00007FFA84DC4D5B  addps       xmm1,xmm2  \r\n00007FFA84DC4D5E  movups      xmm2,xmmword ptr [rsp+28h]  \r\n00007FFA84DC4D63  mulps       xmm0,xmm2  \r\n00007FFA84DC4D66  addps       xmm0,xmm1  \r\n```"},"comment":{"url":"https://api.github.com/repos/dotnet/coreclr/issues/comments/145327344","html_url":"https://github.com/dotnet/coreclr/issues/1678#issuecomment-145327344","issue_url":"https://api.github.com/repos/dotnet/coreclr/issues/1678","id":145327344,"user":{"login":"mikedn","id":9973420,"avatar_url":"https://avatars.githubusercontent.com/u/9973420?v=3","gravatar_id":"","url":"https://api.github.com/users/mikedn","html_url":"https://github.com/mikedn","followers_url":"https://api.github.com/users/mikedn/followers","following_url":"https://api.github.com/users/mikedn/following{/other_user}","gists_url":"https://api.github.com/users/mikedn/gists{/gist_id}","starred_url":"https://api.github.com/users/mikedn/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/mikedn/subscriptions","organizations_url":"https://api.github.com/users/mikedn/orgs","repos_url":"https://api.github.com/users/mikedn/repos","events_url":"https://api.github.com/users/mikedn/events{/privacy}","received_events_url":"https://api.github.com/users/mikedn/received_events","type":"User","site_admin":false},"created_at":"2015-10-04T08:24:24Z","updated_at":"2015-10-04T08:24:24Z","body":"It's probably worth adding that with AVX the Vector3 case generates a slightly better result than the Vector4 case:\r\n\r\n> 4x4 Time: 0.467328449537432\r\n> 3x3 Time: 0.43529047527632\r\n\r\nThat's probably because the Vector4 case uses `vpsrldq` + `vbroadcastss` while the Vector3 case uses `vinsertps` and `vshufps`."}}