{"action":"created","issue":{"url":"https://api.github.com/repos/dotnet/coreclr/issues/8963","repository_url":"https://api.github.com/repos/dotnet/coreclr","labels_url":"https://api.github.com/repos/dotnet/coreclr/issues/8963/labels{/name}","comments_url":"https://api.github.com/repos/dotnet/coreclr/issues/8963/comments","events_url":"https://api.github.com/repos/dotnet/coreclr/issues/8963/events","html_url":"https://github.com/dotnet/coreclr/pull/8963","id":201209976,"number":8963,"title":"Alternative System.Lazy implementation","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":[{"id":175957674,"url":"https://api.github.com/repos/dotnet/coreclr/labels/cla-signed","name":"cla-signed","color":"fef2c0","default":false}],"state":"open","locked":false,"assignee":null,"assignees":[],"milestone":null,"comments":7,"created_at":"2017-01-17T08:56:10Z","updated_at":"2017-01-18T08:56:04Z","closed_at":null,"pull_request":{"url":"https://api.github.com/repos/dotnet/coreclr/pulls/8963","html_url":"https://github.com/dotnet/coreclr/pull/8963","diff_url":"https://github.com/dotnet/coreclr/pull/8963.diff","patch_url":"https://github.com/dotnet/coreclr/pull/8963.patch"},"body":"The current version of System.Lazy<T> isn't particularly performant. @mrange had some analysis in an article called [On the cost of being lazy](https://gist.github.com/mrange/aa82d33e94ad76d0f33ed86e704d7492).\r\n\r\nUltimately this stemmed back to FSharp's implementations of `Seq.init(Infinite)?` which wrapped each call to `IEnumerator<T>.MoveNext()` in a `lazy` that was only evaluated on `Current`. (Which is one of the issues that I have address when composing `Seq`s in my (incomplete) PR [Seq Composer](https://github.com/Microsoft/visualfsharp/pull/1570) - although it still remains in there under some circumstances)\r\n\r\nAnyway, I started hacking away and have come up with this alternative implementation, which:\r\n- creates less objects for `ExecutionAndPublish` (2 objects created, vs 3) - (still creates 2 for `None` and `PublicationOnly`) \r\n- Lazy itself only contains 2 members, the T and an interface pointer.\r\n- Faster access after initialization (just a null check, no casting)\r\n- Faster creation step - just one virtual call away\r\n- Slightly slower `new Lazy...`\r\n- Arguably a more straight-forward implementation that is easier to follow as logic is partitioned.\r\n\r\nThings I'm dubious about:\r\n- Hopefully I have got the thread-safety right! The implementation is conceptually quite simple, so hopefully I have! (basically I'm just relying on the `volatile m_implementation` to only be set after `m_value` is set.)\r\n- I have tried to remain \"serialization compatible\"; I have done this by deriving from ISerializable. I'm not sure if this is necessary, prudent, or a show-stopper. I left the now unnecessary `OnSerializing` method as it was references from some XML data. I left it for someone more knowledgeable in that area. \r\n- Once the object is created I have no information on the Mode of the original object, this means that the Mode of object is incorrect in the Debugger object in that case (Possibly changeable to `Nullable<..mode..>` which might be better? Obviously I could just add the mode to the object when it is \r\n- I didn't see any tests for Lazy in the project. I have a number which I wrote to regression test vs existing implementation - ensuring that I'm returning correct exceptions etc. They are available [here](https://gist.github.com/manofstick/2be555566581c5057b460e53e113cdcd). They probably should be put in, but I haven't spelunked enough around to see where they should go. \r\n\r\nNotes:\r\n- I can remove any excess object creation in the `None` case by moving the ILazyItem definition out of the Lazy object and then implementing that interface on the Lazy object. This would mean that Lazy itself would also have to carry around the factory object.\r\n- I can replace the Lock on `PublicationOnly` with an `Interlocked.CompareAndExchange` by implementing a ILazyItem which returns `T`. This means that it would then create 3 objects. This was slightly faster which uncontested, but much slower under heaving threaded attack.\r\n\r\nAnyway, I guess this isn't a high priority, but given that f# does provide a keyword that ultimately uses System.Lazy I think it is worthy enough for consideration.\r\n\r\nHave fun!\r\n@manofstick."},"comment":{"url":"https://api.github.com/repos/dotnet/coreclr/issues/comments/273418598","html_url":"https://github.com/dotnet/coreclr/pull/8963#issuecomment-273418598","issue_url":"https://api.github.com/repos/dotnet/coreclr/issues/8963","id":273418598,"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":"2017-01-18T08:56:04Z","updated_at":"2017-01-18T08:56:04Z","body":"@mrange \r\n\r\nOK; with a bit of trickery I restored the CompareExchange in PublicaitonOnly without having to create a new object. In my testing with lots of threads this performed very well. Can you update your numbers to see if you get the previous performance?"}}
{"actor":{"display_login":"manofstick"}}