Title: ClassManager Cache Performance Impact · Issue #1407 · pythonnet/pythonnet · GitHub
Open Graph Title: ClassManager Cache Performance Impact · Issue #1407 · pythonnet/pythonnet
X Title: ClassManager Cache Performance Impact · Issue #1407 · pythonnet/pythonnet
Description: Environment Pythonnet version: Latest Dev 3.0.0 Python version: 3.6.8 Operating System: Windows 10 Details I was doing some performance testing on the latest PythonNet and noticed a significant slow down in the conversion of CLR objects ...
Open Graph Description: Environment Pythonnet version: Latest Dev 3.0.0 Python version: 3.6.8 Operating System: Windows 10 Details I was doing some performance testing on the latest PythonNet and noticed a significant slo...
X Description: Environment Pythonnet version: Latest Dev 3.0.0 Python version: 3.6.8 Operating System: Windows 10 Details I was doing some performance testing on the latest PythonNet and noticed a significant slo...
Opengraph URL: https://github.com/pythonnet/pythonnet/issues/1407
X: @github
Domain: github.com
{"@context":"https://schema.org","@type":"DiscussionForumPosting","headline":"ClassManager Cache Performance Impact","articleBody":"### Environment\r\n\r\n- Pythonnet version: Latest Dev 3.0.0\r\n- Python version: 3.6.8\r\n- Operating System: Windows 10\r\n\r\n### Details\r\nI was doing some performance testing on the latest PythonNet and noticed a significant slow down in the conversion of CLR objects to Python. After some digging I believe I found where the slow down is coming from. \r\n\r\nIn #1287 , the addition of the `MaybeType` changed the `ClassManager` dictionary cache, which ended up storing `ClassBases` with `MaybeType` as the key. But because the `ClassManager` 's `GetClass` function attempts to fetch a value out of the cache using a `Type`, the object is implicitly converted to a MaybeType first. This conversion and then comparison (finding obj in dictionary) leads to a major difference in performance when converting large quantities of an object to Python since we have to initialize a new object every time the cache is used. \r\n\r\n\r\n\r\n- What commands did you run to trigger this issue? If you can provide a\r\n [Minimal, Complete, and Verifiable example](http://stackoverflow.com/help/mcve)\r\n this will help us understand the issue.\r\n\r\nHere is a unit test without any pass/fail that I was using to debug this issue. I paired it with dotTrace to see the difference in performance.\r\n```\r\n [Test]\r\n public void Test()\r\n {\r\n for (int i = 0; i \u003c 1000000; i++)\r\n {\r\n var testSlice = new Slice();\r\n testSlice.ToPython();\r\n }\r\n }\r\n\r\n public class Slice\r\n {\r\n public Dictionary\u003cchar, int\u003e intDict = new Dictionary\u003cchar, int\u003e();\r\n public List\u003cint\u003e intList = new List\u003cint\u003e();\r\n public string test = \"pepe\";\r\n public decimal testDecimal = 5.5m;\r\n\r\n public Slice()\r\n {\r\n for (int i = 0; i \u003c 20; i++)\r\n {\r\n var key = 'A' + i;\r\n intDict.Add((char)key, i);\r\n intList.Add(i);\r\n }\r\n\r\n }\r\n }\r\n```\r\n\r\n#### Current Branch Performance on 1 million calls of same object:\r\n\r\n\r\n#### Without MaybeType in ClassManager (Cache dict is \u003cType, ClassBase\u003e) performance:\r\n\r\n\r\n\r\n\r\n\r\n### Attempt at a fix while maintaining `MaybeType`\r\n\r\n\r\n\r\nI did attempt to improve the performance by overriding the `Equals()` and `GetHashCode()` functions for `MaybeType` which did show a little improvement and a bit more visibility into the slowdown. But unfortunately the object is already implicit converted before `Equals()` because the ClassManager cache dictionary is \u003cMaybeType, ClassBase\u003e\r\n\r\n`public static implicit operator MaybeType (Type ob) =\u003e new MaybeType(ob);`\r\n\r\nHere was my attempt.\r\n```\r\n /// \u003csummary\u003e\r\n /// Determines whether the specified \u003csee cref=\"T:System.Object\"/\u003e is equal to the current \u003csee cref=\"T:System.Object\"/\u003e.\r\n /// \u003c/summary\u003e\r\n /// \u003creturns\u003e\r\n /// true if the specified object is equal to the current object; otherwise, false.\r\n /// \u003c/returns\u003e\r\n /// \u003cparam name=\"obj\"\u003eThe object to compare with the current object. \u003c/param\u003e\u003cfilterpriority\u003e2\u003c/filterpriority\u003e\r\n public override bool Equals(object obj)\r\n {\r\n if (ReferenceEquals(null, obj)) return false;\r\n\r\n // Compare as a Type\r\n if (obj is Type typeObj)\r\n {\r\n return this.Value == typeObj;\r\n }\r\n\r\n // If MaybeType just compare Types\r\n if (obj is MaybeType maybeObj)\r\n {\r\n return Value == maybeObj.Value;\r\n }\r\n\r\n // It must be false\r\n return false;\r\n }\r\n\r\n /// \u003csummary\u003e\r\n /// Serves as a hash function for a particular type.\r\n /// \u003c/summary\u003e\r\n /// \u003creturns\u003e\r\n /// A hash code for the current \u003csee cref=\"T:System.Object\"/\u003e.\r\n /// \u003c/returns\u003e\r\n /// \u003cfilterpriority\u003e2\u003c/filterpriority\u003e\r\n public override int GetHashCode()\r\n {\r\n // Use Type HashCode\r\n unchecked { return Value.GetHashCode(); }\r\n }\r\n```\r\n\r\nAnd results:\r\n\r\n\r\n\r\nIt appears that the slowest part is actually the fetching of the fully qualified name, Which could be something that is changed to be lazily fetched when requested instead of during construction.\r\n\r\n\r\n","author":{"url":"https://github.com/C-SELLERS","@type":"Person","name":"C-SELLERS"},"datePublished":"2021-03-04T01:52:11.000Z","interactionStatistic":{"@type":"InteractionCounter","interactionType":"https://schema.org/CommentAction","userInteractionCount":2},"url":"https://github.com/1407/pythonnet/issues/1407"}
| route-pattern | /_view_fragments/issues/show/:user_id/:repository/:id/issue_layout(.:format) |
| route-controller | voltron_issues_fragments |
| route-action | issue_layout |
| fetch-nonce | v2:cc283f3c-9031-00c1-2038-e6f702f443e5 |
| current-catalog-service-hash | 81bb79d38c15960b92d99bca9288a9108c7a47b18f2423d0f6438c5b7bcd2114 |
| request-id | C104:DD6B:1FB0929:2AD89FA:69702DA4 |
| html-safe-nonce | 9e19d55aba213a2a2b5c5fd105135a4bbd5cca6ca594519fc3813c082e35aab3 |
| visitor-payload | eyJyZWZlcnJlciI6IiIsInJlcXVlc3RfaWQiOiJDMTA0OkRENkI6MUZCMDkyOToyQUQ4OUZBOjY5NzAyREE0IiwidmlzaXRvcl9pZCI6IjUwMjY5NzUzMzI5ODg2MjgzODgiLCJyZWdpb25fZWRnZSI6ImlhZCIsInJlZ2lvbl9yZW5kZXIiOiJpYWQifQ== |
| visitor-hmac | 8ccefb775407cfe358888e6fcd91dab9ce681f0d7755f2e5098cc469d9a0bf6a |
| hovercard-subject-tag | issue:821672709 |
| github-keyboard-shortcuts | repository,issues,copilot |
| google-site-verification | Apib7-x98H0j5cPqHWwSMm6dNU4GmODRoqxLiDzdx9I |
| octolytics-url | https://collector.github.com/github/collect |
| analytics-location | / |
| fb:app_id | 1401488693436528 |
| apple-itunes-app | app-id=1477376905, app-argument=https://github.com/_view_fragments/issues/show/pythonnet/pythonnet/1407/issue_layout |
| twitter:image | https://opengraph.githubassets.com/9016ac1c3f2fb6a08fe470456397ff6589c0be050335c48e1e864f5048cd339c/pythonnet/pythonnet/issues/1407 |
| twitter:card | summary_large_image |
| og:image | https://opengraph.githubassets.com/9016ac1c3f2fb6a08fe470456397ff6589c0be050335c48e1e864f5048cd339c/pythonnet/pythonnet/issues/1407 |
| og:image:alt | Environment Pythonnet version: Latest Dev 3.0.0 Python version: 3.6.8 Operating System: Windows 10 Details I was doing some performance testing on the latest PythonNet and noticed a significant slo... |
| og:image:width | 1200 |
| og:image:height | 600 |
| og:site_name | GitHub |
| og:type | object |
| og:author:username | C-SELLERS |
| hostname | github.com |
| expected-hostname | github.com |
| None | 01fa379f5de85ef8e791d09724e69709ce9eb9595278316e0a921312dc88e0bc |
| turbo-cache-control | no-preview |
| go-import | github.com/pythonnet/pythonnet git https://github.com/pythonnet/pythonnet.git |
| octolytics-dimension-user_id | 6050430 |
| octolytics-dimension-user_login | pythonnet |
| octolytics-dimension-repository_id | 14748123 |
| octolytics-dimension-repository_nwo | pythonnet/pythonnet |
| octolytics-dimension-repository_public | true |
| octolytics-dimension-repository_is_fork | false |
| octolytics-dimension-repository_network_root_id | 14748123 |
| octolytics-dimension-repository_network_root_nwo | pythonnet/pythonnet |
| turbo-body-classes | logged-out env-production page-responsive |
| disable-turbo | false |
| browser-stats-url | https://api.github.com/_private/browser/stats |
| browser-errors-url | https://api.github.com/_private/browser/errors |
| release | dda91974c069382b0dfa47b2da7e28bd061c8331 |
| ui-target | full |
| theme-color | #1e2327 |
| color-scheme | light dark |
Links:
Viewport: width=device-width