-
Notifications
You must be signed in to change notification settings - Fork 73
Improved performance of the Instance property #39
Improved performance of the Instance property #39
Conversation
Do you have any sorts of measurements to justify this change? As I'd mentioned in that other thread:
To expand on that, I suspect that the difference between Back to the context where this came up, you could cut the time spent in Further, is that even a performance-sensitive method? I'm not familiar with Also, aren't we talking about a database client anyway? Don't all of these things tend to happen very shortly before the CPU goes to sleep for a few milliseconds waiting on network I/O? I'm not yet convinced that there's a situation where the cost of |
@@ -9,90 +9,67 @@ namespace GeoAPI | |||
/// </summary> | |||
public static class GeometryServiceProvider | |||
{ | |||
private static IGeometryServices _instance; | |||
private static readonly object LockObject = new object(); | |||
private static volatile IGeometryServices _instance; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Foreword: I haven't actually thought this through completely.
I don't actually think we need this to be volatile
, and if we think that this property is going to be "hot", then we actually don't really want it to be volatile
. Every other read/write of this field occurs immediately after a Monitor.Enter
call, which triggers a full memory barrier itself, so the volatile
semantics seem to only affect the hot path.
So it seems to me that the worst that can happen without volatile
is that every CPU comes in here with a stale cache and observes an uninitialized value, then they all hit the lock
, then one of them initializes it properly (slow), then every other CPU sees the value from the other thread one-by-one (fast), then for the rest of the application every access is as fast as it can possibly be.
It seems like that's also the worst-case for volatile
, except that at the end, every access for the rest of the application still has to go through a partial memory barrier. I don't actually see a window where volatile
helps at all, compared to no volatile
...
The running time of lock vs. volatile isn't really the important factor - it's the contention that lock entails and which volatile doesn't. So if a lot of threads are reading the property at the same time, they will all block, accessing it one by one. This is especially problematic in an initialization --interactive scenario where you know writing will never (or very rarely) occur. That's why it's important for the read to not lock.. |
@roji already said what the idea is, but FYI locks also use memory barriers internally on a multiprocessor machine and interlocked too. |
foreach (var constructor in type.GetConstructors()) | ||
{ | ||
if (constructor.IsPublic && constructor.GetParameters().Length == 0) | ||
return _instance = instance = (IGeometryServices)Activator.CreateInstance(type); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
no need for the = instance
part of this
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, fixed.
it bothers me that the entire change can't just be to change the top of the file to this, because private static readonly Lazy<IGeometryServices> DefaultInstance =
new Lazy<IGeometryServices>(ReflectInstance);
private static volatile IGeometryServices _instance;
public static IGeometryServices Instance
{
get => _instance ?? DefaultInstance.Value;
set => _instance = value ?? throw new ArgumentNullException(nameof(value));
} |
It's a bit heavy thing which isn't used internally by Microsoft. Stack Overflow has stopped using it too. Should I squash commits? |
It still doesn't make me happy with the state of That said, I have no technical issues with this, and I'm OK with merging it. @FObermaier do you have any concerns? |
ILSpy suggests that
Doesn't matter to me, personally. |
I know |
Here's a bit about it. I'm not seeing much usage of it compared to Edit: it's also a 4.0+ thing, so we can't use that for GeoAPI either, at least not without keeping "something" like the current stuff around for the |
It's a static class which is used for lazy initializations but without allocating memory to store the state, i.e. you choose where to store a synchronization object and an initializing value. In the current case it makes sense because there is a setter with the lock inside. |
@YohDeadfall, I assume you left the We need to make sure that we have the |
I think it's to avoid this:
Without the
One thing we could do is have everybody but those three target frameworks use the If we wanted to do that fancy |
Right, but it can be removed (only in the setter) if
You can't use |
|
Ah, that way (: From my point of view having a |
@FObermaier I've reviewed this. I'd like to merge it. Is that OK with you? |
@airbreather , no I don't have objections. @YohDeadfall, @airbreather and @roji, thanks for your input. |
As discussed in npgsql/npgsql#1840.
/cc @roji