{"action":"created","issue":{"url":"https://api.github.com/repos/Microsoft/visualfsharp/issues/513","labels_url":"https://api.github.com/repos/Microsoft/visualfsharp/issues/513/labels{/name}","comments_url":"https://api.github.com/repos/Microsoft/visualfsharp/issues/513/comments","events_url":"https://api.github.com/repos/Microsoft/visualfsharp/issues/513/events","html_url":"https://github.com/Microsoft/visualfsharp/pull/513","id":90933857,"number":513,"title":"Optimization: Generic specialization for equality","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-signed","name":"cla-signed","color":"009800"}],"state":"open","locked":false,"assignee":null,"milestone":{"url":"https://api.github.com/repos/Microsoft/visualfsharp/milestones/5","html_url":"https://github.com/Microsoft/visualfsharp/milestones/F%23%204.0%20Update%201","labels_url":"https://api.github.com/repos/Microsoft/visualfsharp/milestones/5/labels","id":1240508,"number":5,"title":"F# 4.0 Update 1","description":"The first update release for Visual F# 4.0. Will be delivered either as part of VS 2015 update 1, or an out-of-band F# release.","creator":{"login":"latkin","id":5943573,"avatar_url":"https://avatars.githubusercontent.com/u/5943573?v=3","gravatar_id":"","url":"https://api.github.com/users/latkin","html_url":"https://github.com/latkin","followers_url":"https://api.github.com/users/latkin/followers","following_url":"https://api.github.com/users/latkin/following{/other_user}","gists_url":"https://api.github.com/users/latkin/gists{/gist_id}","starred_url":"https://api.github.com/users/latkin/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/latkin/subscriptions","organizations_url":"https://api.github.com/users/latkin/orgs","repos_url":"https://api.github.com/users/latkin/repos","events_url":"https://api.github.com/users/latkin/events{/privacy}","received_events_url":"https://api.github.com/users/latkin/received_events","type":"User","site_admin":false},"open_issues":1,"closed_issues":52,"state":"open","created_at":"2015-08-04T16:28:39Z","updated_at":"2015-11-06T21:09:40Z","due_on":null,"closed_at":null},"comments":63,"created_at":"2015-06-25T11:22:11Z","updated_at":"2015-11-17T16:57:33Z","closed_at":null,"pull_request":{"url":"https://api.github.com/repos/Microsoft/visualfsharp/pulls/513","html_url":"https://github.com/Microsoft/visualfsharp/pull/513","diff_url":"https://github.com/Microsoft/visualfsharp/pull/513.diff","patch_url":"https://github.com/Microsoft/visualfsharp/pull/513.patch"},"body":"NB: This is not ready for merging yet (I need to find some more time; two cheeky monkeys consume more of my free time!) but it is intended to start a conversation. I will add more to this over time, assuming you are happy with the direction.\r\n\r\nNB 2: There are some massive performance differences between .net 4.6 and lesser versions. I have been using 4.6 (and results specified here are from that version) but the performance optimizations are still applicable to older versions (I haven't gone to the assembly code level; but it appears to my ignorant eyes that older version were not inlining code that originated in fsharp.core.... or something :-) )\r\n\r\nSo what is this optimization?\r\n\r\nAs you are aware, F# automatically creates GetHashCode/Equals/Compare for Records and structs. In the case properties of concrete types, these are optimized to IL, but where you have generic parameters the functions are delegated to helper functions in LanguagePrimitives. For complex types, the performance of this is probably not a concern, but where you have like a simple struct 2 value key it can be a bit slow.\r\n\r\nThe following is an example (OK; it's a micro-benchmark; take with a grain of salt; blah-blah-blah; but I have come across things like this multiple times in the real world where you find an inner loop that is not too dissimilar):\r\n\r\n    open System\r\n    open System.Diagnostics\r\n    open System.Collections.Generic\r\n\r\n    type StructureInt = \r\n        struct\r\n            val A : int\r\n            val B : int\r\n            new(a, b) = {\r\n                A = a\r\n                B = b\r\n            }\r\n        end\r\n\r\n    type StructureGeneric<'a,'b> = \r\n        struct\r\n            val A : 'a\r\n            val B : 'b\r\n            new(a, b) = {\r\n                A = a\r\n                B = b\r\n            }\r\n        end\r\n\r\n    let mutable r = Random ()\r\n\r\n    let a createRandomObj =\r\n        let set = HashSet ()\r\n        let mutable count = 0\r\n\r\n\r\n        let sw = Stopwatch.StartNew()\r\n\r\n        for i = 0 to 200000 do\r\n            set.Add (createRandomObj ()) |> ignore\r\n\r\n            for i = 0 to 10 do\r\n                count <- count + if set.Contains (createRandomObj ()) then 1 else 0\r\n\r\n        printfn \"%A (%A)\" sw.ElapsedMilliseconds count\r\n\r\n    [<EntryPoint>]\r\n    let main argv =\r\n        r <- Random 314159265\r\n        for i = 0 to 10 do\r\n            a (fun () -> StructureInt (r.Next 250, r.Next 250))\r\n\r\n        printfn \"-------\"\r\n\r\n        r <- Random 314159265\r\n        for i = 0 to 10 do\r\n            a (fun () -> StructureGeneric (r.Next 250, r.Next 250))\r\n\r\n        0\r\n\r\nThe unmodified results for this are (first section is concrete type, the second part after the \"------\" is the generic type; the first number is the time in ms, the second number is just a checksum; first batch and second batch should match, but really it's just junk in these tests....) :\r\n\r\n32-bit:\r\n\r\n\t429L (1542136)\r\n\t405L (1540851)\r\n\t368L (1538895)\r\n\t381L (1538510)\r\n\t335L (1541558)\r\n\t338L (1540543)\r\n\t334L (1539466)\r\n\t335L (1543310)\r\n\t333L (1540047)\r\n\t337L (1541890)\r\n\t338L (1538828)\r\n\t-------\r\n\t1182L (1542136)\r\n\t1160L (1540851)\r\n\t1166L (1538895)\r\n\t1177L (1538510)\r\n\t1163L (1541558)\r\n\t1167L (1540543)\r\n\t1176L (1539466)\r\n\t1162L (1543310)\r\n\t1166L (1540047)\r\n\t1182L (1541890)\r\n\t1163L (1538828)\r\n\r\n64-bit:\r\n\r\n\t468L (1542136)\r\n\t430L (1540851)\r\n\t385L (1538895)\r\n\t381L (1538510)\r\n\t321L (1541558)\r\n\t320L (1540543)\r\n\t326L (1539466)\r\n\t326L (1543310)\r\n\t324L (1540047)\r\n\t325L (1541890)\r\n\t323L (1538828)\r\n\t-------\r\n\t800L (1542136)\r\n\t786L (1540851)\r\n\t780L (1538895)\r\n\t792L (1538510)\r\n\t788L (1541558)\r\n\t773L (1540543)\r\n\t780L (1539466)\r\n\t784L (1543310)\r\n\t776L (1540047)\r\n\t784L (1541890)\r\n\t788L (1538828)\r\n\r\nIncluding the optimization I have trhe following results:\r\n\r\n32-bit:\r\n\r\n\t437L (1542136)\r\n\t354L (1540851)\r\n\t340L (1538895)\r\n\t333L (1538510)\r\n\t334L (1541558)\r\n\t337L (1540543)\r\n\t332L (1539466)\r\n\t340L (1543310)\r\n\t338L (1540047)\r\n\t338L (1541890)\r\n\t336L (1538828)\r\n\t-------\r\n\t615L (1542136)\r\n\t603L (1540851)\r\n\t602L (1538895)\r\n\t618L (1538510)\r\n\t613L (1541558)\r\n\t609L (1540543)\r\n\t618L (1539466)\r\n\t612L (1543310)\r\n\t610L (1540047)\r\n\t619L (1541890)\r\n\t611L (1538828)\r\n\r\n64-bit:\r\n\r\n\t589L (1542136)\r\n\t411L (1540851)\r\n\t324L (1538895)\r\n\t323L (1538510)\r\n\t321L (1541558)\r\n\t325L (1540543)\r\n\t321L (1539466)\r\n\t321L (1543310)\r\n\t320L (1540047)\r\n\t323L (1541890)\r\n\t321L (1538828)\r\n\t-------\r\n\t417L (1542136)\r\n\t402L (1540851)\r\n\t396L (1538895)\r\n\t408L (1538510)\r\n\t405L (1541558)\r\n\t401L (1540543)\r\n\t404L (1539466)\r\n\t403L (1543310)\r\n\t404L (1540047)\r\n\t403L (1541890)\r\n\t406L (1538828)\r\n\r\nSo to summarise; in the example of smashing a HashSet, times for both 32-bit and 64-bit have been halved (obviously it's even greater speed up than that, as that is including all the HashSet functionality).\r\n\r\nAnyway; let me know if this is the kind of thing you are interested in. I have been running the smoketest and everything seems fine (but not really 100% happy as I haven't had time to explore the code around there). Anyway I need to head off to bed now, so hopefully I have left enough information here for you to evaluate!"},"comment":{"url":"https://api.github.com/repos/Microsoft/visualfsharp/issues/comments/157430958","html_url":"https://github.com/Microsoft/visualfsharp/pull/513#issuecomment-157430958","issue_url":"https://api.github.com/repos/Microsoft/visualfsharp/issues/513","id":157430958,"user":{"login":"KevinRansom","id":5175830,"avatar_url":"https://avatars.githubusercontent.com/u/5175830?v=3","gravatar_id":"","url":"https://api.github.com/users/KevinRansom","html_url":"https://github.com/KevinRansom","followers_url":"https://api.github.com/users/KevinRansom/followers","following_url":"https://api.github.com/users/KevinRansom/following{/other_user}","gists_url":"https://api.github.com/users/KevinRansom/gists{/gist_id}","starred_url":"https://api.github.com/users/KevinRansom/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/KevinRansom/subscriptions","organizations_url":"https://api.github.com/users/KevinRansom/orgs","repos_url":"https://api.github.com/users/KevinRansom/repos","events_url":"https://api.github.com/users/KevinRansom/events{/privacy}","received_events_url":"https://api.github.com/users/KevinRansom/received_events","type":"User","site_admin":false},"created_at":"2015-11-17T16:57:33Z","updated_at":"2015-11-17T16:57:33Z","body":"This is a candidate for the Dev 15 branch, I think we probably want to see enough bake time on this, rather than putting it straight in VS2015."}}