${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 |
| <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.
Even with your comment, I don't understand why this has changed :/
There was a problem hiding this comment.
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.
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
| lock (dict) | ||
| { | ||
| dict[item] = value; | ||
| GetWritableDict(dictReadOnly != null && !dict.ContainsKey(item))[item] = value; |
There was a problem hiding this comment.
Yes one could probably make one of the Setters call the other.
src/NLog/GlobalDiagnosticsContext.cs
Outdated
| if (mustCreateNew) | ||
| { | ||
| dict.Clear(); | ||
| var newDict = new Dictionary<string, object>(clone ? _dict.Count + 1 : 0); |
There was a problem hiding this comment.
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.
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.
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.
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
304NotModified
left a comment
There was a problem hiding this comment.
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:
