{"action":"opened","issue":{"url":"https://api.github.com/repos/AngleSharp/AngleSharp/issues/443","repository_url":"https://api.github.com/repos/AngleSharp/AngleSharp","labels_url":"https://api.github.com/repos/AngleSharp/AngleSharp/issues/443/labels{/name}","comments_url":"https://api.github.com/repos/AngleSharp/AngleSharp/issues/443/comments","events_url":"https://api.github.com/repos/AngleSharp/AngleSharp/issues/443/events","html_url":"https://github.com/AngleSharp/AngleSharp/issues/443","id":184653525,"number":443,"title":"Avoid string and other allocations ","user":{"login":"corliss","id":1443143,"avatar_url":"https://avatars.githubusercontent.com/u/1443143?v=3","gravatar_id":"","url":"https://api.github.com/users/corliss","html_url":"https://github.com/corliss","followers_url":"https://api.github.com/users/corliss/followers","following_url":"https://api.github.com/users/corliss/following{/other_user}","gists_url":"https://api.github.com/users/corliss/gists{/gist_id}","starred_url":"https://api.github.com/users/corliss/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/corliss/subscriptions","organizations_url":"https://api.github.com/users/corliss/orgs","repos_url":"https://api.github.com/users/corliss/repos","events_url":"https://api.github.com/users/corliss/events{/privacy}","received_events_url":"https://api.github.com/users/corliss/received_events","type":"User","site_admin":false},"labels":[],"state":"open","locked":false,"assignee":null,"assignees":[],"milestone":null,"comments":0,"created_at":"2016-10-22T20:53:55Z","updated_at":"2016-10-22T20:53:55Z","closed_at":null,"body":"As mentioned in #442, the most important perf factor in a .net library is keeping allocations down. AngleSharp uses pooling and string builders extensively. However there are a few powerful ways to reduce or eliminate temporary string creation.\r\n\r\n- StringBuilder.ToString()` is called in several places where the result is the key in a map. This can be avoided by using a struct containing a StringBuilder and a HashCode as the key, like this:\r\n\r\n\t\t/// <summary>\r\n\t\t/// A StringBuilder together with its hash. used for fast lookups.\r\n\t\t/// </summary>\r\n\t\tstruct StringKey : IEquatable<StringKey>\r\n\t\t{\r\n\t\t\tpublic StringKey(StringBuilder sb)\r\n\t\t\t{\r\n\t\t\t\tthis.sb = sb;\r\n\t\t\t\thash = Hash.GetFNVHashCode(sb);\r\n\t\t\t}\r\n\t\r\n\t\t\tpublic StringBuilder sb;\r\n\t\r\n\t\t\tpublic int hash;\r\n\r\n\t\t\tpublic bool Equals(StringKey other) =>\r\n\t\t\t\tsb != null && other.sb != null && sb.Equals(other.sb);\r\n\t\r\n\t\t\tpublic override int GetHashCode() => hash;\r\n\t\t}\r\n\r\nThe `Hash.GetFNVHashCode()` function has been implemented in the [Roslyn](http://source.roslyn.io/#microsoft.codeanalysis/InternalUtilities/Hash.cs) codebase.\r\n\r\n- Instead of breaking up the document into a number of small strings, a [`StringSlice`](https://github.com/dotnet/roslyn/blob/master/src/Workspaces/Core/Portable/Utilities/StringSlice.cs) can be used. This also uses an FNV hash code. \r\n\r\n\r\n- Avoid `IEnumerable` and instead use Lists or other objects that have struct enumerators. `IEnumerable` is heavyweight and calls a *virtual* `GetEnumerator()` method that allocates and returns a enumerator object. None of this is needed for foreach style enumeration in C#, because foreach is in fact pattern based; it works if the right methods are implemented, even without interfaces. `List<T>`'s GetEnumerator() method returns a struct - which is not boxed. I've used this to get rid of all implemtations of `IEnumerable` and `IEnumerable<T>` in my code base. Now I just use List<T> directly in the method signature. For custom types, I make sure that their implementation of GetEnumerator() returns a struct.\r\n\r\n- Avoid implementing `IComparable<T>` and implement `IEquatable<T>` instead, for the same reason - to avoid boxing.\r\n\r\n- Avoid yield return, which again allocates an enumerable class, an enumerator and so on. Instead pass a (pooled) list into the method, which fills it. That is, instead of \r\n\r\n\t\tIEnumerable<T> Foo() {yield return a; yield return b; }\r\n\r\ndo this:\r\n\r\n\t\tvoid Foo(List<T> result) {result.Add(a); result.Add(b);} // caller pools the list\r\n\r\nAll these go a long way to reducing those GCs (and those milliseconds.)\r\n\r\nThere is a tradeoff between some of these suggestions and API cleanliness, but I do think that all of these should be considered seriously. They've made a huge difference to the performance of Roslyn and to my own code.\r\n\r\nWhat do you think?"}}
{"actor":{"display_login":"corliss"}}