{"action":"created","issue":{"url":"https://api.github.com/repos/Microsoft/visualfsharp/issues/549","labels_url":"https://api.github.com/repos/Microsoft/visualfsharp/issues/549/labels{/name}","comments_url":"https://api.github.com/repos/Microsoft/visualfsharp/issues/549/comments","events_url":"https://api.github.com/repos/Microsoft/visualfsharp/issues/549/events","html_url":"https://github.com/Microsoft/visualfsharp/pull/549","id":96475717,"number":549,"title":"Remove StructBox for Value Types","user":{"login":"manofstick","id":2093966,"avatar_url":"https://avatars.githubusercontent.com/u/2093966?v=3","gravatar_id":"","url":"https://api.github.com/users/manofstick","html_url":"https://github.com/manofstick","followers_url":"https://api.github.com/users/manofstick/followers","following_url":"https://api.github.com/users/manofstick/following{/other_user}","gists_url":"https://api.github.com/users/manofstick/gists{/gist_id}","starred_url":"https://api.github.com/users/manofstick/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/manofstick/subscriptions","organizations_url":"https://api.github.com/users/manofstick/orgs","repos_url":"https://api.github.com/users/manofstick/repos","events_url":"https://api.github.com/users/manofstick/events{/privacy}","received_events_url":"https://api.github.com/users/manofstick/received_events","type":"User","site_admin":false},"labels":[{"url":"https://api.github.com/repos/Microsoft/visualfsharp/labels/cla-already-signed","name":"cla-already-signed","color":"009800"}],"state":"open","locked":false,"assignee":null,"milestone":null,"comments":2,"created_at":"2015-07-22T05:07:20Z","updated_at":"2015-07-22T05:38:44Z","closed_at":null,"pull_request":{"url":"https://api.github.com/repos/Microsoft/visualfsharp/pulls/549","html_url":"https://github.com/Microsoft/visualfsharp/pull/549","diff_url":"https://github.com/Microsoft/visualfsharp/pull/549.diff","patch_url":"https://github.com/Microsoft/visualfsharp/pull/549.patch"},"body":"The StructBox idiom is used for countBy/groupBy/dict so that reference type's with null values can work.\r\n\r\nFor Value Types this is just an extra layer of indirection which adds a performance cost without any benefit. This change request adds checks on the keys type, and then calls a customised value or reference type version.\r\n\r\nThis is related to #513, but can be considered a worthwhile optimization on it's own."},"comment":{"url":"https://api.github.com/repos/Microsoft/visualfsharp/issues/comments/123570269","html_url":"https://github.com/Microsoft/visualfsharp/pull/549#issuecomment-123570269","issue_url":"https://api.github.com/repos/Microsoft/visualfsharp/issues/549","id":123570269,"user":{"login":"manofstick","id":2093966,"avatar_url":"https://avatars.githubusercontent.com/u/2093966?v=3","gravatar_id":"","url":"https://api.github.com/users/manofstick","html_url":"https://github.com/manofstick","followers_url":"https://api.github.com/users/manofstick/followers","following_url":"https://api.github.com/users/manofstick/following{/other_user}","gists_url":"https://api.github.com/users/manofstick/gists{/gist_id}","starred_url":"https://api.github.com/users/manofstick/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/manofstick/subscriptions","organizations_url":"https://api.github.com/users/manofstick/orgs","repos_url":"https://api.github.com/users/manofstick/repos","events_url":"https://api.github.com/users/manofstick/events{/privacy}","received_events_url":"https://api.github.com/users/manofstick/received_events","type":"User","site_admin":false},"created_at":"2015-07-22T05:38:44Z","updated_at":"2015-07-22T05:38:44Z","body":"The following are some results from a timing test listed below. Note that the times for the 64-bit JIT are awful, even after this change (albeit a fair bit better). This is due to the 'hard' tail call optimizations. Removing the StructBox removed a layer of function calls that emitted tails, but there are still other performance layers to tear away (i.e. #513)\r\n\r\n(not sure if the broken build is my fault - I do need to check this [on my laptop on the tram doesn't make for good full builds], but the results from appveyor seem to not have done anything)\r\n\r\nTest | x86_old | x86_new | x64_old | x64_new |  | x86_new | x64_new \r\n------------ | ------------- | ------------- | ------------- | ------------- | ------------- | ------------- | -------------\r\nseqGroupBy | 1707 | 1677 | 4188 | 2980 |  | 98.24% | 71.16%\r\nseqCountBy | 2280 | 2032 | 6976 | 4297 |  | 89.12% | 61.60%\r\nlistGroupBy | 1230 | 1092 | 3538 | 2283 |  | 88.78% | 64.53%\r\nlistCountBy | 1822 | 1554 | 6466 | 3783 |  | 85.29% | 58.51%\r\narrayCountBy | 1624 | 1350 | 6212 | 3589 |  | 83.13% | 57.78%\r\narrayGroupBy | 931 | 774 | 3230 | 1887 |  | 83.14% | 58.42%\r\ndict | 848 | 780 | 1995 | 1336 |  | 91.98% | 66.97%\r\n\r\nThe code\r\n\r\n    module Program\r\n\r\n    open System.Diagnostics\r\n\r\n    type Key1 =\r\n        struct\r\n            val A : int\r\n            new (a)= { A = a }\r\n        end\r\n\r\n    type Key2 =\r\n        struct\r\n            val A : int\r\n            val B : int\r\n            new (a,b)= { A=a;B=b }\r\n        end\r\n\r\n    type Key3 =\r\n        struct\r\n            val A : int\r\n            val B : int\r\n            val C : int\r\n            new (a,b,c)= { A=a;B=b;C=c }\r\n        end\r\n\r\n    type Key4 =\r\n        struct\r\n            val A : int\r\n            val B : int\r\n            val C : int\r\n            val D : int\r\n            new (a,b,c,d)= { A=a;B=b;C=c;D=d }\r\n        end\r\n\r\n    type Key5 =\r\n        struct\r\n            val A : int\r\n            val B : int\r\n            val C : int\r\n            val D : int\r\n            val E : int\r\n            new (a,b,c,d,e)= { A=a;B=b;C=c;D=d;E=e }\r\n        end\r\n\r\n    let seqTest f n createKey =\r\n        let sw = Stopwatch.StartNew ()\r\n        let count =\r\n            Seq.init n id\r\n            |> f createKey\r\n            |> Seq.length\r\n        count, sw.ElapsedMilliseconds\r\n\r\n    let listTest f n createKey =\r\n        let sw = Stopwatch.StartNew ()\r\n        let count =\r\n            List.init n id\r\n            |> f createKey\r\n            |> List.length\r\n        count, sw.ElapsedMilliseconds\r\n\r\n    let arrayTest f n createKey =\r\n        let sw = Stopwatch.StartNew ()\r\n        let count =\r\n            Array.init n id\r\n            |> f createKey\r\n            |> Array.length\r\n        count, sw.ElapsedMilliseconds\r\n\r\n    let dictTest n createKey =\r\n        let sw = Stopwatch.StartNew ()\r\n        let count =\r\n            Array.init n (fun n -> createKey n, n)\r\n            |> dict\r\n            |> fun d -> d.Count\r\n        count, sw.ElapsedMilliseconds\r\n\r\n    let createKey2 n = Key2(n%11,n%7)\r\n    let createKey3 n = Key3(n%11,n%7,n%5)\r\n    let createKey4 n = Key4(n%11,n%7,n%5,n%3)\r\n    let createKey5 n = Key5(n%11,n%7,n%5,n%3,n%2)\r\n    let createDictKey2 n = Key2(n,n)\r\n    let createDictKey3 n = Key3(n,n,n)\r\n    let createDictKey4 n = Key4(n,n,n,n)\r\n    let createDictKey5 n = Key5(n,n,n,n,n)\r\n\r\n    let runSeqGroupByTest () =\r\n        let size = 100000\r\n        let mutable totalTime = 0L\r\n        for i = 1 to 5 do\r\n            let c2, t2 = seqTest Seq.groupBy size createKey2\r\n            let c3, t3 = seqTest Seq.groupBy size createKey3\r\n            let c4, t4 = seqTest Seq.groupBy size createKey4\r\n            let c5, t5 = seqTest Seq.groupBy size createKey5\r\n            totalTime <- totalTime + t2 + t3 + t4 + t5\r\n            printfn \"seqGroupBy %d,%d,%d,%d (%d,%d,%d,%d)\" t2 t3 t4 t5 c2 c3 c4 c5\r\n        printfn \"seqGroupBy totalTime=%d\" totalTime\r\n\r\n    let runSeqCountByTest () =\r\n        let size = 100000\r\n        let mutable totalTime = 0L\r\n        for i = 1 to 5 do\r\n            let c2, t2 = seqTest Seq.countBy size createKey2\r\n            let c3, t3 = seqTest Seq.countBy size createKey3\r\n            let c4, t4 = seqTest Seq.countBy size createKey4\r\n            let c5, t5 = seqTest Seq.countBy size createKey5\r\n            totalTime <- totalTime + t2 + t3 + t4 + t5\r\n            printfn \"seqCountBy %d,%d,%d,%d (%d,%d,%d,%d)\" t2 t3 t4 t5 c2 c3 c4 c5\r\n        printfn \"seqCountBy totalTime=%d\" totalTime\r\n\r\n    let runListGroupByTest () =\r\n        let size = 100000\r\n        let mutable totalTime = 0L\r\n        for i = 1 to 5 do\r\n            let c2, t2 = listTest List.groupBy size createKey2\r\n            let c3, t3 = listTest List.groupBy size createKey3\r\n            let c4, t4 = listTest List.groupBy size createKey4\r\n            let c5, t5 = listTest List.groupBy size createKey5\r\n            totalTime <- totalTime + t2 + t3 + t4 + t5\r\n            printfn \"listGroupBy %d,%d,%d,%d (%d,%d,%d,%d)\" t2 t3 t4 t5 c2 c3 c4 c5\r\n        printfn \"listGroupBy totalTime=%d\" totalTime\r\n\r\n    let runListCountByTest () =\r\n        let size = 100000\r\n        let mutable totalTime = 0L\r\n        for i = 1 to 5 do\r\n            let c2, t2 = listTest List.countBy size createKey2\r\n            let c3, t3 = listTest List.countBy size createKey3\r\n            let c4, t4 = listTest List.countBy size createKey4\r\n            let c5, t5 = listTest List.countBy size createKey5\r\n            totalTime <- totalTime + t2 + t3 + t4 + t5\r\n            printfn \"listCountBy %d,%d,%d,%d (%d,%d,%d,%d)\" t2 t3 t4 t5 c2 c3 c4 c5\r\n        printfn \"listCountBy totalTime=%d\" totalTime\r\n\r\n    let runArrayGroupByTest () =\r\n        let size = 100000\r\n        let mutable totalTime = 0L\r\n        for i = 1 to 5 do\r\n            let c2, t2 = arrayTest Array.groupBy size createKey2\r\n            let c3, t3 = arrayTest Array.groupBy size createKey3\r\n            let c4, t4 = arrayTest Array.groupBy size createKey4\r\n            let c5, t5 = arrayTest Array.groupBy size createKey5\r\n            totalTime <- totalTime + t2 + t3 + t4 + t5\r\n            printfn \"arrayGroupBy %d,%d,%d,%d (%d,%d,%d,%d)\" t2 t3 t4 t5 c2 c3 c4 c5\r\n        printfn \"arrayGroupBy totalTime=%d\" totalTime\r\n\r\n    let runArrayCountByTest () =\r\n        let size = 100000\r\n        let mutable totalTime = 0L\r\n        for i = 1 to 5 do\r\n            let c2, t2 = arrayTest Array.countBy size createKey2\r\n            let c3, t3 = arrayTest Array.countBy size createKey3\r\n            let c4, t4 = arrayTest Array.countBy size createKey4\r\n            let c5, t5 = arrayTest Array.countBy size createKey5\r\n            totalTime <- totalTime + t2 + t3 + t4 + t5\r\n            printfn \"arrayCountBy %d,%d,%d,%d (%d,%d,%d,%d)\" t2 t3 t4 t5 c2 c3 c4 c5\r\n        printfn \"arrayCountBy totalTime=%d\" totalTime\r\n\r\n    let runDictTest () =\r\n        let size = 100000\r\n        let mutable totalTime = 0L\r\n        for i = 1 to 5 do\r\n            let c2, t2 = dictTest size createDictKey2\r\n            let c3, t3 = dictTest size createDictKey3\r\n            let c4, t4 = dictTest size createDictKey4\r\n            let c5, t5 = dictTest size createDictKey5\r\n            totalTime <- totalTime + t2 + t3 + t4 + t5\r\n            printfn \"arrayCountBy %d,%d,%d,%d (%d,%d,%d,%d)\" t2 t3 t4 t5 c2 c3 c4 c5\r\n        printfn \"arrayCountBy totalTime=%d\" totalTime\r\n\r\n\r\n    [<EntryPoint>]\r\n    let main _ =\r\n        //printfn \"%s\" Id.Name\r\n        runSeqGroupByTest()\r\n        runSeqCountByTest()\r\n        runListGroupByTest()\r\n        runListCountByTest()\r\n        runArrayCountByTest()\r\n        runArrayGroupByTest()\r\n        runDictTest ()\r\n        0"}}