-
Notifications
You must be signed in to change notification settings - Fork 1.4k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
${GDC} reading is now lockfree #3201
Conversation
ConcurrentDictionary doesn't make the same "promise" of ordering as normal Dictionary. So JsonLayout.IncludeGdc is not producing identical output. |
01c1462
to
8826d56
Compare
Codecov Report
@@ Coverage Diff @@
## dev #3201 +/- ##
======================================
+ Coverage 80% 80% +<1%
======================================
Files 354 354
Lines 27927 27954 +27
Branches 3709 3713 +4
======================================
+ Hits 22265 22295 +30
+ Misses 4597 4595 -2
+ Partials 1065 1064 -1 |
8826d56
to
5160ae8
Compare
The ordering can be important in the expanding of variables that reference other variables. So reverted back to only using ConcurrentDictionary for GlobalDiagnosticsContext |
Isn't the concurrent queue much slower then the lock? |
Not if you have two threads (Btw. it is a ConcurrentDictionary) and you don't call:
See also https://ayende.com/blog/179329/public-service-announcement-concurrentdictionary-count-is-locking |
Not sure it's coincidence this tests fails now : NLog.UnitTests.Targets.ConcurrentFileTargetTests.SimpleConcurrentTest(numProcesses: 2, numLogs: 500, mode: "none|archive") [FAIL]
From |
@@ -506,7 +506,7 @@ public void IncludeGdcJsonProperties() | |||
<targets> | |||
<target name='asyncDebug' type='AsyncWrapper' timeToSleepBetweenBatches='0'> | |||
<target name='debug' type='Debug' > | |||
<layout type=""JsonLayout"" IncludeGdc='true' ExcludeProperties='Excluded1,Excluded2'> | |||
<layout type=""JsonLayout"" IncludeGdc='true' IncludeAllProperties='true' ExcludeProperties='Excluded1,Excluded2'> |
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.
Even with your comment, I don't understand why this has changed :/
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.
Dictionary makes a very good promise to return items in the same order as inserted (Though not always). ConcurrentDictionary doesn't make this promise.
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.
I have modified the test so GDC only contains the first property, and rest of the properties comes from the LogEvent.
Always been an unstable test. Concurrent processes writing to the same file test is not affected by this change. |
4b53bf0
to
85f92d4
Compare
I think the trouble and the surprises with ConcurrentDictionary will outweight the gain unless in a truly multithreaded environment that is doing nothing but GDC-lookups. Even using readerwriterlockslim will probably have some side-effects: |
@304NotModified Now modified GDC to perform copy on write. This gives high concurrency for loggers but more allocation for writers (if they write a lot while others are reading). Have the nice side-effect that |
@snakefoot is this one safe for NLog 4.6.1? (So low impact)? |
I think it is safe for NLog 4.6.1. One could also consider using the same approach for NLog Configuration Variables. |
ade07df
to
37aa6ec
Compare
src/NLog/GlobalDiagnosticsContext.cs
Outdated
@@ -69,7 +68,7 @@ public static void Set(string item, object value) | |||
{ | |||
lock (dict) | |||
{ | |||
dict[item] = value; | |||
GetWritableDict(dictReadOnly != null && !dict.ContainsKey(item))[item] = value; |
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.
Duplicate
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.
Yes one could probably make one of the Setters call the other.
src/NLog/GlobalDiagnosticsContext.cs
Outdated
{ | ||
dict.Clear(); | ||
var newDict = new Dictionary<string, object>(clone ? _dict.Count + 1 : 0); |
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.
Is it correct that clear has the init capacity of 0, and the field has the default capacity (16?). It that on purpose?
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.
Don't understand your question. But Clear (clone=false) calls with capacity = 0 that means do not allocate internal arrays until insert happens.
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.
indeed, but the init doesn't set capacity to 0
private static Dictionary<string, object> _dict = new Dictionary<string, object>();
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.
Still don't understand your question. The default constructor for Dictionary uses capacity = 0. See also https://referencesource.microsoft.com/#mscorlib/system/collections/generic/dictionary.cs,85 (Means do not allocate until insert happens)
please check snakefoot#5 :) |
@304NotModified Have now extracted the clone-logic from GetWritableDict |
f75267d
to
f913a69
Compare
f913a69
to
3d1d030
Compare
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.
thanks for the refactoring!
Happy to do it. And also very happy about the result :) |
GlobalDiagnosticsContext - Use copy-on-write and lockfree read
See also #3197
Noticed that NLog 4.5.11 often showed threads in progress with GDC-Lookups. On NLog 4.6 one didn't see it because GDC is marked as ThreadAgnostic.
But if using GDC in a Layout with other LayoutRenderers that are not ThreadAgnostic, then the lock-punishment will be back.
Added GDC-Lookup to the file-target-layout and now they get stuck with NLog 4.6: