{"action":"created","issue":{"url":"https://api.github.com/repos/patridge/PerformanceStubs/issues/1","labels_url":"https://api.github.com/repos/patridge/PerformanceStubs/issues/1/labels{/name}","comments_url":"https://api.github.com/repos/patridge/PerformanceStubs/issues/1/comments","events_url":"https://api.github.com/repos/patridge/PerformanceStubs/issues/1/events","html_url":"https://github.com/patridge/PerformanceStubs/issues/1","id":42258352,"number":1,"title":"Possible Improvements with ByteArrayToHexViaLookup & ByteArrayToHexViaLookupPerByte ","user":{"login":"GregDomjan","id":1988114,"avatar_url":"https://avatars.githubusercontent.com/u/1988114?v=3","gravatar_id":"","url":"https://api.github.com/users/GregDomjan","html_url":"https://github.com/GregDomjan","followers_url":"https://api.github.com/users/GregDomjan/followers","following_url":"https://api.github.com/users/GregDomjan/following{/other_user}","gists_url":"https://api.github.com/users/GregDomjan/gists{/gist_id}","starred_url":"https://api.github.com/users/GregDomjan/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/GregDomjan/subscriptions","organizations_url":"https://api.github.com/users/GregDomjan/orgs","repos_url":"https://api.github.com/users/GregDomjan/repos","events_url":"https://api.github.com/users/GregDomjan/events{/privacy}","received_events_url":"https://api.github.com/users/GregDomjan/received_events","type":"User","site_admin":false},"labels":[],"state":"open","locked":false,"assignee":null,"milestone":null,"comments":2,"created_at":"2014-09-09T00:15:47Z","updated_at":"2015-06-15T10:22:12Z","closed_at":null,"body":"I'd make a pull request to include some changes, but git is giving me grief.\r\n\r\nWas originally looking at http://www.codeproject.com/Tips/447938/High-performance-Csharp-byte-array-to-hex-string-t  \r\n\r\nAdding it to the tests showed it had better performance than others, but of course has the problem of being 'Unsafe'.\r\n\r\n\r\nWithout changing the basic types in use and method there are some ways to improve ByteArrayToHexViaLookup.\r\n\r\n 1. Having the table created in the method each time it is called adds an unnecessary overhead.\r\n 2. There is a small performance hit to using ForEach when you only use the variable once inside the loop. (http://www.dotnetperls.com/for-foreach)\r\n\r\n        static readonly string[] HexStringTable = Enumerable.Range(0, 255).Select(i => i.ToString(\"X2\")).ToArray();\r\n\r\n        static string ByteArrayToHexViaLookup(byte[] bytes)\r\n        {\r\n            var result = new StringBuilder(bytes.Length * 2);\r\n            for (var i = 0; i < bytes.Length; i++) {\r\n                result.Append(HexStringTable[bytes[i]]);\r\n            }\r\n            return result.ToString();\r\n        }\r\n\r\n\r\nThe String builder is also slowing this down just a bit - changing how the string is prepared to using an array similar to ByteArrayToHexViaLookupPerByte\r\n\r\n        static string ByteArrayToHexViaLookupArrayIndex(byte[] bytes)\r\n        {\r\n            var result = new char[bytes.Length * 2];\r\n            for (var i = 0; i < bytes.Length; i++)\r\n            {\r\n                var val = HexStringTable[bytes[i]];\r\n                result[2 * i] = val[0];\r\n                result[2 * i + 1] = val[1];\r\n            }\r\n            return new string(result);\r\n        }\r\n\r\nAlso tried Concat but it does worse than ByteArrayToHexViaLookupAndShift as content size increases\r\n\r\n        public static string ByteArrayToHexViaLookupConcat(byte[] bytes)\r\n        {\r\n            var result = new string[bytes.Length];\r\n            for (var i = 0; i < bytes.Length; i++)\r\n            {\r\n                result[i] = HexStringTable[bytes[i]];\r\n            }\r\n            return string.Concat(result);\r\n        }\r\n\r\n\r\n\r\nTrying to see if there was some way to merge the ByteArrayToHexViaLookupPerByte with ideas from the Unsafe method using more direct allocation rather than shifting. The following performs better for larger strings, but takes a hit from double memory alloc on small strings.  I have not seen an alternate method for 'casting' an array type.\r\n\r\n        static string ByteArrayToHexViaLookupPerByteAssignAndBlockCopy(byte[] bytes)\r\n        {\r\n            var temp = new int[bytes.Length]; \r\n            \r\n            for (var i = 0; i < bytes.Length; i++)\r\n            {\r\n                temp[i] = (int)HexUintTable[bytes[i]];\r\n            }\r\n            var result = new char[bytes.Length<<1];\r\n            Buffer.BlockCopy(temp, 0, result, 0, result.Length<<1);\r\n            return new string(result);\r\n        }\r\n\r\n"},"comment":{"url":"https://api.github.com/repos/patridge/PerformanceStubs/issues/comments/112008858","html_url":"https://github.com/patridge/PerformanceStubs/issues/1#issuecomment-112008858","issue_url":"https://api.github.com/repos/patridge/PerformanceStubs/issues/1","id":112008858,"user":{"login":"airbreather","id":3792815,"avatar_url":"https://avatars.githubusercontent.com/u/3792815?v=3","gravatar_id":"","url":"https://api.github.com/users/airbreather","html_url":"https://github.com/airbreather","followers_url":"https://api.github.com/users/airbreather/followers","following_url":"https://api.github.com/users/airbreather/following{/other_user}","gists_url":"https://api.github.com/users/airbreather/gists{/gist_id}","starred_url":"https://api.github.com/users/airbreather/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/airbreather/subscriptions","organizations_url":"https://api.github.com/users/airbreather/orgs","repos_url":"https://api.github.com/users/airbreather/repos","events_url":"https://api.github.com/users/airbreather/events{/privacy}","received_events_url":"https://api.github.com/users/airbreather/received_events","type":"User","site_admin":false},"created_at":"2015-06-15T10:22:12Z","updated_at":"2015-06-15T10:22:12Z","body":"#3 does as much as I could think of to amortize the overhead that would be amortized in practice.\r\n\r\n@GregDomjan you could try declaring the local array as uint[]... but besides the double-allocation and the byte copying, I believe that another part of the reason the unsafe variant outperforms what you have there is the fact that the compiler doesn't implement the smarts to statically prove that \"i\" is always a valid index on \"temp\", so it emits a bounds check on every access through that loop.  There's actually a really scary way to avoid the double allocation along [these lines](https://gist.github.com/OmerMor/1050703), but if we're going into unsafe territory anyway, we might as well just use the unsafe version of this posted by CodesInChaos anyway...\r\n\r\nFWIW, I've heard that Microsoft's optimizer (so probably Mono's as well?) will translate a \"foreach\" loop over an array into an equivalent \"for\" loop in cases like ByteArrayToHexViaLookup, so you probably wouldn't see any runtime difference in changing that particular \"foreach\" into \"for\"."}}