{"action":"created","issue":{"url":"https://api.github.com/repos/dotnet/corefx/issues/12158","repository_url":"https://api.github.com/repos/dotnet/corefx","labels_url":"https://api.github.com/repos/dotnet/corefx/issues/12158/labels{/name}","comments_url":"https://api.github.com/repos/dotnet/corefx/issues/12158/comments","events_url":"https://api.github.com/repos/dotnet/corefx/issues/12158/events","html_url":"https://github.com/dotnet/corefx/pull/12158","id":179886966,"number":12158,"title":"Avoid allocations in Registry.GetBaseKeyFromKeyName","user":{"login":"justinvp","id":710598,"avatar_url":"https://avatars.githubusercontent.com/u/710598?v=3","gravatar_id":"","url":"https://api.github.com/users/justinvp","html_url":"https://github.com/justinvp","followers_url":"https://api.github.com/users/justinvp/followers","following_url":"https://api.github.com/users/justinvp/following{/other_user}","gists_url":"https://api.github.com/users/justinvp/gists{/gist_id}","starred_url":"https://api.github.com/users/justinvp/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/justinvp/subscriptions","organizations_url":"https://api.github.com/users/justinvp/orgs","repos_url":"https://api.github.com/users/justinvp/repos","events_url":"https://api.github.com/users/justinvp/events{/privacy}","received_events_url":"https://api.github.com/users/justinvp/received_events","type":"User","site_admin":false},"labels":[{"url":"https://api.github.com/repos/dotnet/corefx/labels/cla-already-signed","name":"cla-already-signed","color":"009800"}],"state":"open","locked":false,"assignee":null,"assignees":[],"milestone":null,"comments":5,"created_at":"2016-09-28T20:59:10Z","updated_at":"2016-09-30T20:40:18Z","closed_at":null,"pull_request":{"url":"https://api.github.com/repos/dotnet/corefx/pulls/12158","html_url":"https://github.com/dotnet/corefx/pull/12158","diff_url":"https://github.com/dotnet/corefx/pull/12158.diff","patch_url":"https://github.com/dotnet/corefx/pull/12158.patch"},"body":"Avoid allocations in `Registry.GetBaseKeyFromKeyName` for improved perf.\r\n\r\n<details>\r\n<summary>10,000,000 iteration microbenchmark results:</summary>\r\n```c#\r\nusing System;\r\nusing System.Diagnostics;\r\nusing Microsoft.Win32;\r\n\r\nclass Program\r\n{\r\n    private static void Main()\r\n    {\r\n        TimeAction(\"HKEY_USERS\");\r\n        TimeAction(\"HKEY_CLASSES_ROOT\");\r\n        TimeAction(\"HKEY_CURRENT_USER\");\r\n        TimeAction(\"HKEY_LOCAL_MACHINE\");\r\n        TimeAction(\"HKEY_CURRENT_CONFIG\");\r\n        TimeAction(\"HKEY_PERFORMANCE_DATA\");\r\n    }\r\n\r\n    private static void TimeAction(string keyName)\r\n    {\r\n        string subKeyName;\r\n\r\n        Console.WriteLine(\"Looking for: {0}\", keyName);\r\n        TimeAction(\"Old\", () => GetBaseKeyFromKeyNameOld(keyName, out subKeyName));\r\n        TimeAction(\"New\", () => GetBaseKeyFromKeyNameNew(keyName, out subKeyName));\r\n        Console.WriteLine();\r\n    }\r\n\r\n    private static void TimeAction(string prefix, Action action, int times = 5, int iterations = 10000000)\r\n    {\r\n        var sw = new Stopwatch();\r\n        for (int i = 0; i < times; i++)\r\n        {\r\n            int gen0 = GC.CollectionCount(0);\r\n            sw.Restart();\r\n            for (int iter = 0; iter < iterations; iter++)\r\n            {\r\n                action();\r\n            }\r\n            sw.Stop();\r\n            Console.WriteLine($\"{prefix}: Time: {sw.Elapsed.TotalSeconds}\\tGC0: {GC.CollectionCount(0) - gen0}\");\r\n        }\r\n    }\r\n\r\n    private static RegistryKey GetBaseKeyFromKeyNameOld(string keyName, out string subKeyName)\r\n    {\r\n        if (keyName == null)\r\n        {\r\n            throw new ArgumentNullException(nameof(keyName));\r\n        }\r\n\r\n        int i = keyName.IndexOf('\\\\');\r\n        string basekeyName = i != -1 ?\r\n            keyName.Substring(0, i).ToUpperInvariant() :\r\n            keyName.ToUpperInvariant();\r\n\r\n        RegistryKey basekey = null;\r\n        switch (basekeyName)\r\n        {\r\n            case \"HKEY_CURRENT_USER\":\r\n                basekey = CurrentUser;\r\n                break;\r\n            case \"HKEY_LOCAL_MACHINE\":\r\n                basekey = LocalMachine;\r\n                break;\r\n            case \"HKEY_CLASSES_ROOT\":\r\n                basekey = ClassesRoot;\r\n                break;\r\n            case \"HKEY_USERS\":\r\n                basekey = Users;\r\n                break;\r\n            case \"HKEY_PERFORMANCE_DATA\":\r\n                basekey = PerformanceData;\r\n                break;\r\n            case \"HKEY_CURRENT_CONFIG\":\r\n                basekey = CurrentConfig;\r\n                break;\r\n            default:\r\n                throw new ArgumentException(SR.Format(SR.Arg_RegInvalidKeyName, nameof(keyName)), nameof(keyName));\r\n        }\r\n\r\n        subKeyName = (i == -1 || i == keyName.Length) ?\r\n            string.Empty :\r\n            keyName.Substring(i + 1, keyName.Length - i - 1);\r\n\r\n        return basekey;\r\n    }\r\n\r\n    private static RegistryKey GetBaseKeyFromKeyNameNew(string keyName, out string subKeyName)\r\n    {\r\n        if (keyName == null)\r\n        {\r\n            throw new ArgumentNullException(nameof(keyName));\r\n        }\r\n\r\n        int i = keyName.IndexOf('\\\\');\r\n        int length = i != -1 ? i : keyName.Length;\r\n\r\n        // Determine the potential base key from the length. Note that\r\n        // HKEY_CLASSES_ROOT and HKEY_CURRENT_USER are the same length,\r\n        // so for that case, switch on a char at a position with a\r\n        // unique char to determine the potential base key.\r\n        RegistryKey baseKey = null;\r\n        switch (length)\r\n        {\r\n            case 10:\r\n                baseKey = Users; // HKEY_USERS\r\n                break;\r\n\r\n            case 17:\r\n                const int UniqueCharIndex = 6;\r\n                switch (keyName[UniqueCharIndex])\r\n                {\r\n                    case 'l':\r\n                    case 'L':\r\n                        Debug.Assert(ClassesRoot.Name[UniqueCharIndex] == 'L');\r\n                        baseKey = ClassesRoot; // HKEY_C[L]ASSES_ROOT\r\n                        break;\r\n\r\n                    case 'u':\r\n                    case 'U':\r\n                        Debug.Assert(CurrentUser.Name[UniqueCharIndex] == 'U');\r\n                        baseKey = CurrentUser; // HKEY_C[U]RRENT_USER\r\n                        break;\r\n                }\r\n                break;\r\n\r\n            case 18:\r\n                baseKey = LocalMachine; // HKEY_LOCAL_MACHINE\r\n                break;\r\n\r\n            case 19:\r\n                baseKey = CurrentConfig; // HKEY_CURRENT_CONFIG\r\n                break;\r\n\r\n            case 21:\r\n                baseKey = PerformanceData; // HKEY_PERFORMANCE_DATA\r\n                break;\r\n        }\r\n\r\n        Debug.Assert(baseKey == null || baseKey.Name.Length == length);\r\n\r\n        if (baseKey != null && keyName.StartsWith(baseKey.Name, StringComparison.OrdinalIgnoreCase))\r\n        {\r\n            subKeyName = (i == -1 || i == keyName.Length) ?\r\n                string.Empty :\r\n                keyName.Substring(i + 1, keyName.Length - i - 1);\r\n\r\n            return baseKey;\r\n        }\r\n\r\n        throw new ArgumentException(SR.Format(SR.Arg_RegInvalidKeyName, nameof(keyName)), nameof(keyName));\r\n    }\r\n\r\n    private static readonly RegistryKey CurrentUser = Registry.CurrentUser;\r\n    private static readonly RegistryKey LocalMachine = Registry.LocalMachine;\r\n    private static readonly RegistryKey ClassesRoot = Registry.ClassesRoot;\r\n    private static readonly RegistryKey Users = Registry.Users;\r\n    private static readonly RegistryKey PerformanceData = Registry.PerformanceData;\r\n    private static readonly RegistryKey CurrentConfig = Registry.CurrentConfig;\r\n\r\n    private static class SR\r\n    {\r\n        public static readonly string Arg_RegInvalidKeyName = \"\";\r\n        public static string Format(string format, params object[] args) => \"\";\r\n    }\r\n}\r\n```\r\n</details>\r\n\r\n```\r\nLooking for: HKEY_USERS\r\nOld: Time: 0.8788987    GC0: 114\r\nOld: Time: 0.8631488    GC0: 114\r\nOld: Time: 0.8632139    GC0: 115\r\nOld: Time: 0.8610054    GC0: 114\r\nOld: Time: 0.8777991    GC0: 115\r\nNew: Time: 0.1460478    GC0: 0\r\nNew: Time: 0.1435679    GC0: 0\r\nNew: Time: 0.1483825    GC0: 0\r\nNew: Time: 0.1460772    GC0: 0\r\nNew: Time: 0.1466389    GC0: 0\r\n\r\nLooking for: HKEY_CLASSES_ROOT\r\nOld: Time: 1.0584964    GC0: 152\r\nOld: Time: 1.0440053    GC0: 153\r\nOld: Time: 1.0485327    GC0: 152\r\nOld: Time: 1.0397064    GC0: 153\r\nOld: Time: 1.0480944    GC0: 153\r\nNew: Time: 0.1783978    GC0: 0\r\nNew: Time: 0.1783887    GC0: 0\r\nNew: Time: 0.1766059    GC0: 0\r\nNew: Time: 0.1766773    GC0: 0\r\nNew: Time: 0.1754843    GC0: 0\r\n\r\nLooking for: HKEY_CURRENT_USER\r\nOld: Time: 1.0171277    GC0: 152\r\nOld: Time: 1.0149935    GC0: 153\r\nOld: Time: 0.9913258    GC0: 152\r\nOld: Time: 0.9867099    GC0: 153\r\nOld: Time: 0.9848178    GC0: 153\r\nNew: Time: 0.1799622    GC0: 0\r\nNew: Time: 0.1788742    GC0: 0\r\nNew: Time: 0.1795678    GC0: 0\r\nNew: Time: 0.1801555    GC0: 0\r\nNew: Time: 0.1844545    GC0: 0\r\n\r\nLooking for: HKEY_LOCAL_MACHINE\r\nOld: Time: 1.012618     GC0: 152\r\nOld: Time: 1.010238     GC0: 153\r\nOld: Time: 1.0330128    GC0: 152\r\nOld: Time: 1.0363928    GC0: 153\r\nOld: Time: 0.9934053    GC0: 153\r\nNew: Time: 0.1783099    GC0: 0\r\nNew: Time: 0.176293     GC0: 0\r\nNew: Time: 0.178086     GC0: 0\r\nNew: Time: 0.1771917    GC0: 0\r\nNew: Time: 0.176571     GC0: 0\r\n\r\nLooking for: HKEY_CURRENT_CONFIG\r\nOld: Time: 1.0658348    GC0: 152\r\nOld: Time: 1.0611163    GC0: 153\r\nOld: Time: 1.0657168    GC0: 152\r\nOld: Time: 1.0883544    GC0: 153\r\nOld: Time: 1.0980891    GC0: 152\r\nNew: Time: 0.1878427    GC0: 0\r\nNew: Time: 0.1887987    GC0: 0\r\nNew: Time: 0.1885428    GC0: 0\r\nNew: Time: 0.1877254    GC0: 0\r\nNew: Time: 0.1892498    GC0: 0\r\n\r\nLooking for: HKEY_PERFORMANCE_DATA\r\nOld: Time: 1.1319487    GC0: 172\r\nOld: Time: 1.128515     GC0: 172\r\nOld: Time: 1.1219242    GC0: 171\r\nOld: Time: 1.1288843    GC0: 172\r\nOld: Time: 1.1625333    GC0: 172\r\nNew: Time: 0.2075691    GC0: 0\r\nNew: Time: 0.2024885    GC0: 0\r\nNew: Time: 0.1991121    GC0: 0\r\nNew: Time: 0.1958105    GC0: 0\r\nNew: Time: 0.1894965    GC0: 0\r\n```\r\n\r\ncc: @stephentoub"},"comment":{"url":"https://api.github.com/repos/dotnet/corefx/issues/comments/250846927","html_url":"https://github.com/dotnet/corefx/pull/12158#issuecomment-250846927","issue_url":"https://api.github.com/repos/dotnet/corefx/issues/12158","id":250846927,"user":{"login":"justinvp","id":710598,"avatar_url":"https://avatars.githubusercontent.com/u/710598?v=3","gravatar_id":"","url":"https://api.github.com/users/justinvp","html_url":"https://github.com/justinvp","followers_url":"https://api.github.com/users/justinvp/followers","following_url":"https://api.github.com/users/justinvp/following{/other_user}","gists_url":"https://api.github.com/users/justinvp/gists{/gist_id}","starred_url":"https://api.github.com/users/justinvp/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/justinvp/subscriptions","organizations_url":"https://api.github.com/users/justinvp/orgs","repos_url":"https://api.github.com/users/justinvp/repos","events_url":"https://api.github.com/users/justinvp/events{/privacy}","received_events_url":"https://api.github.com/users/justinvp/received_events","type":"User","site_admin":false},"created_at":"2016-09-30T20:40:18Z","updated_at":"2016-09-30T20:40:18Z","body":"Test Innerloop OSX Debug Build and Test"}}
{"actor":{"display_login":"justinvp"}}