Hello, thank you very much for your work! It has been very helpful for my project. In this PR, I suggest replacing the expression cache's Dictionary (with lock) with ConcurrentDictionary.
While the performance impact of a lock is usually acceptable, when Eval() is heavily used to evaluate the same expressions (which is precisely when and why we need caching), it puts more pressure on threads and the CPU, making it more time-consuming compared to the ConcurrentDictionary. ConcurrentDictionary has been heavily optimized for multi-thread scenarios, especially when there are many reads and fewer writes. Its read performance is almost identical to a Dictionary without any lock.
Considering that this library targets net45, netstandard1.3, and netstandard2.0, all of which support ConcurrentDictionary, this change should be feasible. If, for some reason, there is a need to support older versions in the future, we can use #if and #elif compilation directives.
Additionally, I have updated the README. When my colleagues and I first used this library last year and this year, we made the same mistake, instantiated a new instance each time we called it instead of making it a singleton. This wasted its caching capabilities. Also, if a user frequently evaluates many unique expressions, it might put pressure on memory in default. Therefore, I believe it is necessary to include this parameter in the README to help new users utilize this excellent work more effectively.
PR Description
Hello, thank you very much for your work! It has been very helpful for my project. In this PR, I suggest replacing the expression cache's
Dictionary
(with lock) withConcurrentDictionary
.While the performance impact of a lock is usually acceptable, when
Eval()
is heavily used to evaluate the same expressions (which is precisely when and why we need caching), it puts more pressure on threads and the CPU, making it more time-consuming compared to theConcurrentDictionary
.ConcurrentDictionary
has been heavily optimized for multi-thread scenarios, especially when there are many reads and fewer writes. Its read performance is almost identical to aDictionary
without any lock.Considering that this library targets net45, netstandard1.3, and netstandard2.0, all of which support
ConcurrentDictionary
, this change should be feasible. If, for some reason, there is a need to support older versions in the future, we can use #if and #elif compilation directives.Additionally, I have updated the README. When my colleagues and I first used this library last year and this year, we made the same mistake, instantiated a new instance each time we called it instead of making it a singleton. This wasted its caching capabilities. Also, if a user frequently evaluates many unique expressions, it might put pressure on memory in default. Therefore, I believe it is necessary to include this parameter in the README to help new users utilize this excellent work more effectively.