From d9abf602ff8fb8314112a95873c8665e8825e139 Mon Sep 17 00:00:00 2001 From: "Oren (electricessence)" Date: Wed, 18 Oct 2017 10:53:23 -0700 Subject: [PATCH 01/83] Removed unnecessary slow pools and moved to separate branch. --- source/Collection/ConcurrentBagObjectPool.cs | 63 -------------------- source/Collection/LinkedListObjectPool.cs | 53 ---------------- 2 files changed, 116 deletions(-) delete mode 100644 source/Collection/ConcurrentBagObjectPool.cs delete mode 100644 source/Collection/LinkedListObjectPool.cs diff --git a/source/Collection/ConcurrentBagObjectPool.cs b/source/Collection/ConcurrentBagObjectPool.cs deleted file mode 100644 index 5a06559..0000000 --- a/source/Collection/ConcurrentBagObjectPool.cs +++ /dev/null @@ -1,63 +0,0 @@ -using System; -using System.Collections.Concurrent; - -namespace Open.Disposable -{ - public sealed class ConcurrentBagObjectPool : TrimmableObjectPoolBase - where T : class - - { - - public ConcurrentBagObjectPool(Func factory, Action recycler, int capacity = DEFAULT_CAPACITY) - : base(factory, recycler, capacity) - { - Pool = new ConcurrentBag(); - } - - public ConcurrentBagObjectPool(Func factory, int capacity = DEFAULT_CAPACITY) - : this(factory, null, capacity) - { - } - - ConcurrentBag Pool; - - public override int Count => Pool?.Count ?? 0; - - protected override bool Receive(T item) - { - var p = Pool; - if (p == null) return false; - p.Add(item); // It's possible that the count could exceed MaxSize here, but the risk is negligble as a few over the limit won't hurt. - return true; - } - - protected override T TryTakeInternal() - { - var p = Pool; - if (p == null) return null; - p.TryTake(out T item); - return item; - } - - protected override void OnDispose(bool calledExplicitly) - { - Pool = null; - } - - } - - public static class ConcurrentBagObjectPool - { - public static ConcurrentBagObjectPool Create(Func factory, int capacity = Constants.DEFAULT_CAPACITY) - where T : class - { - return new ConcurrentBagObjectPool(factory, capacity); - } - - public static ConcurrentBagObjectPool Create(int capacity = Constants.DEFAULT_CAPACITY) - where T : class, new() - { - return Create(() => new T(), capacity); - } - } -} diff --git a/source/Collection/LinkedListObjectPool.cs b/source/Collection/LinkedListObjectPool.cs deleted file mode 100644 index b64a565..0000000 --- a/source/Collection/LinkedListObjectPool.cs +++ /dev/null @@ -1,53 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Text; - -namespace Open.Disposable -{ - public class LinkedListObjectPool : CollectionWrapperObjectPool> - where T : class - { - public LinkedListObjectPool(Func factory, Action recycler, int capacity = DEFAULT_CAPACITY) - : base(new LinkedList(), factory, recycler, capacity) - { - } - - public LinkedListObjectPool(Func factory, int capacity = DEFAULT_CAPACITY) - : this(factory, null, capacity) - { - } - - - protected override T TryTakeInternal() - { - var p = Pool; - if (p == null) return null; - T item; - lock(p) - { - var node = p.Last; - if (node == null) return null; - item = node.Value; - p.RemoveLast(); - } - - return item; - } - - } - - public static class LinkedListObjectPool - { - public static LinkedListObjectPool Create(Func factory, int capacity = Constants.DEFAULT_CAPACITY) - where T : class - { - return new LinkedListObjectPool(factory, capacity); - } - - public static LinkedListObjectPool Create(int capacity = Constants.DEFAULT_CAPACITY) - where T : class, new() - { - return Create(() => new T(), capacity); - } - } -} From 13e813d3d64a7f7e1b077405577bbdb0e42ea470 Mon Sep 17 00:00:00 2001 From: electricessence Date: Wed, 18 Oct 2017 23:24:57 -0700 Subject: [PATCH 02/83] Revisions to pools Less redundancy and more reuse of interlocking code. OptimisticArrayObjectPool now appropriately inherits from InterlockedArrayObjectPool. --- benchmarking/BenchmarkResult.csv | 69 +++--- benchmarking/BenchmarkResult.txt | 224 +++++++++++------- benchmarking/Program.cs | 18 +- source/Array/InterlockedArrayObjectPool.cs | 174 +++++++------- source/Array/OptimisticArrayObjectPool.cs | 167 +++++-------- .../Collection/CollectionWrapperObjectPool.cs | 2 +- .../Collection/ConcurrentQueueObjectPool.cs | 2 +- .../Collection/ConcurrentStackObjectPool.cs | 65 +++++ source/Collection/QueueObjectPool.cs | 2 +- source/Collection/StackObjectPool.cs | 77 ++++++ source/ITrimmableObjectPool.cs | 4 +- source/ObjectPoolAutoTrimmer.cs | 8 +- source/ObjectPoolBase.cs | 71 +++--- source/ReferenceContainer.cs | 39 +++ source/TrimmableObjectPoolBase.cs | 24 +- 15 files changed, 559 insertions(+), 387 deletions(-) create mode 100644 source/Collection/ConcurrentStackObjectPool.cs create mode 100644 source/Collection/StackObjectPool.cs create mode 100644 source/ReferenceContainer.cs diff --git a/benchmarking/BenchmarkResult.csv b/benchmarking/BenchmarkResult.csv index b43069c..ce1e0f3 100644 --- a/benchmarking/BenchmarkResult.csv +++ b/benchmarking/BenchmarkResult.csv @@ -1,34 +1,37 @@ Batch,Pool Type," From Empty (In Parallel)"," To (In Parallel)",d Read/Write (In Parallel),y Pool (.TryTake()),L, -Repeat 200000 for size 4,QueueObjectPool,00:00:00.8064961,00:00:00.7379976,00:00:00.7372226,00:00:00.0912138,00:00:02.3729301, -Repeat 200000 for size 4,ConcurrentQueueObjectPool,00:00:00.8392163,00:00:00.8090963,00:00:00.8090662,00:00:00.1057385,00:00:02.5631173, -Repeat 200000 for size 4,OptimisticArrayObjectPool,00:00:00.8582319,00:00:00.7316954,00:00:00.7462951,00:00:00.0322101,00:00:02.3684325, -Repeat 80000 for size 10,QueueObjectPool,00:00:00.4891898,00:00:00.4641127,00:00:00.4325487,00:00:00.0887330,00:00:01.4745842, -Repeat 80000 for size 10,ConcurrentQueueObjectPool,00:00:00.4774220,00:00:00.4532694,00:00:00.4577644,00:00:00.1030334,00:00:01.4914892, -Repeat 80000 for size 10,OptimisticArrayObjectPool,00:00:00.5075819,00:00:00.3865528,00:00:00.4139942,00:00:00.0178275,00:00:01.3259564, -Repeat 24000 for size 50,QueueObjectPool,00:00:00.2172210,00:00:00.4786407,00:00:00.2498973,00:00:00.1187320,00:00:01.0644910, -Repeat 24000 for size 50,ConcurrentQueueObjectPool,00:00:00.2130298,00:00:00.2258805,00:00:00.2090524,00:00:00.1446890,00:00:00.7926517, -Repeat 24000 for size 50,OptimisticArrayObjectPool,00:00:00.3218326,00:00:00.1749609,00:00:00.1948959,00:00:00.0136129,00:00:00.7053023, -Repeat 16000 for size 100,QueueObjectPool,00:00:00.1933122,00:00:01.1807893,00:00:00.3700023,00:00:00.1532743,00:00:01.8973781, -Repeat 16000 for size 100,ConcurrentQueueObjectPool,00:00:00.1885421,00:00:00.2231642,00:00:00.1945119,00:00:00.1892646,00:00:00.7954828, -Repeat 16000 for size 100,OptimisticArrayObjectPool,00:00:00.4629300,00:00:00.1296838,00:00:00.2026568,00:00:00.0155329,00:00:00.8108035, -Repeat 25600 for size 250,QueueObjectPool,00:00:00.5119740,00:00:03.6755755,00:00:03.0652345,00:00:00.5573299,00:00:07.8101139, -Repeat 25600 for size 250,ConcurrentQueueObjectPool,00:00:00.5032784,00:00:00.6586173,00:00:00.5488527,00:00:00.7357870,00:00:02.4465354, -Repeat 25600 for size 250,OptimisticArrayObjectPool,00:00:03.0809331,00:00:00.3488741,00:00:00.7180123,00:00:00.0531776,00:00:04.2009971, -Repeat 9600 for size 1000,QueueObjectPool,00:00:00.4419158,00:00:02.4529980,00:00:02.3464286,00:00:00.6892199,00:00:05.9305623, -Repeat 9600 for size 1000,ConcurrentQueueObjectPool,00:00:00.4453723,00:00:00.6651569,00:00:00.5769710,00:00:00.8922154,00:00:02.5797156, -Repeat 9600 for size 1000,OptimisticArrayObjectPool,00:00:13.4891871,00:00:00.2492917,00:00:01.1461595,00:00:00.0718376,00:00:14.9564759, -,OptimisticArrayObjectPool,00:00:03.0074967,00:00:00.3465589,00:00:00.7164117,00:00:00.0529356,00:00:04.1234029, -Repeat 25600 for size 250,ChannelObjectPool,00:00:04.8289131,00:00:05.0525342,00:00:02.7992174,00:00:00.4744166,00:00:13.1550813, -Repeat 9600 for size 1000,QueueObjectPool,00:00:00.4429800,00:00:02.5445855,00:00:02.5696949,00:00:00.7169192,00:00:06.2741796, -Repeat 9600 for size 1000,ConcurrentQueueObjectPool,00:00:00.4447846,00:00:00.6793450,00:00:00.5848884,00:00:00.9255401,00:00:02.6345581, -Repeat 9600 for size 1000,OptimisticArrayObjectPool,00:00:13.5246195,00:00:00.2524943,00:00:01.1490467,00:00:00.0721550,00:00:14.9983155, -Repeat 9600 for size 1000,ChannelObjectPool,00:00:03.0021715,00:00:03.3152862,00:00:01.8869001,00:00:00.5896046,00:00:08.7939624, -1264,00:00:00.4388072,00:00:00.0009975,00:00:01.1470602, -Repeat 4800 for size 4000,QueueObjectPool,00:00:00.7134197,00:00:03.7582117,00:00:03.1855656,00:00:01.2950620,00:00:08.9522590, -Repeat 4800 for size 4000,ConcurrentQueueObjectPool,00:00:00.7232603,00:00:01.2258140,00:00:01.0573101,00:00:01.6096397,00:00:04.6160241, -Repeat 4800 for size 4000,OptimisticArrayObjectPool,00:00:00.6968814,00:00:00.3086700,00:00:00.5845562,00:00:00.0007690,00:00:01.5908766, -Repeat 4800 for size 4000,InterlockedArrayObjectPool,00:00:00.6864968,00:00:00.3074012,00:00:00.6233171,00:00:00.0007314,00:00:01.6179465, -Repeat 2400 for size 8000,QueueObjectPool,00:00:00.7021946,00:00:03.5501399,00:00:02.5203758,00:00:01.2936158,00:00:08.0663261, -Repeat 2400 for size 8000,ConcurrentQueueObjectPool,00:00:00.7088997,00:00:01.2280982,00:00:01.0299925,00:00:01.5836229,00:00:04.5506133, -Repeat 2400 for size 8000,OptimisticArrayObjectPool,00:00:00.6956117,00:00:00.2895694,00:00:00.5699170,00:00:00.0004110,00:00:01.5555091, -Repeat 2400 for size 8000,InterlockedArrayObjectPool,00:00:00.6782009,00:00:00.2871359,00:00:00.6188317,00:00:00.0003863,00:00:01.5845548, +Repeat 200000 for size 4,QueueObjectPool,00:00:01.1287292,00:00:01.0294513,00:00:01.0260755,00:00:00.1590331,00:00:03.3432891, +Repeat 200000 for size 4,ConcurrentQueueObjectPool,00:00:01.1514246,00:00:01.0375667,00:00:01.0105712,00:00:00.1741179,00:00:03.3736804, +Repeat 200000 for size 4,ConcurrentStackObjectPool,00:00:01.0247996,00:00:00.9655175,00:00:00.9336741,00:00:00.1360451,00:00:03.0600363, +Repeat 200000 for size 4,OptimisticArrayObjectPool,00:00:01.0468743,00:00:00.8456746,00:00:00.8382048,00:00:00.1628778,00:00:02.8936315, +Repeat 80000 for size 10,QueueObjectPool,00:00:00.5433300,00:00:00.5461789,00:00:00.4759666,00:00:00.1333091,00:00:01.6987846, +Repeat 80000 for size 10,ConcurrentQueueObjectPool,00:00:00.5099808,00:00:00.4864080,00:00:00.5001707,00:00:00.1520913,00:00:01.6486508, +Repeat 80000 for size 10,ConcurrentStackObjectPool,00:00:00.5763228,00:00:00.5440804,00:00:00.5287139,00:00:00.1281674,00:00:01.7772845, +Repeat 80000 for size 10,OptimisticArrayObjectPool,00:00:00.6038049,00:00:00.5263031,00:00:00.4762357,00:00:00.2268198,00:00:01.8331635, +Repeat 24000 for size 50,QueueObjectPool,00:00:00.2580179,00:00:00.4086527,00:00:00.2688232,00:00:00.1734043,00:00:01.1088981, +Repeat 24000 for size 50,ConcurrentQueueObjectPool,00:00:00.2592864,00:00:00.2647728,00:00:00.2407817,00:00:00.2021228,00:00:00.9669637, +Repeat 24000 for size 50,ConcurrentStackObjectPool,00:00:00.2676966,00:00:00.3135490,00:00:00.2759068,00:00:00.2042573,00:00:01.0614097, +Repeat 24000 for size 50,OptimisticArrayObjectPool,00:00:00.3239524,00:00:00.3545534,00:00:00.2256836,00:00:00.8061559,00:00:01.7103453, +Repeat 16000 for size 100,QueueObjectPool,00:00:00.2503055,00:00:00.4926017,00:00:00.3241812,00:00:00.2220571,00:00:01.2891455, +Repeat 16000 for size 100,ConcurrentQueueObjectPool,00:00:00.2482971,00:00:00.2783954,00:00:00.2356915,00:00:00.2691699,00:00:01.0315539, +Repeat 16000 for size 100,ConcurrentStackObjectPool,00:00:00.2590476,00:00:00.3642821,00:00:00.3237909,00:00:00.3202590,00:00:01.2673796, +Repeat 16000 for size 100,OptimisticArrayObjectPool,00:00:00.3215564,00:00:00.4745108,00:00:00.1784334,00:00:01.5867222,00:00:02.5612228, +Repeat 25600 for size 250,QueueObjectPool,00:00:00.7252826,00:00:01.7557360,00:00:01.1939824,00:00:00.8119776,00:00:04.4869786, +Repeat 25600 for size 250,ConcurrentQueueObjectPool,00:00:00.7722675,00:00:00.9039538,00:00:00.7259809,00:00:01.0339628,00:00:03.4361650, +Repeat 25600 for size 250,ConcurrentStackObjectPool,00:00:00.7369296,00:00:01.5097271,00:00:01.5271033,00:00:01.7198474,00:00:05.4936074, +Repeat 25600 for size 250,OptimisticArrayObjectPool,00:00:01.2872667,00:00:03.1949158,00:00:00.5262142,00:00:11.7748329,00:00:16.7832296, +Repeat 14400 for size 500,QueueObjectPool,00:00:00.7797824,00:00:01.6503887,00:00:01.3768657,00:00:00.8861150,00:00:04.6931518, +Repeat 14400 for size 500,ConcurrentQueueObjectPool,00:00:00.7994849,00:00:00.9658497,00:00:00.7778244,00:00:01.1149906,00:00:03.6581496, +Repeat 14400 for size 500,ConcurrentStackObjectPool,00:00:00.7487525,00:00:02.3665267,00:00:02.6608263,00:00:03.0226290,00:00:08.7987345, +Repeat 14400 for size 500,OptimisticArrayObjectPool,00:00:01.0892049,00:00:05.8599218,00:00:00.5797098,00:00:22.9215261,00:00:30.4503626, +250,QueueObjectPool,00:00:00.7629885,00:00:01.7291545,00:00:01.2741500,00:00:00.8586163,00:00:04.6249093, +Repeat 25600 for size 250,StackObjectPool,00:00:00.7604847,00:00:01.8116364,00:00:01.2369652,00:00:00.8626937,00:00:04.6717800, +Repeat 25600 for size 250,ConcurrentQueueObjectPool,00:00:00.8566894,00:00:00.9466838,00:00:00.7795225,00:00:01.1284017,00:00:03.7112974, +Repeat 25600 for size 250,ConcurrentStackObjectPool,00:00:00.7723668,00:00:01.5778130,00:00:01.6054224,00:00:01.7977731,00:00:05.7533753, +Repeat 25600 for size 250,OptimisticArrayObjectPool,00:00:01.2503504,00:00:03.2370044,00:00:00.5303349,00:00:11.9466373,00:00:16.9643270, +Repeat 25600 for size 250,InterlockedArrayObjectPool,00:00:01.2508355,00:00:06.3817155,00:00:00.8393488,00:00:14.9549267,00:00:23.4268265, +Repeat 14400 for size 500,QueueObjectPool,00:00:00.7519294,00:00:01.5185357,00:00:01.3801529,00:00:00.8505885,00:00:04.5012065, +Repeat 14400 for size 500,StackObjectPool,00:00:00.7628823,00:00:01.6182833,00:00:01.4624384,00:00:00.8960254,00:00:04.7396294, +Repeat 14400 for size 500,ConcurrentQueueObjectPool,00:00:00.8285180,00:00:00.9902911,00:00:00.8006946,00:00:01.1172107,00:00:03.7367144, +Repeat 14400 for size 500,ConcurrentStackObjectPool,00:00:00.6978419,00:00:02.2585037,00:00:02.4760289,00:00:02.8394301,00:00:08.2718046, +Repeat 14400 for size 500,OptimisticArrayObjectPool,00:00:01.0721065,00:00:05.6089585,00:00:00.5478712,00:00:22.2804453,00:00:29.5093815, +Repeat 14400 for size 500,InterlockedArrayObjectPool,00:00:01.1338229,00:00:12.8073240,00:00:00.7613453,00:00:32.4547747,00:00:47.1572669, diff --git a/benchmarking/BenchmarkResult.txt b/benchmarking/BenchmarkResult.txt index 92bf94a..787e0eb 100644 --- a/benchmarking/BenchmarkResult.txt +++ b/benchmarking/BenchmarkResult.txt @@ -2,149 +2,191 @@ Repeat 200000 for size 4 ------------------------------------ QueueObjectPool......................................... -00:00:00.8064961 Take From Empty (In Parallel) -00:00:00.7379976 Give To (In Parallel) -00:00:00.7372226 Mixed Read/Write (In Parallel) -00:00:00.0912138 Empty Pool (.TryTake()) -00:00:02.3729301 TOTAL +00:00:01.1287292 Take From Empty (In Parallel) +00:00:01.0294513 Give To (In Parallel) +00:00:01.0260755 Mixed Read/Write (In Parallel) +00:00:00.1590331 Empty Pool (.TryTake()) +00:00:03.3432891 TOTAL ConcurrentQueueObjectPool............................... -00:00:00.8392163 Take From Empty (In Parallel) -00:00:00.8090963 Give To (In Parallel) -00:00:00.8090662 Mixed Read/Write (In Parallel) -00:00:00.1057385 Empty Pool (.TryTake()) -00:00:02.5631173 TOTAL +00:00:01.1514246 Take From Empty (In Parallel) +00:00:01.0375667 Give To (In Parallel) +00:00:01.0105712 Mixed Read/Write (In Parallel) +00:00:00.1741179 Empty Pool (.TryTake()) +00:00:03.3736804 TOTAL + +ConcurrentStackObjectPool............................... +00:00:01.0247996 Take From Empty (In Parallel) +00:00:00.9655175 Give To (In Parallel) +00:00:00.9336741 Mixed Read/Write (In Parallel) +00:00:00.1360451 Empty Pool (.TryTake()) +00:00:03.0600363 TOTAL OptimisticArrayObjectPool............................... -00:00:00.8582319 Take From Empty (In Parallel) -00:00:00.7316954 Give To (In Parallel) -00:00:00.7462951 Mixed Read/Write (In Parallel) -00:00:00.0322101 Empty Pool (.TryTake()) -00:00:02.3684325 TOTAL +00:00:01.0468743 Take From Empty (In Parallel) +00:00:00.8456746 Give To (In Parallel) +00:00:00.8382048 Mixed Read/Write (In Parallel) +00:00:00.1628778 Empty Pool (.TryTake()) +00:00:02.8936315 TOTAL Repeat 80000 for size 10 ------------------------------------ QueueObjectPool......................................... -00:00:00.4891898 Take From Empty (In Parallel) -00:00:00.4641127 Give To (In Parallel) -00:00:00.4325487 Mixed Read/Write (In Parallel) -00:00:00.0887330 Empty Pool (.TryTake()) -00:00:01.4745842 TOTAL +00:00:00.5433300 Take From Empty (In Parallel) +00:00:00.5461789 Give To (In Parallel) +00:00:00.4759666 Mixed Read/Write (In Parallel) +00:00:00.1333091 Empty Pool (.TryTake()) +00:00:01.6987846 TOTAL ConcurrentQueueObjectPool............................... -00:00:00.4774220 Take From Empty (In Parallel) -00:00:00.4532694 Give To (In Parallel) -00:00:00.4577644 Mixed Read/Write (In Parallel) -00:00:00.1030334 Empty Pool (.TryTake()) -00:00:01.4914892 TOTAL +00:00:00.5099808 Take From Empty (In Parallel) +00:00:00.4864080 Give To (In Parallel) +00:00:00.5001707 Mixed Read/Write (In Parallel) +00:00:00.1520913 Empty Pool (.TryTake()) +00:00:01.6486508 TOTAL + +ConcurrentStackObjectPool............................... +00:00:00.5763228 Take From Empty (In Parallel) +00:00:00.5440804 Give To (In Parallel) +00:00:00.5287139 Mixed Read/Write (In Parallel) +00:00:00.1281674 Empty Pool (.TryTake()) +00:00:01.7772845 TOTAL OptimisticArrayObjectPool............................... -00:00:00.5075819 Take From Empty (In Parallel) -00:00:00.3865528 Give To (In Parallel) -00:00:00.4139942 Mixed Read/Write (In Parallel) -00:00:00.0178275 Empty Pool (.TryTake()) -00:00:01.3259564 TOTAL +00:00:00.6038049 Take From Empty (In Parallel) +00:00:00.5263031 Give To (In Parallel) +00:00:00.4762357 Mixed Read/Write (In Parallel) +00:00:00.2268198 Empty Pool (.TryTake()) +00:00:01.8331635 TOTAL Repeat 24000 for size 50 ------------------------------------ QueueObjectPool......................................... -00:00:00.2172210 Take From Empty (In Parallel) -00:00:00.4786407 Give To (In Parallel) -00:00:00.2498973 Mixed Read/Write (In Parallel) -00:00:00.1187320 Empty Pool (.TryTake()) -00:00:01.0644910 TOTAL +00:00:00.2580179 Take From Empty (In Parallel) +00:00:00.4086527 Give To (In Parallel) +00:00:00.2688232 Mixed Read/Write (In Parallel) +00:00:00.1734043 Empty Pool (.TryTake()) +00:00:01.1088981 TOTAL ConcurrentQueueObjectPool............................... -00:00:00.2130298 Take From Empty (In Parallel) -00:00:00.2258805 Give To (In Parallel) -00:00:00.2090524 Mixed Read/Write (In Parallel) -00:00:00.1446890 Empty Pool (.TryTake()) -00:00:00.7926517 TOTAL +00:00:00.2592864 Take From Empty (In Parallel) +00:00:00.2647728 Give To (In Parallel) +00:00:00.2407817 Mixed Read/Write (In Parallel) +00:00:00.2021228 Empty Pool (.TryTake()) +00:00:00.9669637 TOTAL + +ConcurrentStackObjectPool............................... +00:00:00.2676966 Take From Empty (In Parallel) +00:00:00.3135490 Give To (In Parallel) +00:00:00.2759068 Mixed Read/Write (In Parallel) +00:00:00.2042573 Empty Pool (.TryTake()) +00:00:01.0614097 TOTAL OptimisticArrayObjectPool............................... -00:00:00.3218326 Take From Empty (In Parallel) -00:00:00.1749609 Give To (In Parallel) -00:00:00.1948959 Mixed Read/Write (In Parallel) -00:00:00.0136129 Empty Pool (.TryTake()) -00:00:00.7053023 TOTAL +00:00:00.3239524 Take From Empty (In Parallel) +00:00:00.3545534 Give To (In Parallel) +00:00:00.2256836 Mixed Read/Write (In Parallel) +00:00:00.8061559 Empty Pool (.TryTake()) +00:00:01.7103453 TOTAL Repeat 16000 for size 100 ------------------------------------ QueueObjectPool......................................... -00:00:00.1933122 Take From Empty (In Parallel) -00:00:01.1807893 Give To (In Parallel) -00:00:00.3700023 Mixed Read/Write (In Parallel) -00:00:00.1532743 Empty Pool (.TryTake()) -00:00:01.8973781 TOTAL +00:00:00.2503055 Take From Empty (In Parallel) +00:00:00.4926017 Give To (In Parallel) +00:00:00.3241812 Mixed Read/Write (In Parallel) +00:00:00.2220571 Empty Pool (.TryTake()) +00:00:01.2891455 TOTAL ConcurrentQueueObjectPool............................... -00:00:00.1885421 Take From Empty (In Parallel) -00:00:00.2231642 Give To (In Parallel) -00:00:00.1945119 Mixed Read/Write (In Parallel) -00:00:00.1892646 Empty Pool (.TryTake()) -00:00:00.7954828 TOTAL +00:00:00.2482971 Take From Empty (In Parallel) +00:00:00.2783954 Give To (In Parallel) +00:00:00.2356915 Mixed Read/Write (In Parallel) +00:00:00.2691699 Empty Pool (.TryTake()) +00:00:01.0315539 TOTAL + +ConcurrentStackObjectPool............................... +00:00:00.2590476 Take From Empty (In Parallel) +00:00:00.3642821 Give To (In Parallel) +00:00:00.3237909 Mixed Read/Write (In Parallel) +00:00:00.3202590 Empty Pool (.TryTake()) +00:00:01.2673796 TOTAL OptimisticArrayObjectPool............................... -00:00:00.4629300 Take From Empty (In Parallel) -00:00:00.1296838 Give To (In Parallel) -00:00:00.2026568 Mixed Read/Write (In Parallel) -00:00:00.0155329 Empty Pool (.TryTake()) -00:00:00.8108035 TOTAL +00:00:00.3215564 Take From Empty (In Parallel) +00:00:00.4745108 Give To (In Parallel) +00:00:00.1784334 Mixed Read/Write (In Parallel) +00:00:01.5867222 Empty Pool (.TryTake()) +00:00:02.5612228 TOTAL Repeat 25600 for size 250 ------------------------------------ QueueObjectPool......................................... -00:00:00.5119740 Take From Empty (In Parallel) -00:00:03.6755755 Give To (In Parallel) -00:00:03.0652345 Mixed Read/Write (In Parallel) -00:00:00.5573299 Empty Pool (.TryTake()) -00:00:07.8101139 TOTAL +00:00:00.7252826 Take From Empty (In Parallel) +00:00:01.7557360 Give To (In Parallel) +00:00:01.1939824 Mixed Read/Write (In Parallel) +00:00:00.8119776 Empty Pool (.TryTake()) +00:00:04.4869786 TOTAL ConcurrentQueueObjectPool............................... -00:00:00.5032784 Take From Empty (In Parallel) -00:00:00.6586173 Give To (In Parallel) -00:00:00.5488527 Mixed Read/Write (In Parallel) -00:00:00.7357870 Empty Pool (.TryTake()) -00:00:02.4465354 TOTAL +00:00:00.7722675 Take From Empty (In Parallel) +00:00:00.9039538 Give To (In Parallel) +00:00:00.7259809 Mixed Read/Write (In Parallel) +00:00:01.0339628 Empty Pool (.TryTake()) +00:00:03.4361650 TOTAL + +ConcurrentStackObjectPool............................... +00:00:00.7369296 Take From Empty (In Parallel) +00:00:01.5097271 Give To (In Parallel) +00:00:01.5271033 Mixed Read/Write (In Parallel) +00:00:01.7198474 Empty Pool (.TryTake()) +00:00:05.4936074 TOTAL OptimisticArrayObjectPool............................... -00:00:03.0809331 Take From Empty (In Parallel) -00:00:00.3488741 Give To (In Parallel) -00:00:00.7180123 Mixed Read/Write (In Parallel) -00:00:00.0531776 Empty Pool (.TryTake()) -00:00:04.2009971 TOTAL +00:00:01.2872667 Take From Empty (In Parallel) +00:00:03.1949158 Give To (In Parallel) +00:00:00.5262142 Mixed Read/Write (In Parallel) +00:00:11.7748329 Empty Pool (.TryTake()) +00:00:16.7832296 TOTAL -Repeat 9600 for size 1000 +Repeat 14400 for size 500 ------------------------------------ QueueObjectPool......................................... -00:00:00.4419158 Take From Empty (In Parallel) -00:00:02.4529980 Give To (In Parallel) -00:00:02.3464286 Mixed Read/Write (In Parallel) -00:00:00.6892199 Empty Pool (.TryTake()) -00:00:05.9305623 TOTAL +00:00:00.7797824 Take From Empty (In Parallel) +00:00:01.6503887 Give To (In Parallel) +00:00:01.3768657 Mixed Read/Write (In Parallel) +00:00:00.8861150 Empty Pool (.TryTake()) +00:00:04.6931518 TOTAL ConcurrentQueueObjectPool............................... -00:00:00.4453723 Take From Empty (In Parallel) -00:00:00.6651569 Give To (In Parallel) -00:00:00.5769710 Mixed Read/Write (In Parallel) -00:00:00.8922154 Empty Pool (.TryTake()) -00:00:02.5797156 TOTAL +00:00:00.7994849 Take From Empty (In Parallel) +00:00:00.9658497 Give To (In Parallel) +00:00:00.7778244 Mixed Read/Write (In Parallel) +00:00:01.1149906 Empty Pool (.TryTake()) +00:00:03.6581496 TOTAL + +ConcurrentStackObjectPool............................... +00:00:00.7487525 Take From Empty (In Parallel) +00:00:02.3665267 Give To (In Parallel) +00:00:02.6608263 Mixed Read/Write (In Parallel) +00:00:03.0226290 Empty Pool (.TryTake()) +00:00:08.7987345 TOTAL OptimisticArrayObjectPool............................... -00:00:13.4891871 Take From Empty (In Parallel) -00:00:00.2492917 Give To (In Parallel) -00:00:01.1461595 Mixed Read/Write (In Parallel) -00:00:00.0718376 Empty Pool (.TryTake()) -00:00:14.9564759 TOTAL +00:00:01.0892049 Take From Empty (In Parallel) +00:00:05.8599218 Give To (In Parallel) +00:00:00.5797098 Mixed Read/Write (In Parallel) +00:00:22.9215261 Empty Pool (.TryTake()) +00:00:30.4503626 TOTAL diff --git a/benchmarking/Program.cs b/benchmarking/Program.cs index 8051a10..981b0ea 100644 --- a/benchmarking/Program.cs +++ b/benchmarking/Program.cs @@ -26,12 +26,20 @@ static void Main(string[] args) report.AddBenchmark("QueueObjectPool", // Note, that this one isn't far off from the following in peformance. count => () => QueueObjectPool.Create((int)count * 2)); - // The two contenders... - report.AddBenchmark("ConcurrentQueueObjectPool", // Note, that this one isn't far off from the following in peformance, but definitely is faster than LinkedListObjectPool and the rest. + report.AddBenchmark("ConcurrentQueueObjectPool", // Note, that this one isn't far off from the following in peformance, but definitely is faster than LinkedListObjectPool and the rest. count => () => ConcurrentQueueObjectPool.Create((int)count * 2)); - report.AddBenchmark("OptimisticArrayObjectPool", + + report.AddBenchmark("ConcurrentStackObjectPool", // Note, that this one isn't far off from the following in peformance, but definitely is faster than LinkedListObjectPool and the rest. + count => () => ConcurrentStackObjectPool.Create((int)count * 2)); + + report.AddBenchmark("OptimisticArrayObjectPool", count => () => OptimisticArrayObjectPool.Create((int)count * 2)); - report.Pretest(200, 200); // Run once through first to scramble/warm-up initial conditions. + + // Is ineveitably slower than the above but should be enabled for testing code changes. + //report.AddBenchmark("InterlockedArrayObjectPool", + // count => () => InterlockedArrayObjectPool.Create((int)count * 2)); + + report.Pretest(200, 200); // Run once through first to scramble/warm-up initial conditions. Console.SetCursorPosition(0, Console.CursorTop); @@ -40,7 +48,7 @@ static void Main(string[] args) report.Test(50, 12); report.Test(100, 16); report.Test(250, 64); - report.Test(1000, 96); + //report.Test(500, 72); File.WriteAllText("./BenchmarkResult.txt", sb.ToString()); using (var fs = File.OpenWrite("./BenchmarkResult.csv")) diff --git a/source/Array/InterlockedArrayObjectPool.cs b/source/Array/InterlockedArrayObjectPool.cs index 185af46..fbed78f 100644 --- a/source/Array/InterlockedArrayObjectPool.cs +++ b/source/Array/InterlockedArrayObjectPool.cs @@ -1,101 +1,89 @@ /* Based on Roslyn's ObjectPool */ using System; -using System.Diagnostics; using System.Threading; namespace Open.Disposable { - /// - /// An extremely fast ObjectPool when the capacity is in the low 100s. - /// - /// - public class InterlockedArrayObjectPool : ObjectPoolBase - where T : class - { - - public InterlockedArrayObjectPool(Func factory, Action recycler, int capacity = DEFAULT_CAPACITY) - : base(factory, recycler, capacity) - { - AllowPocket = true; - Pool = new Element[capacity - 1]; - } - - public InterlockedArrayObjectPool(Func factory, int capacity = DEFAULT_CAPACITY) - : this(factory, null, capacity) - { - } - - Element[] Pool; - - [DebuggerDisplay("{Value,nq}")] - protected struct Element - { - T _value; - internal bool Save(ref T value) - { - return _value != null - && null == Interlocked.CompareExchange(ref _value, value, null); - } - - internal T TryRetrieve() - { - var item = _value; - return (item != null - && item == Interlocked.CompareExchange(ref _value, null, item)) - ? item - : null; - } - } - - protected override bool Receive(T item) - { - var elements = Pool; - var len = elements?.Length ?? 0; - - for (int i = 0; i < len; i++) - { - if (elements[i].Save(ref item)) - return true; - } - - return false; - } - - protected override T TryTakeInternal() - { - // We missed getting the first item or it wasn't there. - var elements = Pool; - var len = elements?.Length ?? 0; - - for (int i = 0; i < len; i++) - { - var item = elements[i].TryRetrieve(); - if (item != null) return item; - } - - return null; - } - - protected override void OnDispose(bool calledExplicitly) - { - Pool = null; - } - - } - - public static class InterlockedArrayObjectPool - { - public static InterlockedArrayObjectPool Create(Func factory, int capacity = Constants.DEFAULT_CAPACITY) - where T : class - { - return new InterlockedArrayObjectPool(factory, capacity); - } - - public static InterlockedArrayObjectPool Create(int capacity = Constants.DEFAULT_CAPACITY) - where T : class, new() - { - return Create(() => new T(), capacity); - } - } + /// + /// An extremely fast ObjectPool when the capacity is in the low 100s. + /// + /// + public class InterlockedArrayObjectPool : ObjectPoolBase + where T : class + { + + public InterlockedArrayObjectPool(Func factory, Action recycler, int capacity = DEFAULT_CAPACITY) + : base(factory, recycler, capacity) + { + Pool = new ReferenceContainer[capacity - 1]; + } + + public InterlockedArrayObjectPool(Func factory, int capacity = DEFAULT_CAPACITY) + : this(factory, null, capacity) + { } + + protected ReferenceContainer[] Pool; + + // Sets a limit on what has been stored yet to prevent over searching the array unnecessarily.. + protected int MaxStored = 0; + protected const int MaxStoredIncrement = 5; // Instead of every one. + + protected override bool Receive(T item) + { + var elements = Pool; + var len = elements?.Length ?? 0; + + for (int i = 0; i < len; i++) + { + if (elements[i].TrySave(item)) + { + var m = MaxStored; + if (i >= m) Interlocked.CompareExchange(ref MaxStored, m + MaxStoredIncrement, m); + + return true; + } + } + + return false; + } + + protected override T TryRelease() + { + // We missed getting the first item or it wasn't there. + var elements = Pool; + if (elements == null) return null; + + var len = elements.Length; + for (int i = 0; i < MaxStored && i < len; i++) + { + var item = elements[i].TryRetrieve(); + if (item != null) return item; + } + + return null; + } + + protected override void OnDispose(bool calledExplicitly) + { + Pool = null; + MaxStored = 0; + } + + } + + public static class InterlockedArrayObjectPool + { + public static InterlockedArrayObjectPool Create(Func factory, int capacity = Constants.DEFAULT_CAPACITY) + where T : class + { + return new InterlockedArrayObjectPool(factory, capacity); + } + + public static InterlockedArrayObjectPool Create(int capacity = Constants.DEFAULT_CAPACITY) + where T : class, new() + { + return Create(() => new T(), capacity); + } + } } diff --git a/source/Array/OptimisticArrayObjectPool.cs b/source/Array/OptimisticArrayObjectPool.cs index b2ccce3..675edc2 100644 --- a/source/Array/OptimisticArrayObjectPool.cs +++ b/source/Array/OptimisticArrayObjectPool.cs @@ -1,117 +1,66 @@ /* Based on Roslyn's ObjectPool */ using System; -using System.Diagnostics; using System.Threading; namespace Open.Disposable { - /// - /// An extremely fast ObjectPool when the capacity is in the low 100s. - /// - /// - public class OptimisticArrayObjectPool : ObjectPoolBase - where T : class - { - - public OptimisticArrayObjectPool(Func factory, Action recycler, int capacity = DEFAULT_CAPACITY) - : base(factory, recycler, capacity) - { - AllowPocket = false; - Pool = new Element[capacity - 1]; - } - - public OptimisticArrayObjectPool(Func factory, int capacity = DEFAULT_CAPACITY) - : this(factory, null, capacity) - { - } - - - [DebuggerDisplay("{Value,nq}")] - protected struct Element - { - internal T Value; - } - - T _firstItem; - Element[] Pool; - - protected override bool Receive(T item) - { - // First see if optimisically we can store in _firstItem; - if (_firstItem == null) - { - _firstItem = item; - return true; - } - // Else iterate to find an empty slot. - else - { - var elements = Pool; - var len = elements?.Length ?? 0; - - for (int i = 0; i < len; i++) - { - var e = elements[i]; - // As suggested by Roslyn's implementation, don't worry about interlocking here. It's okay if a few get loose. - if (e.Value == null) - { - e.Value = item; - return true; - } - } - - return false; - } - } - - protected override T TryTakeInternal() - { - T item = _firstItem; - - // First check and see if we actually were able to get the first item. - if (item != null && item == Interlocked.CompareExchange(ref _firstItem, null, item)) - return item; - - // We missed getting the first item or it wasn't there. - var elements = Pool; - var len = elements?.Length ?? 0; - - for (int i = 0; i < len; i++) - { - var e = elements[i]; - item = e.Value; - if (item != null) - { - if (item == Interlocked.CompareExchange(ref e.Value, null, item)) - { - return item; - } - } - } - - return null; - } - - protected override void OnDispose(bool calledExplicitly) - { - Pool = null; - } - - } - - public static class OptimisticArrayObjectPool - { - public static OptimisticArrayObjectPool Create(Func factory, int capacity = Constants.DEFAULT_CAPACITY) - where T : class - { - return new OptimisticArrayObjectPool(factory, capacity); - } - - public static OptimisticArrayObjectPool Create(int capacity = Constants.DEFAULT_CAPACITY) - where T : class, new() - { - return Create(() => new T(), capacity); - } - } + /// + /// An extremely fast ObjectPool when the capacity is in the low 100s. + /// + /// + public class OptimisticArrayObjectPool : InterlockedArrayObjectPool + where T : class + { + + public OptimisticArrayObjectPool(Func factory, Action recycler, int capacity = DEFAULT_CAPACITY) + : base(factory, recycler, capacity) + { } + + public OptimisticArrayObjectPool(Func factory, int capacity = DEFAULT_CAPACITY) + : this(factory, null, capacity) + { } + + // As suggested by Roslyn's implementation, don't worry about interlocking here. It's okay if a few get loose. + protected override bool GiveToPocket(T item) + { + return Pocket.SetIfNull(item); + } + + protected override bool Receive(T item) + { + var elements = Pool; + var len = elements?.Length ?? 0; + + for (int i = 0; i < len; i++) + { + // As suggested by Roslyn's implementation, don't worry about interlocking here. It's okay if a few get loose. + if (elements[i].SetIfNull(item)) + { + var m = MaxStored; + if (i >= m) Interlocked.CompareExchange(ref MaxStored, m + MaxStoredIncrement, m); + + return true; + } + } + + return false; + } + + } + + public static class OptimisticArrayObjectPool + { + public static OptimisticArrayObjectPool Create(Func factory, int capacity = Constants.DEFAULT_CAPACITY) + where T : class + { + return new OptimisticArrayObjectPool(factory, capacity); + } + + public static OptimisticArrayObjectPool Create(int capacity = Constants.DEFAULT_CAPACITY) + where T : class, new() + { + return Create(() => new T(), capacity); + } + } } diff --git a/source/Collection/CollectionWrapperObjectPool.cs b/source/Collection/CollectionWrapperObjectPool.cs index 2b72fd3..f996af6 100644 --- a/source/Collection/CollectionWrapperObjectPool.cs +++ b/source/Collection/CollectionWrapperObjectPool.cs @@ -42,7 +42,7 @@ protected override bool Receive(T item) return false; } - protected override T TryTakeInternal() + protected override T TryRelease() { retry: diff --git a/source/Collection/ConcurrentQueueObjectPool.cs b/source/Collection/ConcurrentQueueObjectPool.cs index 1211877..789c66a 100644 --- a/source/Collection/ConcurrentQueueObjectPool.cs +++ b/source/Collection/ConcurrentQueueObjectPool.cs @@ -33,7 +33,7 @@ protected override bool Receive(T item) return true; } - protected override T TryTakeInternal() + protected override T TryRelease() { var p = Pool; if (p == null) return null; diff --git a/source/Collection/ConcurrentStackObjectPool.cs b/source/Collection/ConcurrentStackObjectPool.cs new file mode 100644 index 0000000..31e3aa4 --- /dev/null +++ b/source/Collection/ConcurrentStackObjectPool.cs @@ -0,0 +1,65 @@ +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Text; + +namespace Open.Disposable +{ + public sealed class ConcurrentStackObjectPool : TrimmableObjectPoolBase + where T : class + { + + public ConcurrentStackObjectPool(Func factory, Action recycler, int capacity = DEFAULT_CAPACITY) + : base(factory, recycler, capacity) + { + Pool = new ConcurrentStack(); + } + + public ConcurrentStackObjectPool(Func factory, int capacity = DEFAULT_CAPACITY) + : this(factory, null, capacity) + { + + } + + ConcurrentStack Pool; + + public override int Count => Pool?.Count ?? 0; + + protected override bool Receive(T item) + { + var p = Pool; + if (p == null) return false; + p.Push(item); // It's possible that the count could exceed MaxSize here, but the risk is negligble as a few over the limit won't hurt. + return true; + } + + protected override T TryRelease() + { + var p = Pool; + if (p == null) return null; + p.TryPop(out T item); + return item; + } + + protected override void OnDispose(bool calledExplicitly) + { + Pool = null; + } + + } + + public static class ConcurrentStackObjectPool + { + public static ConcurrentStackObjectPool Create(Func factory, int capacity = Constants.DEFAULT_CAPACITY) + where T : class + { + return new ConcurrentStackObjectPool(factory, capacity); + } + + public static ConcurrentStackObjectPool Create(int capacity = Constants.DEFAULT_CAPACITY) + where T : class, new() + { + return Create(() => new T(), capacity); + } + } +} diff --git a/source/Collection/QueueObjectPool.cs b/source/Collection/QueueObjectPool.cs index 19e705b..db5e97a 100644 --- a/source/Collection/QueueObjectPool.cs +++ b/source/Collection/QueueObjectPool.cs @@ -38,7 +38,7 @@ protected override bool Receive(T item) return false; } - protected override T TryTakeInternal() + protected override T TryRelease() { var p = Pool; if (p!=null && p.Count != 0) diff --git a/source/Collection/StackObjectPool.cs b/source/Collection/StackObjectPool.cs new file mode 100644 index 0000000..768bd30 --- /dev/null +++ b/source/Collection/StackObjectPool.cs @@ -0,0 +1,77 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Open.Disposable +{ + public sealed class StackObjectPool : TrimmableObjectPoolBase + where T : class + { + + public StackObjectPool(Func factory, Action recycler, int capacity = DEFAULT_CAPACITY) + : base(factory, recycler, capacity) + { + Pool = new Stack(capacity); // Very very slight speed improvment when capacity is set. + } + + public StackObjectPool(Func factory, int capacity = DEFAULT_CAPACITY) + : this(factory, null, capacity) + { + + } + + Stack Pool; + + public override int Count => Pool?.Count ?? 0; + + protected override bool Receive(T item) + { + var p = Pool; + if (p!=null) + { + // It's possible that the count could exceed MaxSize here, but the risk is negligble as a few over the limit won't hurt. + // The lock operation should be quick enough to not pile up too many items. + lock (p) p.Push(item); + return true; + } + + return false; + } + + protected override T TryRelease() + { + var p = Pool; + if (p!=null && p.Count != 0) + { + lock (p) + { + if (p.Count!=0) + return p.Pop(); + } + + } + + return null; + } + + protected override void OnDispose(bool calledExplicitly) + { + Pool = null; + } + } + + public static class StackObjectPool + { + public static StackObjectPool Create(Func factory, int capacity = Constants.DEFAULT_CAPACITY) + where T : class + { + return new StackObjectPool(factory, capacity); + } + + public static StackObjectPool Create(int capacity = Constants.DEFAULT_CAPACITY) + where T : class, new() + { + return Create(() => new T(), capacity); + } + } +} diff --git a/source/ITrimmableObjectPool.cs b/source/ITrimmableObjectPool.cs index 946b73f..902bee9 100644 --- a/source/ITrimmableObjectPool.cs +++ b/source/ITrimmableObjectPool.cs @@ -4,8 +4,8 @@ namespace Open.Disposable { public interface ITrimmableObjectPool { - event ObjectPoolResizeEvent GivenTo; - event ObjectPoolResizeEvent TakenFrom; + event ObjectPoolResizeEvent Received; + event ObjectPoolResizeEvent Released; void TrimTo(int targetSize); } diff --git a/source/ObjectPoolAutoTrimmer.cs b/source/ObjectPoolAutoTrimmer.cs index a1a1bea..308533b 100644 --- a/source/ObjectPoolAutoTrimmer.cs +++ b/source/ObjectPoolAutoTrimmer.cs @@ -40,8 +40,8 @@ public ObjectPoolAutoTrimmer( _trimmer = new ActionRunner(TrimInternal); - pool.GivenTo += Target_GivenTo; - pool.TakenFrom += Target_TakenFrom; + pool.Received += Target_GivenTo; + pool.Released += Target_TakenFrom; if (pool is DisposableBase d) { @@ -78,8 +78,8 @@ protected override void OnDispose(bool calledExplicitly) DisposeOf(ref _trimmer); var target = Nullify(ref _pool); - target.GivenTo -= Target_GivenTo; - target.TakenFrom -= Target_TakenFrom; + target.Received -= Target_GivenTo; + target.Released -= Target_TakenFrom; } } } diff --git a/source/ObjectPoolBase.cs b/source/ObjectPoolBase.cs index 19ee4f2..4c366e0 100644 --- a/source/ObjectPoolBase.cs +++ b/source/ObjectPoolBase.cs @@ -37,7 +37,10 @@ public T Generate() return Factory(); } - protected virtual bool CanReceive => true; + protected ReferenceContainer Pocket; + + #region Receive (.Give(T item)) + protected virtual bool CanReceive => true; protected bool PrepareToReceive(T item) { @@ -57,23 +60,23 @@ protected bool PrepareToReceive(T item) // Contract should be that no item can be null here. protected abstract bool Receive(T item); - protected virtual void OnGivenTo() + protected virtual void OnReceived() { } - protected void OnGivenTo(bool wasGiven) + protected void OnReceived(bool wasGiven) { - if (wasGiven) OnGivenTo(); + if (wasGiven) OnReceived(); } public void Give(T item) { if (PrepareToReceive(item) - && (GaveToPocket(ref item) || Receive(item))) - OnGivenTo(); + && (GiveToPocket(item) || Receive(item))) + OnReceived(); } - protected virtual Task GiveInternalAsync(T item) + protected virtual Task ReceiveAsync(T item) { return Task.Run(() => Receive(item)); } @@ -81,24 +84,27 @@ protected virtual Task GiveInternalAsync(T item) public Task GiveAsync(T item) { // We need to pre-check CanReceive because excessive tasks could build up if not. - if (item == null || !CanReceive) return Task.CompletedTask; + if (item == null || !CanReceive) + return Task.CompletedTask; - return GiveAsyncConditional(item); + return ReceiveConditionalAsync(item); } - async Task GiveAsyncConditional(T item) + async Task ReceiveConditionalAsync(T item) { if(PrepareToReceive(item) - && (GaveToPocket(ref item) || await GiveInternalAsync(item))) - OnGivenTo(); + && (GiveToPocket(item) || await ReceiveAsync(item))) + OnReceived(); } + #endregion - public virtual T Take() + #region Release (.Take()) + public virtual T Take() { return TryTake() ?? Factory(); } - protected virtual Task TakeAsyncInternal() + protected virtual Task ReleaseAsync() { return Task.Run((Func)Take); } @@ -109,7 +115,7 @@ public Task TakeAsync() if (TryTake(out T firstTry)) return Task.FromResult(firstTry); - return TakeAsyncInternal(); + return ReleaseAsync(); } public bool TryTake(out T item) @@ -118,40 +124,35 @@ public bool TryTake(out T item) return item != null; } - protected bool AllowPocket = true; + protected virtual bool GiveToPocket(T item) + { + return Pocket.TrySave(item); + } - protected T Pocket; - protected T TakeFromPocket() - { - if (!AllowPocket || Pocket == null) return null; - return Interlocked.Exchange(ref Pocket, null); - } - - protected bool GaveToPocket(ref T item) - { - if (!AllowPocket || Pocket != null) return false; - item = Interlocked.Exchange(ref Pocket, item); - return item == null; + protected virtual T TakeFromPocket() + { + return Pocket.TryRetrieve(); } - protected abstract T TryTakeInternal(); + protected abstract T TryRelease(); public T TryTake() { - var item = TakeFromPocket() ?? TryTakeInternal(); - if (item != null) OnTakenFrom(); + var item = TakeFromPocket() ?? TryRelease(); + if (item != null) OnReleased(); return item; } - protected virtual void OnTakenFrom() + protected virtual void OnReleased() { } - protected void OnTakenFrom(bool wasTaken) + protected void OnReleased(bool wasTaken) { - if (wasTaken) OnTakenFrom(); + if (wasTaken) OnReleased(); } + #endregion - protected override void OnBeforeDispose() + protected override void OnBeforeDispose() { MaxSize = 0; } diff --git a/source/ReferenceContainer.cs b/source/ReferenceContainer.cs new file mode 100644 index 0000000..0a03e65 --- /dev/null +++ b/source/ReferenceContainer.cs @@ -0,0 +1,39 @@ +using System.Diagnostics; +using System.Threading; + +namespace Open.Disposable +{ + [DebuggerDisplay("{Value,nq}")] + public struct ReferenceContainer + where T : class + { + public T Value; + + public bool SetIfNull(T value) + { + if (Value == null) + { + Value = value; + return true; + } + + return false; + } + + public bool TrySave(T value) + { + return Value == null + && null == Interlocked.CompareExchange(ref Value, value, null); + } + + public T TryRetrieve() + { + var item = Value; + return (item != null + && item == Interlocked.CompareExchange(ref Value, null, item)) + ? item + : null; + } + } + +} diff --git a/source/TrimmableObjectPoolBase.cs b/source/TrimmableObjectPoolBase.cs index 24bee7d..052ad2e 100644 --- a/source/TrimmableObjectPoolBase.cs +++ b/source/TrimmableObjectPoolBase.cs @@ -29,28 +29,28 @@ public TrimmableObjectPoolBase(Func factory, int capacity = DEFAULT_CAPACITY) /// /// Signal for when an item was taken (actually removed) from the pool. /// - public event ObjectPoolResizeEvent TakenFrom; - protected void OnTakenFrom(int newSize) + public event ObjectPoolResizeEvent Released; + protected void OnReleased(int newSize) { - TakenFrom?.Invoke(newSize); + Released?.Invoke(newSize); } - protected override void OnTakenFrom() + protected override void OnReleased() { - OnTakenFrom(Count); + OnReleased(Count); } /// /// Signal for when an item was given (actually accepted) to the pool. /// - public event ObjectPoolResizeEvent GivenTo; - protected void OnGivenTo(int newSize) + public event ObjectPoolResizeEvent Received; + protected void OnReceived(int newSize) { - GivenTo?.Invoke(newSize); + Received?.Invoke(newSize); } - protected override void OnGivenTo() + protected override void OnReceived() { - OnGivenTo(Count); + OnReceived(Count); } public virtual void TrimTo(int targetSize) @@ -64,13 +64,13 @@ public virtual void TrimTo(int targetSize) i < attempts && count > targetSize; i++) { - if (TryTakeInternal() == null) + if (TryRelease() == null) break; count = Count; } - if (i != 0) OnTakenFrom(count); + if (i != 0) OnReleased(count); } } From fb458b4afb9baeaea2355f2459638e21a95d12d6 Mon Sep 17 00:00:00 2001 From: electricessence Date: Thu, 19 Oct 2017 00:32:25 -0700 Subject: [PATCH 03/83] Disabled Pocket use for ConcurrentQueueObjectPool. --- benchmarking/BenchmarkResult.csv | 40 +-- benchmarking/BenchmarkResult.txt | 232 ++++++++---------- source/Array/OptimisticArrayObjectPool.cs | 2 +- .../Collection/ConcurrentQueueObjectPool.cs | 19 +- source/ObjectPoolBase.cs | 6 +- source/ReferenceContainer.cs | 57 ++++- 6 files changed, 187 insertions(+), 169 deletions(-) diff --git a/benchmarking/BenchmarkResult.csv b/benchmarking/BenchmarkResult.csv index ce1e0f3..3cb0582 100644 --- a/benchmarking/BenchmarkResult.csv +++ b/benchmarking/BenchmarkResult.csv @@ -1,24 +1,24 @@ Batch,Pool Type," From Empty (In Parallel)"," To (In Parallel)",d Read/Write (In Parallel),y Pool (.TryTake()),L, -Repeat 200000 for size 4,QueueObjectPool,00:00:01.1287292,00:00:01.0294513,00:00:01.0260755,00:00:00.1590331,00:00:03.3432891, -Repeat 200000 for size 4,ConcurrentQueueObjectPool,00:00:01.1514246,00:00:01.0375667,00:00:01.0105712,00:00:00.1741179,00:00:03.3736804, -Repeat 200000 for size 4,ConcurrentStackObjectPool,00:00:01.0247996,00:00:00.9655175,00:00:00.9336741,00:00:00.1360451,00:00:03.0600363, -Repeat 200000 for size 4,OptimisticArrayObjectPool,00:00:01.0468743,00:00:00.8456746,00:00:00.8382048,00:00:00.1628778,00:00:02.8936315, -Repeat 80000 for size 10,QueueObjectPool,00:00:00.5433300,00:00:00.5461789,00:00:00.4759666,00:00:00.1333091,00:00:01.6987846, -Repeat 80000 for size 10,ConcurrentQueueObjectPool,00:00:00.5099808,00:00:00.4864080,00:00:00.5001707,00:00:00.1520913,00:00:01.6486508, -Repeat 80000 for size 10,ConcurrentStackObjectPool,00:00:00.5763228,00:00:00.5440804,00:00:00.5287139,00:00:00.1281674,00:00:01.7772845, -Repeat 80000 for size 10,OptimisticArrayObjectPool,00:00:00.6038049,00:00:00.5263031,00:00:00.4762357,00:00:00.2268198,00:00:01.8331635, -Repeat 24000 for size 50,QueueObjectPool,00:00:00.2580179,00:00:00.4086527,00:00:00.2688232,00:00:00.1734043,00:00:01.1088981, -Repeat 24000 for size 50,ConcurrentQueueObjectPool,00:00:00.2592864,00:00:00.2647728,00:00:00.2407817,00:00:00.2021228,00:00:00.9669637, -Repeat 24000 for size 50,ConcurrentStackObjectPool,00:00:00.2676966,00:00:00.3135490,00:00:00.2759068,00:00:00.2042573,00:00:01.0614097, -Repeat 24000 for size 50,OptimisticArrayObjectPool,00:00:00.3239524,00:00:00.3545534,00:00:00.2256836,00:00:00.8061559,00:00:01.7103453, -Repeat 16000 for size 100,QueueObjectPool,00:00:00.2503055,00:00:00.4926017,00:00:00.3241812,00:00:00.2220571,00:00:01.2891455, -Repeat 16000 for size 100,ConcurrentQueueObjectPool,00:00:00.2482971,00:00:00.2783954,00:00:00.2356915,00:00:00.2691699,00:00:01.0315539, -Repeat 16000 for size 100,ConcurrentStackObjectPool,00:00:00.2590476,00:00:00.3642821,00:00:00.3237909,00:00:00.3202590,00:00:01.2673796, -Repeat 16000 for size 100,OptimisticArrayObjectPool,00:00:00.3215564,00:00:00.4745108,00:00:00.1784334,00:00:01.5867222,00:00:02.5612228, -Repeat 25600 for size 250,QueueObjectPool,00:00:00.7252826,00:00:01.7557360,00:00:01.1939824,00:00:00.8119776,00:00:04.4869786, -Repeat 25600 for size 250,ConcurrentQueueObjectPool,00:00:00.7722675,00:00:00.9039538,00:00:00.7259809,00:00:01.0339628,00:00:03.4361650, -Repeat 25600 for size 250,ConcurrentStackObjectPool,00:00:00.7369296,00:00:01.5097271,00:00:01.5271033,00:00:01.7198474,00:00:05.4936074, -Repeat 25600 for size 250,OptimisticArrayObjectPool,00:00:01.2872667,00:00:03.1949158,00:00:00.5262142,00:00:11.7748329,00:00:16.7832296, +Repeat 200000 for size 4,QueueObjectPool,00:00:01.0947249,00:00:00.9968247,00:00:00.9656608,00:00:00.1521333,00:00:03.2093437, +Repeat 200000 for size 4,ConcurrentQueueObjectPool,00:00:01.1280539,00:00:01.0784355,00:00:01.0181790,00:00:00.1777503,00:00:03.4024187, +Repeat 200000 for size 4,ConcurrentStackObjectPool,00:00:01.1002830,00:00:01.0578873,00:00:00.9720418,00:00:00.1441359,00:00:03.2743480, +Repeat 200000 for size 4,OptimisticArrayObjectPool,00:00:01.0957806,00:00:00.9225542,00:00:00.8814923,00:00:00.1739174,00:00:03.0737445, +Repeat 80000 for size 10,QueueObjectPool,00:00:00.5346130,00:00:00.5352514,00:00:00.4743420,00:00:00.1366686,00:00:01.6808750, +Repeat 80000 for size 10,ConcurrentQueueObjectPool,00:00:00.5718558,00:00:00.5354731,00:00:00.5125773,00:00:00.1602205,00:00:01.7801267, +Repeat 80000 for size 10,ConcurrentStackObjectPool,00:00:00.5552188,00:00:00.5154776,00:00:00.4801922,00:00:00.1249209,00:00:01.6758095, +Repeat 80000 for size 10,OptimisticArrayObjectPool,00:00:00.5270367,00:00:00.4550340,00:00:00.4407644,00:00:00.2186592,00:00:01.6414943, +Repeat 24000 for size 50,QueueObjectPool,00:00:00.2622830,00:00:00.4367957,00:00:00.2884833,00:00:00.1834193,00:00:01.1709813, +Repeat 24000 for size 50,ConcurrentQueueObjectPool,00:00:00.2719027,00:00:00.2854846,00:00:00.2650888,00:00:00.2203011,00:00:01.0427772, +Repeat 24000 for size 50,ConcurrentStackObjectPool,00:00:00.2640431,00:00:00.3351598,00:00:00.2996782,00:00:00.2149665,00:00:01.1138476, +Repeat 24000 for size 50,OptimisticArrayObjectPool,00:00:00.3047471,00:00:00.3823836,00:00:00.2115898,00:00:00.8433052,00:00:01.7420257, +Repeat 16000 for size 100,QueueObjectPool,00:00:00.2680744,00:00:00.4845459,00:00:00.3117290,00:00:00.2375035,00:00:01.3018528, +Repeat 16000 for size 100,ConcurrentQueueObjectPool,00:00:00.2620082,00:00:00.2949254,00:00:00.2538674,00:00:00.2908684,00:00:01.1016694, +Repeat 16000 for size 100,ConcurrentStackObjectPool,00:00:00.2735071,00:00:00.3903024,00:00:00.3434028,00:00:00.3366711,00:00:01.3438834, +Repeat 16000 for size 100,OptimisticArrayObjectPool,00:00:00.7922812,00:00:00.6641963,00:00:00.2030016,00:00:01.7548815,00:00:03.4143606, +Repeat 25600 for size 250,QueueObjectPool,00:00:00.7668417,00:00:01.8047414,00:00:01.3185354,00:00:00.8664007,00:00:04.7565192, +Repeat 25600 for size 250,ConcurrentQueueObjectPool,00:00:00.8378978,00:00:00.9370109,00:00:00.7695238,00:00:01.0984692,00:00:03.6429017, +Repeat 25600 for size 250,ConcurrentStackObjectPool,00:00:00.7848982,00:00:01.5942377,00:00:01.5995650,00:00:01.7959374,00:00:05.7746383, +Repeat 25600 for size 250,OptimisticArrayObjectPool,00:00:01.2561773,00:00:03.2871367,00:00:00.5424299,00:00:12.0158755,00:00:17.1016194, Repeat 14400 for size 500,QueueObjectPool,00:00:00.7797824,00:00:01.6503887,00:00:01.3768657,00:00:00.8861150,00:00:04.6931518, Repeat 14400 for size 500,ConcurrentQueueObjectPool,00:00:00.7994849,00:00:00.9658497,00:00:00.7778244,00:00:01.1149906,00:00:03.6581496, Repeat 14400 for size 500,ConcurrentStackObjectPool,00:00:00.7487525,00:00:02.3665267,00:00:02.6608263,00:00:03.0226290,00:00:08.7987345, diff --git a/benchmarking/BenchmarkResult.txt b/benchmarking/BenchmarkResult.txt index 787e0eb..52fded2 100644 --- a/benchmarking/BenchmarkResult.txt +++ b/benchmarking/BenchmarkResult.txt @@ -2,191 +2,159 @@ Repeat 200000 for size 4 ------------------------------------ QueueObjectPool......................................... -00:00:01.1287292 Take From Empty (In Parallel) -00:00:01.0294513 Give To (In Parallel) -00:00:01.0260755 Mixed Read/Write (In Parallel) -00:00:00.1590331 Empty Pool (.TryTake()) -00:00:03.3432891 TOTAL +00:00:01.0947249 Take From Empty (In Parallel) +00:00:00.9968247 Give To (In Parallel) +00:00:00.9656608 Mixed Read/Write (In Parallel) +00:00:00.1521333 Empty Pool (.TryTake()) +00:00:03.2093437 TOTAL ConcurrentQueueObjectPool............................... -00:00:01.1514246 Take From Empty (In Parallel) -00:00:01.0375667 Give To (In Parallel) -00:00:01.0105712 Mixed Read/Write (In Parallel) -00:00:00.1741179 Empty Pool (.TryTake()) -00:00:03.3736804 TOTAL +00:00:01.1280539 Take From Empty (In Parallel) +00:00:01.0784355 Give To (In Parallel) +00:00:01.0181790 Mixed Read/Write (In Parallel) +00:00:00.1777503 Empty Pool (.TryTake()) +00:00:03.4024187 TOTAL ConcurrentStackObjectPool............................... -00:00:01.0247996 Take From Empty (In Parallel) -00:00:00.9655175 Give To (In Parallel) -00:00:00.9336741 Mixed Read/Write (In Parallel) -00:00:00.1360451 Empty Pool (.TryTake()) -00:00:03.0600363 TOTAL +00:00:01.1002830 Take From Empty (In Parallel) +00:00:01.0578873 Give To (In Parallel) +00:00:00.9720418 Mixed Read/Write (In Parallel) +00:00:00.1441359 Empty Pool (.TryTake()) +00:00:03.2743480 TOTAL OptimisticArrayObjectPool............................... -00:00:01.0468743 Take From Empty (In Parallel) -00:00:00.8456746 Give To (In Parallel) -00:00:00.8382048 Mixed Read/Write (In Parallel) -00:00:00.1628778 Empty Pool (.TryTake()) -00:00:02.8936315 TOTAL +00:00:01.0957806 Take From Empty (In Parallel) +00:00:00.9225542 Give To (In Parallel) +00:00:00.8814923 Mixed Read/Write (In Parallel) +00:00:00.1739174 Empty Pool (.TryTake()) +00:00:03.0737445 TOTAL Repeat 80000 for size 10 ------------------------------------ QueueObjectPool......................................... -00:00:00.5433300 Take From Empty (In Parallel) -00:00:00.5461789 Give To (In Parallel) -00:00:00.4759666 Mixed Read/Write (In Parallel) -00:00:00.1333091 Empty Pool (.TryTake()) -00:00:01.6987846 TOTAL +00:00:00.5346130 Take From Empty (In Parallel) +00:00:00.5352514 Give To (In Parallel) +00:00:00.4743420 Mixed Read/Write (In Parallel) +00:00:00.1366686 Empty Pool (.TryTake()) +00:00:01.6808750 TOTAL ConcurrentQueueObjectPool............................... -00:00:00.5099808 Take From Empty (In Parallel) -00:00:00.4864080 Give To (In Parallel) -00:00:00.5001707 Mixed Read/Write (In Parallel) -00:00:00.1520913 Empty Pool (.TryTake()) -00:00:01.6486508 TOTAL +00:00:00.5718558 Take From Empty (In Parallel) +00:00:00.5354731 Give To (In Parallel) +00:00:00.5125773 Mixed Read/Write (In Parallel) +00:00:00.1602205 Empty Pool (.TryTake()) +00:00:01.7801267 TOTAL ConcurrentStackObjectPool............................... -00:00:00.5763228 Take From Empty (In Parallel) -00:00:00.5440804 Give To (In Parallel) -00:00:00.5287139 Mixed Read/Write (In Parallel) -00:00:00.1281674 Empty Pool (.TryTake()) -00:00:01.7772845 TOTAL +00:00:00.5552188 Take From Empty (In Parallel) +00:00:00.5154776 Give To (In Parallel) +00:00:00.4801922 Mixed Read/Write (In Parallel) +00:00:00.1249209 Empty Pool (.TryTake()) +00:00:01.6758095 TOTAL OptimisticArrayObjectPool............................... -00:00:00.6038049 Take From Empty (In Parallel) -00:00:00.5263031 Give To (In Parallel) -00:00:00.4762357 Mixed Read/Write (In Parallel) -00:00:00.2268198 Empty Pool (.TryTake()) -00:00:01.8331635 TOTAL +00:00:00.5270367 Take From Empty (In Parallel) +00:00:00.4550340 Give To (In Parallel) +00:00:00.4407644 Mixed Read/Write (In Parallel) +00:00:00.2186592 Empty Pool (.TryTake()) +00:00:01.6414943 TOTAL Repeat 24000 for size 50 ------------------------------------ QueueObjectPool......................................... -00:00:00.2580179 Take From Empty (In Parallel) -00:00:00.4086527 Give To (In Parallel) -00:00:00.2688232 Mixed Read/Write (In Parallel) -00:00:00.1734043 Empty Pool (.TryTake()) -00:00:01.1088981 TOTAL +00:00:00.2622830 Take From Empty (In Parallel) +00:00:00.4367957 Give To (In Parallel) +00:00:00.2884833 Mixed Read/Write (In Parallel) +00:00:00.1834193 Empty Pool (.TryTake()) +00:00:01.1709813 TOTAL ConcurrentQueueObjectPool............................... -00:00:00.2592864 Take From Empty (In Parallel) -00:00:00.2647728 Give To (In Parallel) -00:00:00.2407817 Mixed Read/Write (In Parallel) -00:00:00.2021228 Empty Pool (.TryTake()) -00:00:00.9669637 TOTAL +00:00:00.2719027 Take From Empty (In Parallel) +00:00:00.2854846 Give To (In Parallel) +00:00:00.2650888 Mixed Read/Write (In Parallel) +00:00:00.2203011 Empty Pool (.TryTake()) +00:00:01.0427772 TOTAL ConcurrentStackObjectPool............................... -00:00:00.2676966 Take From Empty (In Parallel) -00:00:00.3135490 Give To (In Parallel) -00:00:00.2759068 Mixed Read/Write (In Parallel) -00:00:00.2042573 Empty Pool (.TryTake()) -00:00:01.0614097 TOTAL +00:00:00.2640431 Take From Empty (In Parallel) +00:00:00.3351598 Give To (In Parallel) +00:00:00.2996782 Mixed Read/Write (In Parallel) +00:00:00.2149665 Empty Pool (.TryTake()) +00:00:01.1138476 TOTAL OptimisticArrayObjectPool............................... -00:00:00.3239524 Take From Empty (In Parallel) -00:00:00.3545534 Give To (In Parallel) -00:00:00.2256836 Mixed Read/Write (In Parallel) -00:00:00.8061559 Empty Pool (.TryTake()) -00:00:01.7103453 TOTAL +00:00:00.3047471 Take From Empty (In Parallel) +00:00:00.3823836 Give To (In Parallel) +00:00:00.2115898 Mixed Read/Write (In Parallel) +00:00:00.8433052 Empty Pool (.TryTake()) +00:00:01.7420257 TOTAL Repeat 16000 for size 100 ------------------------------------ QueueObjectPool......................................... -00:00:00.2503055 Take From Empty (In Parallel) -00:00:00.4926017 Give To (In Parallel) -00:00:00.3241812 Mixed Read/Write (In Parallel) -00:00:00.2220571 Empty Pool (.TryTake()) -00:00:01.2891455 TOTAL +00:00:00.2680744 Take From Empty (In Parallel) +00:00:00.4845459 Give To (In Parallel) +00:00:00.3117290 Mixed Read/Write (In Parallel) +00:00:00.2375035 Empty Pool (.TryTake()) +00:00:01.3018528 TOTAL ConcurrentQueueObjectPool............................... -00:00:00.2482971 Take From Empty (In Parallel) -00:00:00.2783954 Give To (In Parallel) -00:00:00.2356915 Mixed Read/Write (In Parallel) -00:00:00.2691699 Empty Pool (.TryTake()) -00:00:01.0315539 TOTAL +00:00:00.2620082 Take From Empty (In Parallel) +00:00:00.2949254 Give To (In Parallel) +00:00:00.2538674 Mixed Read/Write (In Parallel) +00:00:00.2908684 Empty Pool (.TryTake()) +00:00:01.1016694 TOTAL ConcurrentStackObjectPool............................... -00:00:00.2590476 Take From Empty (In Parallel) -00:00:00.3642821 Give To (In Parallel) -00:00:00.3237909 Mixed Read/Write (In Parallel) -00:00:00.3202590 Empty Pool (.TryTake()) -00:00:01.2673796 TOTAL +00:00:00.2735071 Take From Empty (In Parallel) +00:00:00.3903024 Give To (In Parallel) +00:00:00.3434028 Mixed Read/Write (In Parallel) +00:00:00.3366711 Empty Pool (.TryTake()) +00:00:01.3438834 TOTAL OptimisticArrayObjectPool............................... -00:00:00.3215564 Take From Empty (In Parallel) -00:00:00.4745108 Give To (In Parallel) -00:00:00.1784334 Mixed Read/Write (In Parallel) -00:00:01.5867222 Empty Pool (.TryTake()) -00:00:02.5612228 TOTAL +00:00:00.7922812 Take From Empty (In Parallel) +00:00:00.6641963 Give To (In Parallel) +00:00:00.2030016 Mixed Read/Write (In Parallel) +00:00:01.7548815 Empty Pool (.TryTake()) +00:00:03.4143606 TOTAL Repeat 25600 for size 250 ------------------------------------ QueueObjectPool......................................... -00:00:00.7252826 Take From Empty (In Parallel) -00:00:01.7557360 Give To (In Parallel) -00:00:01.1939824 Mixed Read/Write (In Parallel) -00:00:00.8119776 Empty Pool (.TryTake()) -00:00:04.4869786 TOTAL +00:00:00.7668417 Take From Empty (In Parallel) +00:00:01.8047414 Give To (In Parallel) +00:00:01.3185354 Mixed Read/Write (In Parallel) +00:00:00.8664007 Empty Pool (.TryTake()) +00:00:04.7565192 TOTAL ConcurrentQueueObjectPool............................... -00:00:00.7722675 Take From Empty (In Parallel) -00:00:00.9039538 Give To (In Parallel) -00:00:00.7259809 Mixed Read/Write (In Parallel) -00:00:01.0339628 Empty Pool (.TryTake()) -00:00:03.4361650 TOTAL +00:00:00.8378978 Take From Empty (In Parallel) +00:00:00.9370109 Give To (In Parallel) +00:00:00.7695238 Mixed Read/Write (In Parallel) +00:00:01.0984692 Empty Pool (.TryTake()) +00:00:03.6429017 TOTAL ConcurrentStackObjectPool............................... -00:00:00.7369296 Take From Empty (In Parallel) -00:00:01.5097271 Give To (In Parallel) -00:00:01.5271033 Mixed Read/Write (In Parallel) -00:00:01.7198474 Empty Pool (.TryTake()) -00:00:05.4936074 TOTAL +00:00:00.7848982 Take From Empty (In Parallel) +00:00:01.5942377 Give To (In Parallel) +00:00:01.5995650 Mixed Read/Write (In Parallel) +00:00:01.7959374 Empty Pool (.TryTake()) +00:00:05.7746383 TOTAL OptimisticArrayObjectPool............................... -00:00:01.2872667 Take From Empty (In Parallel) -00:00:03.1949158 Give To (In Parallel) -00:00:00.5262142 Mixed Read/Write (In Parallel) -00:00:11.7748329 Empty Pool (.TryTake()) -00:00:16.7832296 TOTAL - - -Repeat 14400 for size 500 ------------------------------------- - -QueueObjectPool......................................... -00:00:00.7797824 Take From Empty (In Parallel) -00:00:01.6503887 Give To (In Parallel) -00:00:01.3768657 Mixed Read/Write (In Parallel) -00:00:00.8861150 Empty Pool (.TryTake()) -00:00:04.6931518 TOTAL - -ConcurrentQueueObjectPool............................... -00:00:00.7994849 Take From Empty (In Parallel) -00:00:00.9658497 Give To (In Parallel) -00:00:00.7778244 Mixed Read/Write (In Parallel) -00:00:01.1149906 Empty Pool (.TryTake()) -00:00:03.6581496 TOTAL - -ConcurrentStackObjectPool............................... -00:00:00.7487525 Take From Empty (In Parallel) -00:00:02.3665267 Give To (In Parallel) -00:00:02.6608263 Mixed Read/Write (In Parallel) -00:00:03.0226290 Empty Pool (.TryTake()) -00:00:08.7987345 TOTAL - -OptimisticArrayObjectPool............................... -00:00:01.0892049 Take From Empty (In Parallel) -00:00:05.8599218 Give To (In Parallel) -00:00:00.5797098 Mixed Read/Write (In Parallel) -00:00:22.9215261 Empty Pool (.TryTake()) -00:00:30.4503626 TOTAL +00:00:01.2561773 Take From Empty (In Parallel) +00:00:03.2871367 Give To (In Parallel) +00:00:00.5424299 Mixed Read/Write (In Parallel) +00:00:12.0158755 Empty Pool (.TryTake()) +00:00:17.1016194 TOTAL diff --git a/source/Array/OptimisticArrayObjectPool.cs b/source/Array/OptimisticArrayObjectPool.cs index 675edc2..798ea64 100644 --- a/source/Array/OptimisticArrayObjectPool.cs +++ b/source/Array/OptimisticArrayObjectPool.cs @@ -22,7 +22,7 @@ public OptimisticArrayObjectPool(Func factory, int capacity = DEFAULT_CAPACIT { } // As suggested by Roslyn's implementation, don't worry about interlocking here. It's okay if a few get loose. - protected override bool GiveToPocket(T item) + protected override bool SaveToPocket(T item) { return Pocket.SetIfNull(item); } diff --git a/source/Collection/ConcurrentQueueObjectPool.cs b/source/Collection/ConcurrentQueueObjectPool.cs index 789c66a..6d04309 100644 --- a/source/Collection/ConcurrentQueueObjectPool.cs +++ b/source/Collection/ConcurrentQueueObjectPool.cs @@ -1,11 +1,9 @@ using System; using System.Collections.Concurrent; -using System.Collections.Generic; -using System.Text; namespace Open.Disposable { - public sealed class ConcurrentQueueObjectPool : TrimmableObjectPoolBase + public sealed class ConcurrentQueueObjectPool : TrimmableObjectPoolBase where T : class { @@ -24,8 +22,19 @@ public ConcurrentQueueObjectPool(Func factory, int capacity = DEFAULT_CAPACIT ConcurrentQueue Pool; public override int Count => Pool?.Count ?? 0; - - protected override bool Receive(T item) + + // For ConcurrentQueue disable using the Pocket. It's fast enough as it is... + protected override bool SaveToPocket(T item) + { + return false; + } + + protected override T TakeFromPocket() + { + return null; + } + + protected override bool Receive(T item) { var p = Pool; if (p == null) return false; diff --git a/source/ObjectPoolBase.cs b/source/ObjectPoolBase.cs index 4c366e0..ae04a1d 100644 --- a/source/ObjectPoolBase.cs +++ b/source/ObjectPoolBase.cs @@ -72,7 +72,7 @@ protected void OnReceived(bool wasGiven) public void Give(T item) { if (PrepareToReceive(item) - && (GiveToPocket(item) || Receive(item))) + && (SaveToPocket(item) || Receive(item))) OnReceived(); } @@ -93,7 +93,7 @@ public Task GiveAsync(T item) async Task ReceiveConditionalAsync(T item) { if(PrepareToReceive(item) - && (GiveToPocket(item) || await ReceiveAsync(item))) + && (SaveToPocket(item) || await ReceiveAsync(item))) OnReceived(); } #endregion @@ -124,7 +124,7 @@ public bool TryTake(out T item) return item != null; } - protected virtual bool GiveToPocket(T item) + protected virtual bool SaveToPocket(T item) { return Pocket.TrySave(item); } diff --git a/source/ReferenceContainer.cs b/source/ReferenceContainer.cs index 0a03e65..dcb1be0 100644 --- a/source/ReferenceContainer.cs +++ b/source/ReferenceContainer.cs @@ -3,17 +3,34 @@ namespace Open.Disposable { + public interface IReferenceContainer + where T : class + { + int Capacity { get; } + bool SetIfNull(T value); + bool TrySave(T value); + T TryRetrieve(); + } + [DebuggerDisplay("{Value,nq}")] - public struct ReferenceContainer + public struct ReferenceContainer : IReferenceContainer where T : class { - public T Value; + public int Capacity => 1; + + T _value; + + public T Value + { + get => _value; + set => _value = value; + } public bool SetIfNull(T value) { - if (Value == null) + if (_value == null) { - Value = value; + _value = value; return true; } @@ -22,18 +39,42 @@ public bool SetIfNull(T value) public bool TrySave(T value) { - return Value == null - && null == Interlocked.CompareExchange(ref Value, value, null); + return _value == null + && null == Interlocked.CompareExchange(ref _value, value, null); } public T TryRetrieve() { - var item = Value; + var item = _value; return (item != null - && item == Interlocked.CompareExchange(ref Value, null, item)) + && item == Interlocked.CompareExchange(ref _value, null, item)) ? item : null; } } + public struct DualReferenceContainer : IReferenceContainer + where T : class + { + public int Capacity => 2; + + ReferenceContainer _1; + ReferenceContainer _2; + + public bool SetIfNull(T value) + { + return _1.SetIfNull(value) || _2.SetIfNull(value); + } + + public T TryRetrieve() + { + return _1.TryRetrieve() ?? _2.TryRetrieve(); + } + + public bool TrySave(T value) + { + return _1.TrySave(value) || _2.TrySave(value); + } + } + } From 0b8eb4e39ce5295d39068848b6ee7f37be99f850 Mon Sep 17 00:00:00 2001 From: electricessence Date: Thu, 19 Oct 2017 00:37:22 -0700 Subject: [PATCH 04/83] Updated version. --- source/Open.Disposable.ObjectPools.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/Open.Disposable.ObjectPools.csproj b/source/Open.Disposable.ObjectPools.csproj index ae9f78a..bc67087 100644 --- a/source/Open.Disposable.ObjectPools.csproj +++ b/source/Open.Disposable.ObjectPools.csproj @@ -16,7 +16,7 @@ Part of the "Open" set of libraries. git objectpool, dotnet, dotnetcore, cs, idisposable, threadsafe, thread-safe True - 1.0.4 + 1.1.0 1.1.0.0 1.1.0.0 Removed pools that are simply too slow for real world use and migrated to separate branch since they are simply not worth From 804642a2416864de8917b81b63be0aa065bb75e3 Mon Sep 17 00:00:00 2001 From: "Oren (electricessence)" Date: Thu, 19 Oct 2017 11:18:55 -0700 Subject: [PATCH 05/83] Added ConfigureAwait(false) where useful. --- source/ObjectPoolBase.cs | 2 +- source/Recycler.cs | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/source/ObjectPoolBase.cs b/source/ObjectPoolBase.cs index ae04a1d..0a92fe7 100644 --- a/source/ObjectPoolBase.cs +++ b/source/ObjectPoolBase.cs @@ -93,7 +93,7 @@ public Task GiveAsync(T item) async Task ReceiveConditionalAsync(T item) { if(PrepareToReceive(item) - && (SaveToPocket(item) || await ReceiveAsync(item))) + && (SaveToPocket(item) || await ReceiveAsync(item).ConfigureAwait(false))) // Doesn't need original context. Just needs to trigger OnReceived(). OnReceived(); } #endregion diff --git a/source/Recycler.cs b/source/Recycler.cs index 115b0b0..a12b2ca 100644 --- a/source/Recycler.cs +++ b/source/Recycler.cs @@ -12,6 +12,9 @@ public class Recycler : DisposableBase where T : class { IObjectPool _target; + + // Something else could be used and could be more performant. + // But it's an ideal interface for what's needed. And the point is that the recyler should not take up too much cpu time. ActionBlock _bin; public Recycler( From c33353b2ded5504363a297a58dc972bce4e746ad Mon Sep 17 00:00:00 2001 From: electricessence Date: Thu, 19 Oct 2017 13:50:02 -0700 Subject: [PATCH 06/83] Fixed indentation --- .../Collection/ConcurrentQueueObjectPool.cs | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/source/Collection/ConcurrentQueueObjectPool.cs b/source/Collection/ConcurrentQueueObjectPool.cs index 6d04309..387dab0 100644 --- a/source/Collection/ConcurrentQueueObjectPool.cs +++ b/source/Collection/ConcurrentQueueObjectPool.cs @@ -7,21 +7,21 @@ public sealed class ConcurrentQueueObjectPool : TrimmableObjectPoolBase where T : class { - public ConcurrentQueueObjectPool(Func factory, Action recycler, int capacity = DEFAULT_CAPACITY) - : base(factory, recycler, capacity) - { - Pool = new ConcurrentQueue(); - } + public ConcurrentQueueObjectPool(Func factory, Action recycler, int capacity = DEFAULT_CAPACITY) + : base(factory, recycler, capacity) + { + Pool = new ConcurrentQueue(); + } - public ConcurrentQueueObjectPool(Func factory, int capacity = DEFAULT_CAPACITY) - : this(factory, null, capacity) - { - - } + public ConcurrentQueueObjectPool(Func factory, int capacity = DEFAULT_CAPACITY) + : this(factory, null, capacity) + { + + } - ConcurrentQueue Pool; + ConcurrentQueue Pool; - public override int Count => Pool?.Count ?? 0; + public override int Count => Pool?.Count ?? 0; // For ConcurrentQueue disable using the Pocket. It's fast enough as it is... protected override bool SaveToPocket(T item) From 02714c7ae6d5146383c79df62b4f85d2dc394bb3 Mon Sep 17 00:00:00 2001 From: electricessence Date: Fri, 20 Oct 2017 16:18:32 -0700 Subject: [PATCH 07/83] IRecyclable Added IRecylable interface since it's much easier to internally define what is required for recycling. Added static methods to accomodate. Redefined Recycler to only be created by an extension of IObjectPool. --- source/Array/InterlockedArrayObjectPool.cs | 15 +++ source/Array/OptimisticArrayObjectPool.cs | 15 +++ .../Collection/ConcurrentQueueObjectPool.cs | 115 ++++++++++-------- .../Collection/ConcurrentStackObjectPool.cs | 17 ++- source/Collection/QueueObjectPool.cs | 17 ++- source/Collection/StackObjectPool.cs | 17 ++- source/Constants.cs | 2 +- source/IRecyclable.cs | 11 ++ source/Open.Disposable.ObjectPools.csproj | 6 +- source/Recycler.cs | 41 ++++++- 10 files changed, 197 insertions(+), 59 deletions(-) create mode 100644 source/IRecyclable.cs diff --git a/source/Array/InterlockedArrayObjectPool.cs b/source/Array/InterlockedArrayObjectPool.cs index fbed78f..b52a5d1 100644 --- a/source/Array/InterlockedArrayObjectPool.cs +++ b/source/Array/InterlockedArrayObjectPool.cs @@ -85,5 +85,20 @@ public static InterlockedArrayObjectPool Create(int capacity = Constants.D { return Create(() => new T(), capacity); } + + public static InterlockedArrayObjectPool Create(Func factory, bool autoRecycle, int capacity = Constants.DEFAULT_CAPACITY) + where T : class, IRecyclable + { + Action recycler = null; + if (autoRecycle) recycler = Recycler.Recycle; + return new InterlockedArrayObjectPool(factory, recycler, capacity); + } + + public static InterlockedArrayObjectPool Create(bool autoRecycle, int capacity = Constants.DEFAULT_CAPACITY) + where T : class, IRecyclable, new() + { + return Create(() => new T(), autoRecycle, capacity); + } + } } diff --git a/source/Array/OptimisticArrayObjectPool.cs b/source/Array/OptimisticArrayObjectPool.cs index 798ea64..a0c6c4e 100644 --- a/source/Array/OptimisticArrayObjectPool.cs +++ b/source/Array/OptimisticArrayObjectPool.cs @@ -62,5 +62,20 @@ public static OptimisticArrayObjectPool Create(int capacity = Constants.DE { return Create(() => new T(), capacity); } + + public static OptimisticArrayObjectPool Create(Func factory, bool autoRecycle, int capacity = Constants.DEFAULT_CAPACITY) + where T : class, IRecyclable + { + Action recycler = null; + if (autoRecycle) recycler = Recycler.Recycle; + return new OptimisticArrayObjectPool(factory, recycler, capacity); + } + + public static OptimisticArrayObjectPool Create(bool autoRecycle, int capacity = Constants.DEFAULT_CAPACITY) + where T : class, IRecyclable, new() + { + return Create(() => new T(), autoRecycle, capacity); + } + } } diff --git a/source/Collection/ConcurrentQueueObjectPool.cs b/source/Collection/ConcurrentQueueObjectPool.cs index 6d04309..d29ae40 100644 --- a/source/Collection/ConcurrentQueueObjectPool.cs +++ b/source/Collection/ConcurrentQueueObjectPool.cs @@ -4,24 +4,24 @@ namespace Open.Disposable { public sealed class ConcurrentQueueObjectPool : TrimmableObjectPoolBase - where T : class - { + where T : class + { - public ConcurrentQueueObjectPool(Func factory, Action recycler, int capacity = DEFAULT_CAPACITY) - : base(factory, recycler, capacity) - { - Pool = new ConcurrentQueue(); - } + public ConcurrentQueueObjectPool(Func factory, Action recycler, int capacity = DEFAULT_CAPACITY) + : base(factory, recycler, capacity) + { + Pool = new ConcurrentQueue(); + } - public ConcurrentQueueObjectPool(Func factory, int capacity = DEFAULT_CAPACITY) - : this(factory, null, capacity) - { - - } + public ConcurrentQueueObjectPool(Func factory, int capacity = DEFAULT_CAPACITY) + : this(factory, null, capacity) + { - ConcurrentQueue Pool; + } - public override int Count => Pool?.Count ?? 0; + ConcurrentQueue Pool; + + public override int Count => Pool?.Count ?? 0; // For ConcurrentQueue disable using the Pocket. It's fast enough as it is... protected override bool SaveToPocket(T item) @@ -35,40 +35,55 @@ protected override T TakeFromPocket() } protected override bool Receive(T item) - { - var p = Pool; - if (p == null) return false; - p.Enqueue(item); // It's possible that the count could exceed MaxSize here, but the risk is negligble as a few over the limit won't hurt. - return true; - } - - protected override T TryRelease() - { - var p = Pool; - if (p == null) return null; - p.TryDequeue(out T item); - return item; - } - - protected override void OnDispose(bool calledExplicitly) - { - Pool = null; - } - - } - - public static class ConcurrentQueueObjectPool - { - public static ConcurrentQueueObjectPool Create(Func factory, int capacity = Constants.DEFAULT_CAPACITY) - where T : class - { - return new ConcurrentQueueObjectPool(factory, capacity); - } - - public static ConcurrentQueueObjectPool Create(int capacity = Constants.DEFAULT_CAPACITY) - where T : class, new() - { - return Create(() => new T(), capacity); - } - } + { + var p = Pool; + if (p == null) return false; + p.Enqueue(item); // It's possible that the count could exceed MaxSize here, but the risk is negligble as a few over the limit won't hurt. + return true; + } + + protected override T TryRelease() + { + var p = Pool; + if (p == null) return null; + p.TryDequeue(out T item); + return item; + } + + protected override void OnDispose(bool calledExplicitly) + { + Pool = null; + } + + } + + public static class ConcurrentQueueObjectPool + { + public static ConcurrentQueueObjectPool Create(Func factory, int capacity = Constants.DEFAULT_CAPACITY) + where T : class + { + return new ConcurrentQueueObjectPool(factory, capacity); + } + + + public static ConcurrentQueueObjectPool Create(int capacity = Constants.DEFAULT_CAPACITY) + where T : class, new() + { + return Create(() => new T(), capacity); + } + + public static ConcurrentQueueObjectPool Create(Func factory, bool autoRecycle, int capacity = Constants.DEFAULT_CAPACITY) + where T : class, IRecyclable + { + Action recycler = null; + if (autoRecycle) recycler = Recycler.Recycle; + return new ConcurrentQueueObjectPool(factory, recycler, capacity); + } + + public static ConcurrentQueueObjectPool Create(bool autoRecycle, int capacity = Constants.DEFAULT_CAPACITY) + where T : class, IRecyclable, new() + { + return Create(() => new T(), autoRecycle, capacity); + } + } } diff --git a/source/Collection/ConcurrentStackObjectPool.cs b/source/Collection/ConcurrentStackObjectPool.cs index 31e3aa4..8b074da 100644 --- a/source/Collection/ConcurrentStackObjectPool.cs +++ b/source/Collection/ConcurrentStackObjectPool.cs @@ -61,5 +61,20 @@ public static ConcurrentStackObjectPool Create(int capacity = Constants.DE { return Create(() => new T(), capacity); } - } + + public static ConcurrentStackObjectPool Create(Func factory, bool autoRecycle, int capacity = Constants.DEFAULT_CAPACITY) + where T : class, IRecyclable + { + Action recycler = null; + if (autoRecycle) recycler = Recycler.Recycle; + return new ConcurrentStackObjectPool(factory, recycler, capacity); + } + + public static ConcurrentStackObjectPool Create(bool autoRecycle, int capacity = Constants.DEFAULT_CAPACITY) + where T : class, IRecyclable, new() + { + return Create(() => new T(), autoRecycle, capacity); + } + + } } diff --git a/source/Collection/QueueObjectPool.cs b/source/Collection/QueueObjectPool.cs index db5e97a..db4b99a 100644 --- a/source/Collection/QueueObjectPool.cs +++ b/source/Collection/QueueObjectPool.cs @@ -73,5 +73,20 @@ public static QueueObjectPool Create(int capacity = Constants.DEFAULT_CAPA { return Create(() => new T(), capacity); } - } + + public static QueueObjectPool Create(Func factory, bool autoRecycle, int capacity = Constants.DEFAULT_CAPACITY) + where T : class, IRecyclable + { + Action recycler = null; + if (autoRecycle) recycler = Recycler.Recycle; + return new QueueObjectPool(factory, recycler, capacity); + } + + public static QueueObjectPool Create(bool autoRecycle, int capacity = Constants.DEFAULT_CAPACITY) + where T : class, IRecyclable, new() + { + return Create(() => new T(), autoRecycle, capacity); + } + + } } diff --git a/source/Collection/StackObjectPool.cs b/source/Collection/StackObjectPool.cs index 768bd30..f857277 100644 --- a/source/Collection/StackObjectPool.cs +++ b/source/Collection/StackObjectPool.cs @@ -73,5 +73,20 @@ public static StackObjectPool Create(int capacity = Constants.DEFAULT_CAPA { return Create(() => new T(), capacity); } - } + + public static StackObjectPool Create(Func factory, bool autoRecycle, int capacity = Constants.DEFAULT_CAPACITY) + where T : class, IRecyclable + { + Action recycler = null; + if (autoRecycle) recycler = Recycler.Recycle; + return new StackObjectPool(factory, recycler, capacity); + } + + public static StackObjectPool Create(bool autoRecycle, int capacity = Constants.DEFAULT_CAPACITY) + where T : class, IRecyclable, new() + { + return Create(() => new T(), autoRecycle, capacity); + } + + } } diff --git a/source/Constants.cs b/source/Constants.cs index 0477a39..464a259 100644 --- a/source/Constants.cs +++ b/source/Constants.cs @@ -6,6 +6,6 @@ namespace Open.Disposable { internal static class Constants { - internal const int DEFAULT_CAPACITY = 65535; + internal const int DEFAULT_CAPACITY = 128; // Should accomodate all object pools without decreasing their effectiveness. } } diff --git a/source/IRecyclable.cs b/source/IRecyclable.cs new file mode 100644 index 0000000..03a14c5 --- /dev/null +++ b/source/IRecyclable.cs @@ -0,0 +1,11 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Open.Disposable +{ + public interface IRecyclable + { + void Recycle(); + } +} diff --git a/source/Open.Disposable.ObjectPools.csproj b/source/Open.Disposable.ObjectPools.csproj index bc67087..80f6fee 100644 --- a/source/Open.Disposable.ObjectPools.csproj +++ b/source/Open.Disposable.ObjectPools.csproj @@ -16,10 +16,12 @@ Part of the "Open" set of libraries. git objectpool, dotnet, dotnetcore, cs, idisposable, threadsafe, thread-safe True - 1.1.0 + 1.2.0 1.1.0.0 1.1.0.0 - Removed pools that are simply too slow for real world use and migrated to separate branch since they are simply not worth + Added IRecylable interface since it's much easier to internally define what is required for recycling. +Added static methods to accomodate. +Redefined Recycler to only be created by an extension of IObjectPool<T>. diff --git a/source/Recycler.cs b/source/Recycler.cs index 115b0b0..08438df 100644 --- a/source/Recycler.cs +++ b/source/Recycler.cs @@ -14,10 +14,10 @@ public class Recycler : DisposableBase IObjectPool _target; ActionBlock _bin; - public Recycler( + internal Recycler( IObjectPool target, Action recycleFunction, - ushort limit = ushort.MaxValue) + ushort limit = Constants.DEFAULT_CAPACITY) { if(recycleFunction==null) throw new ArgumentNullException("recycleFunction"); _target = target ?? throw new ArgumentNullException("target"); @@ -39,7 +39,7 @@ public Recycler( } } - public Recycler( + internal Recycler( ushort limit, IObjectPool pool, Action recycleFunction) : this(pool, recycleFunction, limit) @@ -72,4 +72,39 @@ protected override void OnDispose(bool calledExplicitly) _target = null; } } + + public static class Recycler + { + public static Recycler CreateRecycler( + this IObjectPool pool, + Action recycleFunction, + ushort limit = Constants.DEFAULT_CAPACITY) + where T : class + { + return new Recycler(pool, recycleFunction, limit); + } + + public static Recycler CreateRecycler( + this IObjectPool pool, + ushort limit, + Action recycleFunction) + where T : class + { + return new Recycler(pool, recycleFunction, limit); + } + + public static void Recycle(IRecyclable r) + { + r.Recycle(); + } + + public static Recycler CreateRecycler( + this IObjectPool pool, + ushort limit = Constants.DEFAULT_CAPACITY) + where T : class, IRecyclable + { + return new Recycler(pool, Recycle, limit); + } + + } } From d12ded6419dacba93a6a010b9eb670a09854d586 Mon Sep 17 00:00:00 2001 From: electricessence Date: Fri, 20 Oct 2017 20:34:18 -0700 Subject: [PATCH 08/83] Cleaned up obsolete smoke tests. --- tests/ObjectPoolSmokeTests.cs | 23 ----------------------- 1 file changed, 23 deletions(-) diff --git a/tests/ObjectPoolSmokeTests.cs b/tests/ObjectPoolSmokeTests.cs index c94226f..ae31da6 100644 --- a/tests/ObjectPoolSmokeTests.cs +++ b/tests/ObjectPoolSmokeTests.cs @@ -10,22 +10,6 @@ class IdContainer public int ID; } - [TestMethod] - public void BufferBlockObjectPool_FactoryTest() - { - int i = 0; - var pool = BufferBlockObjectPool.Create(()=>new IdContainer { ID = ++i }); - Assert.AreEqual(1, pool.Take().ID); - } - - [TestMethod] - public void ConcurrentBagObjectPool_FactoryTest() - { - int i = 0; - var pool = ConcurrentBagObjectPool.Create(() => new IdContainer { ID = ++i }); - Assert.AreEqual(1, pool.Take().ID); - } - [TestMethod] public void OptimisticArrayObjectPool_FactoryTest() { @@ -34,12 +18,5 @@ public void OptimisticArrayObjectPool_FactoryTest() Assert.AreEqual(1, pool.Take().ID); } - [TestMethod] - public void LinkedListObjectPool_FactoryTest() - { - int i = 0; - var pool = LinkedListObjectPool.Create(() => new IdContainer { ID = ++i }); - Assert.AreEqual(1, pool.Take().ID); - } } } From c0665c54de595082b853e254ad29ef249cf2284d Mon Sep 17 00:00:00 2001 From: "Oren (electricessence)" Date: Wed, 1 Nov 2017 22:21:56 -0700 Subject: [PATCH 09/83] Only a test sdk update. --- tests/Open.Disposable.ObjectPools.Tests.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/Open.Disposable.ObjectPools.Tests.csproj b/tests/Open.Disposable.ObjectPools.Tests.csproj index 917c23b..6e9309e 100644 --- a/tests/Open.Disposable.ObjectPools.Tests.csproj +++ b/tests/Open.Disposable.ObjectPools.Tests.csproj @@ -6,7 +6,7 @@ - + From 74e23e4ff933d4e66451fa2c15095e690fbf81b6 Mon Sep 17 00:00:00 2001 From: "Oren (electricessence)" Date: Wed, 3 Jan 2018 21:00:25 -0800 Subject: [PATCH 10/83] Updated to .NET Standard for compatability. --- ...en.Disposable.ObjectPools.Benchmarking.csproj | 6 +++--- source/Open.Disposable.ObjectPools.csproj | 16 +++++++--------- tests/Open.Disposable.ObjectPools.Tests.csproj | 4 ++-- 3 files changed, 12 insertions(+), 14 deletions(-) diff --git a/benchmarking/Open.Disposable.ObjectPools.Benchmarking.csproj b/benchmarking/Open.Disposable.ObjectPools.Benchmarking.csproj index 0dac186..871973e 100644 --- a/benchmarking/Open.Disposable.ObjectPools.Benchmarking.csproj +++ b/benchmarking/Open.Disposable.ObjectPools.Benchmarking.csproj @@ -2,7 +2,7 @@ Exe - netcoreapp1.1 + netstandard2.0 Open.Disposable 1.0.0 @@ -12,8 +12,8 @@ - - + + diff --git a/source/Open.Disposable.ObjectPools.csproj b/source/Open.Disposable.ObjectPools.csproj index 80f6fee..80050c2 100644 --- a/source/Open.Disposable.ObjectPools.csproj +++ b/source/Open.Disposable.ObjectPools.csproj @@ -1,7 +1,7 @@  - netcoreapp1.1 + netstandard2.0 Open.Disposable A set of variations on ObjectPool implementations with differing underlying collections. @@ -16,12 +16,10 @@ Part of the "Open" set of libraries. git objectpool, dotnet, dotnetcore, cs, idisposable, threadsafe, thread-safe True - 1.2.0 - 1.1.0.0 - 1.1.0.0 - Added IRecylable interface since it's much easier to internally define what is required for recycling. -Added static methods to accomodate. -Redefined Recycler to only be created by an extension of IObjectPool<T>. + 1.3.0 + 1.3.0.0 + 1.3.0.0 + Updated to .NET Standard for compatability. @@ -29,8 +27,8 @@ Redefined Recycler to only be created by an extension of IObjectPool<T>. - - + + \ No newline at end of file diff --git a/tests/Open.Disposable.ObjectPools.Tests.csproj b/tests/Open.Disposable.ObjectPools.Tests.csproj index 6e9309e..0138d17 100644 --- a/tests/Open.Disposable.ObjectPools.Tests.csproj +++ b/tests/Open.Disposable.ObjectPools.Tests.csproj @@ -1,12 +1,12 @@  - netcoreapp1.1 + netcoreapp2.0 Open.Disposable - + From 4e23a3376fb4f03426ec8fe54223d6406574140e Mon Sep 17 00:00:00 2001 From: "Oren (electricessence)" Date: Thu, 3 May 2018 09:06:16 -0700 Subject: [PATCH 11/83] Updated reference to Open.Threading.Tasks (only used when trimming). Updated benchmarks. ConcurrentQueue has fallen from grace? --- benchmarking/BenchmarkResult.csv | 40 ++-- benchmarking/BenchmarkResult.txt | 200 +++++++++--------- ...Disposable.ObjectPools.Benchmarking.csproj | 2 +- .../Collection/ConcurrentQueueObjectPool.cs | 5 +- source/Open.Disposable.ObjectPools.csproj | 2 +- 5 files changed, 126 insertions(+), 123 deletions(-) diff --git a/benchmarking/BenchmarkResult.csv b/benchmarking/BenchmarkResult.csv index 3cb0582..7ede6cc 100644 --- a/benchmarking/BenchmarkResult.csv +++ b/benchmarking/BenchmarkResult.csv @@ -1,24 +1,24 @@ Batch,Pool Type," From Empty (In Parallel)"," To (In Parallel)",d Read/Write (In Parallel),y Pool (.TryTake()),L, -Repeat 200000 for size 4,QueueObjectPool,00:00:01.0947249,00:00:00.9968247,00:00:00.9656608,00:00:00.1521333,00:00:03.2093437, -Repeat 200000 for size 4,ConcurrentQueueObjectPool,00:00:01.1280539,00:00:01.0784355,00:00:01.0181790,00:00:00.1777503,00:00:03.4024187, -Repeat 200000 for size 4,ConcurrentStackObjectPool,00:00:01.1002830,00:00:01.0578873,00:00:00.9720418,00:00:00.1441359,00:00:03.2743480, -Repeat 200000 for size 4,OptimisticArrayObjectPool,00:00:01.0957806,00:00:00.9225542,00:00:00.8814923,00:00:00.1739174,00:00:03.0737445, -Repeat 80000 for size 10,QueueObjectPool,00:00:00.5346130,00:00:00.5352514,00:00:00.4743420,00:00:00.1366686,00:00:01.6808750, -Repeat 80000 for size 10,ConcurrentQueueObjectPool,00:00:00.5718558,00:00:00.5354731,00:00:00.5125773,00:00:00.1602205,00:00:01.7801267, -Repeat 80000 for size 10,ConcurrentStackObjectPool,00:00:00.5552188,00:00:00.5154776,00:00:00.4801922,00:00:00.1249209,00:00:01.6758095, -Repeat 80000 for size 10,OptimisticArrayObjectPool,00:00:00.5270367,00:00:00.4550340,00:00:00.4407644,00:00:00.2186592,00:00:01.6414943, -Repeat 24000 for size 50,QueueObjectPool,00:00:00.2622830,00:00:00.4367957,00:00:00.2884833,00:00:00.1834193,00:00:01.1709813, -Repeat 24000 for size 50,ConcurrentQueueObjectPool,00:00:00.2719027,00:00:00.2854846,00:00:00.2650888,00:00:00.2203011,00:00:01.0427772, -Repeat 24000 for size 50,ConcurrentStackObjectPool,00:00:00.2640431,00:00:00.3351598,00:00:00.2996782,00:00:00.2149665,00:00:01.1138476, -Repeat 24000 for size 50,OptimisticArrayObjectPool,00:00:00.3047471,00:00:00.3823836,00:00:00.2115898,00:00:00.8433052,00:00:01.7420257, -Repeat 16000 for size 100,QueueObjectPool,00:00:00.2680744,00:00:00.4845459,00:00:00.3117290,00:00:00.2375035,00:00:01.3018528, -Repeat 16000 for size 100,ConcurrentQueueObjectPool,00:00:00.2620082,00:00:00.2949254,00:00:00.2538674,00:00:00.2908684,00:00:01.1016694, -Repeat 16000 for size 100,ConcurrentStackObjectPool,00:00:00.2735071,00:00:00.3903024,00:00:00.3434028,00:00:00.3366711,00:00:01.3438834, -Repeat 16000 for size 100,OptimisticArrayObjectPool,00:00:00.7922812,00:00:00.6641963,00:00:00.2030016,00:00:01.7548815,00:00:03.4143606, -Repeat 25600 for size 250,QueueObjectPool,00:00:00.7668417,00:00:01.8047414,00:00:01.3185354,00:00:00.8664007,00:00:04.7565192, -Repeat 25600 for size 250,ConcurrentQueueObjectPool,00:00:00.8378978,00:00:00.9370109,00:00:00.7695238,00:00:01.0984692,00:00:03.6429017, -Repeat 25600 for size 250,ConcurrentStackObjectPool,00:00:00.7848982,00:00:01.5942377,00:00:01.5995650,00:00:01.7959374,00:00:05.7746383, -Repeat 25600 for size 250,OptimisticArrayObjectPool,00:00:01.2561773,00:00:03.2871367,00:00:00.5424299,00:00:12.0158755,00:00:17.1016194, +Repeat 200000 for size 4,QueueObjectPool,00:00:00.6037240,00:00:00.5527088,00:00:00.5315828,00:00:00.0900567,00:00:01.7780723, +Repeat 200000 for size 4,ConcurrentQueueObjectPool,00:00:00.6749691,00:00:00.6146747,00:00:00.6101963,00:00:00.0730119,00:00:01.9728520, +Repeat 200000 for size 4,ConcurrentStackObjectPool,00:00:00.6205358,00:00:00.6441162,00:00:00.5418539,00:00:00.0758358,00:00:01.8823417, +Repeat 200000 for size 4,OptimisticArrayObjectPool,00:00:00.6262788,00:00:00.5193854,00:00:00.5256468,00:00:00.0980145,00:00:01.7693255, +Repeat 80000 for size 10,QueueObjectPool,00:00:00.4154226,00:00:00.3984580,00:00:00.3555039,00:00:00.1029956,00:00:01.2723801, +Repeat 80000 for size 10,ConcurrentQueueObjectPool,00:00:00.4010665,00:00:00.3802531,00:00:00.3610812,00:00:00.0846055,00:00:01.2270063, +Repeat 80000 for size 10,ConcurrentStackObjectPool,00:00:00.4181783,00:00:00.4003157,00:00:00.3933568,00:00:00.0930685,00:00:01.3049193, +Repeat 80000 for size 10,OptimisticArrayObjectPool,00:00:00.4154931,00:00:00.3539234,00:00:00.3306813,00:00:00.1595689,00:00:01.2596667, +Repeat 24000 for size 50,QueueObjectPool,00:00:00.1983285,00:00:00.5393018,00:00:00.2520056,00:00:00.1437719,00:00:01.1334078, +Repeat 24000 for size 50,ConcurrentQueueObjectPool,00:00:00.1911597,00:00:00.6099586,00:00:00.2019195,00:00:00.1173744,00:00:01.1204122, +Repeat 24000 for size 50,ConcurrentStackObjectPool,00:00:00.1947059,00:00:00.2563897,00:00:00.2213445,00:00:00.1613517,00:00:00.8337918, +Repeat 24000 for size 50,OptimisticArrayObjectPool,00:00:00.2011463,00:00:00.2458962,00:00:00.1567710,00:00:00.5725949,00:00:01.1764084, +Repeat 16000 for size 100,QueueObjectPool,00:00:00.1917082,00:00:00.7281393,00:00:00.4965600,00:00:00.1853762,00:00:01.6017837, +Repeat 16000 for size 100,ConcurrentQueueObjectPool,00:00:00.1913408,00:00:00.5323921,00:00:06.1167135,00:00:00.3403016,00:00:07.1807480, +Repeat 16000 for size 100,ConcurrentStackObjectPool,00:00:00.1864700,00:00:00.2674701,00:00:00.2333379,00:00:00.2567077,00:00:00.9439857, +Repeat 16000 for size 100,OptimisticArrayObjectPool,00:00:00.2117531,00:00:00.3088107,00:00:00.1494503,00:00:01.2309936,00:00:01.9010077, +Repeat 25600 for size 250,QueueObjectPool,00:00:00.4827432,00:00:02.6029248,00:00:02.6176686,00:00:00.6578479,00:00:06.3611845, +Repeat 25600 for size 250,ConcurrentQueueObjectPool,00:00:00.7486876,00:00:35.1458453,00:00:59.9447445,00:00:05.0064751,00:01:40.8457525, +Repeat 25600 for size 250,ConcurrentStackObjectPool,00:00:00.5360153,00:00:01.0427376,00:00:00.9601208,00:00:01.4689654,00:00:04.0078391, +Repeat 25600 for size 250,OptimisticArrayObjectPool,00:00:00.9522035,00:00:02.1958969,00:00:00.4701271,00:00:10.4725119,00:00:14.0907394, Repeat 14400 for size 500,QueueObjectPool,00:00:00.7797824,00:00:01.6503887,00:00:01.3768657,00:00:00.8861150,00:00:04.6931518, Repeat 14400 for size 500,ConcurrentQueueObjectPool,00:00:00.7994849,00:00:00.9658497,00:00:00.7778244,00:00:01.1149906,00:00:03.6581496, Repeat 14400 for size 500,ConcurrentStackObjectPool,00:00:00.7487525,00:00:02.3665267,00:00:02.6608263,00:00:03.0226290,00:00:08.7987345, diff --git a/benchmarking/BenchmarkResult.txt b/benchmarking/BenchmarkResult.txt index 52fded2..6ed9275 100644 --- a/benchmarking/BenchmarkResult.txt +++ b/benchmarking/BenchmarkResult.txt @@ -2,159 +2,159 @@ Repeat 200000 for size 4 ------------------------------------ QueueObjectPool......................................... -00:00:01.0947249 Take From Empty (In Parallel) -00:00:00.9968247 Give To (In Parallel) -00:00:00.9656608 Mixed Read/Write (In Parallel) -00:00:00.1521333 Empty Pool (.TryTake()) -00:00:03.2093437 TOTAL +00:00:00.6037240 Take From Empty (In Parallel) +00:00:00.5527088 Give To (In Parallel) +00:00:00.5315828 Mixed Read/Write (In Parallel) +00:00:00.0900567 Empty Pool (.TryTake()) +00:00:01.7780723 TOTAL ConcurrentQueueObjectPool............................... -00:00:01.1280539 Take From Empty (In Parallel) -00:00:01.0784355 Give To (In Parallel) -00:00:01.0181790 Mixed Read/Write (In Parallel) -00:00:00.1777503 Empty Pool (.TryTake()) -00:00:03.4024187 TOTAL +00:00:00.6749691 Take From Empty (In Parallel) +00:00:00.6146747 Give To (In Parallel) +00:00:00.6101963 Mixed Read/Write (In Parallel) +00:00:00.0730119 Empty Pool (.TryTake()) +00:00:01.9728520 TOTAL ConcurrentStackObjectPool............................... -00:00:01.1002830 Take From Empty (In Parallel) -00:00:01.0578873 Give To (In Parallel) -00:00:00.9720418 Mixed Read/Write (In Parallel) -00:00:00.1441359 Empty Pool (.TryTake()) -00:00:03.2743480 TOTAL +00:00:00.6205358 Take From Empty (In Parallel) +00:00:00.6441162 Give To (In Parallel) +00:00:00.5418539 Mixed Read/Write (In Parallel) +00:00:00.0758358 Empty Pool (.TryTake()) +00:00:01.8823417 TOTAL OptimisticArrayObjectPool............................... -00:00:01.0957806 Take From Empty (In Parallel) -00:00:00.9225542 Give To (In Parallel) -00:00:00.8814923 Mixed Read/Write (In Parallel) -00:00:00.1739174 Empty Pool (.TryTake()) -00:00:03.0737445 TOTAL +00:00:00.6262788 Take From Empty (In Parallel) +00:00:00.5193854 Give To (In Parallel) +00:00:00.5256468 Mixed Read/Write (In Parallel) +00:00:00.0980145 Empty Pool (.TryTake()) +00:00:01.7693255 TOTAL Repeat 80000 for size 10 ------------------------------------ QueueObjectPool......................................... -00:00:00.5346130 Take From Empty (In Parallel) -00:00:00.5352514 Give To (In Parallel) -00:00:00.4743420 Mixed Read/Write (In Parallel) -00:00:00.1366686 Empty Pool (.TryTake()) -00:00:01.6808750 TOTAL +00:00:00.4154226 Take From Empty (In Parallel) +00:00:00.3984580 Give To (In Parallel) +00:00:00.3555039 Mixed Read/Write (In Parallel) +00:00:00.1029956 Empty Pool (.TryTake()) +00:00:01.2723801 TOTAL ConcurrentQueueObjectPool............................... -00:00:00.5718558 Take From Empty (In Parallel) -00:00:00.5354731 Give To (In Parallel) -00:00:00.5125773 Mixed Read/Write (In Parallel) -00:00:00.1602205 Empty Pool (.TryTake()) -00:00:01.7801267 TOTAL +00:00:00.4010665 Take From Empty (In Parallel) +00:00:00.3802531 Give To (In Parallel) +00:00:00.3610812 Mixed Read/Write (In Parallel) +00:00:00.0846055 Empty Pool (.TryTake()) +00:00:01.2270063 TOTAL ConcurrentStackObjectPool............................... -00:00:00.5552188 Take From Empty (In Parallel) -00:00:00.5154776 Give To (In Parallel) -00:00:00.4801922 Mixed Read/Write (In Parallel) -00:00:00.1249209 Empty Pool (.TryTake()) -00:00:01.6758095 TOTAL +00:00:00.4181783 Take From Empty (In Parallel) +00:00:00.4003157 Give To (In Parallel) +00:00:00.3933568 Mixed Read/Write (In Parallel) +00:00:00.0930685 Empty Pool (.TryTake()) +00:00:01.3049193 TOTAL OptimisticArrayObjectPool............................... -00:00:00.5270367 Take From Empty (In Parallel) -00:00:00.4550340 Give To (In Parallel) -00:00:00.4407644 Mixed Read/Write (In Parallel) -00:00:00.2186592 Empty Pool (.TryTake()) -00:00:01.6414943 TOTAL +00:00:00.4154931 Take From Empty (In Parallel) +00:00:00.3539234 Give To (In Parallel) +00:00:00.3306813 Mixed Read/Write (In Parallel) +00:00:00.1595689 Empty Pool (.TryTake()) +00:00:01.2596667 TOTAL Repeat 24000 for size 50 ------------------------------------ QueueObjectPool......................................... -00:00:00.2622830 Take From Empty (In Parallel) -00:00:00.4367957 Give To (In Parallel) -00:00:00.2884833 Mixed Read/Write (In Parallel) -00:00:00.1834193 Empty Pool (.TryTake()) -00:00:01.1709813 TOTAL +00:00:00.1983285 Take From Empty (In Parallel) +00:00:00.5393018 Give To (In Parallel) +00:00:00.2520056 Mixed Read/Write (In Parallel) +00:00:00.1437719 Empty Pool (.TryTake()) +00:00:01.1334078 TOTAL ConcurrentQueueObjectPool............................... -00:00:00.2719027 Take From Empty (In Parallel) -00:00:00.2854846 Give To (In Parallel) -00:00:00.2650888 Mixed Read/Write (In Parallel) -00:00:00.2203011 Empty Pool (.TryTake()) -00:00:01.0427772 TOTAL +00:00:00.1911597 Take From Empty (In Parallel) +00:00:00.6099586 Give To (In Parallel) +00:00:00.2019195 Mixed Read/Write (In Parallel) +00:00:00.1173744 Empty Pool (.TryTake()) +00:00:01.1204122 TOTAL ConcurrentStackObjectPool............................... -00:00:00.2640431 Take From Empty (In Parallel) -00:00:00.3351598 Give To (In Parallel) -00:00:00.2996782 Mixed Read/Write (In Parallel) -00:00:00.2149665 Empty Pool (.TryTake()) -00:00:01.1138476 TOTAL +00:00:00.1947059 Take From Empty (In Parallel) +00:00:00.2563897 Give To (In Parallel) +00:00:00.2213445 Mixed Read/Write (In Parallel) +00:00:00.1613517 Empty Pool (.TryTake()) +00:00:00.8337918 TOTAL OptimisticArrayObjectPool............................... -00:00:00.3047471 Take From Empty (In Parallel) -00:00:00.3823836 Give To (In Parallel) -00:00:00.2115898 Mixed Read/Write (In Parallel) -00:00:00.8433052 Empty Pool (.TryTake()) -00:00:01.7420257 TOTAL +00:00:00.2011463 Take From Empty (In Parallel) +00:00:00.2458962 Give To (In Parallel) +00:00:00.1567710 Mixed Read/Write (In Parallel) +00:00:00.5725949 Empty Pool (.TryTake()) +00:00:01.1764084 TOTAL Repeat 16000 for size 100 ------------------------------------ QueueObjectPool......................................... -00:00:00.2680744 Take From Empty (In Parallel) -00:00:00.4845459 Give To (In Parallel) -00:00:00.3117290 Mixed Read/Write (In Parallel) -00:00:00.2375035 Empty Pool (.TryTake()) -00:00:01.3018528 TOTAL +00:00:00.1917082 Take From Empty (In Parallel) +00:00:00.7281393 Give To (In Parallel) +00:00:00.4965600 Mixed Read/Write (In Parallel) +00:00:00.1853762 Empty Pool (.TryTake()) +00:00:01.6017837 TOTAL ConcurrentQueueObjectPool............................... -00:00:00.2620082 Take From Empty (In Parallel) -00:00:00.2949254 Give To (In Parallel) -00:00:00.2538674 Mixed Read/Write (In Parallel) -00:00:00.2908684 Empty Pool (.TryTake()) -00:00:01.1016694 TOTAL +00:00:00.1913408 Take From Empty (In Parallel) +00:00:00.5323921 Give To (In Parallel) +00:00:06.1167135 Mixed Read/Write (In Parallel) +00:00:00.3403016 Empty Pool (.TryTake()) +00:00:07.1807480 TOTAL ConcurrentStackObjectPool............................... -00:00:00.2735071 Take From Empty (In Parallel) -00:00:00.3903024 Give To (In Parallel) -00:00:00.3434028 Mixed Read/Write (In Parallel) -00:00:00.3366711 Empty Pool (.TryTake()) -00:00:01.3438834 TOTAL +00:00:00.1864700 Take From Empty (In Parallel) +00:00:00.2674701 Give To (In Parallel) +00:00:00.2333379 Mixed Read/Write (In Parallel) +00:00:00.2567077 Empty Pool (.TryTake()) +00:00:00.9439857 TOTAL OptimisticArrayObjectPool............................... -00:00:00.7922812 Take From Empty (In Parallel) -00:00:00.6641963 Give To (In Parallel) -00:00:00.2030016 Mixed Read/Write (In Parallel) -00:00:01.7548815 Empty Pool (.TryTake()) -00:00:03.4143606 TOTAL +00:00:00.2117531 Take From Empty (In Parallel) +00:00:00.3088107 Give To (In Parallel) +00:00:00.1494503 Mixed Read/Write (In Parallel) +00:00:01.2309936 Empty Pool (.TryTake()) +00:00:01.9010077 TOTAL Repeat 25600 for size 250 ------------------------------------ QueueObjectPool......................................... -00:00:00.7668417 Take From Empty (In Parallel) -00:00:01.8047414 Give To (In Parallel) -00:00:01.3185354 Mixed Read/Write (In Parallel) -00:00:00.8664007 Empty Pool (.TryTake()) -00:00:04.7565192 TOTAL +00:00:00.4827432 Take From Empty (In Parallel) +00:00:02.6029248 Give To (In Parallel) +00:00:02.6176686 Mixed Read/Write (In Parallel) +00:00:00.6578479 Empty Pool (.TryTake()) +00:00:06.3611845 TOTAL ConcurrentQueueObjectPool............................... -00:00:00.8378978 Take From Empty (In Parallel) -00:00:00.9370109 Give To (In Parallel) -00:00:00.7695238 Mixed Read/Write (In Parallel) -00:00:01.0984692 Empty Pool (.TryTake()) -00:00:03.6429017 TOTAL +00:00:00.7486876 Take From Empty (In Parallel) +00:00:35.1458453 Give To (In Parallel) +00:00:59.9447445 Mixed Read/Write (In Parallel) +00:00:05.0064751 Empty Pool (.TryTake()) +00:01:40.8457525 TOTAL ConcurrentStackObjectPool............................... -00:00:00.7848982 Take From Empty (In Parallel) -00:00:01.5942377 Give To (In Parallel) -00:00:01.5995650 Mixed Read/Write (In Parallel) -00:00:01.7959374 Empty Pool (.TryTake()) -00:00:05.7746383 TOTAL +00:00:00.5360153 Take From Empty (In Parallel) +00:00:01.0427376 Give To (In Parallel) +00:00:00.9601208 Mixed Read/Write (In Parallel) +00:00:01.4689654 Empty Pool (.TryTake()) +00:00:04.0078391 TOTAL OptimisticArrayObjectPool............................... -00:00:01.2561773 Take From Empty (In Parallel) -00:00:03.2871367 Give To (In Parallel) -00:00:00.5424299 Mixed Read/Write (In Parallel) -00:00:12.0158755 Empty Pool (.TryTake()) -00:00:17.1016194 TOTAL +00:00:00.9522035 Take From Empty (In Parallel) +00:00:02.1958969 Give To (In Parallel) +00:00:00.4701271 Mixed Read/Write (In Parallel) +00:00:10.4725119 Empty Pool (.TryTake()) +00:00:14.0907394 TOTAL diff --git a/benchmarking/Open.Disposable.ObjectPools.Benchmarking.csproj b/benchmarking/Open.Disposable.ObjectPools.Benchmarking.csproj index 871973e..728a806 100644 --- a/benchmarking/Open.Disposable.ObjectPools.Benchmarking.csproj +++ b/benchmarking/Open.Disposable.ObjectPools.Benchmarking.csproj @@ -2,7 +2,7 @@ Exe - netstandard2.0 + netcoreapp2.0 Open.Disposable 1.0.0 diff --git a/source/Collection/ConcurrentQueueObjectPool.cs b/source/Collection/ConcurrentQueueObjectPool.cs index 0778231..8e9dd41 100644 --- a/source/Collection/ConcurrentQueueObjectPool.cs +++ b/source/Collection/ConcurrentQueueObjectPool.cs @@ -52,7 +52,10 @@ protected override T TryRelease() protected override void OnDispose(bool calledExplicitly) { - Pool = null; + if(calledExplicitly) + { + Pool = null; + } } } diff --git a/source/Open.Disposable.ObjectPools.csproj b/source/Open.Disposable.ObjectPools.csproj index 80050c2..ded371d 100644 --- a/source/Open.Disposable.ObjectPools.csproj +++ b/source/Open.Disposable.ObjectPools.csproj @@ -28,7 +28,7 @@ Part of the "Open" set of libraries. - + \ No newline at end of file From bed82757d104cd5309c7192fecca214269ddfd0d Mon Sep 17 00:00:00 2001 From: "Oren (electricessence)" Date: Thu, 3 May 2018 09:08:24 -0700 Subject: [PATCH 12/83] 1.3.1 Release to nuget --- source/Open.Disposable.ObjectPools.csproj | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/source/Open.Disposable.ObjectPools.csproj b/source/Open.Disposable.ObjectPools.csproj index ded371d..2468cd4 100644 --- a/source/Open.Disposable.ObjectPools.csproj +++ b/source/Open.Disposable.ObjectPools.csproj @@ -16,10 +16,10 @@ Part of the "Open" set of libraries. git objectpool, dotnet, dotnetcore, cs, idisposable, threadsafe, thread-safe True - 1.3.0 - 1.3.0.0 - 1.3.0.0 - Updated to .NET Standard for compatability. + 1.3.1 + 1.3.1.0 + 1.3.1.0 + Updated Open.Threading.Tasks reference (only used for trimming). From 521f1df51b28937566c1acc4fc916770cd24e8e9 Mon Sep 17 00:00:00 2001 From: Oren Ferrari Date: Thu, 3 May 2018 15:19:59 -0700 Subject: [PATCH 13/83] Updates and reformatting. Includes unused prerelease classes using Channels. --- benchmarking/BenchmarkResult.csv | 52 ++-- benchmarking/BenchmarkResult.txt | 245 ++++++++++-------- benchmarking/Program.cs | 14 +- source/Channels/ChannelObjectPool.cs | 68 +++++ source/Channels/ChanneledRecycler.cs | 108 ++++++++ .../Collection/CollectionWrapperObjectPool.cs | 16 +- .../Collection/ConcurrentQueueObjectPool.cs | 42 ++- .../Collection/ConcurrentStackObjectPool.cs | 48 ++-- source/Collection/QueueObjectPool.cs | 27 +- source/Collection/StackObjectPool.cs | 59 ++--- source/Constants.cs | 10 +- source/GlobalSuppressions.cs | 9 + source/IObjectPool.cs | 23 +- source/IRecyclable.cs | 14 +- source/IRecycler.cs | 12 + source/ITrimmableObjectPool.cs | 10 +- source/ObjectPoolAutoTrimmer.cs | 19 +- source/ObjectPoolBase.cs | 43 ++- source/ObjectPoolResizeEvent.cs | 4 +- source/Open.Disposable.ObjectPools.csproj | 6 + source/Recycler.cs | 106 +++----- source/RecyclerBase.cs | 51 ++++ source/ReferenceContainer.cs | 122 ++++----- source/TrimmableObjectPoolBase.cs | 77 ++++-- 24 files changed, 728 insertions(+), 457 deletions(-) create mode 100644 source/Channels/ChannelObjectPool.cs create mode 100644 source/Channels/ChanneledRecycler.cs create mode 100644 source/GlobalSuppressions.cs create mode 100644 source/IRecycler.cs create mode 100644 source/RecyclerBase.cs diff --git a/benchmarking/BenchmarkResult.csv b/benchmarking/BenchmarkResult.csv index 7ede6cc..ed8141b 100644 --- a/benchmarking/BenchmarkResult.csv +++ b/benchmarking/BenchmarkResult.csv @@ -1,30 +1,30 @@ Batch,Pool Type," From Empty (In Parallel)"," To (In Parallel)",d Read/Write (In Parallel),y Pool (.TryTake()),L, -Repeat 200000 for size 4,QueueObjectPool,00:00:00.6037240,00:00:00.5527088,00:00:00.5315828,00:00:00.0900567,00:00:01.7780723, -Repeat 200000 for size 4,ConcurrentQueueObjectPool,00:00:00.6749691,00:00:00.6146747,00:00:00.6101963,00:00:00.0730119,00:00:01.9728520, -Repeat 200000 for size 4,ConcurrentStackObjectPool,00:00:00.6205358,00:00:00.6441162,00:00:00.5418539,00:00:00.0758358,00:00:01.8823417, -Repeat 200000 for size 4,OptimisticArrayObjectPool,00:00:00.6262788,00:00:00.5193854,00:00:00.5256468,00:00:00.0980145,00:00:01.7693255, -Repeat 80000 for size 10,QueueObjectPool,00:00:00.4154226,00:00:00.3984580,00:00:00.3555039,00:00:00.1029956,00:00:01.2723801, -Repeat 80000 for size 10,ConcurrentQueueObjectPool,00:00:00.4010665,00:00:00.3802531,00:00:00.3610812,00:00:00.0846055,00:00:01.2270063, -Repeat 80000 for size 10,ConcurrentStackObjectPool,00:00:00.4181783,00:00:00.4003157,00:00:00.3933568,00:00:00.0930685,00:00:01.3049193, -Repeat 80000 for size 10,OptimisticArrayObjectPool,00:00:00.4154931,00:00:00.3539234,00:00:00.3306813,00:00:00.1595689,00:00:01.2596667, -Repeat 24000 for size 50,QueueObjectPool,00:00:00.1983285,00:00:00.5393018,00:00:00.2520056,00:00:00.1437719,00:00:01.1334078, -Repeat 24000 for size 50,ConcurrentQueueObjectPool,00:00:00.1911597,00:00:00.6099586,00:00:00.2019195,00:00:00.1173744,00:00:01.1204122, -Repeat 24000 for size 50,ConcurrentStackObjectPool,00:00:00.1947059,00:00:00.2563897,00:00:00.2213445,00:00:00.1613517,00:00:00.8337918, -Repeat 24000 for size 50,OptimisticArrayObjectPool,00:00:00.2011463,00:00:00.2458962,00:00:00.1567710,00:00:00.5725949,00:00:01.1764084, -Repeat 16000 for size 100,QueueObjectPool,00:00:00.1917082,00:00:00.7281393,00:00:00.4965600,00:00:00.1853762,00:00:01.6017837, -Repeat 16000 for size 100,ConcurrentQueueObjectPool,00:00:00.1913408,00:00:00.5323921,00:00:06.1167135,00:00:00.3403016,00:00:07.1807480, -Repeat 16000 for size 100,ConcurrentStackObjectPool,00:00:00.1864700,00:00:00.2674701,00:00:00.2333379,00:00:00.2567077,00:00:00.9439857, -Repeat 16000 for size 100,OptimisticArrayObjectPool,00:00:00.2117531,00:00:00.3088107,00:00:00.1494503,00:00:01.2309936,00:00:01.9010077, -Repeat 25600 for size 250,QueueObjectPool,00:00:00.4827432,00:00:02.6029248,00:00:02.6176686,00:00:00.6578479,00:00:06.3611845, -Repeat 25600 for size 250,ConcurrentQueueObjectPool,00:00:00.7486876,00:00:35.1458453,00:00:59.9447445,00:00:05.0064751,00:01:40.8457525, -Repeat 25600 for size 250,ConcurrentStackObjectPool,00:00:00.5360153,00:00:01.0427376,00:00:00.9601208,00:00:01.4689654,00:00:04.0078391, -Repeat 25600 for size 250,OptimisticArrayObjectPool,00:00:00.9522035,00:00:02.1958969,00:00:00.4701271,00:00:10.4725119,00:00:14.0907394, -Repeat 14400 for size 500,QueueObjectPool,00:00:00.7797824,00:00:01.6503887,00:00:01.3768657,00:00:00.8861150,00:00:04.6931518, -Repeat 14400 for size 500,ConcurrentQueueObjectPool,00:00:00.7994849,00:00:00.9658497,00:00:00.7778244,00:00:01.1149906,00:00:03.6581496, -Repeat 14400 for size 500,ConcurrentStackObjectPool,00:00:00.7487525,00:00:02.3665267,00:00:02.6608263,00:00:03.0226290,00:00:08.7987345, -Repeat 14400 for size 500,OptimisticArrayObjectPool,00:00:01.0892049,00:00:05.8599218,00:00:00.5797098,00:00:22.9215261,00:00:30.4503626, -250,QueueObjectPool,00:00:00.7629885,00:00:01.7291545,00:00:01.2741500,00:00:00.8586163,00:00:04.6249093, -Repeat 25600 for size 250,StackObjectPool,00:00:00.7604847,00:00:01.8116364,00:00:01.2369652,00:00:00.8626937,00:00:04.6717800, +Repeat 400000 for size 4,QueueObjectPool,00:00:01.6921162,00:00:01.3999970,00:00:01.4232035,00:00:00.2110440,00:00:04.7263607, +Repeat 400000 for size 4,ChannelObjectPool,00:00:01.7026834,00:00:01.6286904,00:00:01.3645671,00:00:00.2338478,00:00:04.9297887, +Repeat 400000 for size 4,ConcurrentQueueObjectPool,00:00:01.7693712,00:00:01.4210947,00:00:01.3829205,00:00:00.1955725,00:00:04.7689589, +Repeat 400000 for size 4,ConcurrentStackObjectPool,00:00:01.6875804,00:00:01.5369081,00:00:01.3944637,00:00:00.1756264,00:00:04.7945786, +Repeat 400000 for size 4,OptimisticArrayObjectPool,00:00:01.7120099,00:00:01.3737743,00:00:01.3623827,00:00:00.2493802,00:00:04.6975471, +Repeat 160000 for size 10,QueueObjectPool,00:00:01.0495375,00:00:01.0112045,00:00:00.8871979,00:00:00.2492687,00:00:03.1972086, +Repeat 160000 for size 10,ChannelObjectPool,00:00:01.2164743,00:00:01.1531997,00:00:00.8866070,00:00:00.2740465,00:00:03.5303275, +Repeat 160000 for size 10,ConcurrentQueueObjectPool,00:00:01.1038901,00:00:00.9417471,00:00:00.9094443,00:00:00.2277741,00:00:03.1828556, +Repeat 160000 for size 10,ConcurrentStackObjectPool,00:00:01.0859551,00:00:00.9596546,00:00:00.9246417,00:00:00.2256799,00:00:03.1959313, +Repeat 160000 for size 10,OptimisticArrayObjectPool,00:00:01.0775123,00:00:00.9302528,00:00:00.9021115,00:00:00.4166513,00:00:03.3265279, +Repeat 48000 for size 50,QueueObjectPool,00:00:00.5258334,00:00:01.2713375,00:00:00.6293988,00:00:00.3438183,00:00:02.7703880, +Repeat 48000 for size 50,ChannelObjectPool,00:00:02.4067783,00:00:01.5298450,00:00:00.6832894,00:00:00.3926395,00:00:05.0125522, +Repeat 48000 for size 50,ConcurrentQueueObjectPool,00:00:00.5612132,00:00:01.4985338,00:00:00.4656040,00:00:00.3110057,00:00:02.8363567, +Repeat 48000 for size 50,ConcurrentStackObjectPool,00:00:00.4972028,00:00:00.5200011,00:00:00.4378888,00:00:00.2969994,00:00:01.7520921, +Repeat 48000 for size 50,OptimisticArrayObjectPool,00:00:00.5691812,00:00:00.7489502,00:00:00.4934400,00:00:01.5497079,00:00:03.3612793, +Repeat 32000 for size 100,QueueObjectPool,00:00:00.5122336,00:00:02.0243072,00:00:00.8949053,00:00:00.4559207,00:00:03.8873668, +Repeat 32000 for size 100,ChannelObjectPool,00:00:04.0415940,00:00:03.4503339,00:00:00.9470840,00:00:00.5190160,00:00:08.9580279, +Repeat 32000 for size 100,ConcurrentQueueObjectPool,00:00:00.4979936,00:00:01.1025353,00:00:00.4270060,00:00:00.4100476,00:00:02.4375825, +Repeat 32000 for size 100,ConcurrentStackObjectPool,00:00:00.4766648,00:00:00.5666903,00:00:00.5310815,00:00:00.3866519,00:00:01.9610885, +Repeat 32000 for size 100,OptimisticArrayObjectPool,00:00:00.6956861,00:00:01.0975470,00:00:00.4499636,00:00:03.7336458,00:00:05.9768425, +Repeat 51200 for size 250,QueueObjectPool,00:00:01.6247132,00:00:05.7028204,00:00:06.3495433,00:00:01.7208433,00:00:15.3979202, +Repeat 51200 for size 250,ChannelObjectPool,00:00:09.1002442,00:00:09.5148632,00:00:07.0393499,00:00:01.9543981,00:00:27.6088554, +Repeat 51200 for size 250,ConcurrentQueueObjectPool,00:00:01.3937964,00:00:03.7235199,00:00:01.2171737,00:00:01.5041405,00:00:07.8386305, +Repeat 51200 for size 250,ConcurrentStackObjectPool,00:00:01.4858700,00:00:01.8964279,00:00:02.2896336,00:00:01.4461137,00:00:07.1180452, +Repeat 51200 for size 250,OptimisticArrayObjectPool,00:00:02.9579032,00:00:07.0305663,00:00:01.2993861,00:00:27.8503246,00:00:39.1381802, +at 25600 for size 250,StackObjectPool,00:00:00.7604847,00:00:01.8116364,00:00:01.2369652,00:00:00.8626937,00:00:04.6717800, Repeat 25600 for size 250,ConcurrentQueueObjectPool,00:00:00.8566894,00:00:00.9466838,00:00:00.7795225,00:00:01.1284017,00:00:03.7112974, Repeat 25600 for size 250,ConcurrentStackObjectPool,00:00:00.7723668,00:00:01.5778130,00:00:01.6054224,00:00:01.7977731,00:00:05.7533753, Repeat 25600 for size 250,OptimisticArrayObjectPool,00:00:01.2503504,00:00:03.2370044,00:00:00.5303349,00:00:11.9466373,00:00:16.9643270, diff --git a/benchmarking/BenchmarkResult.txt b/benchmarking/BenchmarkResult.txt index 6ed9275..be4d77d 100644 --- a/benchmarking/BenchmarkResult.txt +++ b/benchmarking/BenchmarkResult.txt @@ -1,160 +1,195 @@ -Repeat 200000 for size 4 +Repeat 400000 for size 4 ------------------------------------ QueueObjectPool......................................... -00:00:00.6037240 Take From Empty (In Parallel) -00:00:00.5527088 Give To (In Parallel) -00:00:00.5315828 Mixed Read/Write (In Parallel) -00:00:00.0900567 Empty Pool (.TryTake()) -00:00:01.7780723 TOTAL +00:00:01.6921162 Take From Empty (In Parallel) +00:00:01.3999970 Give To (In Parallel) +00:00:01.4232035 Mixed Read/Write (In Parallel) +00:00:00.2110440 Empty Pool (.TryTake()) +00:00:04.7263607 TOTAL + +ChannelObjectPool....................................... +00:00:01.7026834 Take From Empty (In Parallel) +00:00:01.6286904 Give To (In Parallel) +00:00:01.3645671 Mixed Read/Write (In Parallel) +00:00:00.2338478 Empty Pool (.TryTake()) +00:00:04.9297887 TOTAL ConcurrentQueueObjectPool............................... -00:00:00.6749691 Take From Empty (In Parallel) -00:00:00.6146747 Give To (In Parallel) -00:00:00.6101963 Mixed Read/Write (In Parallel) -00:00:00.0730119 Empty Pool (.TryTake()) -00:00:01.9728520 TOTAL +00:00:01.7693712 Take From Empty (In Parallel) +00:00:01.4210947 Give To (In Parallel) +00:00:01.3829205 Mixed Read/Write (In Parallel) +00:00:00.1955725 Empty Pool (.TryTake()) +00:00:04.7689589 TOTAL ConcurrentStackObjectPool............................... -00:00:00.6205358 Take From Empty (In Parallel) -00:00:00.6441162 Give To (In Parallel) -00:00:00.5418539 Mixed Read/Write (In Parallel) -00:00:00.0758358 Empty Pool (.TryTake()) -00:00:01.8823417 TOTAL +00:00:01.6875804 Take From Empty (In Parallel) +00:00:01.5369081 Give To (In Parallel) +00:00:01.3944637 Mixed Read/Write (In Parallel) +00:00:00.1756264 Empty Pool (.TryTake()) +00:00:04.7945786 TOTAL OptimisticArrayObjectPool............................... -00:00:00.6262788 Take From Empty (In Parallel) -00:00:00.5193854 Give To (In Parallel) -00:00:00.5256468 Mixed Read/Write (In Parallel) -00:00:00.0980145 Empty Pool (.TryTake()) -00:00:01.7693255 TOTAL +00:00:01.7120099 Take From Empty (In Parallel) +00:00:01.3737743 Give To (In Parallel) +00:00:01.3623827 Mixed Read/Write (In Parallel) +00:00:00.2493802 Empty Pool (.TryTake()) +00:00:04.6975471 TOTAL -Repeat 80000 for size 10 +Repeat 160000 for size 10 ------------------------------------ QueueObjectPool......................................... -00:00:00.4154226 Take From Empty (In Parallel) -00:00:00.3984580 Give To (In Parallel) -00:00:00.3555039 Mixed Read/Write (In Parallel) -00:00:00.1029956 Empty Pool (.TryTake()) -00:00:01.2723801 TOTAL +00:00:01.0495375 Take From Empty (In Parallel) +00:00:01.0112045 Give To (In Parallel) +00:00:00.8871979 Mixed Read/Write (In Parallel) +00:00:00.2492687 Empty Pool (.TryTake()) +00:00:03.1972086 TOTAL + +ChannelObjectPool....................................... +00:00:01.2164743 Take From Empty (In Parallel) +00:00:01.1531997 Give To (In Parallel) +00:00:00.8866070 Mixed Read/Write (In Parallel) +00:00:00.2740465 Empty Pool (.TryTake()) +00:00:03.5303275 TOTAL ConcurrentQueueObjectPool............................... -00:00:00.4010665 Take From Empty (In Parallel) -00:00:00.3802531 Give To (In Parallel) -00:00:00.3610812 Mixed Read/Write (In Parallel) -00:00:00.0846055 Empty Pool (.TryTake()) -00:00:01.2270063 TOTAL +00:00:01.1038901 Take From Empty (In Parallel) +00:00:00.9417471 Give To (In Parallel) +00:00:00.9094443 Mixed Read/Write (In Parallel) +00:00:00.2277741 Empty Pool (.TryTake()) +00:00:03.1828556 TOTAL ConcurrentStackObjectPool............................... -00:00:00.4181783 Take From Empty (In Parallel) -00:00:00.4003157 Give To (In Parallel) -00:00:00.3933568 Mixed Read/Write (In Parallel) -00:00:00.0930685 Empty Pool (.TryTake()) -00:00:01.3049193 TOTAL +00:00:01.0859551 Take From Empty (In Parallel) +00:00:00.9596546 Give To (In Parallel) +00:00:00.9246417 Mixed Read/Write (In Parallel) +00:00:00.2256799 Empty Pool (.TryTake()) +00:00:03.1959313 TOTAL OptimisticArrayObjectPool............................... -00:00:00.4154931 Take From Empty (In Parallel) -00:00:00.3539234 Give To (In Parallel) -00:00:00.3306813 Mixed Read/Write (In Parallel) -00:00:00.1595689 Empty Pool (.TryTake()) -00:00:01.2596667 TOTAL +00:00:01.0775123 Take From Empty (In Parallel) +00:00:00.9302528 Give To (In Parallel) +00:00:00.9021115 Mixed Read/Write (In Parallel) +00:00:00.4166513 Empty Pool (.TryTake()) +00:00:03.3265279 TOTAL -Repeat 24000 for size 50 +Repeat 48000 for size 50 ------------------------------------ QueueObjectPool......................................... -00:00:00.1983285 Take From Empty (In Parallel) -00:00:00.5393018 Give To (In Parallel) -00:00:00.2520056 Mixed Read/Write (In Parallel) -00:00:00.1437719 Empty Pool (.TryTake()) -00:00:01.1334078 TOTAL +00:00:00.5258334 Take From Empty (In Parallel) +00:00:01.2713375 Give To (In Parallel) +00:00:00.6293988 Mixed Read/Write (In Parallel) +00:00:00.3438183 Empty Pool (.TryTake()) +00:00:02.7703880 TOTAL + +ChannelObjectPool....................................... +00:00:02.4067783 Take From Empty (In Parallel) +00:00:01.5298450 Give To (In Parallel) +00:00:00.6832894 Mixed Read/Write (In Parallel) +00:00:00.3926395 Empty Pool (.TryTake()) +00:00:05.0125522 TOTAL ConcurrentQueueObjectPool............................... -00:00:00.1911597 Take From Empty (In Parallel) -00:00:00.6099586 Give To (In Parallel) -00:00:00.2019195 Mixed Read/Write (In Parallel) -00:00:00.1173744 Empty Pool (.TryTake()) -00:00:01.1204122 TOTAL +00:00:00.5612132 Take From Empty (In Parallel) +00:00:01.4985338 Give To (In Parallel) +00:00:00.4656040 Mixed Read/Write (In Parallel) +00:00:00.3110057 Empty Pool (.TryTake()) +00:00:02.8363567 TOTAL ConcurrentStackObjectPool............................... -00:00:00.1947059 Take From Empty (In Parallel) -00:00:00.2563897 Give To (In Parallel) -00:00:00.2213445 Mixed Read/Write (In Parallel) -00:00:00.1613517 Empty Pool (.TryTake()) -00:00:00.8337918 TOTAL +00:00:00.4972028 Take From Empty (In Parallel) +00:00:00.5200011 Give To (In Parallel) +00:00:00.4378888 Mixed Read/Write (In Parallel) +00:00:00.2969994 Empty Pool (.TryTake()) +00:00:01.7520921 TOTAL OptimisticArrayObjectPool............................... -00:00:00.2011463 Take From Empty (In Parallel) -00:00:00.2458962 Give To (In Parallel) -00:00:00.1567710 Mixed Read/Write (In Parallel) -00:00:00.5725949 Empty Pool (.TryTake()) -00:00:01.1764084 TOTAL +00:00:00.5691812 Take From Empty (In Parallel) +00:00:00.7489502 Give To (In Parallel) +00:00:00.4934400 Mixed Read/Write (In Parallel) +00:00:01.5497079 Empty Pool (.TryTake()) +00:00:03.3612793 TOTAL -Repeat 16000 for size 100 +Repeat 32000 for size 100 ------------------------------------ QueueObjectPool......................................... -00:00:00.1917082 Take From Empty (In Parallel) -00:00:00.7281393 Give To (In Parallel) -00:00:00.4965600 Mixed Read/Write (In Parallel) -00:00:00.1853762 Empty Pool (.TryTake()) -00:00:01.6017837 TOTAL +00:00:00.5122336 Take From Empty (In Parallel) +00:00:02.0243072 Give To (In Parallel) +00:00:00.8949053 Mixed Read/Write (In Parallel) +00:00:00.4559207 Empty Pool (.TryTake()) +00:00:03.8873668 TOTAL + +ChannelObjectPool....................................... +00:00:04.0415940 Take From Empty (In Parallel) +00:00:03.4503339 Give To (In Parallel) +00:00:00.9470840 Mixed Read/Write (In Parallel) +00:00:00.5190160 Empty Pool (.TryTake()) +00:00:08.9580279 TOTAL ConcurrentQueueObjectPool............................... -00:00:00.1913408 Take From Empty (In Parallel) -00:00:00.5323921 Give To (In Parallel) -00:00:06.1167135 Mixed Read/Write (In Parallel) -00:00:00.3403016 Empty Pool (.TryTake()) -00:00:07.1807480 TOTAL +00:00:00.4979936 Take From Empty (In Parallel) +00:00:01.1025353 Give To (In Parallel) +00:00:00.4270060 Mixed Read/Write (In Parallel) +00:00:00.4100476 Empty Pool (.TryTake()) +00:00:02.4375825 TOTAL ConcurrentStackObjectPool............................... -00:00:00.1864700 Take From Empty (In Parallel) -00:00:00.2674701 Give To (In Parallel) -00:00:00.2333379 Mixed Read/Write (In Parallel) -00:00:00.2567077 Empty Pool (.TryTake()) -00:00:00.9439857 TOTAL +00:00:00.4766648 Take From Empty (In Parallel) +00:00:00.5666903 Give To (In Parallel) +00:00:00.5310815 Mixed Read/Write (In Parallel) +00:00:00.3866519 Empty Pool (.TryTake()) +00:00:01.9610885 TOTAL OptimisticArrayObjectPool............................... -00:00:00.2117531 Take From Empty (In Parallel) -00:00:00.3088107 Give To (In Parallel) -00:00:00.1494503 Mixed Read/Write (In Parallel) -00:00:01.2309936 Empty Pool (.TryTake()) -00:00:01.9010077 TOTAL +00:00:00.6956861 Take From Empty (In Parallel) +00:00:01.0975470 Give To (In Parallel) +00:00:00.4499636 Mixed Read/Write (In Parallel) +00:00:03.7336458 Empty Pool (.TryTake()) +00:00:05.9768425 TOTAL -Repeat 25600 for size 250 +Repeat 51200 for size 250 ------------------------------------ QueueObjectPool......................................... -00:00:00.4827432 Take From Empty (In Parallel) -00:00:02.6029248 Give To (In Parallel) -00:00:02.6176686 Mixed Read/Write (In Parallel) -00:00:00.6578479 Empty Pool (.TryTake()) -00:00:06.3611845 TOTAL +00:00:01.6247132 Take From Empty (In Parallel) +00:00:05.7028204 Give To (In Parallel) +00:00:06.3495433 Mixed Read/Write (In Parallel) +00:00:01.7208433 Empty Pool (.TryTake()) +00:00:15.3979202 TOTAL + +ChannelObjectPool....................................... +00:00:09.1002442 Take From Empty (In Parallel) +00:00:09.5148632 Give To (In Parallel) +00:00:07.0393499 Mixed Read/Write (In Parallel) +00:00:01.9543981 Empty Pool (.TryTake()) +00:00:27.6088554 TOTAL ConcurrentQueueObjectPool............................... -00:00:00.7486876 Take From Empty (In Parallel) -00:00:35.1458453 Give To (In Parallel) -00:00:59.9447445 Mixed Read/Write (In Parallel) -00:00:05.0064751 Empty Pool (.TryTake()) -00:01:40.8457525 TOTAL +00:00:01.3937964 Take From Empty (In Parallel) +00:00:03.7235199 Give To (In Parallel) +00:00:01.2171737 Mixed Read/Write (In Parallel) +00:00:01.5041405 Empty Pool (.TryTake()) +00:00:07.8386305 TOTAL ConcurrentStackObjectPool............................... -00:00:00.5360153 Take From Empty (In Parallel) -00:00:01.0427376 Give To (In Parallel) -00:00:00.9601208 Mixed Read/Write (In Parallel) -00:00:01.4689654 Empty Pool (.TryTake()) -00:00:04.0078391 TOTAL +00:00:01.4858700 Take From Empty (In Parallel) +00:00:01.8964279 Give To (In Parallel) +00:00:02.2896336 Mixed Read/Write (In Parallel) +00:00:01.4461137 Empty Pool (.TryTake()) +00:00:07.1180452 TOTAL OptimisticArrayObjectPool............................... -00:00:00.9522035 Take From Empty (In Parallel) -00:00:02.1958969 Give To (In Parallel) -00:00:00.4701271 Mixed Read/Write (In Parallel) -00:00:10.4725119 Empty Pool (.TryTake()) -00:00:14.0907394 TOTAL +00:00:02.9579032 Take From Empty (In Parallel) +00:00:07.0305663 Give To (In Parallel) +00:00:01.2993861 Mixed Read/Write (In Parallel) +00:00:27.8503246 Empty Pool (.TryTake()) +00:00:39.1381802 TOTAL diff --git a/benchmarking/Program.cs b/benchmarking/Program.cs index 981b0ea..8018345 100644 --- a/benchmarking/Program.cs +++ b/benchmarking/Program.cs @@ -26,6 +26,9 @@ static void Main(string[] args) report.AddBenchmark("QueueObjectPool", // Note, that this one isn't far off from the following in peformance. count => () => QueueObjectPool.Create((int)count * 2)); + //report.AddBenchmark("ChannelObjectPool", + // count => () => ChannelObjectPool.Create((int)count * 2)); + report.AddBenchmark("ConcurrentQueueObjectPool", // Note, that this one isn't far off from the following in peformance, but definitely is faster than LinkedListObjectPool and the rest. count => () => ConcurrentQueueObjectPool.Create((int)count * 2)); @@ -43,11 +46,12 @@ static void Main(string[] args) Console.SetCursorPosition(0, Console.CursorTop); - report.Test(4, 8); - report.Test(10, 8); - report.Test(50, 12); - report.Test(100, 16); - report.Test(250, 64); + const int loopMultiple = 2; + report.Test(4, 8 * loopMultiple); + report.Test(10, 8 * loopMultiple); + report.Test(50, 12 * loopMultiple); + report.Test(100, 16 * loopMultiple); + report.Test(250, 64 * loopMultiple); //report.Test(500, 72); File.WriteAllText("./BenchmarkResult.txt", sb.ToString()); diff --git a/source/Channels/ChannelObjectPool.cs b/source/Channels/ChannelObjectPool.cs new file mode 100644 index 0000000..e9d7cb6 --- /dev/null +++ b/source/Channels/ChannelObjectPool.cs @@ -0,0 +1,68 @@ +using System; +using System.Threading.Channels; + +namespace Open.Disposable +{ + public sealed class ChannelObjectPool : ObjectPoolBase + where T : class + { + public ChannelObjectPool(Func factory, Action recycler, int capacity = DEFAULT_CAPACITY) + : base(factory, recycler, capacity) + { + Pool = Channel.CreateBounded(new BoundedChannelOptions(capacity) { FullMode = BoundedChannelFullMode.DropWrite }); + } + + public ChannelObjectPool(Func factory, int capacity = DEFAULT_CAPACITY) + : this(factory, null, capacity) + { } + + Channel Pool; + + protected override void OnDispose(bool calledExplicitly) + { + Pool?.Writer.Complete(); + Pool = null; + } + + protected override bool Receive(T item) + => Pool?.Writer.TryWrite(item) ?? false; + + protected override T TryRelease() + { + T item = null; + Pool?.Reader.TryRead(out item); + return item; + } + } + + + public static class ChannelObjectPool + { + public static ChannelObjectPool Create(Func factory, int capacity = Constants.DEFAULT_CAPACITY) + where T : class + { + return new ChannelObjectPool(factory, capacity); + } + + + public static ChannelObjectPool Create(int capacity = Constants.DEFAULT_CAPACITY) + where T : class, new() + { + return Create(() => new T(), capacity); + } + + public static ChannelObjectPool Create(Func factory, bool autoRecycle, int capacity = Constants.DEFAULT_CAPACITY) + where T : class, IRecyclable + { + Action recycler = null; + if (autoRecycle) recycler = Recycler.Recycle; + return new ChannelObjectPool(factory, recycler, capacity); + } + + public static ChannelObjectPool Create(bool autoRecycle, int capacity = Constants.DEFAULT_CAPACITY) + where T : class, IRecyclable, new() + { + return Create(() => new T(), autoRecycle, capacity); + } + } +} diff --git a/source/Channels/ChanneledRecycler.cs b/source/Channels/ChanneledRecycler.cs new file mode 100644 index 0000000..5773df4 --- /dev/null +++ b/source/Channels/ChanneledRecycler.cs @@ -0,0 +1,108 @@ +using System; +using System.Threading.Channels; +using System.Threading.Tasks; + +namespace Open.Disposable +{ + /// + /// This class is provided as an asynchronous queue for recycling instead of using a recycle delegate with an object pool and calling GiveAsync() which could pile up unnecessarily. + /// So if recycling an object takes extra time, this might be a good way to toss objects away and not have to worry about the heavy cost as they will one by one be processed back into the target pool. + /// + public sealed class ChanneledRecycler : RecyclerBase + where T : class + { + // Something else could be used and could be more performant. + // But it's an ideal interface for what's needed. And the point is that the recyler should not take up too much cpu time. + Channel _bin; + + internal ChanneledRecycler( + IObjectPool target, + Action recycleFunction, + ushort limit = Constants.DEFAULT_CAPACITY) : base(target, recycleFunction, limit) + { + var b = _bin = Channel.CreateBounded(new BoundedChannelOptions(limit) + { + FullMode = BoundedChannelFullMode.DropWrite + }); + + async Task ProcessAsync() + { + var reader = _bin.Reader; + while (Target != null + && await reader.WaitToReadAsync().ConfigureAwait(false)) + { + while (Target != null + && reader.TryRead(out T item)) + { + recycleFunction(item); + Target?.Give(item); + } + } + } + + Completion = ProcessAsync().ContinueWith( + t => t.IsCompleted + ? b.Reader.Completion + : t) + .Unwrap(); + } + + internal ChanneledRecycler( + ushort limit, + IObjectPool pool, + Action recycleFunction) : this(pool, recycleFunction, limit) + { + + } + + public override bool Recycle(T item) + => _bin?.Writer.TryWrite(item) ?? false; + + protected override void OnCloseRequested() + => _bin?.Writer.Complete(); + + protected override void OnDispose(bool calledExplicitly) + { + _bin?.Writer.Complete(); + _bin = null; + Target = null; + } + } + + public static class ChanneledRecycler + { + public static ChanneledRecycler CreateRecycler( + this IObjectPool pool, + Action recycleFunction, + ushort limit = Constants.DEFAULT_CAPACITY) + where T : class + { + return new ChanneledRecycler(pool, recycleFunction, limit); + } + + public static ChanneledRecycler CreateRecycler( + this IObjectPool pool, + ushort limit, + Action recycleFunction) + where T : class + { + return new ChanneledRecycler(pool, recycleFunction, limit); + } + + public static void Recycle(IRecyclable r) + { + r.Recycle(); + } + + public static ChanneledRecycler CreateRecycler( + this IObjectPool pool, + ushort limit = Constants.DEFAULT_CAPACITY) + where T : class, IRecyclable + { + return new ChanneledRecycler(pool, Recycle, limit); + } + + } + + +} diff --git a/source/Collection/CollectionWrapperObjectPool.cs b/source/Collection/CollectionWrapperObjectPool.cs index f996af6..b34aeac 100644 --- a/source/Collection/CollectionWrapperObjectPool.cs +++ b/source/Collection/CollectionWrapperObjectPool.cs @@ -1,8 +1,6 @@ using System; using System.Collections.Generic; using System.Linq; -using System.Text; -using System.Threading.Tasks; namespace Open.Disposable { @@ -10,21 +8,19 @@ public class CollectionWrapperObjectPool : TrimmableObjectPoolBa where T : class where TCollection : class, ICollection { - public CollectionWrapperObjectPool(TCollection pool, Func factory, Action recycler, int capacity = DEFAULT_CAPACITY) - : base(factory, recycler, capacity) + public CollectionWrapperObjectPool(TCollection pool, Func factory, Action recycler, int capacity = DEFAULT_CAPACITY, bool countTrackingEnabled = false) + : base(factory, recycler, capacity, countTrackingEnabled) { Pool = pool; } - public CollectionWrapperObjectPool(TCollection pool, Func factory, int capacity = DEFAULT_CAPACITY) - : this(pool, factory, null, capacity) + public CollectionWrapperObjectPool(TCollection pool, Func factory, int capacity = DEFAULT_CAPACITY, bool countTrackingEnabled = false) + : this(pool, factory, null, capacity, countTrackingEnabled) { } protected TCollection Pool; - public override int Count => Pool?.Count ?? 0; - protected override bool Receive(T item) { var p = Pool; @@ -60,7 +56,7 @@ protected override T TryRelease() } return item; - } + } protected override void OnDispose(bool calledExplicitly) { @@ -71,7 +67,7 @@ protected override void OnDispose(bool calledExplicitly) public class CollectionWrapperObjectPool : CollectionWrapperObjectPool> where T : class { - public CollectionWrapperObjectPool(ICollection pool, Func factory, int capacity = DEFAULT_CAPACITY) : base(pool, factory, capacity) + public CollectionWrapperObjectPool(ICollection pool, Func factory, int capacity = DEFAULT_CAPACITY, bool countTrackingEnabled = false) : base(pool, factory, capacity, countTrackingEnabled) { } } diff --git a/source/Collection/ConcurrentQueueObjectPool.cs b/source/Collection/ConcurrentQueueObjectPool.cs index 8e9dd41..a6d8e3e 100644 --- a/source/Collection/ConcurrentQueueObjectPool.cs +++ b/source/Collection/ConcurrentQueueObjectPool.cs @@ -7,32 +7,24 @@ public sealed class ConcurrentQueueObjectPool : TrimmableObjectPoolBase where T : class { - public ConcurrentQueueObjectPool(Func factory, Action recycler, int capacity = DEFAULT_CAPACITY) - : base(factory, recycler, capacity) + public ConcurrentQueueObjectPool(Func factory, Action recycler, int capacity = DEFAULT_CAPACITY, bool countTrackingEnabled = false) + : base(factory, recycler, capacity, countTrackingEnabled) { Pool = new ConcurrentQueue(); } - public ConcurrentQueueObjectPool(Func factory, int capacity = DEFAULT_CAPACITY) - : this(factory, null, capacity) + public ConcurrentQueueObjectPool(Func factory, int capacity = DEFAULT_CAPACITY, bool countTrackingEnabled = false) + : this(factory, null, capacity, countTrackingEnabled) { } ConcurrentQueue Pool; - public override int Count => Pool?.Count ?? 0; - - // For ConcurrentQueue disable using the Pocket. It's fast enough as it is... - protected override bool SaveToPocket(T item) - { - return false; - } - - protected override T TakeFromPocket() - { - return null; - } + /* + * NOTE: ConcurrentQueue is very fast and will perform quite well without using the 'Pocket' feature. + * Benchmarking reveals that mixed read/writes (what really matters) are still faster with the pocket enabled so best to keep it so. + */ protected override bool Receive(T item) { @@ -52,7 +44,7 @@ protected override T TryRelease() protected override void OnDispose(bool calledExplicitly) { - if(calledExplicitly) + if (calledExplicitly) { Pool = null; } @@ -62,31 +54,31 @@ protected override void OnDispose(bool calledExplicitly) public static class ConcurrentQueueObjectPool { - public static ConcurrentQueueObjectPool Create(Func factory, int capacity = Constants.DEFAULT_CAPACITY) + public static ConcurrentQueueObjectPool Create(Func factory, int capacity = Constants.DEFAULT_CAPACITY, bool countTrackingEnabled = false) where T : class { - return new ConcurrentQueueObjectPool(factory, capacity); + return new ConcurrentQueueObjectPool(factory, capacity, countTrackingEnabled); } - public static ConcurrentQueueObjectPool Create(int capacity = Constants.DEFAULT_CAPACITY) + public static ConcurrentQueueObjectPool Create(int capacity = Constants.DEFAULT_CAPACITY, bool countTrackingEnabled = false) where T : class, new() { - return Create(() => new T(), capacity); + return Create(() => new T(), capacity, countTrackingEnabled); } - public static ConcurrentQueueObjectPool Create(Func factory, bool autoRecycle, int capacity = Constants.DEFAULT_CAPACITY) + public static ConcurrentQueueObjectPool Create(Func factory, bool autoRecycle, int capacity = Constants.DEFAULT_CAPACITY, bool countTrackingEnabled = false) where T : class, IRecyclable { Action recycler = null; if (autoRecycle) recycler = Recycler.Recycle; - return new ConcurrentQueueObjectPool(factory, recycler, capacity); + return new ConcurrentQueueObjectPool(factory, recycler, capacity, countTrackingEnabled); } - public static ConcurrentQueueObjectPool Create(bool autoRecycle, int capacity = Constants.DEFAULT_CAPACITY) + public static ConcurrentQueueObjectPool Create(bool autoRecycle, int capacity = Constants.DEFAULT_CAPACITY, bool countTrackingEnabled = false) where T : class, IRecyclable, new() { - return Create(() => new T(), autoRecycle, capacity); + return Create(() => new T(), autoRecycle, capacity, countTrackingEnabled); } } } diff --git a/source/Collection/ConcurrentStackObjectPool.cs b/source/Collection/ConcurrentStackObjectPool.cs index 8b074da..9fd3861 100644 --- a/source/Collection/ConcurrentStackObjectPool.cs +++ b/source/Collection/ConcurrentStackObjectPool.cs @@ -1,7 +1,5 @@ using System; using System.Collections.Concurrent; -using System.Collections.Generic; -using System.Text; namespace Open.Disposable { @@ -9,22 +7,20 @@ public sealed class ConcurrentStackObjectPool : TrimmableObjectPoolBase where T : class { - public ConcurrentStackObjectPool(Func factory, Action recycler, int capacity = DEFAULT_CAPACITY) - : base(factory, recycler, capacity) + public ConcurrentStackObjectPool(Func factory, Action recycler, int capacity = DEFAULT_CAPACITY, bool countTrackingEnabled = false) + : base(factory, recycler, capacity, countTrackingEnabled) { Pool = new ConcurrentStack(); } - public ConcurrentStackObjectPool(Func factory, int capacity = DEFAULT_CAPACITY) - : this(factory, null, capacity) + public ConcurrentStackObjectPool(Func factory, int capacity = DEFAULT_CAPACITY, bool countTrackingEnabled = false) + : this(factory, null, capacity, countTrackingEnabled) { - + } ConcurrentStack Pool; - public override int Count => Pool?.Count ?? 0; - protected override bool Receive(T item) { var p = Pool; @@ -50,31 +46,31 @@ protected override void OnDispose(bool calledExplicitly) public static class ConcurrentStackObjectPool { - public static ConcurrentStackObjectPool Create(Func factory, int capacity = Constants.DEFAULT_CAPACITY) + public static ConcurrentStackObjectPool Create(Func factory, int capacity = Constants.DEFAULT_CAPACITY, bool countTrackingEnabled = false) where T : class { - return new ConcurrentStackObjectPool(factory, capacity); + return new ConcurrentStackObjectPool(factory, capacity, countTrackingEnabled); } - public static ConcurrentStackObjectPool Create(int capacity = Constants.DEFAULT_CAPACITY) + public static ConcurrentStackObjectPool Create(int capacity = Constants.DEFAULT_CAPACITY, bool countTrackingEnabled = false) where T : class, new() { - return Create(() => new T(), capacity); + return Create(() => new T(), capacity, countTrackingEnabled); } - public static ConcurrentStackObjectPool Create(Func factory, bool autoRecycle, int capacity = Constants.DEFAULT_CAPACITY) - where T : class, IRecyclable - { - Action recycler = null; - if (autoRecycle) recycler = Recycler.Recycle; - return new ConcurrentStackObjectPool(factory, recycler, capacity); - } + public static ConcurrentStackObjectPool Create(Func factory, bool autoRecycle, int capacity = Constants.DEFAULT_CAPACITY, bool countTrackingEnabled = false) + where T : class, IRecyclable + { + Action recycler = null; + if (autoRecycle) recycler = Recycler.Recycle; + return new ConcurrentStackObjectPool(factory, recycler, capacity, countTrackingEnabled); + } - public static ConcurrentStackObjectPool Create(bool autoRecycle, int capacity = Constants.DEFAULT_CAPACITY) - where T : class, IRecyclable, new() - { - return Create(() => new T(), autoRecycle, capacity); - } + public static ConcurrentStackObjectPool Create(bool autoRecycle, int capacity = Constants.DEFAULT_CAPACITY, bool countTrackingEnabled = false) + where T : class, IRecyclable, new() + { + return Create(() => new T(), autoRecycle, capacity, countTrackingEnabled); + } - } + } } diff --git a/source/Collection/QueueObjectPool.cs b/source/Collection/QueueObjectPool.cs index db4b99a..4ca7fa9 100644 --- a/source/Collection/QueueObjectPool.cs +++ b/source/Collection/QueueObjectPool.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.Text; namespace Open.Disposable { @@ -8,22 +7,20 @@ public sealed class QueueObjectPool : TrimmableObjectPoolBase where T : class { - public QueueObjectPool(Func factory, Action recycler, int capacity = DEFAULT_CAPACITY) - : base(factory, recycler, capacity) + public QueueObjectPool(Func factory, Action recycler, int capacity = DEFAULT_CAPACITY, bool countTrackingEnabled = false) + : base(factory, recycler, capacity, countTrackingEnabled) { Pool = new Queue(capacity); // Very very slight speed improvment when capacity is set. } - public QueueObjectPool(Func factory, int capacity = DEFAULT_CAPACITY) - : this(factory, null, capacity) + public QueueObjectPool(Func factory, int capacity = DEFAULT_CAPACITY, bool countTrackingEnabled = false) + : this(factory, null, capacity, countTrackingEnabled) { } Queue Pool; - public override int Count => Pool?.Count ?? 0; - protected override bool Receive(T item) { var p = Pool; @@ -62,30 +59,30 @@ protected override void OnDispose(bool calledExplicitly) public static class QueueObjectPool { - public static QueueObjectPool Create(Func factory, int capacity = Constants.DEFAULT_CAPACITY) + public static QueueObjectPool Create(Func factory, int capacity = Constants.DEFAULT_CAPACITY, bool countTrackingEnabled = false) where T : class { - return new QueueObjectPool(factory, capacity); + return new QueueObjectPool(factory, capacity, countTrackingEnabled); } - public static QueueObjectPool Create(int capacity = Constants.DEFAULT_CAPACITY) + public static QueueObjectPool Create(int capacity = Constants.DEFAULT_CAPACITY, bool countTrackingEnabled = false) where T : class, new() { - return Create(() => new T(), capacity); + return Create(() => new T(), capacity, countTrackingEnabled); } - public static QueueObjectPool Create(Func factory, bool autoRecycle, int capacity = Constants.DEFAULT_CAPACITY) + public static QueueObjectPool Create(Func factory, bool autoRecycle, int capacity = Constants.DEFAULT_CAPACITY, bool countTrackingEnabled = false) where T : class, IRecyclable { Action recycler = null; if (autoRecycle) recycler = Recycler.Recycle; - return new QueueObjectPool(factory, recycler, capacity); + return new QueueObjectPool(factory, recycler, capacity, countTrackingEnabled); } - public static QueueObjectPool Create(bool autoRecycle, int capacity = Constants.DEFAULT_CAPACITY) + public static QueueObjectPool Create(bool autoRecycle, int capacity = Constants.DEFAULT_CAPACITY, bool countTrackingEnabled = false) where T : class, IRecyclable, new() { - return Create(() => new T(), autoRecycle, capacity); + return Create(() => new T(), autoRecycle, capacity, countTrackingEnabled); } } diff --git a/source/Collection/StackObjectPool.cs b/source/Collection/StackObjectPool.cs index f857277..5a28b7b 100644 --- a/source/Collection/StackObjectPool.cs +++ b/source/Collection/StackObjectPool.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.Text; namespace Open.Disposable { @@ -8,26 +7,24 @@ public sealed class StackObjectPool : TrimmableObjectPoolBase where T : class { - public StackObjectPool(Func factory, Action recycler, int capacity = DEFAULT_CAPACITY) - : base(factory, recycler, capacity) + public StackObjectPool(Func factory, Action recycler, int capacity = DEFAULT_CAPACITY, bool countTrackingEnabled = false) + : base(factory, recycler, capacity, countTrackingEnabled) { Pool = new Stack(capacity); // Very very slight speed improvment when capacity is set. } - public StackObjectPool(Func factory, int capacity = DEFAULT_CAPACITY) - : this(factory, null, capacity) + public StackObjectPool(Func factory, int capacity = DEFAULT_CAPACITY, bool countTrackingEnabled = false) + : this(factory, null, capacity, countTrackingEnabled) { - + } Stack Pool; - public override int Count => Pool?.Count ?? 0; - protected override bool Receive(T item) { var p = Pool; - if (p!=null) + if (p != null) { // It's possible that the count could exceed MaxSize here, but the risk is negligble as a few over the limit won't hurt. // The lock operation should be quick enough to not pile up too many items. @@ -41,11 +38,11 @@ protected override bool Receive(T item) protected override T TryRelease() { var p = Pool; - if (p!=null && p.Count != 0) + if (p != null && p.Count != 0) { lock (p) { - if (p.Count!=0) + if (p.Count != 0) return p.Pop(); } @@ -61,32 +58,32 @@ protected override void OnDispose(bool calledExplicitly) } public static class StackObjectPool - { - public static StackObjectPool Create(Func factory, int capacity = Constants.DEFAULT_CAPACITY) + { + public static StackObjectPool Create(Func factory, int capacity = Constants.DEFAULT_CAPACITY, bool countTrackingEnabled = false) where T : class { - return new StackObjectPool(factory, capacity); + return new StackObjectPool(factory, capacity, countTrackingEnabled); } - public static StackObjectPool Create(int capacity = Constants.DEFAULT_CAPACITY) + public static StackObjectPool Create(int capacity = Constants.DEFAULT_CAPACITY, bool countTrackingEnabled = false) where T : class, new() { - return Create(() => new T(), capacity); + return Create(() => new T(), capacity, countTrackingEnabled); + } + + public static StackObjectPool Create(Func factory, bool autoRecycle, int capacity = Constants.DEFAULT_CAPACITY, bool countTrackingEnabled = false) + where T : class, IRecyclable + { + Action recycler = null; + if (autoRecycle) recycler = Recycler.Recycle; + return new StackObjectPool(factory, recycler, capacity, countTrackingEnabled); } - public static StackObjectPool Create(Func factory, bool autoRecycle, int capacity = Constants.DEFAULT_CAPACITY) - where T : class, IRecyclable - { - Action recycler = null; - if (autoRecycle) recycler = Recycler.Recycle; - return new StackObjectPool(factory, recycler, capacity); - } - - public static StackObjectPool Create(bool autoRecycle, int capacity = Constants.DEFAULT_CAPACITY) - where T : class, IRecyclable, new() - { - return Create(() => new T(), autoRecycle, capacity); - } - - } + public static StackObjectPool Create(bool autoRecycle, int capacity = Constants.DEFAULT_CAPACITY, bool countTrackingEnabled = false) + where T : class, IRecyclable, new() + { + return Create(() => new T(), autoRecycle, capacity, countTrackingEnabled); + } + + } } diff --git a/source/Constants.cs b/source/Constants.cs index 464a259..fc50592 100644 --- a/source/Constants.cs +++ b/source/Constants.cs @@ -1,11 +1,7 @@ -using System; -using System.Collections.Generic; -using System.Text; - -namespace Open.Disposable +namespace Open.Disposable { - internal static class Constants - { + internal static class Constants + { internal const int DEFAULT_CAPACITY = 128; // Should accomodate all object pools without decreasing their effectiveness. } } diff --git a/source/GlobalSuppressions.cs b/source/GlobalSuppressions.cs new file mode 100644 index 0000000..564903a --- /dev/null +++ b/source/GlobalSuppressions.cs @@ -0,0 +1,9 @@ + +// This file is used by Code Analysis to maintain SuppressMessage +// attributes that are applied to this project. +// Project-level suppressions either have no target or are given +// a specific target and scoped to a namespace, type, member, etc. + +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Performance", "CA1815:Override equals and operator equals on value types", Justification = "", Scope = "type", Target = "~T:Open.Disposable.ReferenceContainer`1")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Performance", "CA1815:Override equals and operator equals on value types", Justification = "", Scope = "type", Target = "~T:Open.Disposable.DualReferenceContainer`1")] + diff --git a/source/IObjectPool.cs b/source/IObjectPool.cs index d4864ab..be3a16f 100644 --- a/source/IObjectPool.cs +++ b/source/IObjectPool.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.Text; using System.Threading.Tasks; namespace Open.Disposable @@ -26,14 +25,6 @@ public interface IObjectPool : IDisposable /// An optional action exectue on the item only if it's possible to return to the pool. void Give(T item); - /// - /// Asynchronously receives an item and adds it to the pool. Ignores null references. - /// WARNING: The item is considered 'dead' but resurrectable so be sure not to hold on to the item's reference. - /// - /// The item to give up to the pool. - /// An optional action exectue on the item only if it's possible to return to the pool. - Task GiveAsync(T item); - /// /// If the pool has an item currently avaialable, removes it from the pool and provides it as the out parameter. /// @@ -55,6 +46,16 @@ public interface IObjectPool : IDisposable /// An item removed from the pool or generated. Should never be null. T Take(); + + + /// + /// Asynchronously receives an item and adds it to the pool. Ignores null references. + /// WARNING: The item is considered 'dead' but resurrectable so be sure not to hold on to the item's reference. + /// + /// The item to give up to the pool. + /// An optional action exectue on the item only if it's possible to return to the pool. + Task GiveAsync(T item); + /// /// Awaits an available item from the pool. If none are available it generates one. /// @@ -62,7 +63,7 @@ public interface IObjectPool : IDisposable Task TakeAsync(); } - public static class ObjectPoolExtensions + public static partial class ObjectPoolExtensions { /// /// Receives items and iteratively adds them to the pool. @@ -92,6 +93,8 @@ public static void Give(this IObjectPool target, T item1, T item2, params target.Give(items); } + + /// /// Asynchronously receives items and iteratively adds them to the pool. /// WARNING: These items are considered 'dead' but resurrectable so be sure not to hold on to their reference. diff --git a/source/IRecyclable.cs b/source/IRecyclable.cs index 03a14c5..a87e734 100644 --- a/source/IRecyclable.cs +++ b/source/IRecyclable.cs @@ -1,11 +1,7 @@ -using System; -using System.Collections.Generic; -using System.Text; - -namespace Open.Disposable +namespace Open.Disposable { - public interface IRecyclable - { - void Recycle(); - } + public interface IRecyclable + { + void Recycle(); + } } diff --git a/source/IRecycler.cs b/source/IRecycler.cs new file mode 100644 index 0000000..445bdd7 --- /dev/null +++ b/source/IRecycler.cs @@ -0,0 +1,12 @@ +using System; +using System.Threading.Tasks; + +namespace Open.Disposable +{ + interface IRecycler : IDisposable + where T : class + { + bool Recycle(T item); + Task Close(); + } +} diff --git a/source/ITrimmableObjectPool.cs b/source/ITrimmableObjectPool.cs index 902bee9..f9fc680 100644 --- a/source/ITrimmableObjectPool.cs +++ b/source/ITrimmableObjectPool.cs @@ -1,12 +1,12 @@ -using System; - -namespace Open.Disposable +namespace Open.Disposable { public interface ITrimmableObjectPool - { + { + bool CountTrackingEnabled { get; } + event ObjectPoolResizeEvent Received; event ObjectPoolResizeEvent Released; void TrimTo(int targetSize); - } + } } diff --git a/source/ObjectPoolAutoTrimmer.cs b/source/ObjectPoolAutoTrimmer.cs index 308533b..6dbfd03 100644 --- a/source/ObjectPoolAutoTrimmer.cs +++ b/source/ObjectPoolAutoTrimmer.cs @@ -33,7 +33,16 @@ public ObjectPoolAutoTrimmer( ITrimmableObjectPool pool, TimeSpan? trimDelay = null) { - _pool = pool ?? throw new ArgumentNullException("pool"); + _pool = pool ?? throw new ArgumentNullException(nameof(pool)); + + if (!pool.CountTrackingEnabled) + throw new ArgumentException("Only pools with count tracking enabled can be trimmed.", nameof(pool)); + + if (pool is DisposableBase d) + { + if (d.IsDisposed) throw new ArgumentException("Cannot trim for an object pool that is already disposed."); + d.BeforeDispose += Pool_BeforeDispose; + } TrimmedSize = _trimmedSize = trimmedSize; TrimDelay = _trimDelay = trimDelay ?? TimeSpan.FromMilliseconds(500); @@ -42,17 +51,11 @@ public ObjectPoolAutoTrimmer( pool.Received += Target_GivenTo; pool.Released += Target_TakenFrom; - - if (pool is DisposableBase d) - { - if (d.IsDisposed) throw new ArgumentException("Cannot trim for an object pool that is already disposed."); - d.BeforeDispose += Pool_BeforeDispose; - } } protected virtual void Target_GivenTo(int newSize) { - if (newSize>=0 && newSize > _trimmedSize) + if (newSize >= 0 && newSize > _trimmedSize) _trimmer?.Defer(_trimDelay, false); } diff --git a/source/ObjectPoolBase.cs b/source/ObjectPoolBase.cs index 0a92fe7..f51813d 100644 --- a/source/ObjectPoolBase.cs +++ b/source/ObjectPoolBase.cs @@ -1,5 +1,4 @@ using System; -using System.Threading; using System.Threading.Tasks; namespace Open.Disposable @@ -12,8 +11,8 @@ public abstract class ObjectPoolBase : DisposableBase, IObjectPool protected ObjectPoolBase(Func factory, Action recycler, int capacity = DEFAULT_CAPACITY) { if (capacity < 1) - throw new ArgumentOutOfRangeException("capacity", capacity, "Must be at least 1."); - Factory = factory ?? throw new ArgumentNullException("factory"); + throw new ArgumentOutOfRangeException(nameof(capacity), capacity, "Must be at least 1."); + Factory = factory ?? throw new ArgumentNullException(nameof(factory)); MaxSize = capacity; Recycler = recycler; } @@ -37,10 +36,10 @@ public T Generate() return Factory(); } - protected ReferenceContainer Pocket; + protected ReferenceContainer Pocket; - #region Receive (.Give(T item)) - protected virtual bool CanReceive => true; + #region Receive (.Give(T item)) + protected virtual bool CanReceive => true; protected bool PrepareToReceive(T item) { @@ -78,28 +77,28 @@ public void Give(T item) protected virtual Task ReceiveAsync(T item) { - return Task.Run(() => Receive(item)); + return Task.Run(() => Receive(item)); // Default implemnation, can be overridden. } public Task GiveAsync(T item) { // We need to pre-check CanReceive because excessive tasks could build up if not. if (item == null || !CanReceive) - return Task.CompletedTask; - + return Task.CompletedTask; + return ReceiveConditionalAsync(item); } async Task ReceiveConditionalAsync(T item) { - if(PrepareToReceive(item) + if (PrepareToReceive(item) && (SaveToPocket(item) || await ReceiveAsync(item).ConfigureAwait(false))) // Doesn't need original context. Just needs to trigger OnReceived(). OnReceived(); } - #endregion + #endregion - #region Release (.Take()) - public virtual T Take() + #region Release (.Take()) + public virtual T Take() { return TryTake() ?? Factory(); } @@ -124,14 +123,14 @@ public bool TryTake(out T item) return item != null; } - protected virtual bool SaveToPocket(T item) - { - return Pocket.TrySave(item); - } + protected virtual bool SaveToPocket(T item) + { + return Pocket.TrySave(item); + } - protected virtual T TakeFromPocket() - { - return Pocket.TryRetrieve(); + protected virtual T TakeFromPocket() + { + return Pocket.TryRetrieve(); } protected abstract T TryRelease(); @@ -150,9 +149,9 @@ protected void OnReleased(bool wasTaken) { if (wasTaken) OnReleased(); } - #endregion + #endregion - protected override void OnBeforeDispose() + protected override void OnBeforeDispose() { MaxSize = 0; } diff --git a/source/ObjectPoolResizeEvent.cs b/source/ObjectPoolResizeEvent.cs index faf66c1..71b92ff 100644 --- a/source/ObjectPoolResizeEvent.cs +++ b/source/ObjectPoolResizeEvent.cs @@ -1,6 +1,4 @@ -using System; - -namespace Open.Disposable +namespace Open.Disposable { public delegate void ObjectPoolResizeEvent(int newSize); } diff --git a/source/Open.Disposable.ObjectPools.csproj b/source/Open.Disposable.ObjectPools.csproj index 2468cd4..b3b59c4 100644 --- a/source/Open.Disposable.ObjectPools.csproj +++ b/source/Open.Disposable.ObjectPools.csproj @@ -22,6 +22,12 @@ Part of the "Open" set of libraries. Updated Open.Threading.Tasks reference (only used for trimming). + + + + + + diff --git a/source/Recycler.cs b/source/Recycler.cs index d2327ba..9256649 100644 --- a/source/Recycler.cs +++ b/source/Recycler.cs @@ -1,5 +1,4 @@ using System; -using System.Threading.Tasks; using System.Threading.Tasks.Dataflow; namespace Open.Disposable @@ -8,41 +7,31 @@ namespace Open.Disposable /// This class is provided as an asynchronous queue for recycling instead of using a recycle delegate with an object pool and calling GiveAsync() which could pile up unnecessarily. /// So if recycling an object takes extra time, this might be a good way to toss objects away and not have to worry about the heavy cost as they will one by one be processed back into the target pool. /// - public class Recycler : DisposableBase + public class Recycler : RecyclerBase where T : class { - IObjectPool _target; - // Something else could be used and could be more performant. // But it's an ideal interface for what's needed. And the point is that the recyler should not take up too much cpu time. ActionBlock _bin; - internal Recycler( + internal Recycler( IObjectPool target, Action recycleFunction, - ushort limit = Constants.DEFAULT_CAPACITY) + ushort limit = Constants.DEFAULT_CAPACITY) : base(target, recycleFunction, limit) { - if(recycleFunction==null) throw new ArgumentNullException("recycleFunction"); - _target = target ?? throw new ArgumentNullException("target"); - _bin = new ActionBlock(item => { - if (_target != null) + if (Target != null) { recycleFunction(item); - _target?.Give(item); + Target?.Give(item); } }); - if (target is DisposableBase d) - { - if (d.IsDisposed) throw new ArgumentException("Cannot recycle for an object pool that is already disposed."); - d.BeforeDispose += Pool_BeforeDispose; - // Could possibly dispose before this line somewhere... But that's just nasty. :P - } + Completion = _bin.Completion; } - internal Recycler( + internal Recycler( ushort limit, IObjectPool pool, Action recycleFunction) : this(pool, recycleFunction, limit) @@ -50,64 +39,53 @@ internal Recycler( } - void Pool_BeforeDispose(object sender, EventArgs e) - { - Dispose(); - } - - public bool Recycle(T item) + public override bool Recycle(T item) { return _bin?.Post(item) ?? false; } - public Task Close() - { - var b = _bin; - if (b == null) return Task.CompletedTask; - b.Complete(); - return b.Completion; - } + protected override void OnCloseRequested() + => _bin?.Complete(); protected override void OnDispose(bool calledExplicitly) { - _bin.Complete(); - _bin = null; - _target = null; + base.OnDispose(calledExplicitly); + if (calledExplicitly) _bin = null; } } - public static class Recycler - { - public static Recycler CreateRecycler( - this IObjectPool pool, - Action recycleFunction, - ushort limit = Constants.DEFAULT_CAPACITY) - where T : class - { - return new Recycler(pool, recycleFunction, limit); - } + public static class Recycler + { + public static Recycler CreateRecycler( + this IObjectPool pool, + Action recycleFunction, + ushort limit = Constants.DEFAULT_CAPACITY) + where T : class + { + return new Recycler(pool, recycleFunction, limit); + } - public static Recycler CreateRecycler( - this IObjectPool pool, - ushort limit, - Action recycleFunction) - where T : class - { - return new Recycler(pool, recycleFunction, limit); - } + public static Recycler CreateRecycler( + this IObjectPool pool, + ushort limit, + Action recycleFunction) + where T : class + { + return new Recycler(pool, recycleFunction, limit); + } - public static void Recycle(IRecyclable r) - { - r.Recycle(); - } + public static void Recycle(IRecyclable r) + { + r.Recycle(); + } - public static Recycler CreateRecycler( - this IObjectPool pool, - ushort limit = Constants.DEFAULT_CAPACITY) - where T : class, IRecyclable - { - return new Recycler(pool, Recycle, limit); - } + public static Recycler CreateRecycler( + this IObjectPool pool, + ushort limit = Constants.DEFAULT_CAPACITY) + where T : class, IRecyclable + { + return new Recycler(pool, Recycle, limit); + } - } + } } diff --git a/source/RecyclerBase.cs b/source/RecyclerBase.cs new file mode 100644 index 0000000..a83fcab --- /dev/null +++ b/source/RecyclerBase.cs @@ -0,0 +1,51 @@ +using System; +using System.Threading.Tasks; + +namespace Open.Disposable +{ + /// + /// This class is provided as an asynchronous queue for recycling instead of using a recycle delegate with an object pool and calling GiveAsync() which could pile up unnecessarily. + /// So if recycling an object takes extra time, this might be a good way to toss objects away and not have to worry about the heavy cost as they will one by one be processed back into the target pool. + /// + public abstract class RecyclerBase : DisposableBase, IRecycler + where T : class + { + protected IObjectPool Target; + + protected RecyclerBase( + IObjectPool target, + Action recycleFunction, + ushort limit = Constants.DEFAULT_CAPACITY) + { + if (recycleFunction == null) throw new ArgumentNullException(nameof(recycleFunction)); + Target = target ?? throw new ArgumentNullException(nameof(target)); + if (target is DisposableBase d) + { + if (d.IsDisposed) throw new ArgumentException("Cannot recycle for an object pool that is already disposed."); + d.BeforeDispose += Pool_BeforeDispose; + // Could possibly dispose before this line somewhere... But that's just nasty. :P + } + } + + void Pool_BeforeDispose(object sender, EventArgs e) => Dispose(); + + public abstract bool Recycle(T item); + + protected abstract void OnCloseRequested(); + + public Task Completion { get; protected set; } + + public Task Close() + { + OnCloseRequested(); + return Completion; + } + + protected override void OnDispose(bool calledExplicitly) + { + OnCloseRequested(); + if (calledExplicitly) Target = null; + } + } + +} diff --git a/source/ReferenceContainer.cs b/source/ReferenceContainer.cs index dcb1be0..fe4ebd2 100644 --- a/source/ReferenceContainer.cs +++ b/source/ReferenceContainer.cs @@ -3,78 +3,78 @@ namespace Open.Disposable { - public interface IReferenceContainer - where T : class - { - int Capacity { get; } - bool SetIfNull(T value); - bool TrySave(T value); - T TryRetrieve(); - } + public interface IReferenceContainer + where T : class + { + int Capacity { get; } + bool SetIfNull(T value); + bool TrySave(T value); + T TryRetrieve(); + } - [DebuggerDisplay("{Value,nq}")] - public struct ReferenceContainer : IReferenceContainer - where T : class - { - public int Capacity => 1; + [DebuggerDisplay("{Value,nq}")] + public struct ReferenceContainer : IReferenceContainer + where T : class + { + public int Capacity => 1; - T _value; + T _value; - public T Value - { - get => _value; - set => _value = value; - } + public T Value + { + get => _value; + set => _value = value; + } - public bool SetIfNull(T value) - { - if (_value == null) - { - _value = value; - return true; - } + public bool SetIfNull(T value) + { + if (_value == null) + { + _value = value; + return true; + } - return false; - } + return false; + } - public bool TrySave(T value) - { - return _value == null - && null == Interlocked.CompareExchange(ref _value, value, null); - } + public bool TrySave(T value) + { + return _value == null + && null == Interlocked.CompareExchange(ref _value, value, null); + } - public T TryRetrieve() - { - var item = _value; - return (item != null - && item == Interlocked.CompareExchange(ref _value, null, item)) - ? item - : null; - } - } + public T TryRetrieve() + { + var item = _value; + return (item != null + && item == Interlocked.CompareExchange(ref _value, null, item)) + ? item + : null; + } + } - public struct DualReferenceContainer : IReferenceContainer - where T : class - { - public int Capacity => 2; + public struct DualReferenceContainer : IReferenceContainer + where T : class + { + public int Capacity => 2; - ReferenceContainer _1; - ReferenceContainer _2; + ReferenceContainer _1; + ReferenceContainer _2; - public bool SetIfNull(T value) - { - return _1.SetIfNull(value) || _2.SetIfNull(value); - } + public bool SetIfNull(T value) + { + return _1.SetIfNull(value) || _2.SetIfNull(value); + } - public T TryRetrieve() - { - return _1.TryRetrieve() ?? _2.TryRetrieve(); - } + public T TryRetrieve() + { + return _1.TryRetrieve() ?? _2.TryRetrieve(); + } - public bool TrySave(T value) - { - return _1.TrySave(value) || _2.TrySave(value); - } - } + public bool TrySave(T value) + { + return _1.TrySave(value) || _2.TrySave(value); + } + } } diff --git a/source/TrimmableObjectPoolBase.cs b/source/TrimmableObjectPoolBase.cs index 052ad2e..f79e180 100644 --- a/source/TrimmableObjectPoolBase.cs +++ b/source/TrimmableObjectPoolBase.cs @@ -1,30 +1,36 @@ using System; using System.Diagnostics; +using System.Threading; namespace Open.Disposable { - [DebuggerDisplay("Count = {Count}")] + [DebuggerDisplay("Count = {_count}")] public abstract class TrimmableObjectPoolBase : ObjectPoolBase, ITrimmableObjectPool where T : class - { - - - public TrimmableObjectPoolBase(Func factory, Action recycler, int capacity = DEFAULT_CAPACITY) + { + protected TrimmableObjectPoolBase(Func factory, Action recycler, int capacity, bool countTrackingEnabled) : base(factory, recycler, capacity) { + _countTrackingEnabled = countTrackingEnabled; } - public TrimmableObjectPoolBase(Func factory, int capacity = DEFAULT_CAPACITY) - : this(factory, null, capacity) + protected TrimmableObjectPoolBase(Func factory, int capacity, bool countTrackingEnabled) + : this(factory, null, capacity, countTrackingEnabled) { } + bool _countTrackingEnabled; + public bool CountTrackingEnabled => _countTrackingEnabled; + + + int _count; + /// /// Total number of items in the pool. /// - public abstract int Count { get; } + public virtual int Count => _count; // Track this number instead of calling .Count on a collection. - protected override bool CanReceive => Count < MaxSize; + protected override bool CanReceive => _count < MaxSize; /// /// Signal for when an item was taken (actually removed) from the pool. @@ -32,45 +38,66 @@ public TrimmableObjectPoolBase(Func factory, int capacity = DEFAULT_CAPACITY) public event ObjectPoolResizeEvent Released; protected void OnReleased(int newSize) { - Released?.Invoke(newSize); + if (_countTrackingEnabled) + { + Released?.Invoke(newSize); + } + } protected override void OnReleased() { - OnReleased(Count); + if (_countTrackingEnabled) + { + var c = Interlocked.Decrement(ref _count); + OnReleased(c); + Debug.Assert(c >= 0); + } } - + /// /// Signal for when an item was given (actually accepted) to the pool. /// public event ObjectPoolResizeEvent Received; protected void OnReceived(int newSize) { - Received?.Invoke(newSize); + if (_countTrackingEnabled) + { + Received?.Invoke(newSize); + } } protected override void OnReceived() { - OnReceived(Count); + if (_countTrackingEnabled) + { + OnReceived(Interlocked.Increment(ref _count)); + } } public virtual void TrimTo(int targetSize) { + if (!_countTrackingEnabled) + throw new InvalidOperationException("Cannot trim an object pool with count tracking disabled."); + if (targetSize < 0) return; // Possible upstream math hiccup or default -1. Silently dismiss. - int count = Count; + int count = _count; - int i = 0; // Prevent an possiblility of indefinite loop. - for ( - var attempts = count - targetSize; - i < attempts && count > targetSize; - i++) + if (count > targetSize) { - if (TryRelease() == null) - break; + int i = 0; // Prevent an possiblility of indefinite loop. + for ( + var attempts = count - targetSize; + i < attempts; + i++) + { + if (TryRelease() == null) + break; - count = Count; - } + Interlocked.Decrement(ref _count); + } - if (i != 0) OnReleased(count); + if (i != 0) OnReleased(_count); + } } } From e58aaf10400bcc94fd6696430b8daa44bf34332a Mon Sep 17 00:00:00 2001 From: Oren Ferrari Date: Thu, 3 May 2018 15:33:31 -0700 Subject: [PATCH 14/83] There is simply no identifiable reason object pools should be asynchronous. --- Open.Disposable.ObjectPools.sln | 31 ++++++++++++++++ source/Channels/ChannelObjectPool.cs | 2 +- source/IObjectPool.cs | 44 ++--------------------- source/ObjectPoolBase.cs | 36 ------------------- source/Open.Disposable.ObjectPools.csproj | 7 +--- 5 files changed, 35 insertions(+), 85 deletions(-) create mode 100644 Open.Disposable.ObjectPools.sln diff --git a/Open.Disposable.ObjectPools.sln b/Open.Disposable.ObjectPools.sln new file mode 100644 index 0000000..4d8472b --- /dev/null +++ b/Open.Disposable.ObjectPools.sln @@ -0,0 +1,31 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.27130.2036 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Open.Disposable.ObjectPools.Benchmarking", "benchmarking\Open.Disposable.ObjectPools.Benchmarking.csproj", "{8404875C-ED46-4057-8D97-598B4D7B8040}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Open.Disposable.ObjectPools", "source\Open.Disposable.ObjectPools.csproj", "{7DF5DA35-AB11-479D-BD87-27461AC3F5A8}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {8404875C-ED46-4057-8D97-598B4D7B8040}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {8404875C-ED46-4057-8D97-598B4D7B8040}.Debug|Any CPU.Build.0 = Debug|Any CPU + {8404875C-ED46-4057-8D97-598B4D7B8040}.Release|Any CPU.ActiveCfg = Release|Any CPU + {8404875C-ED46-4057-8D97-598B4D7B8040}.Release|Any CPU.Build.0 = Release|Any CPU + {7DF5DA35-AB11-479D-BD87-27461AC3F5A8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7DF5DA35-AB11-479D-BD87-27461AC3F5A8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7DF5DA35-AB11-479D-BD87-27461AC3F5A8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7DF5DA35-AB11-479D-BD87-27461AC3F5A8}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {86DED392-9553-4DB9-9EC3-BEBE4F470390} + EndGlobalSection +EndGlobal diff --git a/source/Channels/ChannelObjectPool.cs b/source/Channels/ChannelObjectPool.cs index e9d7cb6..0998e11 100644 --- a/source/Channels/ChannelObjectPool.cs +++ b/source/Channels/ChannelObjectPool.cs @@ -20,7 +20,7 @@ public ChannelObjectPool(Func factory, int capacity = DEFAULT_CAPACITY) protected override void OnDispose(bool calledExplicitly) { - Pool?.Writer.Complete(); + Pool?.Writer.TryComplete(); Pool = null; } diff --git a/source/IObjectPool.cs b/source/IObjectPool.cs index be3a16f..dd689cf 100644 --- a/source/IObjectPool.cs +++ b/source/IObjectPool.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.Threading.Tasks; namespace Open.Disposable { @@ -46,21 +45,7 @@ public interface IObjectPool : IDisposable /// An item removed from the pool or generated. Should never be null. T Take(); - - - /// - /// Asynchronously receives an item and adds it to the pool. Ignores null references. - /// WARNING: The item is considered 'dead' but resurrectable so be sure not to hold on to the item's reference. - /// - /// The item to give up to the pool. - /// An optional action exectue on the item only if it's possible to return to the pool. - Task GiveAsync(T item); - - /// - /// Awaits an available item from the pool. If none are available it generates one. - /// - /// An item removed from the pool or generated. - Task TakeAsync(); + } public static partial class ObjectPoolExtensions @@ -92,31 +77,6 @@ public static void Give(this IObjectPool target, T item1, T item2, params target.Give(item2); target.Give(items); } - - - - /// - /// Asynchronously receives items and iteratively adds them to the pool. - /// WARNING: These items are considered 'dead' but resurrectable so be sure not to hold on to their reference. - /// - /// The items to give up to the pool. - public static Task GiveAsync(this IObjectPool target, IEnumerable items) - where T : class - { - return Task.Run(() => target.Give(items)); - } - - /// - /// Asynchronously receives items and iteratively adds them to the pool. - /// WARNING: These items are considered 'dead' but resurrectable so be sure not to hold on to their reference. - /// - /// The first item to give up to the pool. - /// The second item to give up to the pool. - /// The remaining items to give up to the pool. - public static Task GiveAsync(this IObjectPool target, T item1, T item2, params T[] items) - where T : class - { - return Task.Run(() => target.Give(item1, item2, items)); - } + } } diff --git a/source/ObjectPoolBase.cs b/source/ObjectPoolBase.cs index f51813d..256abb7 100644 --- a/source/ObjectPoolBase.cs +++ b/source/ObjectPoolBase.cs @@ -1,5 +1,4 @@ using System; -using System.Threading.Tasks; namespace Open.Disposable { @@ -74,27 +73,6 @@ public void Give(T item) && (SaveToPocket(item) || Receive(item))) OnReceived(); } - - protected virtual Task ReceiveAsync(T item) - { - return Task.Run(() => Receive(item)); // Default implemnation, can be overridden. - } - - public Task GiveAsync(T item) - { - // We need to pre-check CanReceive because excessive tasks could build up if not. - if (item == null || !CanReceive) - return Task.CompletedTask; - - return ReceiveConditionalAsync(item); - } - - async Task ReceiveConditionalAsync(T item) - { - if (PrepareToReceive(item) - && (SaveToPocket(item) || await ReceiveAsync(item).ConfigureAwait(false))) // Doesn't need original context. Just needs to trigger OnReceived(). - OnReceived(); - } #endregion #region Release (.Take()) @@ -103,20 +81,6 @@ public virtual T Take() return TryTake() ?? Factory(); } - protected virtual Task ReleaseAsync() - { - return Task.Run((Func)Take); - } - - public Task TakeAsync() - { - // See if there's one available already. - if (TryTake(out T firstTry)) - return Task.FromResult(firstTry); - - return ReleaseAsync(); - } - public bool TryTake(out T item) { item = TryTake(); diff --git a/source/Open.Disposable.ObjectPools.csproj b/source/Open.Disposable.ObjectPools.csproj index b3b59c4..144ab0a 100644 --- a/source/Open.Disposable.ObjectPools.csproj +++ b/source/Open.Disposable.ObjectPools.csproj @@ -22,12 +22,6 @@ Part of the "Open" set of libraries. Updated Open.Threading.Tasks reference (only used for trimming). - - - - - - @@ -35,6 +29,7 @@ Part of the "Open" set of libraries. + \ No newline at end of file From 50136f9ff8ac4426dbb73d2390409817991fe781 Mon Sep 17 00:00:00 2001 From: Oren Ferrari Date: Thu, 3 May 2018 15:34:40 -0700 Subject: [PATCH 15/83] Removed channels. --- source/Open.Disposable.ObjectPools.csproj | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/source/Open.Disposable.ObjectPools.csproj b/source/Open.Disposable.ObjectPools.csproj index 144ab0a..b3b59c4 100644 --- a/source/Open.Disposable.ObjectPools.csproj +++ b/source/Open.Disposable.ObjectPools.csproj @@ -22,6 +22,12 @@ Part of the "Open" set of libraries. Updated Open.Threading.Tasks reference (only used for trimming). + + + + + + @@ -29,7 +35,6 @@ Part of the "Open" set of libraries. - \ No newline at end of file From a2cd0150ae558c6c70819bb5c5799da133170130 Mon Sep 17 00:00:00 2001 From: electricessence Date: Thu, 3 May 2018 19:48:01 -0700 Subject: [PATCH 16/83] v2.0.0 --- benchmarking/BenchmarkResult.csv | 42 ++-- benchmarking/BenchmarkResult.txt | 235 +++++++++------------- source/Open.Disposable.ObjectPools.csproj | 12 +- 3 files changed, 129 insertions(+), 160 deletions(-) diff --git a/benchmarking/BenchmarkResult.csv b/benchmarking/BenchmarkResult.csv index ed8141b..99d5cb3 100644 --- a/benchmarking/BenchmarkResult.csv +++ b/benchmarking/BenchmarkResult.csv @@ -1,25 +1,25 @@ Batch,Pool Type," From Empty (In Parallel)"," To (In Parallel)",d Read/Write (In Parallel),y Pool (.TryTake()),L, -Repeat 400000 for size 4,QueueObjectPool,00:00:01.6921162,00:00:01.3999970,00:00:01.4232035,00:00:00.2110440,00:00:04.7263607, -Repeat 400000 for size 4,ChannelObjectPool,00:00:01.7026834,00:00:01.6286904,00:00:01.3645671,00:00:00.2338478,00:00:04.9297887, -Repeat 400000 for size 4,ConcurrentQueueObjectPool,00:00:01.7693712,00:00:01.4210947,00:00:01.3829205,00:00:00.1955725,00:00:04.7689589, -Repeat 400000 for size 4,ConcurrentStackObjectPool,00:00:01.6875804,00:00:01.5369081,00:00:01.3944637,00:00:00.1756264,00:00:04.7945786, -Repeat 400000 for size 4,OptimisticArrayObjectPool,00:00:01.7120099,00:00:01.3737743,00:00:01.3623827,00:00:00.2493802,00:00:04.6975471, -Repeat 160000 for size 10,QueueObjectPool,00:00:01.0495375,00:00:01.0112045,00:00:00.8871979,00:00:00.2492687,00:00:03.1972086, -Repeat 160000 for size 10,ChannelObjectPool,00:00:01.2164743,00:00:01.1531997,00:00:00.8866070,00:00:00.2740465,00:00:03.5303275, -Repeat 160000 for size 10,ConcurrentQueueObjectPool,00:00:01.1038901,00:00:00.9417471,00:00:00.9094443,00:00:00.2277741,00:00:03.1828556, -Repeat 160000 for size 10,ConcurrentStackObjectPool,00:00:01.0859551,00:00:00.9596546,00:00:00.9246417,00:00:00.2256799,00:00:03.1959313, -Repeat 160000 for size 10,OptimisticArrayObjectPool,00:00:01.0775123,00:00:00.9302528,00:00:00.9021115,00:00:00.4166513,00:00:03.3265279, -Repeat 48000 for size 50,QueueObjectPool,00:00:00.5258334,00:00:01.2713375,00:00:00.6293988,00:00:00.3438183,00:00:02.7703880, -Repeat 48000 for size 50,ChannelObjectPool,00:00:02.4067783,00:00:01.5298450,00:00:00.6832894,00:00:00.3926395,00:00:05.0125522, -Repeat 48000 for size 50,ConcurrentQueueObjectPool,00:00:00.5612132,00:00:01.4985338,00:00:00.4656040,00:00:00.3110057,00:00:02.8363567, -Repeat 48000 for size 50,ConcurrentStackObjectPool,00:00:00.4972028,00:00:00.5200011,00:00:00.4378888,00:00:00.2969994,00:00:01.7520921, -Repeat 48000 for size 50,OptimisticArrayObjectPool,00:00:00.5691812,00:00:00.7489502,00:00:00.4934400,00:00:01.5497079,00:00:03.3612793, -Repeat 32000 for size 100,QueueObjectPool,00:00:00.5122336,00:00:02.0243072,00:00:00.8949053,00:00:00.4559207,00:00:03.8873668, -Repeat 32000 for size 100,ChannelObjectPool,00:00:04.0415940,00:00:03.4503339,00:00:00.9470840,00:00:00.5190160,00:00:08.9580279, -Repeat 32000 for size 100,ConcurrentQueueObjectPool,00:00:00.4979936,00:00:01.1025353,00:00:00.4270060,00:00:00.4100476,00:00:02.4375825, -Repeat 32000 for size 100,ConcurrentStackObjectPool,00:00:00.4766648,00:00:00.5666903,00:00:00.5310815,00:00:00.3866519,00:00:01.9610885, -Repeat 32000 for size 100,OptimisticArrayObjectPool,00:00:00.6956861,00:00:01.0975470,00:00:00.4499636,00:00:03.7336458,00:00:05.9768425, -Repeat 51200 for size 250,QueueObjectPool,00:00:01.6247132,00:00:05.7028204,00:00:06.3495433,00:00:01.7208433,00:00:15.3979202, +Repeat 400000 for size 4,QueueObjectPool,00:00:03.1829368,00:00:02.8245204,00:00:02.7274550,00:00:00.4644069,00:00:09.1993191, +Repeat 400000 for size 4,ConcurrentQueueObjectPool,00:00:03.9520475,00:00:03.3141114,00:00:03.2446456,00:00:00.5033748,00:00:11.0141793, +Repeat 400000 for size 4,ConcurrentStackObjectPool,00:00:03.7274029,00:00:03.4699671,00:00:03.1910788,00:00:00.4871689,00:00:10.8756177, +Repeat 400000 for size 4,OptimisticArrayObjectPool,00:00:03.1819627,00:00:02.6187847,00:00:02.5613898,00:00:00.5260779,00:00:08.8882151, +Repeat 160000 for size 10,QueueObjectPool,00:00:00.9753926,00:00:00.9346630,00:00:00.8060045,00:00:00.2545299,00:00:02.9705900, +Repeat 160000 for size 10,ConcurrentQueueObjectPool,00:00:01.0289733,00:00:00.8683338,00:00:00.8201646,00:00:00.2358702,00:00:02.9533419, +Repeat 160000 for size 10,ConcurrentStackObjectPool,00:00:01.0970613,00:00:01.0251371,00:00:00.8980626,00:00:00.2443304,00:00:03.2645914, +Repeat 160000 for size 10,OptimisticArrayObjectPool,00:00:01.1247373,00:00:00.9535141,00:00:00.8851396,00:00:00.4598056,00:00:03.4231966, +Repeat 48000 for size 50,QueueObjectPool,00:00:00.5223797,00:00:00.8268179,00:00:00.5445827,00:00:00.3488348,00:00:02.2426151, +Repeat 48000 for size 50,ConcurrentQueueObjectPool,00:00:00.5395239,00:00:00.9860749,00:00:00.4412272,00:00:00.3090006,00:00:02.2758266, +Repeat 48000 for size 50,ConcurrentStackObjectPool,00:00:00.5258119,00:00:00.5466085,00:00:00.4650011,00:00:00.3059624,00:00:01.8433839, +Repeat 48000 for size 50,OptimisticArrayObjectPool,00:00:00.5831005,00:00:00.7148946,00:00:00.4330670,00:00:01.7100735,00:00:03.4411356, +Repeat 32000 for size 100,QueueObjectPool,00:00:00.5055389,00:00:00.9263350,00:00:00.5684372,00:00:00.4567166,00:00:02.4570277, +Repeat 32000 for size 100,ConcurrentQueueObjectPool,00:00:00.5348503,00:00:00.7617627,00:00:00.4222624,00:00:00.4125298,00:00:02.1314052, +Repeat 32000 for size 100,ConcurrentStackObjectPool,00:00:00.5154681,00:00:00.5769678,00:00:00.4597205,00:00:00.4073109,00:00:01.9594673, +Repeat 32000 for size 100,OptimisticArrayObjectPool,00:00:00.7254637,00:00:01.0581905,00:00:00.3984681,00:00:03.7309017,00:00:05.9130240, +Repeat 51200 for size 250,QueueObjectPool,00:00:01.5711793,00:00:03.1183660,00:00:02.1775394,00:00:01.7555068,00:00:08.6225915, +Repeat 51200 for size 250,ConcurrentQueueObjectPool,00:00:02.0386582,00:00:02.5414010,00:00:01.4334623,00:00:01.7010927,00:00:07.7146142, +Repeat 51200 for size 250,ConcurrentStackObjectPool,00:00:01.8341826,00:00:02.0561458,00:00:01.4740427,00:00:01.6809933,00:00:07.0453644, +Repeat 51200 for size 250,OptimisticArrayObjectPool,00:00:02.7181693,00:00:06.9277528,00:00:01.1086113,00:00:25.9591372,00:00:36.7136706, +0,QueueObjectPool,00:00:01.6247132,00:00:05.7028204,00:00:06.3495433,00:00:01.7208433,00:00:15.3979202, Repeat 51200 for size 250,ChannelObjectPool,00:00:09.1002442,00:00:09.5148632,00:00:07.0393499,00:00:01.9543981,00:00:27.6088554, Repeat 51200 for size 250,ConcurrentQueueObjectPool,00:00:01.3937964,00:00:03.7235199,00:00:01.2171737,00:00:01.5041405,00:00:07.8386305, Repeat 51200 for size 250,ConcurrentStackObjectPool,00:00:01.4858700,00:00:01.8964279,00:00:02.2896336,00:00:01.4461137,00:00:07.1180452, diff --git a/benchmarking/BenchmarkResult.txt b/benchmarking/BenchmarkResult.txt index be4d77d..48fd8ba 100644 --- a/benchmarking/BenchmarkResult.txt +++ b/benchmarking/BenchmarkResult.txt @@ -2,194 +2,159 @@ Repeat 400000 for size 4 ------------------------------------ QueueObjectPool......................................... -00:00:01.6921162 Take From Empty (In Parallel) -00:00:01.3999970 Give To (In Parallel) -00:00:01.4232035 Mixed Read/Write (In Parallel) -00:00:00.2110440 Empty Pool (.TryTake()) -00:00:04.7263607 TOTAL - -ChannelObjectPool....................................... -00:00:01.7026834 Take From Empty (In Parallel) -00:00:01.6286904 Give To (In Parallel) -00:00:01.3645671 Mixed Read/Write (In Parallel) -00:00:00.2338478 Empty Pool (.TryTake()) -00:00:04.9297887 TOTAL +00:00:03.1829368 Take From Empty (In Parallel) +00:00:02.8245204 Give To (In Parallel) +00:00:02.7274550 Mixed Read/Write (In Parallel) +00:00:00.4644069 Empty Pool (.TryTake()) +00:00:09.1993191 TOTAL ConcurrentQueueObjectPool............................... -00:00:01.7693712 Take From Empty (In Parallel) -00:00:01.4210947 Give To (In Parallel) -00:00:01.3829205 Mixed Read/Write (In Parallel) -00:00:00.1955725 Empty Pool (.TryTake()) -00:00:04.7689589 TOTAL +00:00:03.9520475 Take From Empty (In Parallel) +00:00:03.3141114 Give To (In Parallel) +00:00:03.2446456 Mixed Read/Write (In Parallel) +00:00:00.5033748 Empty Pool (.TryTake()) +00:00:11.0141793 TOTAL ConcurrentStackObjectPool............................... -00:00:01.6875804 Take From Empty (In Parallel) -00:00:01.5369081 Give To (In Parallel) -00:00:01.3944637 Mixed Read/Write (In Parallel) -00:00:00.1756264 Empty Pool (.TryTake()) -00:00:04.7945786 TOTAL +00:00:03.7274029 Take From Empty (In Parallel) +00:00:03.4699671 Give To (In Parallel) +00:00:03.1910788 Mixed Read/Write (In Parallel) +00:00:00.4871689 Empty Pool (.TryTake()) +00:00:10.8756177 TOTAL OptimisticArrayObjectPool............................... -00:00:01.7120099 Take From Empty (In Parallel) -00:00:01.3737743 Give To (In Parallel) -00:00:01.3623827 Mixed Read/Write (In Parallel) -00:00:00.2493802 Empty Pool (.TryTake()) -00:00:04.6975471 TOTAL +00:00:03.1819627 Take From Empty (In Parallel) +00:00:02.6187847 Give To (In Parallel) +00:00:02.5613898 Mixed Read/Write (In Parallel) +00:00:00.5260779 Empty Pool (.TryTake()) +00:00:08.8882151 TOTAL Repeat 160000 for size 10 ------------------------------------ QueueObjectPool......................................... -00:00:01.0495375 Take From Empty (In Parallel) -00:00:01.0112045 Give To (In Parallel) -00:00:00.8871979 Mixed Read/Write (In Parallel) -00:00:00.2492687 Empty Pool (.TryTake()) -00:00:03.1972086 TOTAL - -ChannelObjectPool....................................... -00:00:01.2164743 Take From Empty (In Parallel) -00:00:01.1531997 Give To (In Parallel) -00:00:00.8866070 Mixed Read/Write (In Parallel) -00:00:00.2740465 Empty Pool (.TryTake()) -00:00:03.5303275 TOTAL +00:00:00.9753926 Take From Empty (In Parallel) +00:00:00.9346630 Give To (In Parallel) +00:00:00.8060045 Mixed Read/Write (In Parallel) +00:00:00.2545299 Empty Pool (.TryTake()) +00:00:02.9705900 TOTAL ConcurrentQueueObjectPool............................... -00:00:01.1038901 Take From Empty (In Parallel) -00:00:00.9417471 Give To (In Parallel) -00:00:00.9094443 Mixed Read/Write (In Parallel) -00:00:00.2277741 Empty Pool (.TryTake()) -00:00:03.1828556 TOTAL +00:00:01.0289733 Take From Empty (In Parallel) +00:00:00.8683338 Give To (In Parallel) +00:00:00.8201646 Mixed Read/Write (In Parallel) +00:00:00.2358702 Empty Pool (.TryTake()) +00:00:02.9533419 TOTAL ConcurrentStackObjectPool............................... -00:00:01.0859551 Take From Empty (In Parallel) -00:00:00.9596546 Give To (In Parallel) -00:00:00.9246417 Mixed Read/Write (In Parallel) -00:00:00.2256799 Empty Pool (.TryTake()) -00:00:03.1959313 TOTAL +00:00:01.0970613 Take From Empty (In Parallel) +00:00:01.0251371 Give To (In Parallel) +00:00:00.8980626 Mixed Read/Write (In Parallel) +00:00:00.2443304 Empty Pool (.TryTake()) +00:00:03.2645914 TOTAL OptimisticArrayObjectPool............................... -00:00:01.0775123 Take From Empty (In Parallel) -00:00:00.9302528 Give To (In Parallel) -00:00:00.9021115 Mixed Read/Write (In Parallel) -00:00:00.4166513 Empty Pool (.TryTake()) -00:00:03.3265279 TOTAL +00:00:01.1247373 Take From Empty (In Parallel) +00:00:00.9535141 Give To (In Parallel) +00:00:00.8851396 Mixed Read/Write (In Parallel) +00:00:00.4598056 Empty Pool (.TryTake()) +00:00:03.4231966 TOTAL Repeat 48000 for size 50 ------------------------------------ QueueObjectPool......................................... -00:00:00.5258334 Take From Empty (In Parallel) -00:00:01.2713375 Give To (In Parallel) -00:00:00.6293988 Mixed Read/Write (In Parallel) -00:00:00.3438183 Empty Pool (.TryTake()) -00:00:02.7703880 TOTAL - -ChannelObjectPool....................................... -00:00:02.4067783 Take From Empty (In Parallel) -00:00:01.5298450 Give To (In Parallel) -00:00:00.6832894 Mixed Read/Write (In Parallel) -00:00:00.3926395 Empty Pool (.TryTake()) -00:00:05.0125522 TOTAL +00:00:00.5223797 Take From Empty (In Parallel) +00:00:00.8268179 Give To (In Parallel) +00:00:00.5445827 Mixed Read/Write (In Parallel) +00:00:00.3488348 Empty Pool (.TryTake()) +00:00:02.2426151 TOTAL ConcurrentQueueObjectPool............................... -00:00:00.5612132 Take From Empty (In Parallel) -00:00:01.4985338 Give To (In Parallel) -00:00:00.4656040 Mixed Read/Write (In Parallel) -00:00:00.3110057 Empty Pool (.TryTake()) -00:00:02.8363567 TOTAL +00:00:00.5395239 Take From Empty (In Parallel) +00:00:00.9860749 Give To (In Parallel) +00:00:00.4412272 Mixed Read/Write (In Parallel) +00:00:00.3090006 Empty Pool (.TryTake()) +00:00:02.2758266 TOTAL ConcurrentStackObjectPool............................... -00:00:00.4972028 Take From Empty (In Parallel) -00:00:00.5200011 Give To (In Parallel) -00:00:00.4378888 Mixed Read/Write (In Parallel) -00:00:00.2969994 Empty Pool (.TryTake()) -00:00:01.7520921 TOTAL +00:00:00.5258119 Take From Empty (In Parallel) +00:00:00.5466085 Give To (In Parallel) +00:00:00.4650011 Mixed Read/Write (In Parallel) +00:00:00.3059624 Empty Pool (.TryTake()) +00:00:01.8433839 TOTAL OptimisticArrayObjectPool............................... -00:00:00.5691812 Take From Empty (In Parallel) -00:00:00.7489502 Give To (In Parallel) -00:00:00.4934400 Mixed Read/Write (In Parallel) -00:00:01.5497079 Empty Pool (.TryTake()) -00:00:03.3612793 TOTAL +00:00:00.5831005 Take From Empty (In Parallel) +00:00:00.7148946 Give To (In Parallel) +00:00:00.4330670 Mixed Read/Write (In Parallel) +00:00:01.7100735 Empty Pool (.TryTake()) +00:00:03.4411356 TOTAL Repeat 32000 for size 100 ------------------------------------ QueueObjectPool......................................... -00:00:00.5122336 Take From Empty (In Parallel) -00:00:02.0243072 Give To (In Parallel) -00:00:00.8949053 Mixed Read/Write (In Parallel) -00:00:00.4559207 Empty Pool (.TryTake()) -00:00:03.8873668 TOTAL - -ChannelObjectPool....................................... -00:00:04.0415940 Take From Empty (In Parallel) -00:00:03.4503339 Give To (In Parallel) -00:00:00.9470840 Mixed Read/Write (In Parallel) -00:00:00.5190160 Empty Pool (.TryTake()) -00:00:08.9580279 TOTAL +00:00:00.5055389 Take From Empty (In Parallel) +00:00:00.9263350 Give To (In Parallel) +00:00:00.5684372 Mixed Read/Write (In Parallel) +00:00:00.4567166 Empty Pool (.TryTake()) +00:00:02.4570277 TOTAL ConcurrentQueueObjectPool............................... -00:00:00.4979936 Take From Empty (In Parallel) -00:00:01.1025353 Give To (In Parallel) -00:00:00.4270060 Mixed Read/Write (In Parallel) -00:00:00.4100476 Empty Pool (.TryTake()) -00:00:02.4375825 TOTAL +00:00:00.5348503 Take From Empty (In Parallel) +00:00:00.7617627 Give To (In Parallel) +00:00:00.4222624 Mixed Read/Write (In Parallel) +00:00:00.4125298 Empty Pool (.TryTake()) +00:00:02.1314052 TOTAL ConcurrentStackObjectPool............................... -00:00:00.4766648 Take From Empty (In Parallel) -00:00:00.5666903 Give To (In Parallel) -00:00:00.5310815 Mixed Read/Write (In Parallel) -00:00:00.3866519 Empty Pool (.TryTake()) -00:00:01.9610885 TOTAL +00:00:00.5154681 Take From Empty (In Parallel) +00:00:00.5769678 Give To (In Parallel) +00:00:00.4597205 Mixed Read/Write (In Parallel) +00:00:00.4073109 Empty Pool (.TryTake()) +00:00:01.9594673 TOTAL OptimisticArrayObjectPool............................... -00:00:00.6956861 Take From Empty (In Parallel) -00:00:01.0975470 Give To (In Parallel) -00:00:00.4499636 Mixed Read/Write (In Parallel) -00:00:03.7336458 Empty Pool (.TryTake()) -00:00:05.9768425 TOTAL +00:00:00.7254637 Take From Empty (In Parallel) +00:00:01.0581905 Give To (In Parallel) +00:00:00.3984681 Mixed Read/Write (In Parallel) +00:00:03.7309017 Empty Pool (.TryTake()) +00:00:05.9130240 TOTAL Repeat 51200 for size 250 ------------------------------------ QueueObjectPool......................................... -00:00:01.6247132 Take From Empty (In Parallel) -00:00:05.7028204 Give To (In Parallel) -00:00:06.3495433 Mixed Read/Write (In Parallel) -00:00:01.7208433 Empty Pool (.TryTake()) -00:00:15.3979202 TOTAL - -ChannelObjectPool....................................... -00:00:09.1002442 Take From Empty (In Parallel) -00:00:09.5148632 Give To (In Parallel) -00:00:07.0393499 Mixed Read/Write (In Parallel) -00:00:01.9543981 Empty Pool (.TryTake()) -00:00:27.6088554 TOTAL +00:00:01.5711793 Take From Empty (In Parallel) +00:00:03.1183660 Give To (In Parallel) +00:00:02.1775394 Mixed Read/Write (In Parallel) +00:00:01.7555068 Empty Pool (.TryTake()) +00:00:08.6225915 TOTAL ConcurrentQueueObjectPool............................... -00:00:01.3937964 Take From Empty (In Parallel) -00:00:03.7235199 Give To (In Parallel) -00:00:01.2171737 Mixed Read/Write (In Parallel) -00:00:01.5041405 Empty Pool (.TryTake()) -00:00:07.8386305 TOTAL +00:00:02.0386582 Take From Empty (In Parallel) +00:00:02.5414010 Give To (In Parallel) +00:00:01.4334623 Mixed Read/Write (In Parallel) +00:00:01.7010927 Empty Pool (.TryTake()) +00:00:07.7146142 TOTAL ConcurrentStackObjectPool............................... -00:00:01.4858700 Take From Empty (In Parallel) -00:00:01.8964279 Give To (In Parallel) -00:00:02.2896336 Mixed Read/Write (In Parallel) -00:00:01.4461137 Empty Pool (.TryTake()) -00:00:07.1180452 TOTAL +00:00:01.8341826 Take From Empty (In Parallel) +00:00:02.0561458 Give To (In Parallel) +00:00:01.4740427 Mixed Read/Write (In Parallel) +00:00:01.6809933 Empty Pool (.TryTake()) +00:00:07.0453644 TOTAL OptimisticArrayObjectPool............................... -00:00:02.9579032 Take From Empty (In Parallel) -00:00:07.0305663 Give To (In Parallel) -00:00:01.2993861 Mixed Read/Write (In Parallel) -00:00:27.8503246 Empty Pool (.TryTake()) -00:00:39.1381802 TOTAL +00:00:02.7181693 Take From Empty (In Parallel) +00:00:06.9277528 Give To (In Parallel) +00:00:01.1086113 Mixed Read/Write (In Parallel) +00:00:25.9591372 Empty Pool (.TryTake()) +00:00:36.7136706 TOTAL diff --git a/source/Open.Disposable.ObjectPools.csproj b/source/Open.Disposable.ObjectPools.csproj index b3b59c4..b4f4a6a 100644 --- a/source/Open.Disposable.ObjectPools.csproj +++ b/source/Open.Disposable.ObjectPools.csproj @@ -16,10 +16,14 @@ Part of the "Open" set of libraries. git objectpool, dotnet, dotnetcore, cs, idisposable, threadsafe, thread-safe True - 1.3.1 - 1.3.1.0 - 1.3.1.0 - Updated Open.Threading.Tasks reference (only used for trimming). + 2.0.0 + 2.0.0.0 + 2.0.0.0 + Improved overall performance by eliminating forced count of trimmable object pools. +Tracking counts is now optional and disabled by default. +Removed Async Take/Give methods since they offer no benefit and may acutally be problematic. Object pools should not have direct async methods. + +More interfaces and base classes added for extensibility and future development. From 50192738579300c3acf0a465c93d0a6048f3fcd0 Mon Sep 17 00:00:00 2001 From: "Oren (electricessence)" Date: Sat, 5 May 2018 15:51:13 -0700 Subject: [PATCH 17/83] Reformat and contract improvments. --- benchmarking/Benchmark.cs | 8 +- benchmarking/ConsoleReport.cs | 8 +- benchmarking/Program.cs | 26 +-- source/Array/InterlockedArrayObjectPool.cs | 192 +++++++++--------- source/Array/OptimisticArrayObjectPool.cs | 120 +++++------ source/Collection/QueueObjectPool.cs | 38 ++-- source/GlobalSuppressions.cs | 1 - source/IObjectPool.cs | 4 +- source/Open.Disposable.ObjectPools.csproj | 4 + .../Open.Disposable.ObjectPools.Tests.csproj | 6 +- 10 files changed, 204 insertions(+), 203 deletions(-) diff --git a/benchmarking/Benchmark.cs b/benchmarking/Benchmark.cs index e423ab3..e420bb3 100644 --- a/benchmarking/Benchmark.cs +++ b/benchmarking/Benchmark.cs @@ -1,9 +1,6 @@ using Open.Diagnostics; using System; -using System.Collections.Concurrent; using System.Collections.Generic; -using System.Diagnostics; -using System.Linq; using System.Threading.Tasks; namespace Open.Disposable.ObjectPools @@ -11,7 +8,7 @@ namespace Open.Disposable.ObjectPools public class Benchmark : BenchmarkBase>> where T : class { - public Benchmark(uint size, uint repeat, Func> poolFactory) : base(size,repeat,poolFactory) + public Benchmark(uint size, uint repeat, Func> poolFactory) : base(size, repeat, poolFactory) { // Because some pools do comparison checks on values, we have have unique instances/values. _items = new T[(int)size]; @@ -47,7 +44,8 @@ protected override IEnumerable TestOnceInternal() yield return TimedResult.Measure("Empty Pool (.TryTake())", () => { - while (pool.TryTake() != null) { + while (pool.TryTake() != null) + { // remaining++; } }); diff --git a/benchmarking/ConsoleReport.cs b/benchmarking/ConsoleReport.cs index b331fff..55ea738 100644 --- a/benchmarking/ConsoleReport.cs +++ b/benchmarking/ConsoleReport.cs @@ -6,14 +6,14 @@ namespace Open.Disposable.ObjectPools { public class ConsoleReport : Open.Diagnostics.BenchmarkConsoleReport>> where T : class - { + { const int ITERATIONS = 100000; - public ConsoleReport(TextWriter output = null) : base(ITERATIONS, output, (c,r,p)=> Benchmark.Results(c, r, p)) + public ConsoleReport(TextWriter output = null) : base(ITERATIONS, output, (c, r, p) => Benchmark.Results(c, r, p)) { } - public ConsoleReport(StringBuilder output) : this (new StringWriter(output)) + public ConsoleReport(StringBuilder output) : this(new StringWriter(output)) { - } + } } } diff --git a/benchmarking/Program.cs b/benchmarking/Program.cs index 8018345..d14ce7d 100644 --- a/benchmarking/Program.cs +++ b/benchmarking/Program.cs @@ -21,32 +21,32 @@ static void Main(string[] args) * 3) A sync locked Queue is faster than a sync locked LinkedList. * 4) ConcurrentQueue seems to be the overall winner when dealing with pools larger than 100 but is the clear loser for very small sizes. */ - + // Start with a baseline... report.AddBenchmark("QueueObjectPool", // Note, that this one isn't far off from the following in peformance. count => () => QueueObjectPool.Create((int)count * 2)); - //report.AddBenchmark("ChannelObjectPool", - // count => () => ChannelObjectPool.Create((int)count * 2)); + //report.AddBenchmark("ChannelObjectPool", + // count => () => ChannelObjectPool.Create((int)count * 2)); - report.AddBenchmark("ConcurrentQueueObjectPool", // Note, that this one isn't far off from the following in peformance, but definitely is faster than LinkedListObjectPool and the rest. + report.AddBenchmark("ConcurrentQueueObjectPool", // Note, that this one isn't far off from the following in peformance, but definitely is faster than LinkedListObjectPool and the rest. count => () => ConcurrentQueueObjectPool.Create((int)count * 2)); - report.AddBenchmark("ConcurrentStackObjectPool", // Note, that this one isn't far off from the following in peformance, but definitely is faster than LinkedListObjectPool and the rest. - count => () => ConcurrentStackObjectPool.Create((int)count * 2)); + report.AddBenchmark("ConcurrentStackObjectPool", // Note, that this one isn't far off from the following in peformance, but definitely is faster than LinkedListObjectPool and the rest. + count => () => ConcurrentStackObjectPool.Create((int)count * 2)); - report.AddBenchmark("OptimisticArrayObjectPool", + report.AddBenchmark("OptimisticArrayObjectPool", count => () => OptimisticArrayObjectPool.Create((int)count * 2)); - // Is ineveitably slower than the above but should be enabled for testing code changes. - //report.AddBenchmark("InterlockedArrayObjectPool", - // count => () => InterlockedArrayObjectPool.Create((int)count * 2)); + // Is ineveitably slower than the above but should be enabled for testing code changes. + //report.AddBenchmark("InterlockedArrayObjectPool", + // count => () => InterlockedArrayObjectPool.Create((int)count * 2)); - report.Pretest(200, 200); // Run once through first to scramble/warm-up initial conditions. + report.Pretest(200, 200); // Run once through first to scramble/warm-up initial conditions. Console.SetCursorPosition(0, Console.CursorTop); - const int loopMultiple = 2; + const int loopMultiple = 2; report.Test(4, 8 * loopMultiple); report.Test(10, 8 * loopMultiple); report.Test(50, 12 * loopMultiple); @@ -68,4 +68,4 @@ static void Main(string[] args) Console.ReadKey(); } -} \ No newline at end of file +} diff --git a/source/Array/InterlockedArrayObjectPool.cs b/source/Array/InterlockedArrayObjectPool.cs index b52a5d1..851e24a 100644 --- a/source/Array/InterlockedArrayObjectPool.cs +++ b/source/Array/InterlockedArrayObjectPool.cs @@ -5,100 +5,100 @@ namespace Open.Disposable { - /// - /// An extremely fast ObjectPool when the capacity is in the low 100s. - /// - /// - public class InterlockedArrayObjectPool : ObjectPoolBase - where T : class - { - - public InterlockedArrayObjectPool(Func factory, Action recycler, int capacity = DEFAULT_CAPACITY) - : base(factory, recycler, capacity) - { - Pool = new ReferenceContainer[capacity - 1]; - } - - public InterlockedArrayObjectPool(Func factory, int capacity = DEFAULT_CAPACITY) - : this(factory, null, capacity) - { } - - protected ReferenceContainer[] Pool; - - // Sets a limit on what has been stored yet to prevent over searching the array unnecessarily.. - protected int MaxStored = 0; - protected const int MaxStoredIncrement = 5; // Instead of every one. - - protected override bool Receive(T item) - { - var elements = Pool; - var len = elements?.Length ?? 0; - - for (int i = 0; i < len; i++) - { - if (elements[i].TrySave(item)) - { - var m = MaxStored; - if (i >= m) Interlocked.CompareExchange(ref MaxStored, m + MaxStoredIncrement, m); - - return true; - } - } - - return false; - } - - protected override T TryRelease() - { - // We missed getting the first item or it wasn't there. - var elements = Pool; - if (elements == null) return null; - - var len = elements.Length; - for (int i = 0; i < MaxStored && i < len; i++) - { - var item = elements[i].TryRetrieve(); - if (item != null) return item; - } - - return null; - } - - protected override void OnDispose(bool calledExplicitly) - { - Pool = null; - MaxStored = 0; - } - - } - - public static class InterlockedArrayObjectPool - { - public static InterlockedArrayObjectPool Create(Func factory, int capacity = Constants.DEFAULT_CAPACITY) - where T : class - { - return new InterlockedArrayObjectPool(factory, capacity); - } - - public static InterlockedArrayObjectPool Create(int capacity = Constants.DEFAULT_CAPACITY) - where T : class, new() - { - return Create(() => new T(), capacity); - } - - public static InterlockedArrayObjectPool Create(Func factory, bool autoRecycle, int capacity = Constants.DEFAULT_CAPACITY) - where T : class, IRecyclable - { - Action recycler = null; - if (autoRecycle) recycler = Recycler.Recycle; - return new InterlockedArrayObjectPool(factory, recycler, capacity); - } - - public static InterlockedArrayObjectPool Create(bool autoRecycle, int capacity = Constants.DEFAULT_CAPACITY) - where T : class, IRecyclable, new() - { - return Create(() => new T(), autoRecycle, capacity); - } - - } + /// + /// An extremely fast ObjectPool when the capacity is in the low 100s. + /// + /// + public class InterlockedArrayObjectPool : ObjectPoolBase + where T : class + { + + public InterlockedArrayObjectPool(Func factory, Action recycler, int capacity = DEFAULT_CAPACITY) + : base(factory, recycler, capacity) + { + Pool = new ReferenceContainer[capacity - 1]; + } + + public InterlockedArrayObjectPool(Func factory, int capacity = DEFAULT_CAPACITY) + : this(factory, null, capacity) + { } + + protected ReferenceContainer[] Pool; + + // Sets a limit on what has been stored yet to prevent over searching the array unnecessarily.. + protected int MaxStored = 0; + protected const int MaxStoredIncrement = 5; // Instead of every one. + + protected override bool Receive(T item) + { + var elements = Pool; + var len = elements?.Length ?? 0; + + for (int i = 0; i < len; i++) + { + if (elements[i].TrySave(item)) + { + var m = MaxStored; + if (i >= m) Interlocked.CompareExchange(ref MaxStored, m + MaxStoredIncrement, m); + + return true; + } + } + + return false; + } + + protected override T TryRelease() + { + // We missed getting the first item or it wasn't there. + var elements = Pool; + if (elements == null) return null; + + var len = elements.Length; + for (int i = 0; i < MaxStored && i < len; i++) + { + var item = elements[i].TryRetrieve(); + if (item != null) return item; + } + + return null; + } + + protected override void OnDispose(bool calledExplicitly) + { + Pool = null; + MaxStored = 0; + } + + } + + public static class InterlockedArrayObjectPool + { + public static InterlockedArrayObjectPool Create(Func factory, int capacity = Constants.DEFAULT_CAPACITY) + where T : class + { + return new InterlockedArrayObjectPool(factory, capacity); + } + + public static InterlockedArrayObjectPool Create(int capacity = Constants.DEFAULT_CAPACITY) + where T : class, new() + { + return Create(() => new T(), capacity); + } + + public static InterlockedArrayObjectPool Create(Func factory, bool autoRecycle, int capacity = Constants.DEFAULT_CAPACITY) + where T : class, IRecyclable + { + Action recycler = null; + if (autoRecycle) recycler = Recycler.Recycle; + return new InterlockedArrayObjectPool(factory, recycler, capacity); + } + + public static InterlockedArrayObjectPool Create(bool autoRecycle, int capacity = Constants.DEFAULT_CAPACITY) + where T : class, IRecyclable, new() + { + return Create(() => new T(), autoRecycle, capacity); + } + + } } diff --git a/source/Array/OptimisticArrayObjectPool.cs b/source/Array/OptimisticArrayObjectPool.cs index a0c6c4e..db858c9 100644 --- a/source/Array/OptimisticArrayObjectPool.cs +++ b/source/Array/OptimisticArrayObjectPool.cs @@ -5,77 +5,77 @@ namespace Open.Disposable { - /// - /// An extremely fast ObjectPool when the capacity is in the low 100s. - /// - /// - public class OptimisticArrayObjectPool : InterlockedArrayObjectPool - where T : class - { + /// + /// An extremely fast ObjectPool when the capacity is in the low 100s. + /// + /// + public class OptimisticArrayObjectPool : InterlockedArrayObjectPool + where T : class + { - public OptimisticArrayObjectPool(Func factory, Action recycler, int capacity = DEFAULT_CAPACITY) - : base(factory, recycler, capacity) - { } + public OptimisticArrayObjectPool(Func factory, Action recycler, int capacity = DEFAULT_CAPACITY) + : base(factory, recycler, capacity) + { } - public OptimisticArrayObjectPool(Func factory, int capacity = DEFAULT_CAPACITY) - : this(factory, null, capacity) - { } + public OptimisticArrayObjectPool(Func factory, int capacity = DEFAULT_CAPACITY) + : this(factory, null, capacity) + { } - // As suggested by Roslyn's implementation, don't worry about interlocking here. It's okay if a few get loose. - protected override bool SaveToPocket(T item) - { - return Pocket.SetIfNull(item); - } + // As suggested by Roslyn's implementation, don't worry about interlocking here. It's okay if a few get loose. + protected override bool SaveToPocket(T item) + { + return Pocket.SetIfNull(item); + } - protected override bool Receive(T item) - { - var elements = Pool; - var len = elements?.Length ?? 0; + protected override bool Receive(T item) + { + var elements = Pool; + var len = elements?.Length ?? 0; - for (int i = 0; i < len; i++) - { - // As suggested by Roslyn's implementation, don't worry about interlocking here. It's okay if a few get loose. - if (elements[i].SetIfNull(item)) - { - var m = MaxStored; - if (i >= m) Interlocked.CompareExchange(ref MaxStored, m + MaxStoredIncrement, m); + for (int i = 0; i < len; i++) + { + // As suggested by Roslyn's implementation, don't worry about interlocking here. It's okay if a few get loose. + if (elements[i].SetIfNull(item)) + { + var m = MaxStored; + if (i >= m) Interlocked.CompareExchange(ref MaxStored, m + MaxStoredIncrement, m); - return true; - } - } + return true; + } + } - return false; - } + return false; + } - } + } - public static class OptimisticArrayObjectPool - { - public static OptimisticArrayObjectPool Create(Func factory, int capacity = Constants.DEFAULT_CAPACITY) - where T : class - { - return new OptimisticArrayObjectPool(factory, capacity); - } + public static class OptimisticArrayObjectPool + { + public static OptimisticArrayObjectPool Create(Func factory, int capacity = Constants.DEFAULT_CAPACITY) + where T : class + { + return new OptimisticArrayObjectPool(factory, capacity); + } - public static OptimisticArrayObjectPool Create(int capacity = Constants.DEFAULT_CAPACITY) - where T : class, new() - { - return Create(() => new T(), capacity); - } + public static OptimisticArrayObjectPool Create(int capacity = Constants.DEFAULT_CAPACITY) + where T : class, new() + { + return Create(() => new T(), capacity); + } - public static OptimisticArrayObjectPool Create(Func factory, bool autoRecycle, int capacity = Constants.DEFAULT_CAPACITY) - where T : class, IRecyclable - { - Action recycler = null; - if (autoRecycle) recycler = Recycler.Recycle; - return new OptimisticArrayObjectPool(factory, recycler, capacity); - } + public static OptimisticArrayObjectPool Create(Func factory, bool autoRecycle, int capacity = Constants.DEFAULT_CAPACITY) + where T : class, IRecyclable + { + Action recycler = null; + if (autoRecycle) recycler = Recycler.Recycle; + return new OptimisticArrayObjectPool(factory, recycler, capacity); + } - public static OptimisticArrayObjectPool Create(bool autoRecycle, int capacity = Constants.DEFAULT_CAPACITY) - where T : class, IRecyclable, new() - { - return Create(() => new T(), autoRecycle, capacity); - } + public static OptimisticArrayObjectPool Create(bool autoRecycle, int capacity = Constants.DEFAULT_CAPACITY) + where T : class, IRecyclable, new() + { + return Create(() => new T(), autoRecycle, capacity); + } - } + } } diff --git a/source/Collection/QueueObjectPool.cs b/source/Collection/QueueObjectPool.cs index 4ca7fa9..e8ccacb 100644 --- a/source/Collection/QueueObjectPool.cs +++ b/source/Collection/QueueObjectPool.cs @@ -16,7 +16,7 @@ public QueueObjectPool(Func factory, Action recycler, int capacity = DEFAU public QueueObjectPool(Func factory, int capacity = DEFAULT_CAPACITY, bool countTrackingEnabled = false) : this(factory, null, capacity, countTrackingEnabled) { - + } Queue Pool; @@ -24,7 +24,7 @@ public QueueObjectPool(Func factory, int capacity = DEFAULT_CAPACITY, bool co protected override bool Receive(T item) { var p = Pool; - if (p!=null) + if (p != null) { // It's possible that the count could exceed MaxSize here, but the risk is negligble as a few over the limit won't hurt. // The lock operation should be quick enough to not pile up too many items. @@ -38,11 +38,11 @@ protected override bool Receive(T item) protected override T TryRelease() { var p = Pool; - if (p!=null && p.Count != 0) + if (p != null && p.Count != 0) { lock (p) { - if (p.Count!=0) + if (p.Count != 0) return p.Dequeue(); } @@ -71,19 +71,19 @@ public static QueueObjectPool Create(int capacity = Constants.DEFAULT_CAPA return Create(() => new T(), capacity, countTrackingEnabled); } - public static QueueObjectPool Create(Func factory, bool autoRecycle, int capacity = Constants.DEFAULT_CAPACITY, bool countTrackingEnabled = false) - where T : class, IRecyclable - { - Action recycler = null; - if (autoRecycle) recycler = Recycler.Recycle; - return new QueueObjectPool(factory, recycler, capacity, countTrackingEnabled); - } - - public static QueueObjectPool Create(bool autoRecycle, int capacity = Constants.DEFAULT_CAPACITY, bool countTrackingEnabled = false) - where T : class, IRecyclable, new() - { - return Create(() => new T(), autoRecycle, capacity, countTrackingEnabled); - } - - } + public static QueueObjectPool Create(Func factory, bool autoRecycle, int capacity = Constants.DEFAULT_CAPACITY, bool countTrackingEnabled = false) + where T : class, IRecyclable + { + Action recycler = null; + if (autoRecycle) recycler = Recycler.Recycle; + return new QueueObjectPool(factory, recycler, capacity, countTrackingEnabled); + } + + public static QueueObjectPool Create(bool autoRecycle, int capacity = Constants.DEFAULT_CAPACITY, bool countTrackingEnabled = false) + where T : class, IRecyclable, new() + { + return Create(() => new T(), autoRecycle, capacity, countTrackingEnabled); + } + + } } diff --git a/source/GlobalSuppressions.cs b/source/GlobalSuppressions.cs index 564903a..922b9de 100644 --- a/source/GlobalSuppressions.cs +++ b/source/GlobalSuppressions.cs @@ -6,4 +6,3 @@ [assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Performance", "CA1815:Override equals and operator equals on value types", Justification = "", Scope = "type", Target = "~T:Open.Disposable.ReferenceContainer`1")] [assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Performance", "CA1815:Override equals and operator equals on value types", Justification = "", Scope = "type", Target = "~T:Open.Disposable.DualReferenceContainer`1")] - diff --git a/source/IObjectPool.cs b/source/IObjectPool.cs index dd689cf..2697ab9 100644 --- a/source/IObjectPool.cs +++ b/source/IObjectPool.cs @@ -45,7 +45,7 @@ public interface IObjectPool : IDisposable /// An item removed from the pool or generated. Should never be null. T Take(); - + } public static partial class ObjectPoolExtensions @@ -77,6 +77,6 @@ public static void Give(this IObjectPool target, T item1, T item2, params target.Give(item2); target.Give(items); } - + } } diff --git a/source/Open.Disposable.ObjectPools.csproj b/source/Open.Disposable.ObjectPools.csproj index b4f4a6a..17759ae 100644 --- a/source/Open.Disposable.ObjectPools.csproj +++ b/source/Open.Disposable.ObjectPools.csproj @@ -26,6 +26,10 @@ Removed Async Take/Give methods since they offer no benefit and may acutally be More interfaces and base classes added for extensibility and future development. + + latest + + diff --git a/tests/Open.Disposable.ObjectPools.Tests.csproj b/tests/Open.Disposable.ObjectPools.Tests.csproj index 0138d17..7d58ae5 100644 --- a/tests/Open.Disposable.ObjectPools.Tests.csproj +++ b/tests/Open.Disposable.ObjectPools.Tests.csproj @@ -6,9 +6,9 @@ - - - + + + From 88ef50a2d8ebd0d76a27db5fe664aab2914bab57 Mon Sep 17 00:00:00 2001 From: "Oren (electricessence)" Date: Sun, 6 May 2018 22:05:10 -0700 Subject: [PATCH 18/83] Updates. --- benchmarking/Benchmark.cs | 24 +- benchmarking/BenchmarkResult.csv | 59 ++-- benchmarking/BenchmarkResult.txt | 252 ++++++++++-------- benchmarking/Program.cs | 12 +- source/Array/InterlockedArrayObjectPool.cs | 34 ++- source/Array/OptimisticArrayObjectPool.cs | 40 +-- .../Collection/CollectionWrapperObjectPool.cs | 19 +- .../Collection/ConcurrentQueueObjectPool.cs | 56 ++-- .../Collection/ConcurrentStackObjectPool.cs | 50 ++-- source/Collection/QueueObjectPool.cs | 51 ++-- source/Collection/StackObjectPool.cs | 51 ++-- source/ITrimmableObjectPool.cs | 2 - source/ObjectPoolAutoTrimmer.cs | 3 - source/ObjectPoolBase.cs | 65 ++--- source/Open.Disposable.ObjectPools.csproj | 13 +- source/TrimmableCollectionObjectPoolBase.cs | 74 +++++ source/TrimmableObjectPoolBase.cs | 75 +++--- 17 files changed, 489 insertions(+), 391 deletions(-) create mode 100644 source/TrimmableCollectionObjectPoolBase.cs diff --git a/benchmarking/Benchmark.cs b/benchmarking/Benchmark.cs index e420bb3..0a0f851 100644 --- a/benchmarking/Benchmark.cs +++ b/benchmarking/Benchmark.cs @@ -31,7 +31,18 @@ protected override IEnumerable TestOnceInternal() Parallel.For(0, TestSize, i => pool.Give(_items[i])); }); - yield return TimedResult.Measure("Mixed Read/Write (In Parallel)", () => + yield return TimedResult.Measure("Mixed 90%-Take/10%-Give (In Parallel)", () => + { + Parallel.For(0, TestSize, i => + { + if (i % 10 == 0) + pool.Give(_items[i]); + else + _items[i] = pool.Take(); + }); + }); + + yield return TimedResult.Measure("Mixed 50%-Take/50%-Give (In Parallel)", () => { Parallel.For(0, TestSize, i => { @@ -42,6 +53,17 @@ protected override IEnumerable TestOnceInternal() }); }); + yield return TimedResult.Measure("Mixed 10%-Take/90%-Give (In Parallel)", () => + { + Parallel.For(0, TestSize, i => + { + if (i % 10 == 0) + _items[i] = pool.Take(); + else + pool.Give(_items[i]); + }); + }); + yield return TimedResult.Measure("Empty Pool (.TryTake())", () => { while (pool.TryTake() != null) diff --git a/benchmarking/BenchmarkResult.csv b/benchmarking/BenchmarkResult.csv index 99d5cb3..18db769 100644 --- a/benchmarking/BenchmarkResult.csv +++ b/benchmarking/BenchmarkResult.csv @@ -1,36 +1,29 @@ -Batch,Pool Type," From Empty (In Parallel)"," To (In Parallel)",d Read/Write (In Parallel),y Pool (.TryTake()),L, -Repeat 400000 for size 4,QueueObjectPool,00:00:03.1829368,00:00:02.8245204,00:00:02.7274550,00:00:00.4644069,00:00:09.1993191, -Repeat 400000 for size 4,ConcurrentQueueObjectPool,00:00:03.9520475,00:00:03.3141114,00:00:03.2446456,00:00:00.5033748,00:00:11.0141793, -Repeat 400000 for size 4,ConcurrentStackObjectPool,00:00:03.7274029,00:00:03.4699671,00:00:03.1910788,00:00:00.4871689,00:00:10.8756177, -Repeat 400000 for size 4,OptimisticArrayObjectPool,00:00:03.1819627,00:00:02.6187847,00:00:02.5613898,00:00:00.5260779,00:00:08.8882151, -Repeat 160000 for size 10,QueueObjectPool,00:00:00.9753926,00:00:00.9346630,00:00:00.8060045,00:00:00.2545299,00:00:02.9705900, -Repeat 160000 for size 10,ConcurrentQueueObjectPool,00:00:01.0289733,00:00:00.8683338,00:00:00.8201646,00:00:00.2358702,00:00:02.9533419, -Repeat 160000 for size 10,ConcurrentStackObjectPool,00:00:01.0970613,00:00:01.0251371,00:00:00.8980626,00:00:00.2443304,00:00:03.2645914, -Repeat 160000 for size 10,OptimisticArrayObjectPool,00:00:01.1247373,00:00:00.9535141,00:00:00.8851396,00:00:00.4598056,00:00:03.4231966, -Repeat 48000 for size 50,QueueObjectPool,00:00:00.5223797,00:00:00.8268179,00:00:00.5445827,00:00:00.3488348,00:00:02.2426151, -Repeat 48000 for size 50,ConcurrentQueueObjectPool,00:00:00.5395239,00:00:00.9860749,00:00:00.4412272,00:00:00.3090006,00:00:02.2758266, -Repeat 48000 for size 50,ConcurrentStackObjectPool,00:00:00.5258119,00:00:00.5466085,00:00:00.4650011,00:00:00.3059624,00:00:01.8433839, -Repeat 48000 for size 50,OptimisticArrayObjectPool,00:00:00.5831005,00:00:00.7148946,00:00:00.4330670,00:00:01.7100735,00:00:03.4411356, -Repeat 32000 for size 100,QueueObjectPool,00:00:00.5055389,00:00:00.9263350,00:00:00.5684372,00:00:00.4567166,00:00:02.4570277, -Repeat 32000 for size 100,ConcurrentQueueObjectPool,00:00:00.5348503,00:00:00.7617627,00:00:00.4222624,00:00:00.4125298,00:00:02.1314052, -Repeat 32000 for size 100,ConcurrentStackObjectPool,00:00:00.5154681,00:00:00.5769678,00:00:00.4597205,00:00:00.4073109,00:00:01.9594673, -Repeat 32000 for size 100,OptimisticArrayObjectPool,00:00:00.7254637,00:00:01.0581905,00:00:00.3984681,00:00:03.7309017,00:00:05.9130240, -Repeat 51200 for size 250,QueueObjectPool,00:00:01.5711793,00:00:03.1183660,00:00:02.1775394,00:00:01.7555068,00:00:08.6225915, -Repeat 51200 for size 250,ConcurrentQueueObjectPool,00:00:02.0386582,00:00:02.5414010,00:00:01.4334623,00:00:01.7010927,00:00:07.7146142, -Repeat 51200 for size 250,ConcurrentStackObjectPool,00:00:01.8341826,00:00:02.0561458,00:00:01.4740427,00:00:01.6809933,00:00:07.0453644, -Repeat 51200 for size 250,OptimisticArrayObjectPool,00:00:02.7181693,00:00:06.9277528,00:00:01.1086113,00:00:25.9591372,00:00:36.7136706, -0,QueueObjectPool,00:00:01.6247132,00:00:05.7028204,00:00:06.3495433,00:00:01.7208433,00:00:15.3979202, -Repeat 51200 for size 250,ChannelObjectPool,00:00:09.1002442,00:00:09.5148632,00:00:07.0393499,00:00:01.9543981,00:00:27.6088554, -Repeat 51200 for size 250,ConcurrentQueueObjectPool,00:00:01.3937964,00:00:03.7235199,00:00:01.2171737,00:00:01.5041405,00:00:07.8386305, -Repeat 51200 for size 250,ConcurrentStackObjectPool,00:00:01.4858700,00:00:01.8964279,00:00:02.2896336,00:00:01.4461137,00:00:07.1180452, -Repeat 51200 for size 250,OptimisticArrayObjectPool,00:00:02.9579032,00:00:07.0305663,00:00:01.2993861,00:00:27.8503246,00:00:39.1381802, -at 25600 for size 250,StackObjectPool,00:00:00.7604847,00:00:01.8116364,00:00:01.2369652,00:00:00.8626937,00:00:04.6717800, -Repeat 25600 for size 250,ConcurrentQueueObjectPool,00:00:00.8566894,00:00:00.9466838,00:00:00.7795225,00:00:01.1284017,00:00:03.7112974, -Repeat 25600 for size 250,ConcurrentStackObjectPool,00:00:00.7723668,00:00:01.5778130,00:00:01.6054224,00:00:01.7977731,00:00:05.7533753, -Repeat 25600 for size 250,OptimisticArrayObjectPool,00:00:01.2503504,00:00:03.2370044,00:00:00.5303349,00:00:11.9466373,00:00:16.9643270, -Repeat 25600 for size 250,InterlockedArrayObjectPool,00:00:01.2508355,00:00:06.3817155,00:00:00.8393488,00:00:14.9549267,00:00:23.4268265, -Repeat 14400 for size 500,QueueObjectPool,00:00:00.7519294,00:00:01.5185357,00:00:01.3801529,00:00:00.8505885,00:00:04.5012065, -Repeat 14400 for size 500,StackObjectPool,00:00:00.7628823,00:00:01.6182833,00:00:01.4624384,00:00:00.8960254,00:00:04.7396294, +Batch,Pool Type," From Empty (In Parallel)"," To (In Parallel)",d 90%-Take/10%-Give (In Parallel),d 50%-Take/50%-Give (In Parallel),d 10%-Take/90%-Give (In Parallel),y Pool (.TryTake()),L, +Repeat 800000 for size 4,ConcurrentQueueObjectPool,00:00:02.1768070,00:00:01.6760555,00:00:01.7056065,00:00:01.6512698,00:00:01.6459856,00:00:00.0942697,00:00:08.9499941, +Repeat 800000 for size 4,ConcurrentStackObjectPool,00:00:02.0861011,00:00:01.9051266,00:00:01.7180590,00:00:01.6934710,00:00:01.8172521,00:00:00.0935848,00:00:09.3135946, +Repeat 800000 for size 4,OptimisticArrayObjectPool,00:00:02.1028619,00:00:01.7289187,00:00:01.7391071,00:00:01.7308129,00:00:01.7911409,00:00:00.0850814,00:00:09.1779229, +Repeat 320000 for size 10,ConcurrentQueueObjectPool,00:00:01.4097002,00:00:01.0808791,00:00:01.0795562,00:00:01.0359202,00:00:01.0900044,00:00:00.1022724,00:00:05.7983325, +Repeat 320000 for size 10,ConcurrentStackObjectPool,00:00:01.3884640,00:00:01.2335304,00:00:01.0874214,00:00:01.0858229,00:00:01.1916481,00:00:00.1097261,00:00:06.0966129, +Repeat 320000 for size 10,OptimisticArrayObjectPool,00:00:01.3395438,00:00:01.0630741,00:00:01.0581905,00:00:01.0257850,00:00:01.0596437,00:00:00.1231254,00:00:05.6693625, +Repeat 96000 for size 50,ConcurrentQueueObjectPool,00:00:00.6724161,00:00:00.9303053,00:00:00.6667530,00:00:00.4869434,00:00:00.5629880,00:00:00.1474862,00:00:03.4668920, +Repeat 96000 for size 50,ConcurrentStackObjectPool,00:00:00.6404036,00:00:00.7253187,00:00:00.5973183,00:00:00.5004547,00:00:00.6653687,00:00:00.1585221,00:00:03.2873861, +Repeat 96000 for size 50,OptimisticArrayObjectPool,00:00:00.6273288,00:00:00.6296415,00:00:00.5758157,00:00:00.4575284,00:00:00.5484878,00:00:00.3576192,00:00:03.1964214, +Repeat 64000 for size 100,ConcurrentQueueObjectPool,00:00:00.5739788,00:00:00.8879708,00:00:00.6868932,00:00:00.5038095,00:00:00.6414144,00:00:00.1906175,00:00:03.4846842, +Repeat 64000 for size 100,ConcurrentStackObjectPool,00:00:00.5710317,00:00:00.8181417,00:00:02.3020598,00:00:00.5377935,00:00:00.7552635,00:00:00.2106115,00:00:05.1949017, +Repeat 64000 for size 100,OptimisticArrayObjectPool,00:00:00.5741654,00:00:00.6708564,00:00:00.6465793,00:00:00.4123009,00:00:00.5587647,00:00:00.5961525,00:00:03.4588192, +Repeat 51200 for size 250,ConcurrentQueueObjectPool,00:00:00.7338318,00:00:02.7582430,00:00:01.3118591,00:00:00.8866236,00:00:01.2641219,00:00:00.3621620,00:00:07.3168414, +Repeat 51200 for size 250,ConcurrentStackObjectPool,00:00:00.7063932,00:00:01.4843162,00:00:04.0788532,00:00:01.6259396,00:00:01.3564185,00:00:00.4047656,00:00:09.6566863, +Repeat 51200 for size 250,OptimisticArrayObjectPool,00:00:00.7798597,00:00:01.3369029,00:00:01.3656621,00:00:00.6762451,00:00:01.0179464,00:00:01.8689033,00:00:07.0455195, +Repeat 12800 for size 2000,ConcurrentQueueObjectPool,00:00:00.7690881,00:00:02.9469849,00:00:02.0202982,00:00:01.3725082,00:00:02.1013278,00:00:00.6418618,00:00:09.8520690, +Repeat 12800 for size 2000,ConcurrentStackObjectPool,00:00:00.6845217,00:00:02.5773779,00:00:04.7847969,00:00:04.7546912,00:00:03.4618654,00:00:00.6506477,00:00:16.9139008, +Repeat 12800 for size 2000,OptimisticArrayObjectPool,00:00:00.7170882,00:00:07.3855602,00:00:07.4918848,00:00:01.1312204,00:00:04.8420972,00:00:13.1760797,00:00:34.7439305, +epeat 51200 for size 250,OptimisticArrayObjectPool,00:00:00.7651948,00:00:01.3195712,00:00:01.3758641,00:00:00.6837150,00:00:01.0134658,00:00:01.9616149,00:00:07.1194258, +Repeat 51200 for size 250,InterlockedArrayObjectPool,00:00:00.8386708,00:00:02.2800174,00:00:01.4244575,00:00:00.6435472,00:00:01.6845873,00:00:05.4327218,00:00:12.3040020, +Repeat 12800 for size 2000,ConcurrentQueueObjectPool,00:00:00.7692018,00:00:03.0317303,00:00:02.0615927,00:00:01.3597065,00:00:02.0767858,00:00:00.6488593,00:00:09.9478764, +Repeat 12800 for size 2000,ConcurrentStackObjectPool,00:00:00.6708805,00:00:02.5628201,00:00:04.7978301,00:00:04.7563902,00:00:03.4723738,00:00:00.6475781,00:00:16.9078728, +Repeat 12800 for size 2000,OptimisticArrayObjectPool,00:00:00.7184635,00:00:07.2485577,00:00:07.4304114,00:00:01.1244839,00:00:04.7572699,00:00:15.8850338,00:00:37.1642202, +Repeat 12800 for size 2000,InterlockedArrayObjectPool,00:00:01.2202062,00:00:19.5837353,00:00:08.9766760,00:00:01.0202604,00:00:12.8356061,00:01:23.7957059,00:02:07.4321899, +0:00:00.7628823,00:00:01.6182833,00:00:01.4624384,00:00:00.8960254,00:00:04.7396294, Repeat 14400 for size 500,ConcurrentQueueObjectPool,00:00:00.8285180,00:00:00.9902911,00:00:00.8006946,00:00:01.1172107,00:00:03.7367144, Repeat 14400 for size 500,ConcurrentStackObjectPool,00:00:00.6978419,00:00:02.2585037,00:00:02.4760289,00:00:02.8394301,00:00:08.2718046, Repeat 14400 for size 500,OptimisticArrayObjectPool,00:00:01.0721065,00:00:05.6089585,00:00:00.5478712,00:00:22.2804453,00:00:29.5093815, diff --git a/benchmarking/BenchmarkResult.txt b/benchmarking/BenchmarkResult.txt index 48fd8ba..56b9ab2 100644 --- a/benchmarking/BenchmarkResult.txt +++ b/benchmarking/BenchmarkResult.txt @@ -1,160 +1,186 @@ -Repeat 400000 for size 4 +Repeat 800000 for size 4 ------------------------------------ -QueueObjectPool......................................... -00:00:03.1829368 Take From Empty (In Parallel) -00:00:02.8245204 Give To (In Parallel) -00:00:02.7274550 Mixed Read/Write (In Parallel) -00:00:00.4644069 Empty Pool (.TryTake()) -00:00:09.1993191 TOTAL - ConcurrentQueueObjectPool............................... -00:00:03.9520475 Take From Empty (In Parallel) -00:00:03.3141114 Give To (In Parallel) -00:00:03.2446456 Mixed Read/Write (In Parallel) -00:00:00.5033748 Empty Pool (.TryTake()) -00:00:11.0141793 TOTAL +00:00:02.1768070 Take From Empty (In Parallel) +00:00:01.6760555 Give To (In Parallel) +00:00:01.7056065 Mixed 90%-Take/10%-Give (In Parallel) +00:00:01.6512698 Mixed 50%-Take/50%-Give (In Parallel) +00:00:01.6459856 Mixed 10%-Take/90%-Give (In Parallel) +00:00:00.0942697 Empty Pool (.TryTake()) +00:00:08.9499941 TOTAL ConcurrentStackObjectPool............................... -00:00:03.7274029 Take From Empty (In Parallel) -00:00:03.4699671 Give To (In Parallel) -00:00:03.1910788 Mixed Read/Write (In Parallel) -00:00:00.4871689 Empty Pool (.TryTake()) -00:00:10.8756177 TOTAL +00:00:02.0861011 Take From Empty (In Parallel) +00:00:01.9051266 Give To (In Parallel) +00:00:01.7180590 Mixed 90%-Take/10%-Give (In Parallel) +00:00:01.6934710 Mixed 50%-Take/50%-Give (In Parallel) +00:00:01.8172521 Mixed 10%-Take/90%-Give (In Parallel) +00:00:00.0935848 Empty Pool (.TryTake()) +00:00:09.3135946 TOTAL OptimisticArrayObjectPool............................... -00:00:03.1819627 Take From Empty (In Parallel) -00:00:02.6187847 Give To (In Parallel) -00:00:02.5613898 Mixed Read/Write (In Parallel) -00:00:00.5260779 Empty Pool (.TryTake()) -00:00:08.8882151 TOTAL +00:00:02.1028619 Take From Empty (In Parallel) +00:00:01.7289187 Give To (In Parallel) +00:00:01.7391071 Mixed 90%-Take/10%-Give (In Parallel) +00:00:01.7308129 Mixed 50%-Take/50%-Give (In Parallel) +00:00:01.7911409 Mixed 10%-Take/90%-Give (In Parallel) +00:00:00.0850814 Empty Pool (.TryTake()) +00:00:09.1779229 TOTAL -Repeat 160000 for size 10 +Repeat 320000 for size 10 ------------------------------------ -QueueObjectPool......................................... -00:00:00.9753926 Take From Empty (In Parallel) -00:00:00.9346630 Give To (In Parallel) -00:00:00.8060045 Mixed Read/Write (In Parallel) -00:00:00.2545299 Empty Pool (.TryTake()) -00:00:02.9705900 TOTAL - ConcurrentQueueObjectPool............................... -00:00:01.0289733 Take From Empty (In Parallel) -00:00:00.8683338 Give To (In Parallel) -00:00:00.8201646 Mixed Read/Write (In Parallel) -00:00:00.2358702 Empty Pool (.TryTake()) -00:00:02.9533419 TOTAL +00:00:01.4097002 Take From Empty (In Parallel) +00:00:01.0808791 Give To (In Parallel) +00:00:01.0795562 Mixed 90%-Take/10%-Give (In Parallel) +00:00:01.0359202 Mixed 50%-Take/50%-Give (In Parallel) +00:00:01.0900044 Mixed 10%-Take/90%-Give (In Parallel) +00:00:00.1022724 Empty Pool (.TryTake()) +00:00:05.7983325 TOTAL ConcurrentStackObjectPool............................... -00:00:01.0970613 Take From Empty (In Parallel) -00:00:01.0251371 Give To (In Parallel) -00:00:00.8980626 Mixed Read/Write (In Parallel) -00:00:00.2443304 Empty Pool (.TryTake()) -00:00:03.2645914 TOTAL +00:00:01.3884640 Take From Empty (In Parallel) +00:00:01.2335304 Give To (In Parallel) +00:00:01.0874214 Mixed 90%-Take/10%-Give (In Parallel) +00:00:01.0858229 Mixed 50%-Take/50%-Give (In Parallel) +00:00:01.1916481 Mixed 10%-Take/90%-Give (In Parallel) +00:00:00.1097261 Empty Pool (.TryTake()) +00:00:06.0966129 TOTAL OptimisticArrayObjectPool............................... -00:00:01.1247373 Take From Empty (In Parallel) -00:00:00.9535141 Give To (In Parallel) -00:00:00.8851396 Mixed Read/Write (In Parallel) -00:00:00.4598056 Empty Pool (.TryTake()) -00:00:03.4231966 TOTAL +00:00:01.3395438 Take From Empty (In Parallel) +00:00:01.0630741 Give To (In Parallel) +00:00:01.0581905 Mixed 90%-Take/10%-Give (In Parallel) +00:00:01.0257850 Mixed 50%-Take/50%-Give (In Parallel) +00:00:01.0596437 Mixed 10%-Take/90%-Give (In Parallel) +00:00:00.1231254 Empty Pool (.TryTake()) +00:00:05.6693625 TOTAL -Repeat 48000 for size 50 +Repeat 96000 for size 50 ------------------------------------ -QueueObjectPool......................................... -00:00:00.5223797 Take From Empty (In Parallel) -00:00:00.8268179 Give To (In Parallel) -00:00:00.5445827 Mixed Read/Write (In Parallel) -00:00:00.3488348 Empty Pool (.TryTake()) -00:00:02.2426151 TOTAL - ConcurrentQueueObjectPool............................... -00:00:00.5395239 Take From Empty (In Parallel) -00:00:00.9860749 Give To (In Parallel) -00:00:00.4412272 Mixed Read/Write (In Parallel) -00:00:00.3090006 Empty Pool (.TryTake()) -00:00:02.2758266 TOTAL +00:00:00.6724161 Take From Empty (In Parallel) +00:00:00.9303053 Give To (In Parallel) +00:00:00.6667530 Mixed 90%-Take/10%-Give (In Parallel) +00:00:00.4869434 Mixed 50%-Take/50%-Give (In Parallel) +00:00:00.5629880 Mixed 10%-Take/90%-Give (In Parallel) +00:00:00.1474862 Empty Pool (.TryTake()) +00:00:03.4668920 TOTAL ConcurrentStackObjectPool............................... -00:00:00.5258119 Take From Empty (In Parallel) -00:00:00.5466085 Give To (In Parallel) -00:00:00.4650011 Mixed Read/Write (In Parallel) -00:00:00.3059624 Empty Pool (.TryTake()) -00:00:01.8433839 TOTAL +00:00:00.6404036 Take From Empty (In Parallel) +00:00:00.7253187 Give To (In Parallel) +00:00:00.5973183 Mixed 90%-Take/10%-Give (In Parallel) +00:00:00.5004547 Mixed 50%-Take/50%-Give (In Parallel) +00:00:00.6653687 Mixed 10%-Take/90%-Give (In Parallel) +00:00:00.1585221 Empty Pool (.TryTake()) +00:00:03.2873861 TOTAL OptimisticArrayObjectPool............................... -00:00:00.5831005 Take From Empty (In Parallel) -00:00:00.7148946 Give To (In Parallel) -00:00:00.4330670 Mixed Read/Write (In Parallel) -00:00:01.7100735 Empty Pool (.TryTake()) -00:00:03.4411356 TOTAL +00:00:00.6273288 Take From Empty (In Parallel) +00:00:00.6296415 Give To (In Parallel) +00:00:00.5758157 Mixed 90%-Take/10%-Give (In Parallel) +00:00:00.4575284 Mixed 50%-Take/50%-Give (In Parallel) +00:00:00.5484878 Mixed 10%-Take/90%-Give (In Parallel) +00:00:00.3576192 Empty Pool (.TryTake()) +00:00:03.1964214 TOTAL -Repeat 32000 for size 100 +Repeat 64000 for size 100 ------------------------------------ -QueueObjectPool......................................... -00:00:00.5055389 Take From Empty (In Parallel) -00:00:00.9263350 Give To (In Parallel) -00:00:00.5684372 Mixed Read/Write (In Parallel) -00:00:00.4567166 Empty Pool (.TryTake()) -00:00:02.4570277 TOTAL - ConcurrentQueueObjectPool............................... -00:00:00.5348503 Take From Empty (In Parallel) -00:00:00.7617627 Give To (In Parallel) -00:00:00.4222624 Mixed Read/Write (In Parallel) -00:00:00.4125298 Empty Pool (.TryTake()) -00:00:02.1314052 TOTAL +00:00:00.5739788 Take From Empty (In Parallel) +00:00:00.8879708 Give To (In Parallel) +00:00:00.6868932 Mixed 90%-Take/10%-Give (In Parallel) +00:00:00.5038095 Mixed 50%-Take/50%-Give (In Parallel) +00:00:00.6414144 Mixed 10%-Take/90%-Give (In Parallel) +00:00:00.1906175 Empty Pool (.TryTake()) +00:00:03.4846842 TOTAL ConcurrentStackObjectPool............................... -00:00:00.5154681 Take From Empty (In Parallel) -00:00:00.5769678 Give To (In Parallel) -00:00:00.4597205 Mixed Read/Write (In Parallel) -00:00:00.4073109 Empty Pool (.TryTake()) -00:00:01.9594673 TOTAL +00:00:00.5710317 Take From Empty (In Parallel) +00:00:00.8181417 Give To (In Parallel) +00:00:02.3020598 Mixed 90%-Take/10%-Give (In Parallel) +00:00:00.5377935 Mixed 50%-Take/50%-Give (In Parallel) +00:00:00.7552635 Mixed 10%-Take/90%-Give (In Parallel) +00:00:00.2106115 Empty Pool (.TryTake()) +00:00:05.1949017 TOTAL OptimisticArrayObjectPool............................... -00:00:00.7254637 Take From Empty (In Parallel) -00:00:01.0581905 Give To (In Parallel) -00:00:00.3984681 Mixed Read/Write (In Parallel) -00:00:03.7309017 Empty Pool (.TryTake()) -00:00:05.9130240 TOTAL +00:00:00.5741654 Take From Empty (In Parallel) +00:00:00.6708564 Give To (In Parallel) +00:00:00.6465793 Mixed 90%-Take/10%-Give (In Parallel) +00:00:00.4123009 Mixed 50%-Take/50%-Give (In Parallel) +00:00:00.5587647 Mixed 10%-Take/90%-Give (In Parallel) +00:00:00.5961525 Empty Pool (.TryTake()) +00:00:03.4588192 TOTAL Repeat 51200 for size 250 ------------------------------------ -QueueObjectPool......................................... -00:00:01.5711793 Take From Empty (In Parallel) -00:00:03.1183660 Give To (In Parallel) -00:00:02.1775394 Mixed Read/Write (In Parallel) -00:00:01.7555068 Empty Pool (.TryTake()) -00:00:08.6225915 TOTAL +ConcurrentQueueObjectPool............................... +00:00:00.7338318 Take From Empty (In Parallel) +00:00:02.7582430 Give To (In Parallel) +00:00:01.3118591 Mixed 90%-Take/10%-Give (In Parallel) +00:00:00.8866236 Mixed 50%-Take/50%-Give (In Parallel) +00:00:01.2641219 Mixed 10%-Take/90%-Give (In Parallel) +00:00:00.3621620 Empty Pool (.TryTake()) +00:00:07.3168414 TOTAL + +ConcurrentStackObjectPool............................... +00:00:00.7063932 Take From Empty (In Parallel) +00:00:01.4843162 Give To (In Parallel) +00:00:04.0788532 Mixed 90%-Take/10%-Give (In Parallel) +00:00:01.6259396 Mixed 50%-Take/50%-Give (In Parallel) +00:00:01.3564185 Mixed 10%-Take/90%-Give (In Parallel) +00:00:00.4047656 Empty Pool (.TryTake()) +00:00:09.6566863 TOTAL + +OptimisticArrayObjectPool............................... +00:00:00.7798597 Take From Empty (In Parallel) +00:00:01.3369029 Give To (In Parallel) +00:00:01.3656621 Mixed 90%-Take/10%-Give (In Parallel) +00:00:00.6762451 Mixed 50%-Take/50%-Give (In Parallel) +00:00:01.0179464 Mixed 10%-Take/90%-Give (In Parallel) +00:00:01.8689033 Empty Pool (.TryTake()) +00:00:07.0455195 TOTAL + + +Repeat 12800 for size 2000 +------------------------------------ ConcurrentQueueObjectPool............................... -00:00:02.0386582 Take From Empty (In Parallel) -00:00:02.5414010 Give To (In Parallel) -00:00:01.4334623 Mixed Read/Write (In Parallel) -00:00:01.7010927 Empty Pool (.TryTake()) -00:00:07.7146142 TOTAL +00:00:00.7690881 Take From Empty (In Parallel) +00:00:02.9469849 Give To (In Parallel) +00:00:02.0202982 Mixed 90%-Take/10%-Give (In Parallel) +00:00:01.3725082 Mixed 50%-Take/50%-Give (In Parallel) +00:00:02.1013278 Mixed 10%-Take/90%-Give (In Parallel) +00:00:00.6418618 Empty Pool (.TryTake()) +00:00:09.8520690 TOTAL ConcurrentStackObjectPool............................... -00:00:01.8341826 Take From Empty (In Parallel) -00:00:02.0561458 Give To (In Parallel) -00:00:01.4740427 Mixed Read/Write (In Parallel) -00:00:01.6809933 Empty Pool (.TryTake()) -00:00:07.0453644 TOTAL +00:00:00.6845217 Take From Empty (In Parallel) +00:00:02.5773779 Give To (In Parallel) +00:00:04.7847969 Mixed 90%-Take/10%-Give (In Parallel) +00:00:04.7546912 Mixed 50%-Take/50%-Give (In Parallel) +00:00:03.4618654 Mixed 10%-Take/90%-Give (In Parallel) +00:00:00.6506477 Empty Pool (.TryTake()) +00:00:16.9139008 TOTAL OptimisticArrayObjectPool............................... -00:00:02.7181693 Take From Empty (In Parallel) -00:00:06.9277528 Give To (In Parallel) -00:00:01.1086113 Mixed Read/Write (In Parallel) -00:00:25.9591372 Empty Pool (.TryTake()) -00:00:36.7136706 TOTAL +00:00:00.7170882 Take From Empty (In Parallel) +00:00:07.3855602 Give To (In Parallel) +00:00:07.4918848 Mixed 90%-Take/10%-Give (In Parallel) +00:00:01.1312204 Mixed 50%-Take/50%-Give (In Parallel) +00:00:04.8420972 Mixed 10%-Take/90%-Give (In Parallel) +00:00:13.1760797 Empty Pool (.TryTake()) +00:00:34.7439305 TOTAL diff --git a/benchmarking/Program.cs b/benchmarking/Program.cs index d14ce7d..7e59411 100644 --- a/benchmarking/Program.cs +++ b/benchmarking/Program.cs @@ -23,8 +23,8 @@ static void Main(string[] args) */ // Start with a baseline... - report.AddBenchmark("QueueObjectPool", // Note, that this one isn't far off from the following in peformance. - count => () => QueueObjectPool.Create((int)count * 2)); + //report.AddBenchmark("QueueObjectPool", // Note, that this one isn't far off from the following in peformance. + // count => () => QueueObjectPool.Create((int)count * 2)); //report.AddBenchmark("ChannelObjectPool", // count => () => ChannelObjectPool.Create((int)count * 2)); @@ -40,19 +40,19 @@ static void Main(string[] args) // Is ineveitably slower than the above but should be enabled for testing code changes. //report.AddBenchmark("InterlockedArrayObjectPool", - // count => () => InterlockedArrayObjectPool.Create((int)count * 2)); + // count => () => InterlockedArrayObjectPool.Create((int)count * 2)); report.Pretest(200, 200); // Run once through first to scramble/warm-up initial conditions. Console.SetCursorPosition(0, Console.CursorTop); - const int loopMultiple = 2; + const int loopMultiple = 4; report.Test(4, 8 * loopMultiple); report.Test(10, 8 * loopMultiple); report.Test(50, 12 * loopMultiple); report.Test(100, 16 * loopMultiple); - report.Test(250, 64 * loopMultiple); - //report.Test(500, 72); + report.Test(250, 32 * loopMultiple); + report.Test(2000, 64 * loopMultiple); File.WriteAllText("./BenchmarkResult.txt", sb.ToString()); using (var fs = File.OpenWrite("./BenchmarkResult.csv")) diff --git a/source/Array/InterlockedArrayObjectPool.cs b/source/Array/InterlockedArrayObjectPool.cs index 851e24a..a68921c 100644 --- a/source/Array/InterlockedArrayObjectPool.cs +++ b/source/Array/InterlockedArrayObjectPool.cs @@ -13,14 +13,14 @@ public class InterlockedArrayObjectPool : ObjectPoolBase where T : class { - public InterlockedArrayObjectPool(Func factory, Action recycler, int capacity = DEFAULT_CAPACITY) - : base(factory, recycler, capacity) + public InterlockedArrayObjectPool(Func factory, Action recycler, Action disposer, int capacity = DEFAULT_CAPACITY) + : base(factory, recycler, disposer, capacity) { Pool = new ReferenceContainer[capacity - 1]; } public InterlockedArrayObjectPool(Func factory, int capacity = DEFAULT_CAPACITY) - : this(factory, null, capacity) + : this(factory, null, null, capacity) { } protected ReferenceContainer[] Pool; @@ -29,6 +29,9 @@ public InterlockedArrayObjectPool(Func factory, int capacity = DEFAULT_CAPACI protected int MaxStored = 0; protected const int MaxStoredIncrement = 5; // Instead of every one. + protected virtual bool Store(ReferenceContainer[] p, T item, int index) + => p[index].TrySave(item); + protected override bool Receive(T item) { var elements = Pool; @@ -36,7 +39,7 @@ protected override bool Receive(T item) for (int i = 0; i < len; i++) { - if (elements[i].TrySave(item)) + if (Store(elements, item, i)) { var m = MaxStored; if (i >= m) Interlocked.CompareExchange(ref MaxStored, m + MaxStoredIncrement, m); @@ -66,6 +69,7 @@ protected override T TryRelease() protected override void OnDispose(bool calledExplicitly) { + base.OnDispose(calledExplicitly); Pool = null; MaxStored = 0; } @@ -86,18 +90,28 @@ public static InterlockedArrayObjectPool Create(int capacity = Constants.D return Create(() => new T(), capacity); } - public static InterlockedArrayObjectPool Create(Func factory, bool autoRecycle, int capacity = Constants.DEFAULT_CAPACITY) + public static InterlockedArrayObjectPool CreateAutoRecycle(Func factory, int capacity = Constants.DEFAULT_CAPACITY) where T : class, IRecyclable { - Action recycler = null; - if (autoRecycle) recycler = Recycler.Recycle; - return new InterlockedArrayObjectPool(factory, recycler, capacity); + return new InterlockedArrayObjectPool(factory, Recycler.Recycle, null, capacity); } - public static InterlockedArrayObjectPool Create(bool autoRecycle, int capacity = Constants.DEFAULT_CAPACITY) + public static InterlockedArrayObjectPool CreateAutoRecycle(int capacity = Constants.DEFAULT_CAPACITY) where T : class, IRecyclable, new() { - return Create(() => new T(), autoRecycle, capacity); + return CreateAutoRecycle(() => new T(), capacity); + } + + public static InterlockedArrayObjectPool CreateAutoDisposal(Func factory, int capacity = Constants.DEFAULT_CAPACITY) + where T : class, IDisposable + { + return new InterlockedArrayObjectPool(factory, null, d => d.Dispose(), capacity); + } + + public static InterlockedArrayObjectPool CreateAutoDisposal(int capacity = Constants.DEFAULT_CAPACITY) + where T : class, IDisposable, new() + { + return CreateAutoDisposal(() => new T(), capacity); } } diff --git a/source/Array/OptimisticArrayObjectPool.cs b/source/Array/OptimisticArrayObjectPool.cs index db858c9..06f09ed 100644 --- a/source/Array/OptimisticArrayObjectPool.cs +++ b/source/Array/OptimisticArrayObjectPool.cs @@ -1,7 +1,6 @@ /* Based on Roslyn's ObjectPool */ using System; -using System.Threading; namespace Open.Disposable { @@ -14,7 +13,7 @@ public class OptimisticArrayObjectPool : InterlockedArrayObjectPool { public OptimisticArrayObjectPool(Func factory, Action recycler, int capacity = DEFAULT_CAPACITY) - : base(factory, recycler, capacity) + : base(factory, recycler, null /* disposer not applicable here */, capacity) { } public OptimisticArrayObjectPool(Func factory, int capacity = DEFAULT_CAPACITY) @@ -23,30 +22,11 @@ public OptimisticArrayObjectPool(Func factory, int capacity = DEFAULT_CAPACIT // As suggested by Roslyn's implementation, don't worry about interlocking here. It's okay if a few get loose. protected override bool SaveToPocket(T item) - { - return Pocket.SetIfNull(item); - } - - protected override bool Receive(T item) - { - var elements = Pool; - var len = elements?.Length ?? 0; - - for (int i = 0; i < len; i++) - { - // As suggested by Roslyn's implementation, don't worry about interlocking here. It's okay if a few get loose. - if (elements[i].SetIfNull(item)) - { - var m = MaxStored; - if (i >= m) Interlocked.CompareExchange(ref MaxStored, m + MaxStoredIncrement, m); - - return true; - } - } - - return false; - } + => Pocket.SetIfNull(item); + // As suggested by Roslyn's implementation, don't worry about interlocking here. It's okay if a few get loose. + protected override bool Store(ReferenceContainer[] p, T item, int index) + => p[index].SetIfNull(item); } public static class OptimisticArrayObjectPool @@ -63,18 +43,16 @@ public static OptimisticArrayObjectPool Create(int capacity = Constants.DE return Create(() => new T(), capacity); } - public static OptimisticArrayObjectPool Create(Func factory, bool autoRecycle, int capacity = Constants.DEFAULT_CAPACITY) + public static OptimisticArrayObjectPool CreateAutoRecycle(Func factory, int capacity = Constants.DEFAULT_CAPACITY) where T : class, IRecyclable { - Action recycler = null; - if (autoRecycle) recycler = Recycler.Recycle; - return new OptimisticArrayObjectPool(factory, recycler, capacity); + return new OptimisticArrayObjectPool(factory, Recycler.Recycle, capacity); } - public static OptimisticArrayObjectPool Create(bool autoRecycle, int capacity = Constants.DEFAULT_CAPACITY) + public static OptimisticArrayObjectPool CreateAutoRecycle(int capacity = Constants.DEFAULT_CAPACITY) where T : class, IRecyclable, new() { - return Create(() => new T(), autoRecycle, capacity); + return CreateAutoRecycle(() => new T(), capacity); } } diff --git a/source/Collection/CollectionWrapperObjectPool.cs b/source/Collection/CollectionWrapperObjectPool.cs index b34aeac..963dede 100644 --- a/source/Collection/CollectionWrapperObjectPool.cs +++ b/source/Collection/CollectionWrapperObjectPool.cs @@ -4,23 +4,20 @@ namespace Open.Disposable { - public class CollectionWrapperObjectPool : TrimmableObjectPoolBase + public class CollectionWrapperObjectPool : TrimmableGenericCollectionObjectPoolBase where T : class where TCollection : class, ICollection { - public CollectionWrapperObjectPool(TCollection pool, Func factory, Action recycler, int capacity = DEFAULT_CAPACITY, bool countTrackingEnabled = false) - : base(factory, recycler, capacity, countTrackingEnabled) + public CollectionWrapperObjectPool(TCollection pool, Func factory, Action recycler, Action disposer, int capacity = DEFAULT_CAPACITY, bool countTrackingEnabled = true) + : base(pool, factory, recycler, disposer, capacity, countTrackingEnabled) { - Pool = pool; } - public CollectionWrapperObjectPool(TCollection pool, Func factory, int capacity = DEFAULT_CAPACITY, bool countTrackingEnabled = false) - : this(pool, factory, null, capacity, countTrackingEnabled) + public CollectionWrapperObjectPool(TCollection pool, Func factory, int capacity = DEFAULT_CAPACITY, bool countTrackingEnabled = true) + : this(pool, factory, null, null, capacity, countTrackingEnabled) { } - protected TCollection Pool; - protected override bool Receive(T item) { var p = Pool; @@ -58,16 +55,12 @@ protected override T TryRelease() return item; } - protected override void OnDispose(bool calledExplicitly) - { - Pool = null; - } } public class CollectionWrapperObjectPool : CollectionWrapperObjectPool> where T : class { - public CollectionWrapperObjectPool(ICollection pool, Func factory, int capacity = DEFAULT_CAPACITY, bool countTrackingEnabled = false) : base(pool, factory, capacity, countTrackingEnabled) + public CollectionWrapperObjectPool(ICollection pool, Func factory, int capacity = DEFAULT_CAPACITY) : base(pool, factory, capacity) { } } diff --git a/source/Collection/ConcurrentQueueObjectPool.cs b/source/Collection/ConcurrentQueueObjectPool.cs index a6d8e3e..bb2564a 100644 --- a/source/Collection/ConcurrentQueueObjectPool.cs +++ b/source/Collection/ConcurrentQueueObjectPool.cs @@ -3,24 +3,21 @@ namespace Open.Disposable { - public sealed class ConcurrentQueueObjectPool : TrimmableObjectPoolBase + public sealed class ConcurrentQueueObjectPool : TrimmableCollectionObjectPoolBase> where T : class { - public ConcurrentQueueObjectPool(Func factory, Action recycler, int capacity = DEFAULT_CAPACITY, bool countTrackingEnabled = false) - : base(factory, recycler, capacity, countTrackingEnabled) + public ConcurrentQueueObjectPool(Func factory, Action recycler, Action disposer, int capacity = DEFAULT_CAPACITY) + : base(new ConcurrentQueue(), factory, recycler, disposer, capacity) { - Pool = new ConcurrentQueue(); } - public ConcurrentQueueObjectPool(Func factory, int capacity = DEFAULT_CAPACITY, bool countTrackingEnabled = false) - : this(factory, null, capacity, countTrackingEnabled) + public ConcurrentQueueObjectPool(Func factory, int capacity = DEFAULT_CAPACITY) + : this(factory, null, null, capacity) { } - ConcurrentQueue Pool; - /* * NOTE: ConcurrentQueue is very fast and will perform quite well without using the 'Pocket' feature. * Benchmarking reveals that mixed read/writes (what really matters) are still faster with the pocket enabled so best to keep it so. @@ -42,43 +39,44 @@ protected override T TryRelease() return item; } - protected override void OnDispose(bool calledExplicitly) - { - if (calledExplicitly) - { - Pool = null; - } - } - } - public static class ConcurrentQueueObjectPool { - public static ConcurrentQueueObjectPool Create(Func factory, int capacity = Constants.DEFAULT_CAPACITY, bool countTrackingEnabled = false) + public static ConcurrentQueueObjectPool Create(Func factory, int capacity = Constants.DEFAULT_CAPACITY) where T : class { - return new ConcurrentQueueObjectPool(factory, capacity, countTrackingEnabled); + return new ConcurrentQueueObjectPool(factory, capacity); } - - public static ConcurrentQueueObjectPool Create(int capacity = Constants.DEFAULT_CAPACITY, bool countTrackingEnabled = false) + public static ConcurrentQueueObjectPool Create(int capacity = Constants.DEFAULT_CAPACITY) where T : class, new() { - return Create(() => new T(), capacity, countTrackingEnabled); + return Create(() => new T(), capacity); } - public static ConcurrentQueueObjectPool Create(Func factory, bool autoRecycle, int capacity = Constants.DEFAULT_CAPACITY, bool countTrackingEnabled = false) - where T : class, IRecyclable + public static ConcurrentQueueObjectPool CreateAutoRecycle(Func factory, int capacity = Constants.DEFAULT_CAPACITY) + where T : class, IRecyclable { - Action recycler = null; - if (autoRecycle) recycler = Recycler.Recycle; - return new ConcurrentQueueObjectPool(factory, recycler, capacity, countTrackingEnabled); + return new ConcurrentQueueObjectPool(factory, Recycler.Recycle, null, capacity); } - public static ConcurrentQueueObjectPool Create(bool autoRecycle, int capacity = Constants.DEFAULT_CAPACITY, bool countTrackingEnabled = false) + public static ConcurrentQueueObjectPool CreateAutoRecycle(int capacity = Constants.DEFAULT_CAPACITY) where T : class, IRecyclable, new() { - return Create(() => new T(), autoRecycle, capacity, countTrackingEnabled); + return CreateAutoRecycle(() => new T(), capacity); } + + public static ConcurrentQueueObjectPool CreateAutoDisposal(Func factory, int capacity = Constants.DEFAULT_CAPACITY) + where T : class, IDisposable + { + return new ConcurrentQueueObjectPool(factory, null, d => d.Dispose(), capacity); + } + + public static ConcurrentQueueObjectPool CreateAutoDisposal(int capacity = Constants.DEFAULT_CAPACITY) + where T : class, IDisposable, new() + { + return CreateAutoDisposal(() => new T(), capacity); + } + } } diff --git a/source/Collection/ConcurrentStackObjectPool.cs b/source/Collection/ConcurrentStackObjectPool.cs index 9fd3861..888fcf8 100644 --- a/source/Collection/ConcurrentStackObjectPool.cs +++ b/source/Collection/ConcurrentStackObjectPool.cs @@ -3,24 +3,21 @@ namespace Open.Disposable { - public sealed class ConcurrentStackObjectPool : TrimmableObjectPoolBase + public sealed class ConcurrentStackObjectPool : TrimmableCollectionObjectPoolBase> where T : class { - public ConcurrentStackObjectPool(Func factory, Action recycler, int capacity = DEFAULT_CAPACITY, bool countTrackingEnabled = false) - : base(factory, recycler, capacity, countTrackingEnabled) + public ConcurrentStackObjectPool(Func factory, Action recycler, Action disposer, int capacity = DEFAULT_CAPACITY) + : base(new ConcurrentStack(), factory, recycler, disposer, capacity) { - Pool = new ConcurrentStack(); } - public ConcurrentStackObjectPool(Func factory, int capacity = DEFAULT_CAPACITY, bool countTrackingEnabled = false) - : this(factory, null, capacity, countTrackingEnabled) + public ConcurrentStackObjectPool(Func factory, int capacity = DEFAULT_CAPACITY) + : this(factory, null, null, capacity) { } - ConcurrentStack Pool; - protected override bool Receive(T item) { var p = Pool; @@ -37,39 +34,44 @@ protected override T TryRelease() return item; } - protected override void OnDispose(bool calledExplicitly) - { - Pool = null; - } - } public static class ConcurrentStackObjectPool { - public static ConcurrentStackObjectPool Create(Func factory, int capacity = Constants.DEFAULT_CAPACITY, bool countTrackingEnabled = false) + public static ConcurrentStackObjectPool Create(Func factory, int capacity = Constants.DEFAULT_CAPACITY) where T : class { - return new ConcurrentStackObjectPool(factory, capacity, countTrackingEnabled); + return new ConcurrentStackObjectPool(factory, capacity); } - public static ConcurrentStackObjectPool Create(int capacity = Constants.DEFAULT_CAPACITY, bool countTrackingEnabled = false) + public static ConcurrentStackObjectPool Create(int capacity = Constants.DEFAULT_CAPACITY) where T : class, new() { - return Create(() => new T(), capacity, countTrackingEnabled); + return Create(() => new T(), capacity); } - public static ConcurrentStackObjectPool Create(Func factory, bool autoRecycle, int capacity = Constants.DEFAULT_CAPACITY, bool countTrackingEnabled = false) - where T : class, IRecyclable + public static ConcurrentStackObjectPool CreateAutoRecycle(Func factory, int capacity = Constants.DEFAULT_CAPACITY) + where T : class, IRecyclable { - Action recycler = null; - if (autoRecycle) recycler = Recycler.Recycle; - return new ConcurrentStackObjectPool(factory, recycler, capacity, countTrackingEnabled); + return new ConcurrentStackObjectPool(factory, Recycler.Recycle, null, capacity); } - public static ConcurrentStackObjectPool Create(bool autoRecycle, int capacity = Constants.DEFAULT_CAPACITY, bool countTrackingEnabled = false) + public static ConcurrentStackObjectPool CreateAutoRecycle(int capacity = Constants.DEFAULT_CAPACITY) where T : class, IRecyclable, new() { - return Create(() => new T(), autoRecycle, capacity, countTrackingEnabled); + return CreateAutoRecycle(() => new T(), capacity); + } + + public static ConcurrentStackObjectPool CreateAutoDisposal(Func factory, int capacity = Constants.DEFAULT_CAPACITY) + where T : class, IDisposable + { + return new ConcurrentStackObjectPool(factory, null, d => d.Dispose(), capacity); + } + + public static ConcurrentStackObjectPool CreateAutoDisposal(int capacity = Constants.DEFAULT_CAPACITY) + where T : class, IDisposable, new() + { + return CreateAutoDisposal(() => new T(), capacity); } } diff --git a/source/Collection/QueueObjectPool.cs b/source/Collection/QueueObjectPool.cs index e8ccacb..d0556d1 100644 --- a/source/Collection/QueueObjectPool.cs +++ b/source/Collection/QueueObjectPool.cs @@ -3,24 +3,23 @@ namespace Open.Disposable { - public sealed class QueueObjectPool : TrimmableObjectPoolBase + public sealed class QueueObjectPool : TrimmableCollectionObjectPoolBase> where T : class { - public QueueObjectPool(Func factory, Action recycler, int capacity = DEFAULT_CAPACITY, bool countTrackingEnabled = false) - : base(factory, recycler, capacity, countTrackingEnabled) + public QueueObjectPool(Func factory, Action recycler, Action disposer, int capacity = DEFAULT_CAPACITY) + : base( + new Queue(Math.Min(DEFAULT_CAPACITY, capacity)) /* Very very slight speed improvment when capacity is initially set. */, + factory, recycler, disposer, capacity, false) { - Pool = new Queue(capacity); // Very very slight speed improvment when capacity is set. } - public QueueObjectPool(Func factory, int capacity = DEFAULT_CAPACITY, bool countTrackingEnabled = false) - : this(factory, null, capacity, countTrackingEnabled) + public QueueObjectPool(Func factory, int capacity = DEFAULT_CAPACITY) + : this(factory, null, null, capacity) { } - Queue Pool; - protected override bool Receive(T item) { var p = Pool; @@ -51,38 +50,44 @@ protected override T TryRelease() return null; } - protected override void OnDispose(bool calledExplicitly) - { - Pool = null; - } } public static class QueueObjectPool { - public static QueueObjectPool Create(Func factory, int capacity = Constants.DEFAULT_CAPACITY, bool countTrackingEnabled = false) + public static QueueObjectPool Create(Func factory, int capacity = Constants.DEFAULT_CAPACITY) where T : class { - return new QueueObjectPool(factory, capacity, countTrackingEnabled); + return new QueueObjectPool(factory, capacity); } - public static QueueObjectPool Create(int capacity = Constants.DEFAULT_CAPACITY, bool countTrackingEnabled = false) + public static QueueObjectPool Create(int capacity = Constants.DEFAULT_CAPACITY) where T : class, new() { - return Create(() => new T(), capacity, countTrackingEnabled); + return Create(() => new T(), capacity); } - public static QueueObjectPool Create(Func factory, bool autoRecycle, int capacity = Constants.DEFAULT_CAPACITY, bool countTrackingEnabled = false) - where T : class, IRecyclable + public static QueueObjectPool CreateAutoRecycle(Func factory, int capacity = Constants.DEFAULT_CAPACITY) + where T : class, IRecyclable { - Action recycler = null; - if (autoRecycle) recycler = Recycler.Recycle; - return new QueueObjectPool(factory, recycler, capacity, countTrackingEnabled); + return new QueueObjectPool(factory, Recycler.Recycle, null, capacity); } - public static QueueObjectPool Create(bool autoRecycle, int capacity = Constants.DEFAULT_CAPACITY, bool countTrackingEnabled = false) + public static QueueObjectPool CreateAutoRecycle(int capacity = Constants.DEFAULT_CAPACITY) where T : class, IRecyclable, new() { - return Create(() => new T(), autoRecycle, capacity, countTrackingEnabled); + return CreateAutoRecycle(() => new T(), capacity); + } + + public static QueueObjectPool CreateAutoDisposal(Func factory, int capacity = Constants.DEFAULT_CAPACITY) + where T : class, IDisposable + { + return new QueueObjectPool(factory, null, d => d.Dispose(), capacity); + } + + public static QueueObjectPool CreateAutoDisposal(int capacity = Constants.DEFAULT_CAPACITY) + where T : class, IDisposable, new() + { + return CreateAutoDisposal(() => new T(), capacity); } } diff --git a/source/Collection/StackObjectPool.cs b/source/Collection/StackObjectPool.cs index 5a28b7b..9c69d82 100644 --- a/source/Collection/StackObjectPool.cs +++ b/source/Collection/StackObjectPool.cs @@ -3,24 +3,23 @@ namespace Open.Disposable { - public sealed class StackObjectPool : TrimmableObjectPoolBase + public sealed class StackObjectPool : TrimmableCollectionObjectPoolBase> where T : class { - public StackObjectPool(Func factory, Action recycler, int capacity = DEFAULT_CAPACITY, bool countTrackingEnabled = false) - : base(factory, recycler, capacity, countTrackingEnabled) + public StackObjectPool(Func factory, Action recycler, Action disposer, int capacity = DEFAULT_CAPACITY) + : base( + new Stack(Math.Min(DEFAULT_CAPACITY, capacity)) /* Very very slight speed improvment when capacity is set. */, + factory, recycler, disposer, capacity, false) { - Pool = new Stack(capacity); // Very very slight speed improvment when capacity is set. } - public StackObjectPool(Func factory, int capacity = DEFAULT_CAPACITY, bool countTrackingEnabled = false) - : this(factory, null, capacity, countTrackingEnabled) + public StackObjectPool(Func factory, int capacity = DEFAULT_CAPACITY) + : this(factory, null, null, capacity) { } - Stack Pool; - protected override bool Receive(T item) { var p = Pool; @@ -50,39 +49,45 @@ protected override T TryRelease() return null; } - - protected override void OnDispose(bool calledExplicitly) - { - Pool = null; - } } + public static class StackObjectPool { - public static StackObjectPool Create(Func factory, int capacity = Constants.DEFAULT_CAPACITY, bool countTrackingEnabled = false) + public static StackObjectPool Create(Func factory, int capacity = Constants.DEFAULT_CAPACITY) where T : class { - return new StackObjectPool(factory, capacity, countTrackingEnabled); + return new StackObjectPool(factory, capacity); } - public static StackObjectPool Create(int capacity = Constants.DEFAULT_CAPACITY, bool countTrackingEnabled = false) + public static StackObjectPool Create(int capacity = Constants.DEFAULT_CAPACITY) where T : class, new() { - return Create(() => new T(), capacity, countTrackingEnabled); + return Create(() => new T(), capacity); } - public static StackObjectPool Create(Func factory, bool autoRecycle, int capacity = Constants.DEFAULT_CAPACITY, bool countTrackingEnabled = false) + public static StackObjectPool CreateAutoRecycle(Func factory, int capacity = Constants.DEFAULT_CAPACITY) where T : class, IRecyclable { - Action recycler = null; - if (autoRecycle) recycler = Recycler.Recycle; - return new StackObjectPool(factory, recycler, capacity, countTrackingEnabled); + return new StackObjectPool(factory, Recycler.Recycle, null, capacity); } - public static StackObjectPool Create(bool autoRecycle, int capacity = Constants.DEFAULT_CAPACITY, bool countTrackingEnabled = false) + public static StackObjectPool CreateAutoRecycle(int capacity = Constants.DEFAULT_CAPACITY) where T : class, IRecyclable, new() { - return Create(() => new T(), autoRecycle, capacity, countTrackingEnabled); + return CreateAutoRecycle(() => new T(), capacity); + } + + public static StackObjectPool CreateAutoDisposal(Func factory, int capacity = Constants.DEFAULT_CAPACITY) + where T : class, IDisposable + { + return new StackObjectPool(factory, null, d => d.Dispose(), capacity); + } + + public static StackObjectPool CreateAutoDisposal(int capacity = Constants.DEFAULT_CAPACITY) + where T : class, IDisposable, new() + { + return CreateAutoDisposal(() => new T(), capacity); } } diff --git a/source/ITrimmableObjectPool.cs b/source/ITrimmableObjectPool.cs index f9fc680..351fc7a 100644 --- a/source/ITrimmableObjectPool.cs +++ b/source/ITrimmableObjectPool.cs @@ -2,8 +2,6 @@ { public interface ITrimmableObjectPool { - bool CountTrackingEnabled { get; } - event ObjectPoolResizeEvent Received; event ObjectPoolResizeEvent Released; diff --git a/source/ObjectPoolAutoTrimmer.cs b/source/ObjectPoolAutoTrimmer.cs index 6dbfd03..0ec836e 100644 --- a/source/ObjectPoolAutoTrimmer.cs +++ b/source/ObjectPoolAutoTrimmer.cs @@ -35,9 +35,6 @@ public ObjectPoolAutoTrimmer( { _pool = pool ?? throw new ArgumentNullException(nameof(pool)); - if (!pool.CountTrackingEnabled) - throw new ArgumentException("Only pools with count tracking enabled can be trimmed.", nameof(pool)); - if (pool is DisposableBase d) { if (d.IsDisposed) throw new ArgumentException("Cannot trim for an object pool that is already disposed."); diff --git a/source/ObjectPoolBase.cs b/source/ObjectPoolBase.cs index 256abb7..ef27bba 100644 --- a/source/ObjectPoolBase.cs +++ b/source/ObjectPoolBase.cs @@ -7,52 +7,52 @@ public abstract class ObjectPoolBase : DisposableBase, IObjectPool { protected const int DEFAULT_CAPACITY = Constants.DEFAULT_CAPACITY; - protected ObjectPoolBase(Func factory, Action recycler, int capacity = DEFAULT_CAPACITY) + protected ObjectPoolBase(Func factory, Action recycler, Action disposer, int capacity = DEFAULT_CAPACITY) { if (capacity < 1) throw new ArgumentOutOfRangeException(nameof(capacity), capacity, "Must be at least 1."); Factory = factory ?? throw new ArgumentNullException(nameof(factory)); MaxSize = capacity; Recycler = recycler; + OnDiscarded = disposer; } - protected ObjectPoolBase(Func factory, int capacity = DEFAULT_CAPACITY) - : this(factory, null, capacity) - { - - } - - protected Action Recycler; protected int MaxSize; public int Capacity => MaxSize; + protected Action Recycler; // Before entering the pool. + protected Action OnDiscarded; // When not able to be used. + + // Read-only because if Take() is called after disposal, this still facilitates returing an object. // Allow the GC to do the final cleanup after dispose. protected readonly Func Factory; - public T Generate() - { - return Factory(); - } + public T Generate() => Factory(); - protected ReferenceContainer Pocket; + protected ReferenceContainer Pocket; // Default struct constructs itself. #region Receive (.Give(T item)) - protected virtual bool CanReceive => true; + protected virtual bool CanReceive => true; // A default of true is acceptable, enabling the Recieve method to do the actual deciding. protected bool PrepareToReceive(T item) { if (item == null) return false; - if (!CanReceive) return false; - var r = Recycler; - if (r != null) + if (CanReceive) { - r(item); - if (!CanReceive) return false; + var r = Recycler; + if (r != null) + { + r(item); + // Did the recycle take so long that the state changed? + if (!CanReceive) return false; + } + + return true; } - return true; + return false; } // Contract should be that no item can be null here. @@ -62,6 +62,7 @@ protected virtual void OnReceived() { } + protected void OnReceived(bool wasGiven) { if (wasGiven) OnReceived(); @@ -72,6 +73,8 @@ public void Give(T item) if (PrepareToReceive(item) && (SaveToPocket(item) || Receive(item))) OnReceived(); + else + OnDiscarded?.Invoke(item); } #endregion @@ -88,14 +91,11 @@ public bool TryTake(out T item) } protected virtual bool SaveToPocket(T item) - { - return Pocket.TrySave(item); - } + => Pocket.TrySave(item); protected virtual T TakeFromPocket() - { - return Pocket.TryRetrieve(); - } + => Pocket.TryRetrieve(); + protected abstract T TryRelease(); @@ -109,15 +109,20 @@ public T TryTake() protected virtual void OnReleased() { } - protected void OnReleased(bool wasTaken) - { - if (wasTaken) OnReleased(); - } #endregion protected override void OnBeforeDispose() { MaxSize = 0; } + + protected override void OnDispose(bool calledExplicitly) + { + if (calledExplicitly && OnDiscarded != null) + { + T d; + while ((d = TryRelease()) != null) OnDiscarded(d); + } + } } } diff --git a/source/Open.Disposable.ObjectPools.csproj b/source/Open.Disposable.ObjectPools.csproj index 17759ae..2207d55 100644 --- a/source/Open.Disposable.ObjectPools.csproj +++ b/source/Open.Disposable.ObjectPools.csproj @@ -16,14 +16,11 @@ Part of the "Open" set of libraries. git objectpool, dotnet, dotnetcore, cs, idisposable, threadsafe, thread-safe True - 2.0.0 - 2.0.0.0 - 2.0.0.0 - Improved overall performance by eliminating forced count of trimmable object pools. -Tracking counts is now optional and disabled by default. -Removed Async Take/Give methods since they offer no benefit and may acutally be problematic. Object pools should not have direct async methods. - -More interfaces and base classes added for extensibility and future development. + 2.1.0 + 2.1.0.0 + 2.1.0.0 + Enabled tracking for all collection based object pools to ensure proper refusal when capacity is met. +Added 'OnDiscarded' for special handling of objects that will never be used again. diff --git a/source/TrimmableCollectionObjectPoolBase.cs b/source/TrimmableCollectionObjectPoolBase.cs new file mode 100644 index 0000000..52ed482 --- /dev/null +++ b/source/TrimmableCollectionObjectPoolBase.cs @@ -0,0 +1,74 @@ +using System; +using System.Collections; +using System.Collections.Generic; + +namespace Open.Disposable +{ + public abstract class TrimmableCollectionObjectPoolBase : TrimmableObjectPoolBase + where T : class + where TCollection : class, ICollection + { + + public TrimmableCollectionObjectPoolBase(TCollection pool, Func factory, Action recycler, Action disposer, int capacity = DEFAULT_CAPACITY, bool countTrackingEnabled = true) + : base(factory, recycler, disposer, capacity, countTrackingEnabled) + { + Pool = pool ?? throw new ArgumentNullException(nameof(pool)); + } + + public TrimmableCollectionObjectPoolBase(TCollection pool, Func factory, int capacity = DEFAULT_CAPACITY, bool countTrackingEnabled = true) + : this(pool, factory, null, null, capacity, countTrackingEnabled) + { + } + + protected TCollection Pool; + + public override int Count => Pool?.Count ?? 0; + + protected override void OnDispose(bool calledExplicitly) + { + base.OnDispose(calledExplicitly); // Do not call because the following is more optimized. + if (calledExplicitly) Pool = null; + } + } + + public abstract class TrimmableGenericCollectionObjectPoolBase : TrimmableObjectPoolBase + where T : class + where TCollection : class, ICollection + { + + public TrimmableGenericCollectionObjectPoolBase(TCollection pool, Func factory, Action recycler, Action disposer, int capacity = DEFAULT_CAPACITY, bool countTrackingEnabled = true) + : base(factory, recycler, disposer, capacity, countTrackingEnabled) + { + Pool = pool; + } + + public TrimmableGenericCollectionObjectPoolBase(TCollection pool, Func factory, int capacity = DEFAULT_CAPACITY, bool countTrackingEnabled = true) + : this(pool, factory, null, null, capacity, countTrackingEnabled) + { + } + + protected TCollection Pool; + + public override int Count => Pool?.Count ?? 0; + + protected override void OnDispose(bool calledExplicitly) + { + //base.OnDispose(calledExplicitly); // Do not call because the following is more optimized. + + if (calledExplicitly) + { + var p = Pool; + Pool = null; + + if (OnDiscarded != null) + { + foreach (var item in p) + OnDiscarded(item); + } + + p.Clear(); + } + } + } + +} diff --git a/source/TrimmableObjectPoolBase.cs b/source/TrimmableObjectPoolBase.cs index f79e180..9977f78 100644 --- a/source/TrimmableObjectPoolBase.cs +++ b/source/TrimmableObjectPoolBase.cs @@ -4,33 +4,27 @@ namespace Open.Disposable { - [DebuggerDisplay("Count = {_count}")] + [DebuggerDisplay("Count = {Count}")] public abstract class TrimmableObjectPoolBase : ObjectPoolBase, ITrimmableObjectPool where T : class { - protected TrimmableObjectPoolBase(Func factory, Action recycler, int capacity, bool countTrackingEnabled) - : base(factory, recycler, capacity) + protected TrimmableObjectPoolBase(Func factory, Action recycler, Action disposer, int capacity, bool countTrackingEnabled = true) + : base(factory, recycler, disposer, capacity) { _countTrackingEnabled = countTrackingEnabled; } - protected TrimmableObjectPoolBase(Func factory, int capacity, bool countTrackingEnabled) - : this(factory, null, capacity, countTrackingEnabled) - { - } - - bool _countTrackingEnabled; - public bool CountTrackingEnabled => _countTrackingEnabled; - - - int _count; + int _count = 0; + bool _countTrackingEnabled; // When true this enables tracking the number of entries entering and exiting the pool instead of calling '.Count'. /// /// Total number of items in the pool. /// - public virtual int Count => _count; // Track this number instead of calling .Count on a collection. + public abstract int Count { get; } - protected override bool CanReceive => _count < MaxSize; + protected int CountInternal => _countTrackingEnabled ? _count : Count; + + protected override bool CanReceive => CountInternal < MaxSize; /// /// Signal for when an item was taken (actually removed) from the pool. @@ -38,20 +32,12 @@ protected TrimmableObjectPoolBase(Func factory, int capacity, bool countTrack public event ObjectPoolResizeEvent Released; protected void OnReleased(int newSize) { - if (_countTrackingEnabled) - { - Released?.Invoke(newSize); - } - + Debug.Assert(newSize > -2, $"newSize: {newSize}, _count: {_count}"); // Should never get out of control. It may go negative temporarily but should be 100% accounted for. + Released?.Invoke(newSize); } protected override void OnReleased() { - if (_countTrackingEnabled) - { - var c = Interlocked.Decrement(ref _count); - OnReleased(c); - Debug.Assert(c >= 0); - } + OnReleased(_countTrackingEnabled ? Interlocked.Decrement(ref _count) : Count); } /// @@ -60,27 +46,18 @@ protected override void OnReleased() public event ObjectPoolResizeEvent Received; protected void OnReceived(int newSize) { - if (_countTrackingEnabled) - { - Received?.Invoke(newSize); - } + Received?.Invoke(newSize); } protected override void OnReceived() { - if (_countTrackingEnabled) - { - OnReceived(Interlocked.Increment(ref _count)); - } + OnReceived(_countTrackingEnabled ? Interlocked.Increment(ref _count) : Count); } public virtual void TrimTo(int targetSize) { - if (!_countTrackingEnabled) - throw new InvalidOperationException("Cannot trim an object pool with count tracking disabled."); - if (targetSize < 0) return; // Possible upstream math hiccup or default -1. Silently dismiss. - int count = _count; + int count = CountInternal; if (count > targetSize) { @@ -90,15 +67,29 @@ public virtual void TrimTo(int targetSize) i < attempts; i++) { - if (TryRelease() == null) - break; + var e = TryRelease(); + if (e == null) break; + + if (_countTrackingEnabled) + { + Interlocked.Decrement(ref _count); + //var c = Interlocked.Decrement(ref _count); + //Debug.Assert(c >= 0); + } - Interlocked.Decrement(ref _count); + OnDiscarded?.Invoke(e); } - if (i != 0) OnReleased(_count); + if (i != 0) OnReleased(CountInternal); } } + //protected ReferenceContainer Pocket2; // Default struct constructs itself. + + //protected override bool SaveToPocket(T item) + // => false;// Pocket.TrySave(item) || Pocket2.TrySave(item); + + //protected override T TakeFromPocket() + // => null; // Pocket.TryRetrieve() ?? Pocket2.TryRetrieve(); } } From 8faa488b50a664fd0c00988223c1decf46b68b9a Mon Sep 17 00:00:00 2001 From: electricessence Date: Mon, 7 May 2018 22:42:54 -0700 Subject: [PATCH 19/83] Valdation updates. --- benchmarking/Benchmark.cs | 13 +- benchmarking/BenchmarkResult.csv | 41 ++-- benchmarking/BenchmarkResult.txt | 236 ++++++++------------ benchmarking/Program.cs | 4 +- source/Array/InterlockedArrayObjectPool.cs | 4 + source/IObjectPool.cs | 6 +- source/ObjectPoolBase.cs | 10 +- source/TrimmableCollectionObjectPoolBase.cs | 16 +- source/TrimmableObjectPoolBase.cs | 12 +- 9 files changed, 152 insertions(+), 190 deletions(-) diff --git a/benchmarking/Benchmark.cs b/benchmarking/Benchmark.cs index 0a0f851..65080db 100644 --- a/benchmarking/Benchmark.cs +++ b/benchmarking/Benchmark.cs @@ -1,6 +1,7 @@ using Open.Diagnostics; using System; using System.Collections.Generic; +using System.Diagnostics; using System.Threading.Tasks; namespace Open.Disposable.ObjectPools @@ -12,6 +13,8 @@ public Benchmark(uint size, uint repeat, Func> poolFactory) : bas { // Because some pools do comparison checks on values, we have have unique instances/values. _items = new T[(int)size]; + var pool = Param(); + Parallel.For(0, TestSize, i => _items[i] = pool.Take()); } readonly T[] _items; @@ -21,14 +24,16 @@ protected override IEnumerable TestOnceInternal() using (var pool = Param()) { - yield return TimedResult.Measure("Take From Empty (In Parallel)", () => - { - Parallel.For(0, TestSize, i => _items[i] = pool.Take()); - }); + //yield return TimedResult.Measure("Take From Empty (In Parallel)", () => + //{ + // Parallel.For(0, TestSize, i => _items[i] = pool.Take()); + //}); yield return TimedResult.Measure("Give To (In Parallel)", () => { Parallel.For(0, TestSize, i => pool.Give(_items[i])); + var count = pool.Count; + Debug.Assert(pool is OptimisticArrayObjectPool || count == TestSize, $"Expected {TestSize}, acutal count: {count}"); }); yield return TimedResult.Measure("Mixed 90%-Take/10%-Give (In Parallel)", () => diff --git a/benchmarking/BenchmarkResult.csv b/benchmarking/BenchmarkResult.csv index 18db769..13d68f1 100644 --- a/benchmarking/BenchmarkResult.csv +++ b/benchmarking/BenchmarkResult.csv @@ -1,22 +1,25 @@ -Batch,Pool Type," From Empty (In Parallel)"," To (In Parallel)",d 90%-Take/10%-Give (In Parallel),d 50%-Take/50%-Give (In Parallel),d 10%-Take/90%-Give (In Parallel),y Pool (.TryTake()),L, -Repeat 800000 for size 4,ConcurrentQueueObjectPool,00:00:02.1768070,00:00:01.6760555,00:00:01.7056065,00:00:01.6512698,00:00:01.6459856,00:00:00.0942697,00:00:08.9499941, -Repeat 800000 for size 4,ConcurrentStackObjectPool,00:00:02.0861011,00:00:01.9051266,00:00:01.7180590,00:00:01.6934710,00:00:01.8172521,00:00:00.0935848,00:00:09.3135946, -Repeat 800000 for size 4,OptimisticArrayObjectPool,00:00:02.1028619,00:00:01.7289187,00:00:01.7391071,00:00:01.7308129,00:00:01.7911409,00:00:00.0850814,00:00:09.1779229, -Repeat 320000 for size 10,ConcurrentQueueObjectPool,00:00:01.4097002,00:00:01.0808791,00:00:01.0795562,00:00:01.0359202,00:00:01.0900044,00:00:00.1022724,00:00:05.7983325, -Repeat 320000 for size 10,ConcurrentStackObjectPool,00:00:01.3884640,00:00:01.2335304,00:00:01.0874214,00:00:01.0858229,00:00:01.1916481,00:00:00.1097261,00:00:06.0966129, -Repeat 320000 for size 10,OptimisticArrayObjectPool,00:00:01.3395438,00:00:01.0630741,00:00:01.0581905,00:00:01.0257850,00:00:01.0596437,00:00:00.1231254,00:00:05.6693625, -Repeat 96000 for size 50,ConcurrentQueueObjectPool,00:00:00.6724161,00:00:00.9303053,00:00:00.6667530,00:00:00.4869434,00:00:00.5629880,00:00:00.1474862,00:00:03.4668920, -Repeat 96000 for size 50,ConcurrentStackObjectPool,00:00:00.6404036,00:00:00.7253187,00:00:00.5973183,00:00:00.5004547,00:00:00.6653687,00:00:00.1585221,00:00:03.2873861, -Repeat 96000 for size 50,OptimisticArrayObjectPool,00:00:00.6273288,00:00:00.6296415,00:00:00.5758157,00:00:00.4575284,00:00:00.5484878,00:00:00.3576192,00:00:03.1964214, -Repeat 64000 for size 100,ConcurrentQueueObjectPool,00:00:00.5739788,00:00:00.8879708,00:00:00.6868932,00:00:00.5038095,00:00:00.6414144,00:00:00.1906175,00:00:03.4846842, -Repeat 64000 for size 100,ConcurrentStackObjectPool,00:00:00.5710317,00:00:00.8181417,00:00:02.3020598,00:00:00.5377935,00:00:00.7552635,00:00:00.2106115,00:00:05.1949017, -Repeat 64000 for size 100,OptimisticArrayObjectPool,00:00:00.5741654,00:00:00.6708564,00:00:00.6465793,00:00:00.4123009,00:00:00.5587647,00:00:00.5961525,00:00:03.4588192, -Repeat 51200 for size 250,ConcurrentQueueObjectPool,00:00:00.7338318,00:00:02.7582430,00:00:01.3118591,00:00:00.8866236,00:00:01.2641219,00:00:00.3621620,00:00:07.3168414, -Repeat 51200 for size 250,ConcurrentStackObjectPool,00:00:00.7063932,00:00:01.4843162,00:00:04.0788532,00:00:01.6259396,00:00:01.3564185,00:00:00.4047656,00:00:09.6566863, -Repeat 51200 for size 250,OptimisticArrayObjectPool,00:00:00.7798597,00:00:01.3369029,00:00:01.3656621,00:00:00.6762451,00:00:01.0179464,00:00:01.8689033,00:00:07.0455195, -Repeat 12800 for size 2000,ConcurrentQueueObjectPool,00:00:00.7690881,00:00:02.9469849,00:00:02.0202982,00:00:01.3725082,00:00:02.1013278,00:00:00.6418618,00:00:09.8520690, -Repeat 12800 for size 2000,ConcurrentStackObjectPool,00:00:00.6845217,00:00:02.5773779,00:00:04.7847969,00:00:04.7546912,00:00:03.4618654,00:00:00.6506477,00:00:16.9139008, -Repeat 12800 for size 2000,OptimisticArrayObjectPool,00:00:00.7170882,00:00:07.3855602,00:00:07.4918848,00:00:01.1312204,00:00:04.8420972,00:00:13.1760797,00:00:34.7439305, +Batch,Pool Type," To (In Parallel)",d 90%-Take/10%-Give (In Parallel),d 50%-Take/50%-Give (In Parallel),d 10%-Take/90%-Give (In Parallel),y Pool (.TryTake()),L, +Repeat 400000 for size 4,ConcurrentQueueObjectPool,00:00:01.8472469,00:00:01.7363986,00:00:01.6837968,00:00:01.7096510,00:00:00.1076125,00:00:07.0847058, +Repeat 400000 for size 4,ConcurrentStackObjectPool,00:00:01.8102817,00:00:01.7426871,00:00:01.6518815,00:00:01.8414613,00:00:00.1049529,00:00:07.1512645, +Repeat 400000 for size 4,OptimisticArrayObjectPool,00:00:01.8589242,00:00:01.7585402,00:00:01.7489384,00:00:01.7687298,00:00:00.0952550,00:00:07.2303876, +Repeat 160000 for size 10,ConcurrentQueueObjectPool,00:00:00.7782376,00:00:00.7324783,00:00:00.7163121,00:00:00.7306797,00:00:00.0971813,00:00:03.0548890, +Repeat 160000 for size 10,ConcurrentStackObjectPool,00:00:00.9064832,00:00:00.7690028,00:00:00.7207622,00:00:00.8417267,00:00:00.1034971,00:00:03.3414720, +Repeat 160000 for size 10,OptimisticArrayObjectPool,00:00:00.8577028,00:00:00.7887449,00:00:00.7333412,00:00:00.8531175,00:00:00.1088223,00:00:03.3417287, +Repeat 48000 for size 50,ConcurrentQueueObjectPool,00:00:00.4952668,00:00:00.3843341,00:00:00.3179560,00:00:00.3515590,00:00:00.1171762,00:00:01.6662921, +Repeat 48000 for size 50,ConcurrentStackObjectPool,00:00:00.5346734,00:00:00.3824365,00:00:00.3211063,00:00:00.4865511,00:00:00.1248121,00:00:01.8495794, +Repeat 48000 for size 50,OptimisticArrayObjectPool,00:00:00.5833078,00:00:00.4477068,00:00:00.3296106,00:00:00.4408652,00:00:00.3016180,00:00:02.1031084, +Repeat 32000 for size 100,ConcurrentQueueObjectPool,00:00:00.4969536,00:00:00.4326518,00:00:00.3199614,00:00:00.4396957,00:00:00.1608001,00:00:01.8500626, +Repeat 32000 for size 100,ConcurrentStackObjectPool,00:00:00.5521412,00:00:00.4853524,00:00:00.3175640,00:00:00.5159456,00:00:00.1572408,00:00:02.0282440, +Repeat 32000 for size 100,OptimisticArrayObjectPool,00:00:00.7719640,00:00:00.5488668,00:00:00.2717297,00:00:00.5357692,00:00:00.6489823,00:00:02.7773120, +Repeat 25600 for size 250,ConcurrentQueueObjectPool,00:00:00.7980353,00:00:00.6178042,00:00:00.5431682,00:00:00.6892625,00:00:00.2954847,00:00:02.9437549, +Repeat 25600 for size 250,ConcurrentStackObjectPool,00:00:00.9389921,00:00:00.9667758,00:00:00.5609015,00:00:00.8204988,00:00:00.3075525,00:00:03.5947207, +Repeat 25600 for size 250,OptimisticArrayObjectPool,00:00:02.1193418,00:00:01.3828499,00:00:00.4523577,00:00:01.3159924,00:00:02.9909843,00:00:08.2615261, +Repeat 6400 for size 2000,ConcurrentQueueObjectPool,00:00:01.3830355,00:00:00.9243686,00:00:00.9780430,00:00:00.9865309,00:00:00.5832766,00:00:04.8552546, +Repeat 6400 for size 2000,ConcurrentStackObjectPool,00:00:01.6064533,00:00:02.5455114,00:00:01.2388087,00:00:01.5112497,00:00:00.5781890,00:00:07.4802121, +Repeat 6400 for size 2000,OptimisticArrayObjectPool,00:00:13.5147753,00:00:15.1956294,00:00:00.9013497,00:00:12.0385696,00:00:32.8558145,00:01:14.5061385, +for size 2000,ConcurrentStackObjectPool,00:00:01.4597004,00:00:01.7435741,00:00:02.4213177,00:00:01.0803513,00:00:01.6261815,00:00:00.6094295,00:00:08.9405545, +Repeat 6400 for size 2000,OptimisticArrayObjectPool,00:00:01.2265087,00:00:23.0488821,00:00:15.8054484,00:00:00.9435990,00:00:14.8800972,00:00:51.4640465,00:01:47.3685819, +, epeat 51200 for size 250,OptimisticArrayObjectPool,00:00:00.7651948,00:00:01.3195712,00:00:01.3758641,00:00:00.6837150,00:00:01.0134658,00:00:01.9616149,00:00:07.1194258, Repeat 51200 for size 250,InterlockedArrayObjectPool,00:00:00.8386708,00:00:02.2800174,00:00:01.4244575,00:00:00.6435472,00:00:01.6845873,00:00:05.4327218,00:00:12.3040020, Repeat 12800 for size 2000,ConcurrentQueueObjectPool,00:00:00.7692018,00:00:03.0317303,00:00:02.0615927,00:00:01.3597065,00:00:02.0767858,00:00:00.6488593,00:00:09.9478764, diff --git a/benchmarking/BenchmarkResult.txt b/benchmarking/BenchmarkResult.txt index 56b9ab2..179542f 100644 --- a/benchmarking/BenchmarkResult.txt +++ b/benchmarking/BenchmarkResult.txt @@ -1,186 +1,140 @@ -Repeat 800000 for size 4 +Repeat 400000 for size 4 ------------------------------------ ConcurrentQueueObjectPool............................... -00:00:02.1768070 Take From Empty (In Parallel) -00:00:01.6760555 Give To (In Parallel) -00:00:01.7056065 Mixed 90%-Take/10%-Give (In Parallel) -00:00:01.6512698 Mixed 50%-Take/50%-Give (In Parallel) -00:00:01.6459856 Mixed 10%-Take/90%-Give (In Parallel) -00:00:00.0942697 Empty Pool (.TryTake()) -00:00:08.9499941 TOTAL +00:00:01.8472469 Give To (In Parallel) +00:00:01.7363986 Mixed 90%-Take/10%-Give (In Parallel) +00:00:01.6837968 Mixed 50%-Take/50%-Give (In Parallel) +00:00:01.7096510 Mixed 10%-Take/90%-Give (In Parallel) +00:00:00.1076125 Empty Pool (.TryTake()) +00:00:07.0847058 TOTAL ConcurrentStackObjectPool............................... -00:00:02.0861011 Take From Empty (In Parallel) -00:00:01.9051266 Give To (In Parallel) -00:00:01.7180590 Mixed 90%-Take/10%-Give (In Parallel) -00:00:01.6934710 Mixed 50%-Take/50%-Give (In Parallel) -00:00:01.8172521 Mixed 10%-Take/90%-Give (In Parallel) -00:00:00.0935848 Empty Pool (.TryTake()) -00:00:09.3135946 TOTAL +00:00:01.8102817 Give To (In Parallel) +00:00:01.7426871 Mixed 90%-Take/10%-Give (In Parallel) +00:00:01.6518815 Mixed 50%-Take/50%-Give (In Parallel) +00:00:01.8414613 Mixed 10%-Take/90%-Give (In Parallel) +00:00:00.1049529 Empty Pool (.TryTake()) +00:00:07.1512645 TOTAL OptimisticArrayObjectPool............................... -00:00:02.1028619 Take From Empty (In Parallel) -00:00:01.7289187 Give To (In Parallel) -00:00:01.7391071 Mixed 90%-Take/10%-Give (In Parallel) -00:00:01.7308129 Mixed 50%-Take/50%-Give (In Parallel) -00:00:01.7911409 Mixed 10%-Take/90%-Give (In Parallel) -00:00:00.0850814 Empty Pool (.TryTake()) -00:00:09.1779229 TOTAL +00:00:01.8589242 Give To (In Parallel) +00:00:01.7585402 Mixed 90%-Take/10%-Give (In Parallel) +00:00:01.7489384 Mixed 50%-Take/50%-Give (In Parallel) +00:00:01.7687298 Mixed 10%-Take/90%-Give (In Parallel) +00:00:00.0952550 Empty Pool (.TryTake()) +00:00:07.2303876 TOTAL -Repeat 320000 for size 10 +Repeat 160000 for size 10 ------------------------------------ ConcurrentQueueObjectPool............................... -00:00:01.4097002 Take From Empty (In Parallel) -00:00:01.0808791 Give To (In Parallel) -00:00:01.0795562 Mixed 90%-Take/10%-Give (In Parallel) -00:00:01.0359202 Mixed 50%-Take/50%-Give (In Parallel) -00:00:01.0900044 Mixed 10%-Take/90%-Give (In Parallel) -00:00:00.1022724 Empty Pool (.TryTake()) -00:00:05.7983325 TOTAL +00:00:00.7782376 Give To (In Parallel) +00:00:00.7324783 Mixed 90%-Take/10%-Give (In Parallel) +00:00:00.7163121 Mixed 50%-Take/50%-Give (In Parallel) +00:00:00.7306797 Mixed 10%-Take/90%-Give (In Parallel) +00:00:00.0971813 Empty Pool (.TryTake()) +00:00:03.0548890 TOTAL ConcurrentStackObjectPool............................... -00:00:01.3884640 Take From Empty (In Parallel) -00:00:01.2335304 Give To (In Parallel) -00:00:01.0874214 Mixed 90%-Take/10%-Give (In Parallel) -00:00:01.0858229 Mixed 50%-Take/50%-Give (In Parallel) -00:00:01.1916481 Mixed 10%-Take/90%-Give (In Parallel) -00:00:00.1097261 Empty Pool (.TryTake()) -00:00:06.0966129 TOTAL +00:00:00.9064832 Give To (In Parallel) +00:00:00.7690028 Mixed 90%-Take/10%-Give (In Parallel) +00:00:00.7207622 Mixed 50%-Take/50%-Give (In Parallel) +00:00:00.8417267 Mixed 10%-Take/90%-Give (In Parallel) +00:00:00.1034971 Empty Pool (.TryTake()) +00:00:03.3414720 TOTAL OptimisticArrayObjectPool............................... -00:00:01.3395438 Take From Empty (In Parallel) -00:00:01.0630741 Give To (In Parallel) -00:00:01.0581905 Mixed 90%-Take/10%-Give (In Parallel) -00:00:01.0257850 Mixed 50%-Take/50%-Give (In Parallel) -00:00:01.0596437 Mixed 10%-Take/90%-Give (In Parallel) -00:00:00.1231254 Empty Pool (.TryTake()) -00:00:05.6693625 TOTAL +00:00:00.8577028 Give To (In Parallel) +00:00:00.7887449 Mixed 90%-Take/10%-Give (In Parallel) +00:00:00.7333412 Mixed 50%-Take/50%-Give (In Parallel) +00:00:00.8531175 Mixed 10%-Take/90%-Give (In Parallel) +00:00:00.1088223 Empty Pool (.TryTake()) +00:00:03.3417287 TOTAL -Repeat 96000 for size 50 +Repeat 48000 for size 50 ------------------------------------ ConcurrentQueueObjectPool............................... -00:00:00.6724161 Take From Empty (In Parallel) -00:00:00.9303053 Give To (In Parallel) -00:00:00.6667530 Mixed 90%-Take/10%-Give (In Parallel) -00:00:00.4869434 Mixed 50%-Take/50%-Give (In Parallel) -00:00:00.5629880 Mixed 10%-Take/90%-Give (In Parallel) -00:00:00.1474862 Empty Pool (.TryTake()) -00:00:03.4668920 TOTAL +00:00:00.4952668 Give To (In Parallel) +00:00:00.3843341 Mixed 90%-Take/10%-Give (In Parallel) +00:00:00.3179560 Mixed 50%-Take/50%-Give (In Parallel) +00:00:00.3515590 Mixed 10%-Take/90%-Give (In Parallel) +00:00:00.1171762 Empty Pool (.TryTake()) +00:00:01.6662921 TOTAL ConcurrentStackObjectPool............................... -00:00:00.6404036 Take From Empty (In Parallel) -00:00:00.7253187 Give To (In Parallel) -00:00:00.5973183 Mixed 90%-Take/10%-Give (In Parallel) -00:00:00.5004547 Mixed 50%-Take/50%-Give (In Parallel) -00:00:00.6653687 Mixed 10%-Take/90%-Give (In Parallel) -00:00:00.1585221 Empty Pool (.TryTake()) -00:00:03.2873861 TOTAL +00:00:00.5346734 Give To (In Parallel) +00:00:00.3824365 Mixed 90%-Take/10%-Give (In Parallel) +00:00:00.3211063 Mixed 50%-Take/50%-Give (In Parallel) +00:00:00.4865511 Mixed 10%-Take/90%-Give (In Parallel) +00:00:00.1248121 Empty Pool (.TryTake()) +00:00:01.8495794 TOTAL OptimisticArrayObjectPool............................... -00:00:00.6273288 Take From Empty (In Parallel) -00:00:00.6296415 Give To (In Parallel) -00:00:00.5758157 Mixed 90%-Take/10%-Give (In Parallel) -00:00:00.4575284 Mixed 50%-Take/50%-Give (In Parallel) -00:00:00.5484878 Mixed 10%-Take/90%-Give (In Parallel) -00:00:00.3576192 Empty Pool (.TryTake()) -00:00:03.1964214 TOTAL +00:00:00.5833078 Give To (In Parallel) +00:00:00.4477068 Mixed 90%-Take/10%-Give (In Parallel) +00:00:00.3296106 Mixed 50%-Take/50%-Give (In Parallel) +00:00:00.4408652 Mixed 10%-Take/90%-Give (In Parallel) +00:00:00.3016180 Empty Pool (.TryTake()) +00:00:02.1031084 TOTAL -Repeat 64000 for size 100 +Repeat 32000 for size 100 ------------------------------------ ConcurrentQueueObjectPool............................... -00:00:00.5739788 Take From Empty (In Parallel) -00:00:00.8879708 Give To (In Parallel) -00:00:00.6868932 Mixed 90%-Take/10%-Give (In Parallel) -00:00:00.5038095 Mixed 50%-Take/50%-Give (In Parallel) -00:00:00.6414144 Mixed 10%-Take/90%-Give (In Parallel) -00:00:00.1906175 Empty Pool (.TryTake()) -00:00:03.4846842 TOTAL +00:00:00.4969536 Give To (In Parallel) +00:00:00.4326518 Mixed 90%-Take/10%-Give (In Parallel) +00:00:00.3199614 Mixed 50%-Take/50%-Give (In Parallel) +00:00:00.4396957 Mixed 10%-Take/90%-Give (In Parallel) +00:00:00.1608001 Empty Pool (.TryTake()) +00:00:01.8500626 TOTAL ConcurrentStackObjectPool............................... -00:00:00.5710317 Take From Empty (In Parallel) -00:00:00.8181417 Give To (In Parallel) -00:00:02.3020598 Mixed 90%-Take/10%-Give (In Parallel) -00:00:00.5377935 Mixed 50%-Take/50%-Give (In Parallel) -00:00:00.7552635 Mixed 10%-Take/90%-Give (In Parallel) -00:00:00.2106115 Empty Pool (.TryTake()) -00:00:05.1949017 TOTAL +00:00:00.5521412 Give To (In Parallel) +00:00:00.4853524 Mixed 90%-Take/10%-Give (In Parallel) +00:00:00.3175640 Mixed 50%-Take/50%-Give (In Parallel) +00:00:00.5159456 Mixed 10%-Take/90%-Give (In Parallel) +00:00:00.1572408 Empty Pool (.TryTake()) +00:00:02.0282440 TOTAL OptimisticArrayObjectPool............................... -00:00:00.5741654 Take From Empty (In Parallel) -00:00:00.6708564 Give To (In Parallel) -00:00:00.6465793 Mixed 90%-Take/10%-Give (In Parallel) -00:00:00.4123009 Mixed 50%-Take/50%-Give (In Parallel) -00:00:00.5587647 Mixed 10%-Take/90%-Give (In Parallel) -00:00:00.5961525 Empty Pool (.TryTake()) -00:00:03.4588192 TOTAL +00:00:00.7719640 Give To (In Parallel) +00:00:00.5488668 Mixed 90%-Take/10%-Give (In Parallel) +00:00:00.2717297 Mixed 50%-Take/50%-Give (In Parallel) +00:00:00.5357692 Mixed 10%-Take/90%-Give (In Parallel) +00:00:00.6489823 Empty Pool (.TryTake()) +00:00:02.7773120 TOTAL -Repeat 51200 for size 250 +Repeat 25600 for size 250 ------------------------------------ ConcurrentQueueObjectPool............................... -00:00:00.7338318 Take From Empty (In Parallel) -00:00:02.7582430 Give To (In Parallel) -00:00:01.3118591 Mixed 90%-Take/10%-Give (In Parallel) -00:00:00.8866236 Mixed 50%-Take/50%-Give (In Parallel) -00:00:01.2641219 Mixed 10%-Take/90%-Give (In Parallel) -00:00:00.3621620 Empty Pool (.TryTake()) -00:00:07.3168414 TOTAL +00:00:00.7980353 Give To (In Parallel) +00:00:00.6178042 Mixed 90%-Take/10%-Give (In Parallel) +00:00:00.5431682 Mixed 50%-Take/50%-Give (In Parallel) +00:00:00.6892625 Mixed 10%-Take/90%-Give (In Parallel) +00:00:00.2954847 Empty Pool (.TryTake()) +00:00:02.9437549 TOTAL ConcurrentStackObjectPool............................... -00:00:00.7063932 Take From Empty (In Parallel) -00:00:01.4843162 Give To (In Parallel) -00:00:04.0788532 Mixed 90%-Take/10%-Give (In Parallel) -00:00:01.6259396 Mixed 50%-Take/50%-Give (In Parallel) -00:00:01.3564185 Mixed 10%-Take/90%-Give (In Parallel) -00:00:00.4047656 Empty Pool (.TryTake()) -00:00:09.6566863 TOTAL +00:00:00.9389921 Give To (In Parallel) +00:00:00.9667758 Mixed 90%-Take/10%-Give (In Parallel) +00:00:00.5609015 Mixed 50%-Take/50%-Give (In Parallel) +00:00:00.8204988 Mixed 10%-Take/90%-Give (In Parallel) +00:00:00.3075525 Empty Pool (.TryTake()) +00:00:03.5947207 TOTAL OptimisticArrayObjectPool............................... -00:00:00.7798597 Take From Empty (In Parallel) -00:00:01.3369029 Give To (In Parallel) -00:00:01.3656621 Mixed 90%-Take/10%-Give (In Parallel) -00:00:00.6762451 Mixed 50%-Take/50%-Give (In Parallel) -00:00:01.0179464 Mixed 10%-Take/90%-Give (In Parallel) -00:00:01.8689033 Empty Pool (.TryTake()) -00:00:07.0455195 TOTAL - - -Repeat 12800 for size 2000 ------------------------------------- - -ConcurrentQueueObjectPool............................... -00:00:00.7690881 Take From Empty (In Parallel) -00:00:02.9469849 Give To (In Parallel) -00:00:02.0202982 Mixed 90%-Take/10%-Give (In Parallel) -00:00:01.3725082 Mixed 50%-Take/50%-Give (In Parallel) -00:00:02.1013278 Mixed 10%-Take/90%-Give (In Parallel) -00:00:00.6418618 Empty Pool (.TryTake()) -00:00:09.8520690 TOTAL - -ConcurrentStackObjectPool............................... -00:00:00.6845217 Take From Empty (In Parallel) -00:00:02.5773779 Give To (In Parallel) -00:00:04.7847969 Mixed 90%-Take/10%-Give (In Parallel) -00:00:04.7546912 Mixed 50%-Take/50%-Give (In Parallel) -00:00:03.4618654 Mixed 10%-Take/90%-Give (In Parallel) -00:00:00.6506477 Empty Pool (.TryTake()) -00:00:16.9139008 TOTAL - -OptimisticArrayObjectPool............................... -00:00:00.7170882 Take From Empty (In Parallel) -00:00:07.3855602 Give To (In Parallel) -00:00:07.4918848 Mixed 90%-Take/10%-Give (In Parallel) -00:00:01.1312204 Mixed 50%-Take/50%-Give (In Parallel) -00:00:04.8420972 Mixed 10%-Take/90%-Give (In Parallel) -00:00:13.1760797 Empty Pool (.TryTake()) -00:00:34.7439305 TOTAL +00:00:02.1193418 Give To (In Parallel) +00:00:01.3828499 Mixed 90%-Take/10%-Give (In Parallel) +00:00:00.4523577 Mixed 50%-Take/50%-Give (In Parallel) +00:00:01.3159924 Mixed 10%-Take/90%-Give (In Parallel) +00:00:02.9909843 Empty Pool (.TryTake()) +00:00:08.2615261 TOTAL diff --git a/benchmarking/Program.cs b/benchmarking/Program.cs index 7e59411..136a8f6 100644 --- a/benchmarking/Program.cs +++ b/benchmarking/Program.cs @@ -46,13 +46,13 @@ static void Main(string[] args) Console.SetCursorPosition(0, Console.CursorTop); - const int loopMultiple = 4; + const int loopMultiple = 2; report.Test(4, 8 * loopMultiple); report.Test(10, 8 * loopMultiple); report.Test(50, 12 * loopMultiple); report.Test(100, 16 * loopMultiple); report.Test(250, 32 * loopMultiple); - report.Test(2000, 64 * loopMultiple); + //report.Test(2000, 64 * loopMultiple); File.WriteAllText("./BenchmarkResult.txt", sb.ToString()); using (var fs = File.OpenWrite("./BenchmarkResult.csv")) diff --git a/source/Array/InterlockedArrayObjectPool.cs b/source/Array/InterlockedArrayObjectPool.cs index a68921c..b72d0c8 100644 --- a/source/Array/InterlockedArrayObjectPool.cs +++ b/source/Array/InterlockedArrayObjectPool.cs @@ -1,6 +1,7 @@ /* Based on Roslyn's ObjectPool */ using System; +using System.Linq; using System.Threading; namespace Open.Disposable @@ -29,6 +30,9 @@ public InterlockedArrayObjectPool(Func factory, int capacity = DEFAULT_CAPACI protected int MaxStored = 0; protected const int MaxStoredIncrement = 5; // Instead of every one. + public override int Count + => Pool.Count(e => e.Value != null) + PocketCount; + protected virtual bool Store(ReferenceContainer[] p, T item, int index) => p[index].TrySave(item); diff --git a/source/IObjectPool.cs b/source/IObjectPool.cs index 2697ab9..0cf027b 100644 --- a/source/IObjectPool.cs +++ b/source/IObjectPool.cs @@ -45,7 +45,11 @@ public interface IObjectPool : IDisposable /// An item removed from the pool or generated. Should never be null. T Take(); - + /// + /// Total number of items in the pool. + /// Depending on the implementation could be an O(n) operation and should only be used for debugging. + /// + int Count { get; } } public static partial class ObjectPoolExtensions diff --git a/source/ObjectPoolBase.cs b/source/ObjectPoolBase.cs index ef27bba..3724580 100644 --- a/source/ObjectPoolBase.cs +++ b/source/ObjectPoolBase.cs @@ -20,6 +20,12 @@ protected ObjectPoolBase(Func factory, Action recycler, Action disposer protected int MaxSize; public int Capacity => MaxSize; + /// + /// Total number of items in the pool. + /// + public abstract int Count { get; } + protected virtual int PocketCount => Pocket.Value == null ? 0 : 1; + protected Action Recycler; // Before entering the pool. protected Action OnDiscarded; // When not able to be used. @@ -37,9 +43,7 @@ protected ObjectPoolBase(Func factory, Action recycler, Action disposer protected bool PrepareToReceive(T item) { - if (item == null) return false; - - if (CanReceive) + if (item!=null && CanReceive) { var r = Recycler; if (r != null) diff --git a/source/TrimmableCollectionObjectPoolBase.cs b/source/TrimmableCollectionObjectPoolBase.cs index 52ed482..57efb7e 100644 --- a/source/TrimmableCollectionObjectPoolBase.cs +++ b/source/TrimmableCollectionObjectPoolBase.cs @@ -9,20 +9,15 @@ public abstract class TrimmableCollectionObjectPoolBase : Trimma where TCollection : class, ICollection { - public TrimmableCollectionObjectPoolBase(TCollection pool, Func factory, Action recycler, Action disposer, int capacity = DEFAULT_CAPACITY, bool countTrackingEnabled = true) + protected TrimmableCollectionObjectPoolBase(TCollection pool, Func factory, Action recycler, Action disposer, int capacity = DEFAULT_CAPACITY, bool countTrackingEnabled = true) : base(factory, recycler, disposer, capacity, countTrackingEnabled) { Pool = pool ?? throw new ArgumentNullException(nameof(pool)); } - public TrimmableCollectionObjectPoolBase(TCollection pool, Func factory, int capacity = DEFAULT_CAPACITY, bool countTrackingEnabled = true) - : this(pool, factory, null, null, capacity, countTrackingEnabled) - { - } - protected TCollection Pool; - public override int Count => Pool?.Count ?? 0; + public override int Count => (Pool?.Count ?? 0) + PocketCount; protected override void OnDispose(bool calledExplicitly) { @@ -36,17 +31,12 @@ public abstract class TrimmableGenericCollectionObjectPoolBase : where TCollection : class, ICollection { - public TrimmableGenericCollectionObjectPoolBase(TCollection pool, Func factory, Action recycler, Action disposer, int capacity = DEFAULT_CAPACITY, bool countTrackingEnabled = true) + protected TrimmableGenericCollectionObjectPoolBase(TCollection pool, Func factory, Action recycler, Action disposer, int capacity = DEFAULT_CAPACITY, bool countTrackingEnabled = true) : base(factory, recycler, disposer, capacity, countTrackingEnabled) { Pool = pool; } - public TrimmableGenericCollectionObjectPoolBase(TCollection pool, Func factory, int capacity = DEFAULT_CAPACITY, bool countTrackingEnabled = true) - : this(pool, factory, null, null, capacity, countTrackingEnabled) - { - } - protected TCollection Pool; public override int Count => Pool?.Count ?? 0; diff --git a/source/TrimmableObjectPoolBase.cs b/source/TrimmableObjectPoolBase.cs index 9977f78..25450c6 100644 --- a/source/TrimmableObjectPoolBase.cs +++ b/source/TrimmableObjectPoolBase.cs @@ -17,11 +17,6 @@ protected TrimmableObjectPoolBase(Func factory, Action recycler, Action int _count = 0; bool _countTrackingEnabled; // When true this enables tracking the number of entries entering and exiting the pool instead of calling '.Count'. - /// - /// Total number of items in the pool. - /// - public abstract int Count { get; } - protected int CountInternal => _countTrackingEnabled ? _count : Count; protected override bool CanReceive => CountInternal < MaxSize; @@ -87,9 +82,12 @@ public virtual void TrimTo(int targetSize) //protected ReferenceContainer Pocket2; // Default struct constructs itself. //protected override bool SaveToPocket(T item) - // => false;// Pocket.TrySave(item) || Pocket2.TrySave(item); + // => Pocket.TrySave(item) || Pocket2.TrySave(item); //protected override T TakeFromPocket() - // => null; // Pocket.TryRetrieve() ?? Pocket2.TryRetrieve(); + // => Pocket.TryRetrieve() ?? Pocket2.TryRetrieve(); + + //protected override int PocketCount => + // base.PocketCount + (Pocket2.Value == null ? 0 : 1); } } From ad2d8085f69d0f629354159671dc342f8f9fe906 Mon Sep 17 00:00:00 2001 From: "Oren (electricessence)" Date: Mon, 7 May 2018 23:30:26 -0700 Subject: [PATCH 20/83] Updated language version. --- source/Open.Disposable.ObjectPools.csproj | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/source/Open.Disposable.ObjectPools.csproj b/source/Open.Disposable.ObjectPools.csproj index 2207d55..227fc9d 100644 --- a/source/Open.Disposable.ObjectPools.csproj +++ b/source/Open.Disposable.ObjectPools.csproj @@ -27,6 +27,10 @@ Added 'OnDiscarded' for special handling of objects that will never be used agai latest + + latest + + From 02a0312d4ed72c68d9dfa3bcc735b4a4fe14b36c Mon Sep 17 00:00:00 2001 From: "Oren (electricessence)" Date: Mon, 7 May 2018 23:32:32 -0700 Subject: [PATCH 21/83] Version update. --- source/Open.Disposable.ObjectPools.csproj | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/source/Open.Disposable.ObjectPools.csproj b/source/Open.Disposable.ObjectPools.csproj index 227fc9d..b380237 100644 --- a/source/Open.Disposable.ObjectPools.csproj +++ b/source/Open.Disposable.ObjectPools.csproj @@ -16,11 +16,10 @@ Part of the "Open" set of libraries. git objectpool, dotnet, dotnetcore, cs, idisposable, threadsafe, thread-safe True - 2.1.0 - 2.1.0.0 - 2.1.0.0 - Enabled tracking for all collection based object pools to ensure proper refusal when capacity is met. -Added 'OnDiscarded' for special handling of objects that will never be used again. + 2.1.1 + 2.1.1.0 + 2.1.1.0 + From b58c310610247065efd4100cdda60bbda040c360 Mon Sep 17 00:00:00 2001 From: "Oren (electricessence)" Date: Tue, 17 Jul 2018 21:02:09 -0700 Subject: [PATCH 22/83] Updates after resharper inspection. --- ...Disposable.ObjectPools.Benchmarking.csproj | 8 +++ benchmarking/Program.cs | 6 +- source/Array/InterlockedArrayObjectPool.cs | 8 +-- .../Collection/CollectionWrapperObjectPool.cs | 12 ++-- .../Collection/ConcurrentQueueObjectPool.cs | 2 +- .../Collection/ConcurrentStackObjectPool.cs | 2 +- source/IObjectPool.cs | 13 ++-- source/IRecycler.cs | 2 +- source/ObjectPoolAutoTrimmer.cs | 4 ++ source/ObjectPoolBase.cs | 20 +++--- source/Open.Disposable.ObjectPools.csproj | 11 ++-- source/Recycler.cs | 62 ++++++++----------- source/RecyclerBase.cs | 18 +++--- source/ReferenceContainer.cs | 54 ++++++++-------- source/TrimmableObjectPoolBase.cs | 8 +-- tests/ObjectPoolSmokeTests.cs | 4 +- .../Open.Disposable.ObjectPools.Tests.csproj | 14 ++++- 17 files changed, 128 insertions(+), 120 deletions(-) diff --git a/benchmarking/Open.Disposable.ObjectPools.Benchmarking.csproj b/benchmarking/Open.Disposable.ObjectPools.Benchmarking.csproj index 728a806..ab10537 100644 --- a/benchmarking/Open.Disposable.ObjectPools.Benchmarking.csproj +++ b/benchmarking/Open.Disposable.ObjectPools.Benchmarking.csproj @@ -7,6 +7,14 @@ 1.0.0 + + latest + + + + latest + + diff --git a/benchmarking/Program.cs b/benchmarking/Program.cs index 136a8f6..e4236e6 100644 --- a/benchmarking/Program.cs +++ b/benchmarking/Program.cs @@ -39,14 +39,14 @@ static void Main(string[] args) count => () => OptimisticArrayObjectPool.Create((int)count * 2)); // Is ineveitably slower than the above but should be enabled for testing code changes. - //report.AddBenchmark("InterlockedArrayObjectPool", - // count => () => InterlockedArrayObjectPool.Create((int)count * 2)); + report.AddBenchmark("InterlockedArrayObjectPool", + count => () => InterlockedArrayObjectPool.Create((int)count * 2)); report.Pretest(200, 200); // Run once through first to scramble/warm-up initial conditions. Console.SetCursorPosition(0, Console.CursorTop); - const int loopMultiple = 2; + const int loopMultiple = 6; report.Test(4, 8 * loopMultiple); report.Test(10, 8 * loopMultiple); report.Test(50, 12 * loopMultiple); diff --git a/source/Array/InterlockedArrayObjectPool.cs b/source/Array/InterlockedArrayObjectPool.cs index b72d0c8..a628665 100644 --- a/source/Array/InterlockedArrayObjectPool.cs +++ b/source/Array/InterlockedArrayObjectPool.cs @@ -27,11 +27,11 @@ public InterlockedArrayObjectPool(Func factory, int capacity = DEFAULT_CAPACI protected ReferenceContainer[] Pool; // Sets a limit on what has been stored yet to prevent over searching the array unnecessarily.. - protected int MaxStored = 0; + protected int MaxStored; protected const int MaxStoredIncrement = 5; // Instead of every one. public override int Count - => Pool.Count(e => e.Value != null) + PocketCount; + => Pool.Count(e => e.Value != null) + PocketCount; protected virtual bool Store(ReferenceContainer[] p, T item, int index) => p[index].TrySave(item); @@ -41,7 +41,7 @@ protected override bool Receive(T item) var elements = Pool; var len = elements?.Length ?? 0; - for (int i = 0; i < len; i++) + for (var i = 0; i < len; i++) { if (Store(elements, item, i)) { @@ -62,7 +62,7 @@ protected override T TryRelease() if (elements == null) return null; var len = elements.Length; - for (int i = 0; i < MaxStored && i < len; i++) + for (var i = 0; i < MaxStored && i < len; i++) { var item = elements[i].TryRetrieve(); if (item != null) return item; diff --git a/source/Collection/CollectionWrapperObjectPool.cs b/source/Collection/CollectionWrapperObjectPool.cs index 963dede..51ed834 100644 --- a/source/Collection/CollectionWrapperObjectPool.cs +++ b/source/Collection/CollectionWrapperObjectPool.cs @@ -41,16 +41,14 @@ protected override T TryRelease() var p = Pool; var item = p?.FirstOrDefault(); - if (item != null) - { - /* Removing the first item is typically horribly inefficient but we can't make assumptions about the implementation here. + if (item == null) return null; + /* Removing the first item is typically horribly inefficient but we can't make assumptions about the implementation here. * It's a trade off between potentially iterating the entire collection before removing the last item, or relying on the underlying implementation. * This implementation is in place for reference more than practice. Sub classes should override. */ - bool wasRemoved = false; - lock (p) wasRemoved = p.Remove(item); - if (!wasRemoved) goto retry; - } + bool wasRemoved; + lock (p) wasRemoved = p.Remove(item); + if (!wasRemoved) goto retry; return item; } diff --git a/source/Collection/ConcurrentQueueObjectPool.cs b/source/Collection/ConcurrentQueueObjectPool.cs index bb2564a..ab54acf 100644 --- a/source/Collection/ConcurrentQueueObjectPool.cs +++ b/source/Collection/ConcurrentQueueObjectPool.cs @@ -35,7 +35,7 @@ protected override T TryRelease() { var p = Pool; if (p == null) return null; - p.TryDequeue(out T item); + p.TryDequeue(out var item); return item; } diff --git a/source/Collection/ConcurrentStackObjectPool.cs b/source/Collection/ConcurrentStackObjectPool.cs index 888fcf8..ba8d28c 100644 --- a/source/Collection/ConcurrentStackObjectPool.cs +++ b/source/Collection/ConcurrentStackObjectPool.cs @@ -30,7 +30,7 @@ protected override T TryRelease() { var p = Pool; if (p == null) return null; - p.TryPop(out T item); + p.TryPop(out var item); return item; } diff --git a/source/IObjectPool.cs b/source/IObjectPool.cs index 0cf027b..de7ff17 100644 --- a/source/IObjectPool.cs +++ b/source/IObjectPool.cs @@ -21,7 +21,6 @@ public interface IObjectPool : IDisposable /// WARNING: The item is considered 'dead' but resurrectable so be sure not to hold on to the item's reference. /// /// The item to give up to the pool. - /// An optional action exectue on the item only if it's possible to return to the pool. void Give(T item); /// @@ -52,26 +51,28 @@ public interface IObjectPool : IDisposable int Count { get; } } - public static partial class ObjectPoolExtensions + public static class ObjectPoolExtensions { /// /// Receives items and iteratively adds them to the pool. /// WARNING: These items are considered 'dead' but resurrectable so be sure not to hold on to their reference. /// + /// The pool to give to. /// The items to give up to the pool. public static void Give(this IObjectPool target, IEnumerable items) where T : class { - if (items != null) - foreach (var i in items) - target.Give(i); + if (items == null) return; + foreach (var i in items) + target.Give(i); } /// /// Receives items and iteratively adds them to the pool. /// WARNING: These items are considered 'dead' but resurrectable so be sure not to hold on to their reference. /// - /// The first item to give up to the pool. + /// The pool to give to. + /// The first item to give up to the pool. /// The second item to give up to the pool. /// The remaining items to give up to the pool. public static void Give(this IObjectPool target, T item1, T item2, params T[] items) diff --git a/source/IRecycler.cs b/source/IRecycler.cs index 445bdd7..c6df542 100644 --- a/source/IRecycler.cs +++ b/source/IRecycler.cs @@ -3,7 +3,7 @@ namespace Open.Disposable { - interface IRecycler : IDisposable + interface IRecycler : IDisposable where T : class { bool Recycle(T item); diff --git a/source/ObjectPoolAutoTrimmer.cs b/source/ObjectPoolAutoTrimmer.cs index 0ec836e..977e53a 100644 --- a/source/ObjectPoolAutoTrimmer.cs +++ b/source/ObjectPoolAutoTrimmer.cs @@ -1,8 +1,10 @@ using Open.Threading.Tasks; using System; +using System.Diagnostics.CodeAnalysis; namespace Open.Disposable { + [SuppressMessage("ReSharper", "FieldCanBeMadeReadOnly.Local")] public class ObjectPoolAutoTrimmer : DisposableBase { ITrimmableObjectPool _pool; @@ -15,11 +17,13 @@ public class ObjectPoolAutoTrimmer : DisposableBase /// /// Max size that trimming will allow. /// + // ReSharper disable once NotAccessedField.Global public readonly ushort TrimmedSize; /// /// Time to wait/defer trimming. Default is 500 milliSeconds. /// + // ReSharper disable once NotAccessedField.Global public readonly TimeSpan TrimDelay; /// diff --git a/source/ObjectPoolBase.cs b/source/ObjectPoolBase.cs index 3724580..5555895 100644 --- a/source/ObjectPoolBase.cs +++ b/source/ObjectPoolBase.cs @@ -20,14 +20,15 @@ protected ObjectPoolBase(Func factory, Action recycler, Action disposer protected int MaxSize; public int Capacity => MaxSize; + /// /// /// Total number of items in the pool. /// public abstract int Count { get; } - protected virtual int PocketCount => Pocket.Value == null ? 0 : 1; + protected int PocketCount => Pocket.Value == null ? 0 : 1; - protected Action Recycler; // Before entering the pool. - protected Action OnDiscarded; // When not able to be used. + protected readonly Action Recycler; // Before entering the pool. + protected readonly Action OnDiscarded; // When not able to be used. // Read-only because if Take() is called after disposal, this still facilitates returing an object. @@ -36,6 +37,7 @@ protected ObjectPoolBase(Func factory, Action recycler, Action disposer public T Generate() => Factory(); + // ReSharper disable once UnassignedField.Global protected ReferenceContainer Pocket; // Default struct constructs itself. #region Receive (.Give(T item)) @@ -43,7 +45,7 @@ protected ObjectPoolBase(Func factory, Action recycler, Action disposer protected bool PrepareToReceive(T item) { - if (item!=null && CanReceive) + if (item != null && CanReceive) { var r = Recycler; if (r != null) @@ -97,7 +99,7 @@ public bool TryTake(out T item) protected virtual bool SaveToPocket(T item) => Pocket.TrySave(item); - protected virtual T TakeFromPocket() + protected T TakeFromPocket() => Pocket.TryRetrieve(); @@ -122,11 +124,9 @@ protected override void OnBeforeDispose() protected override void OnDispose(bool calledExplicitly) { - if (calledExplicitly && OnDiscarded != null) - { - T d; - while ((d = TryRelease()) != null) OnDiscarded(d); - } + if (!calledExplicitly || OnDiscarded == null) return; + T d; + while ((d = TryRelease()) != null) OnDiscarded(d); } } } diff --git a/source/Open.Disposable.ObjectPools.csproj b/source/Open.Disposable.ObjectPools.csproj index b380237..bf6415c 100644 --- a/source/Open.Disposable.ObjectPools.csproj +++ b/source/Open.Disposable.ObjectPools.csproj @@ -16,9 +16,9 @@ Part of the "Open" set of libraries. git objectpool, dotnet, dotnetcore, cs, idisposable, threadsafe, thread-safe True - 2.1.1 - 2.1.1.0 - 2.1.1.0 + 2.1.3 + 2.1.3.0 + 2.1.3.0 @@ -41,8 +41,9 @@ Part of the "Open" set of libraries. - - + + + \ No newline at end of file diff --git a/source/Recycler.cs b/source/Recycler.cs index 9256649..7f4e6c6 100644 --- a/source/Recycler.cs +++ b/source/Recycler.cs @@ -1,56 +1,55 @@ using System; -using System.Threading.Tasks.Dataflow; +using System.Threading.Channels; +using System.Threading.Tasks; namespace Open.Disposable { - /// - /// This class is provided as an asynchronous queue for recycling instead of using a recycle delegate with an object pool and calling GiveAsync() which could pile up unnecessarily. - /// So if recycling an object takes extra time, this might be a good way to toss objects away and not have to worry about the heavy cost as they will one by one be processed back into the target pool. - /// + /// public class Recycler : RecyclerBase where T : class { - // Something else could be used and could be more performant. - // But it's an ideal interface for what's needed. And the point is that the recyler should not take up too much cpu time. - ActionBlock _bin; + Channel _bin; internal Recycler( IObjectPool target, Action recycleFunction, - ushort limit = Constants.DEFAULT_CAPACITY) : base(target, recycleFunction, limit) + ushort limit = Constants.DEFAULT_CAPACITY) : base(target, recycleFunction) { - _bin = new ActionBlock(item => + _bin = Channel.CreateBounded(limit); + Completion = Processor(recycleFunction); + } + + async Task Processor(Action recycleFunction) + { + var bin = _bin; + do { - if (Target != null) + while (bin.Reader.TryRead(out var item)) { recycleFunction(item); Target?.Give(item); } - }); - - Completion = _bin.Completion; + } + while (await bin.Reader.WaitToReadAsync()); } internal Recycler( ushort limit, IObjectPool pool, - Action recycleFunction) : this(pool, recycleFunction, limit) - { - - } + Action recycleFunction) : this(pool, recycleFunction, limit) { } public override bool Recycle(T item) - { - return _bin?.Post(item) ?? false; - } + => _bin?.Writer.TryWrite(item) ?? false; protected override void OnCloseRequested() - => _bin?.Complete(); + => _bin?.Writer.Complete(); protected override void OnDispose(bool calledExplicitly) { base.OnDispose(calledExplicitly); - if (calledExplicitly) _bin = null; + if (!calledExplicitly) return; + _bin.Writer.TryComplete(); + _bin = null; } } @@ -61,31 +60,22 @@ public static Recycler CreateRecycler( Action recycleFunction, ushort limit = Constants.DEFAULT_CAPACITY) where T : class - { - return new Recycler(pool, recycleFunction, limit); - } + => new Recycler(pool, recycleFunction, limit); public static Recycler CreateRecycler( this IObjectPool pool, ushort limit, Action recycleFunction) where T : class - { - return new Recycler(pool, recycleFunction, limit); - } + => new Recycler(pool, recycleFunction, limit); public static void Recycle(IRecyclable r) - { - r.Recycle(); - } + => r.Recycle(); public static Recycler CreateRecycler( this IObjectPool pool, ushort limit = Constants.DEFAULT_CAPACITY) where T : class, IRecyclable - { - return new Recycler(pool, Recycle, limit); - } - + => new Recycler(pool, Recycle, limit); } } diff --git a/source/RecyclerBase.cs b/source/RecyclerBase.cs index a83fcab..cba5c01 100644 --- a/source/RecyclerBase.cs +++ b/source/RecyclerBase.cs @@ -1,4 +1,5 @@ using System; +using System.Diagnostics.Contracts; using System.Threading.Tasks; namespace Open.Disposable @@ -7,6 +8,7 @@ namespace Open.Disposable /// This class is provided as an asynchronous queue for recycling instead of using a recycle delegate with an object pool and calling GiveAsync() which could pile up unnecessarily. /// So if recycling an object takes extra time, this might be a good way to toss objects away and not have to worry about the heavy cost as they will one by one be processed back into the target pool. /// + // ReSharper disable once InheritdocConsiderUsage public abstract class RecyclerBase : DisposableBase, IRecycler where T : class { @@ -14,17 +16,16 @@ public abstract class RecyclerBase : DisposableBase, IRecycler protected RecyclerBase( IObjectPool target, - Action recycleFunction, - ushort limit = Constants.DEFAULT_CAPACITY) + Action recycleFunction) { if (recycleFunction == null) throw new ArgumentNullException(nameof(recycleFunction)); Target = target ?? throw new ArgumentNullException(nameof(target)); - if (target is DisposableBase d) - { - if (d.IsDisposed) throw new ArgumentException("Cannot recycle for an object pool that is already disposed."); - d.BeforeDispose += Pool_BeforeDispose; - // Could possibly dispose before this line somewhere... But that's just nasty. :P - } + Contract.EndContractBlock(); + + if (!(target is DisposableBase d)) return; + if (d.IsDisposed) throw new ArgumentException("Cannot recycle for an object pool that is already disposed."); + d.BeforeDispose += Pool_BeforeDispose; + // Could possibly dispose before this line somewhere... But that's just nasty. :P } void Pool_BeforeDispose(object sender, EventArgs e) => Dispose(); @@ -33,6 +34,7 @@ protected RecyclerBase( protected abstract void OnCloseRequested(); + // ReSharper disable once MemberCanBeProtected.Global public Task Completion { get; protected set; } public Task Close() diff --git a/source/ReferenceContainer.cs b/source/ReferenceContainer.cs index fe4ebd2..4cabed8 100644 --- a/source/ReferenceContainer.cs +++ b/source/ReferenceContainer.cs @@ -1,8 +1,10 @@ using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using System.Threading; namespace Open.Disposable { + [SuppressMessage("ReSharper", "UnusedMemberInSuper.Global")] public interface IReferenceContainer where T : class { @@ -28,20 +30,14 @@ public T Value public bool SetIfNull(T value) { - if (_value == null) - { - _value = value; - return true; - } - - return false; + if (_value != null) return false; + _value = value; + return true; } public bool TrySave(T value) - { - return _value == null + => _value == null && null == Interlocked.CompareExchange(ref _value, value, null); - } public T TryRetrieve() { @@ -53,28 +49,28 @@ public T TryRetrieve() } } - public struct DualReferenceContainer : IReferenceContainer - where T : class - { - public int Capacity => 2; + //public struct DualReferenceContainer : IReferenceContainer + // where T : class + //{ + // public int Capacity => 2; - ReferenceContainer _1; - ReferenceContainer _2; + // ReferenceContainer _1; + // ReferenceContainer _2; - public bool SetIfNull(T value) - { - return _1.SetIfNull(value) || _2.SetIfNull(value); - } + // public bool SetIfNull(T value) + // { + // return _1.SetIfNull(value) || _2.SetIfNull(value); + // } - public T TryRetrieve() - { - return _1.TryRetrieve() ?? _2.TryRetrieve(); - } + // public T TryRetrieve() + // { + // return _1.TryRetrieve() ?? _2.TryRetrieve(); + // } - public bool TrySave(T value) - { - return _1.TrySave(value) || _2.TrySave(value); - } - } + // public bool TrySave(T value) + // { + // return _1.TrySave(value) || _2.TrySave(value); + // } + //} } diff --git a/source/TrimmableObjectPoolBase.cs b/source/TrimmableObjectPoolBase.cs index 25450c6..8f902d3 100644 --- a/source/TrimmableObjectPoolBase.cs +++ b/source/TrimmableObjectPoolBase.cs @@ -14,8 +14,8 @@ protected TrimmableObjectPoolBase(Func factory, Action recycler, Action _countTrackingEnabled = countTrackingEnabled; } - int _count = 0; - bool _countTrackingEnabled; // When true this enables tracking the number of entries entering and exiting the pool instead of calling '.Count'. + int _count; + readonly bool _countTrackingEnabled; // When true this enables tracking the number of entries entering and exiting the pool instead of calling '.Count'. protected int CountInternal => _countTrackingEnabled ? _count : Count; @@ -52,11 +52,11 @@ protected override void OnReceived() public virtual void TrimTo(int targetSize) { if (targetSize < 0) return; // Possible upstream math hiccup or default -1. Silently dismiss. - int count = CountInternal; + var count = CountInternal; if (count > targetSize) { - int i = 0; // Prevent an possiblility of indefinite loop. + var i = 0; // Prevent an possiblility of indefinite loop. for ( var attempts = count - targetSize; i < attempts; diff --git a/tests/ObjectPoolSmokeTests.cs b/tests/ObjectPoolSmokeTests.cs index ae31da6..090cf6d 100644 --- a/tests/ObjectPoolSmokeTests.cs +++ b/tests/ObjectPoolSmokeTests.cs @@ -1,4 +1,4 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; +using Microsoft.VisualStudio.TestTools.UnitTesting; namespace Open.Disposable { @@ -13,7 +13,7 @@ class IdContainer [TestMethod] public void OptimisticArrayObjectPool_FactoryTest() { - int i = 0; + var i = 0; var pool = OptimisticArrayObjectPool.Create(() => new IdContainer { ID = ++i }); Assert.AreEqual(1, pool.Take().ID); } diff --git a/tests/Open.Disposable.ObjectPools.Tests.csproj b/tests/Open.Disposable.ObjectPools.Tests.csproj index 7d58ae5..4af16ab 100644 --- a/tests/Open.Disposable.ObjectPools.Tests.csproj +++ b/tests/Open.Disposable.ObjectPools.Tests.csproj @@ -5,10 +5,18 @@ Open.Disposable + + latest + + + + latest + + - - - + + + From f13a26f985d96bc210128234668f092a89ee548a Mon Sep 17 00:00:00 2001 From: "Oren (electricessence)" Date: Tue, 24 Jul 2018 03:01:02 -0700 Subject: [PATCH 23/83] Non essential inspection and benchmark tuneup. --- benchmarking/Benchmark.cs | 12 ++++++++++-- benchmarking/ConsoleReport.cs | 4 ++-- benchmarking/Program.cs | 4 +++- source/IObjectPool.cs | 2 ++ tests/Open.Disposable.ObjectPools.Tests.csproj | 2 +- 5 files changed, 18 insertions(+), 6 deletions(-) diff --git a/benchmarking/Benchmark.cs b/benchmarking/Benchmark.cs index 65080db..3c436fb 100644 --- a/benchmarking/Benchmark.cs +++ b/benchmarking/Benchmark.cs @@ -1,11 +1,16 @@ using Open.Diagnostics; using System; using System.Collections.Generic; -using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using System.Threading.Tasks; +#if DEBUG +using System.Diagnostics; +#endif + namespace Open.Disposable.ObjectPools { + [SuppressMessage("ReSharper", "AccessToDisposedClosure")] public class Benchmark : BenchmarkBase>> where T : class { @@ -23,7 +28,7 @@ protected override IEnumerable TestOnceInternal() { using (var pool = Param()) { - + if (pool == null) throw new NullReferenceException(); //yield return TimedResult.Measure("Take From Empty (In Parallel)", () => //{ // Parallel.For(0, TestSize, i => _items[i] = pool.Take()); @@ -31,9 +36,12 @@ protected override IEnumerable TestOnceInternal() yield return TimedResult.Measure("Give To (In Parallel)", () => { + // ReSharper disable once AccessToDisposedClosure Parallel.For(0, TestSize, i => pool.Give(_items[i])); +#if DEBUG var count = pool.Count; Debug.Assert(pool is OptimisticArrayObjectPool || count == TestSize, $"Expected {TestSize}, acutal count: {count}"); +#endif }); yield return TimedResult.Measure("Mixed 90%-Take/10%-Give (In Parallel)", () => diff --git a/benchmarking/ConsoleReport.cs b/benchmarking/ConsoleReport.cs index 55ea738..d8037a6 100644 --- a/benchmarking/ConsoleReport.cs +++ b/benchmarking/ConsoleReport.cs @@ -4,11 +4,11 @@ namespace Open.Disposable.ObjectPools { - public class ConsoleReport : Open.Diagnostics.BenchmarkConsoleReport>> + public class ConsoleReport : Diagnostics.BenchmarkConsoleReport>> where T : class { const int ITERATIONS = 100000; - public ConsoleReport(TextWriter output = null) : base(ITERATIONS, output, (c, r, p) => Benchmark.Results(c, r, p)) + public ConsoleReport(TextWriter output = null) : base(ITERATIONS, output, Benchmark.Results) { } diff --git a/benchmarking/Program.cs b/benchmarking/Program.cs index e4236e6..5cc4ba4 100644 --- a/benchmarking/Program.cs +++ b/benchmarking/Program.cs @@ -2,12 +2,14 @@ using Open.Disposable.ObjectPools; using Open.Text.CSV; using System; +using System.Diagnostics.CodeAnalysis; using System.IO; using System.Text; class Program { - static void Main(string[] args) + [SuppressMessage("ReSharper", "CoVariantArrayConversion")] + static void Main() { Console.Write("Initializing..."); diff --git a/source/IObjectPool.cs b/source/IObjectPool.cs index de7ff17..9c6c1b1 100644 --- a/source/IObjectPool.cs +++ b/source/IObjectPool.cs @@ -1,8 +1,10 @@ using System; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; namespace Open.Disposable { + [SuppressMessage("ReSharper", "UnusedMemberInSuper.Global")] public interface IObjectPool : IDisposable where T : class { diff --git a/tests/Open.Disposable.ObjectPools.Tests.csproj b/tests/Open.Disposable.ObjectPools.Tests.csproj index 4af16ab..8f53fad 100644 --- a/tests/Open.Disposable.ObjectPools.Tests.csproj +++ b/tests/Open.Disposable.ObjectPools.Tests.csproj @@ -1,7 +1,7 @@  - netcoreapp2.0 + netcoreapp2.1 Open.Disposable From a7c46db491f76752ad4671ca8a8e2fab01654857 Mon Sep 17 00:00:00 2001 From: "Oren (electricessence)" Date: Tue, 24 Jul 2018 09:53:36 -0700 Subject: [PATCH 24/83] Version updates. --- benchmarking/Open.Disposable.ObjectPools.Benchmarking.csproj | 2 +- tests/Open.Disposable.ObjectPools.Tests.csproj | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/benchmarking/Open.Disposable.ObjectPools.Benchmarking.csproj b/benchmarking/Open.Disposable.ObjectPools.Benchmarking.csproj index ab10537..3bcb321 100644 --- a/benchmarking/Open.Disposable.ObjectPools.Benchmarking.csproj +++ b/benchmarking/Open.Disposable.ObjectPools.Benchmarking.csproj @@ -2,7 +2,7 @@ Exe - netcoreapp2.0 + netcoreapp2.1 Open.Disposable 1.0.0 diff --git a/tests/Open.Disposable.ObjectPools.Tests.csproj b/tests/Open.Disposable.ObjectPools.Tests.csproj index 8f53fad..c080ae2 100644 --- a/tests/Open.Disposable.ObjectPools.Tests.csproj +++ b/tests/Open.Disposable.ObjectPools.Tests.csproj @@ -14,7 +14,7 @@ - + From 00abe502778d69b702f67db466e407d1f23d0a66 Mon Sep 17 00:00:00 2001 From: "Oren (electricessence)" Date: Sat, 29 Jun 2019 19:35:37 -0700 Subject: [PATCH 25/83] Revisoins --- benchmarking/BenchmarkResult.csv | 41 +-- benchmarking/BenchmarkResult.txt | 246 ++++++++++-------- ...Disposable.ObjectPools.Benchmarking.csproj | 2 +- benchmarking/Program.cs | 4 +- source/Channels/ChannelObjectPool.cs | 10 +- source/Channels/ChanneledRecycler.cs | 2 +- source/Open.Disposable.ObjectPools.csproj | 9 +- 7 files changed, 176 insertions(+), 138 deletions(-) diff --git a/benchmarking/BenchmarkResult.csv b/benchmarking/BenchmarkResult.csv index 13d68f1..02d7129 100644 --- a/benchmarking/BenchmarkResult.csv +++ b/benchmarking/BenchmarkResult.csv @@ -1,24 +1,25 @@ Batch,Pool Type," To (In Parallel)",d 90%-Take/10%-Give (In Parallel),d 50%-Take/50%-Give (In Parallel),d 10%-Take/90%-Give (In Parallel),y Pool (.TryTake()),L, -Repeat 400000 for size 4,ConcurrentQueueObjectPool,00:00:01.8472469,00:00:01.7363986,00:00:01.6837968,00:00:01.7096510,00:00:00.1076125,00:00:07.0847058, -Repeat 400000 for size 4,ConcurrentStackObjectPool,00:00:01.8102817,00:00:01.7426871,00:00:01.6518815,00:00:01.8414613,00:00:00.1049529,00:00:07.1512645, -Repeat 400000 for size 4,OptimisticArrayObjectPool,00:00:01.8589242,00:00:01.7585402,00:00:01.7489384,00:00:01.7687298,00:00:00.0952550,00:00:07.2303876, -Repeat 160000 for size 10,ConcurrentQueueObjectPool,00:00:00.7782376,00:00:00.7324783,00:00:00.7163121,00:00:00.7306797,00:00:00.0971813,00:00:03.0548890, -Repeat 160000 for size 10,ConcurrentStackObjectPool,00:00:00.9064832,00:00:00.7690028,00:00:00.7207622,00:00:00.8417267,00:00:00.1034971,00:00:03.3414720, -Repeat 160000 for size 10,OptimisticArrayObjectPool,00:00:00.8577028,00:00:00.7887449,00:00:00.7333412,00:00:00.8531175,00:00:00.1088223,00:00:03.3417287, -Repeat 48000 for size 50,ConcurrentQueueObjectPool,00:00:00.4952668,00:00:00.3843341,00:00:00.3179560,00:00:00.3515590,00:00:00.1171762,00:00:01.6662921, -Repeat 48000 for size 50,ConcurrentStackObjectPool,00:00:00.5346734,00:00:00.3824365,00:00:00.3211063,00:00:00.4865511,00:00:00.1248121,00:00:01.8495794, -Repeat 48000 for size 50,OptimisticArrayObjectPool,00:00:00.5833078,00:00:00.4477068,00:00:00.3296106,00:00:00.4408652,00:00:00.3016180,00:00:02.1031084, -Repeat 32000 for size 100,ConcurrentQueueObjectPool,00:00:00.4969536,00:00:00.4326518,00:00:00.3199614,00:00:00.4396957,00:00:00.1608001,00:00:01.8500626, -Repeat 32000 for size 100,ConcurrentStackObjectPool,00:00:00.5521412,00:00:00.4853524,00:00:00.3175640,00:00:00.5159456,00:00:00.1572408,00:00:02.0282440, -Repeat 32000 for size 100,OptimisticArrayObjectPool,00:00:00.7719640,00:00:00.5488668,00:00:00.2717297,00:00:00.5357692,00:00:00.6489823,00:00:02.7773120, -Repeat 25600 for size 250,ConcurrentQueueObjectPool,00:00:00.7980353,00:00:00.6178042,00:00:00.5431682,00:00:00.6892625,00:00:00.2954847,00:00:02.9437549, -Repeat 25600 for size 250,ConcurrentStackObjectPool,00:00:00.9389921,00:00:00.9667758,00:00:00.5609015,00:00:00.8204988,00:00:00.3075525,00:00:03.5947207, -Repeat 25600 for size 250,OptimisticArrayObjectPool,00:00:02.1193418,00:00:01.3828499,00:00:00.4523577,00:00:01.3159924,00:00:02.9909843,00:00:08.2615261, -Repeat 6400 for size 2000,ConcurrentQueueObjectPool,00:00:01.3830355,00:00:00.9243686,00:00:00.9780430,00:00:00.9865309,00:00:00.5832766,00:00:04.8552546, -Repeat 6400 for size 2000,ConcurrentStackObjectPool,00:00:01.6064533,00:00:02.5455114,00:00:01.2388087,00:00:01.5112497,00:00:00.5781890,00:00:07.4802121, -Repeat 6400 for size 2000,OptimisticArrayObjectPool,00:00:13.5147753,00:00:15.1956294,00:00:00.9013497,00:00:12.0385696,00:00:32.8558145,00:01:14.5061385, -for size 2000,ConcurrentStackObjectPool,00:00:01.4597004,00:00:01.7435741,00:00:02.4213177,00:00:01.0803513,00:00:01.6261815,00:00:00.6094295,00:00:08.9405545, -Repeat 6400 for size 2000,OptimisticArrayObjectPool,00:00:01.2265087,00:00:23.0488821,00:00:15.8054484,00:00:00.9435990,00:00:14.8800972,00:00:51.4640465,00:01:47.3685819, +Repeat 120000 for size 4,ConcurrentQueueObjectPool,00:00:00.4328809,00:00:00.4157680,00:00:00.4065244,00:00:00.4045626,00:00:00.0271763,00:00:01.6869122, +Repeat 120000 for size 4,ConcurrentStackObjectPool,00:00:01.0126419,00:00:00.9358291,00:00:00.9317805,00:00:00.9543443,00:00:00.0426221,00:00:03.8772179, +Repeat 120000 for size 4,OptimisticArrayObjectPool,00:00:00.3072837,00:00:00.2791047,00:00:00.2824113,00:00:00.2763940,00:00:00.0181322,00:00:01.1633259, +Repeat 120000 for size 4,InterlockedArrayObjectPool,00:00:00.2849269,00:00:00.2799949,00:00:00.2987070,00:00:00.2980612,00:00:00.0179067,00:00:01.1795967, +Repeat 48000 for size 10,ConcurrentQueueObjectPool,00:00:00.2049617,00:00:00.1879537,00:00:00.1863383,00:00:00.1791973,00:00:00.0187886,00:00:00.7772396, +Repeat 48000 for size 10,ConcurrentStackObjectPool,00:00:00.2312536,00:00:00.2063132,00:00:00.1973173,00:00:00.2137496,00:00:00.0209974,00:00:00.8696311, +Repeat 48000 for size 10,OptimisticArrayObjectPool,00:00:00.1948958,00:00:00.1866085,00:00:00.1851642,00:00:00.1743788,00:00:00.0212318,00:00:00.7622791, +Repeat 48000 for size 10,InterlockedArrayObjectPool,00:00:00.2025915,00:00:00.1947096,00:00:00.1832321,00:00:00.1855078,00:00:00.0213919,00:00:00.7874329, +Repeat 14400 for size 50,ConcurrentQueueObjectPool,00:00:00.1153192,00:00:00.0930024,00:00:00.0847000,00:00:00.0976083,00:00:00.0225625,00:00:00.4131924, +Repeat 14400 for size 50,ConcurrentStackObjectPool,00:00:00.1210192,00:00:00.0938168,00:00:00.0859916,00:00:00.1097679,00:00:00.0232161,00:00:00.4338116, +Repeat 14400 for size 50,OptimisticArrayObjectPool,00:00:00.1162956,00:00:00.1051234,00:00:00.0809849,00:00:00.0920824,00:00:00.0592899,00:00:00.4537762, +Repeat 14400 for size 50,InterlockedArrayObjectPool,00:00:00.1351777,00:00:00.1015328,00:00:00.0834721,00:00:00.1115662,00:00:00.0940603,00:00:00.5258091, +Repeat 9600 for size 100,ConcurrentQueueObjectPool,00:00:00.1223762,00:00:00.0972226,00:00:00.0758479,00:00:00.0921301,00:00:00.0327353,00:00:00.4203121, +Repeat 9600 for size 100,ConcurrentStackObjectPool,00:00:00.1517036,00:00:00.2118948,00:00:00.1054921,00:00:00.1368268,00:00:00.0473453,00:00:00.6532626, +Repeat 9600 for size 100,OptimisticArrayObjectPool,00:00:00.1073093,00:00:00.1045937,00:00:00.0705415,00:00:00.0824673,00:00:00.0956365,00:00:00.4605483, +Repeat 9600 for size 100,InterlockedArrayObjectPool,00:00:00.1494063,00:00:00.1077739,00:00:00.0746742,00:00:00.1128479,00:00:00.2238334,00:00:00.6685357, +Repeat 7680 for size 250,ConcurrentQueueObjectPool,00:00:00.2123888,00:00:00.1725807,00:00:00.1277665,00:00:00.1629961,00:00:00.0699733,00:00:00.7457054, +Repeat 7680 for size 250,ConcurrentStackObjectPool,00:00:00.7008323,00:00:03.4680108,00:00:00.7312092,00:00:00.7532056,00:00:00.2733349,00:00:05.9265928, +Repeat 7680 for size 250,OptimisticArrayObjectPool,00:00:00.2225184,00:00:00.2290041,00:00:00.1110785,00:00:00.1400907,00:00:00.3309545,00:00:01.0336462, +Repeat 7680 for size 250,InterlockedArrayObjectPool,00:00:00.4984438,00:00:00.2336851,00:00:00.1059643,00:00:00.2670714,00:00:00.8555392,00:00:01.9607038, +:51.4640465,00:01:47.3685819, , epeat 51200 for size 250,OptimisticArrayObjectPool,00:00:00.7651948,00:00:01.3195712,00:00:01.3758641,00:00:00.6837150,00:00:01.0134658,00:00:01.9616149,00:00:07.1194258, Repeat 51200 for size 250,InterlockedArrayObjectPool,00:00:00.8386708,00:00:02.2800174,00:00:01.4244575,00:00:00.6435472,00:00:01.6845873,00:00:05.4327218,00:00:12.3040020, diff --git a/benchmarking/BenchmarkResult.txt b/benchmarking/BenchmarkResult.txt index 179542f..c2fa178 100644 --- a/benchmarking/BenchmarkResult.txt +++ b/benchmarking/BenchmarkResult.txt @@ -1,140 +1,180 @@ -Repeat 400000 for size 4 +Repeat 120000 for size 4 ------------------------------------ ConcurrentQueueObjectPool............................... -00:00:01.8472469 Give To (In Parallel) -00:00:01.7363986 Mixed 90%-Take/10%-Give (In Parallel) -00:00:01.6837968 Mixed 50%-Take/50%-Give (In Parallel) -00:00:01.7096510 Mixed 10%-Take/90%-Give (In Parallel) -00:00:00.1076125 Empty Pool (.TryTake()) -00:00:07.0847058 TOTAL +00:00:00.4328809 Give To (In Parallel) +00:00:00.4157680 Mixed 90%-Take/10%-Give (In Parallel) +00:00:00.4065244 Mixed 50%-Take/50%-Give (In Parallel) +00:00:00.4045626 Mixed 10%-Take/90%-Give (In Parallel) +00:00:00.0271763 Empty Pool (.TryTake()) +00:00:01.6869122 TOTAL ConcurrentStackObjectPool............................... -00:00:01.8102817 Give To (In Parallel) -00:00:01.7426871 Mixed 90%-Take/10%-Give (In Parallel) -00:00:01.6518815 Mixed 50%-Take/50%-Give (In Parallel) -00:00:01.8414613 Mixed 10%-Take/90%-Give (In Parallel) -00:00:00.1049529 Empty Pool (.TryTake()) -00:00:07.1512645 TOTAL +00:00:01.0126419 Give To (In Parallel) +00:00:00.9358291 Mixed 90%-Take/10%-Give (In Parallel) +00:00:00.9317805 Mixed 50%-Take/50%-Give (In Parallel) +00:00:00.9543443 Mixed 10%-Take/90%-Give (In Parallel) +00:00:00.0426221 Empty Pool (.TryTake()) +00:00:03.8772179 TOTAL OptimisticArrayObjectPool............................... -00:00:01.8589242 Give To (In Parallel) -00:00:01.7585402 Mixed 90%-Take/10%-Give (In Parallel) -00:00:01.7489384 Mixed 50%-Take/50%-Give (In Parallel) -00:00:01.7687298 Mixed 10%-Take/90%-Give (In Parallel) -00:00:00.0952550 Empty Pool (.TryTake()) -00:00:07.2303876 TOTAL - - -Repeat 160000 for size 10 +00:00:00.3072837 Give To (In Parallel) +00:00:00.2791047 Mixed 90%-Take/10%-Give (In Parallel) +00:00:00.2824113 Mixed 50%-Take/50%-Give (In Parallel) +00:00:00.2763940 Mixed 10%-Take/90%-Give (In Parallel) +00:00:00.0181322 Empty Pool (.TryTake()) +00:00:01.1633259 TOTAL + +InterlockedArrayObjectPool.............................. +00:00:00.2849269 Give To (In Parallel) +00:00:00.2799949 Mixed 90%-Take/10%-Give (In Parallel) +00:00:00.2987070 Mixed 50%-Take/50%-Give (In Parallel) +00:00:00.2980612 Mixed 10%-Take/90%-Give (In Parallel) +00:00:00.0179067 Empty Pool (.TryTake()) +00:00:01.1795967 TOTAL + + +Repeat 48000 for size 10 ------------------------------------ ConcurrentQueueObjectPool............................... -00:00:00.7782376 Give To (In Parallel) -00:00:00.7324783 Mixed 90%-Take/10%-Give (In Parallel) -00:00:00.7163121 Mixed 50%-Take/50%-Give (In Parallel) -00:00:00.7306797 Mixed 10%-Take/90%-Give (In Parallel) -00:00:00.0971813 Empty Pool (.TryTake()) -00:00:03.0548890 TOTAL +00:00:00.2049617 Give To (In Parallel) +00:00:00.1879537 Mixed 90%-Take/10%-Give (In Parallel) +00:00:00.1863383 Mixed 50%-Take/50%-Give (In Parallel) +00:00:00.1791973 Mixed 10%-Take/90%-Give (In Parallel) +00:00:00.0187886 Empty Pool (.TryTake()) +00:00:00.7772396 TOTAL ConcurrentStackObjectPool............................... -00:00:00.9064832 Give To (In Parallel) -00:00:00.7690028 Mixed 90%-Take/10%-Give (In Parallel) -00:00:00.7207622 Mixed 50%-Take/50%-Give (In Parallel) -00:00:00.8417267 Mixed 10%-Take/90%-Give (In Parallel) -00:00:00.1034971 Empty Pool (.TryTake()) -00:00:03.3414720 TOTAL +00:00:00.2312536 Give To (In Parallel) +00:00:00.2063132 Mixed 90%-Take/10%-Give (In Parallel) +00:00:00.1973173 Mixed 50%-Take/50%-Give (In Parallel) +00:00:00.2137496 Mixed 10%-Take/90%-Give (In Parallel) +00:00:00.0209974 Empty Pool (.TryTake()) +00:00:00.8696311 TOTAL OptimisticArrayObjectPool............................... -00:00:00.8577028 Give To (In Parallel) -00:00:00.7887449 Mixed 90%-Take/10%-Give (In Parallel) -00:00:00.7333412 Mixed 50%-Take/50%-Give (In Parallel) -00:00:00.8531175 Mixed 10%-Take/90%-Give (In Parallel) -00:00:00.1088223 Empty Pool (.TryTake()) -00:00:03.3417287 TOTAL - - -Repeat 48000 for size 50 +00:00:00.1948958 Give To (In Parallel) +00:00:00.1866085 Mixed 90%-Take/10%-Give (In Parallel) +00:00:00.1851642 Mixed 50%-Take/50%-Give (In Parallel) +00:00:00.1743788 Mixed 10%-Take/90%-Give (In Parallel) +00:00:00.0212318 Empty Pool (.TryTake()) +00:00:00.7622791 TOTAL + +InterlockedArrayObjectPool.............................. +00:00:00.2025915 Give To (In Parallel) +00:00:00.1947096 Mixed 90%-Take/10%-Give (In Parallel) +00:00:00.1832321 Mixed 50%-Take/50%-Give (In Parallel) +00:00:00.1855078 Mixed 10%-Take/90%-Give (In Parallel) +00:00:00.0213919 Empty Pool (.TryTake()) +00:00:00.7874329 TOTAL + + +Repeat 14400 for size 50 ------------------------------------ ConcurrentQueueObjectPool............................... -00:00:00.4952668 Give To (In Parallel) -00:00:00.3843341 Mixed 90%-Take/10%-Give (In Parallel) -00:00:00.3179560 Mixed 50%-Take/50%-Give (In Parallel) -00:00:00.3515590 Mixed 10%-Take/90%-Give (In Parallel) -00:00:00.1171762 Empty Pool (.TryTake()) -00:00:01.6662921 TOTAL +00:00:00.1153192 Give To (In Parallel) +00:00:00.0930024 Mixed 90%-Take/10%-Give (In Parallel) +00:00:00.0847000 Mixed 50%-Take/50%-Give (In Parallel) +00:00:00.0976083 Mixed 10%-Take/90%-Give (In Parallel) +00:00:00.0225625 Empty Pool (.TryTake()) +00:00:00.4131924 TOTAL ConcurrentStackObjectPool............................... -00:00:00.5346734 Give To (In Parallel) -00:00:00.3824365 Mixed 90%-Take/10%-Give (In Parallel) -00:00:00.3211063 Mixed 50%-Take/50%-Give (In Parallel) -00:00:00.4865511 Mixed 10%-Take/90%-Give (In Parallel) -00:00:00.1248121 Empty Pool (.TryTake()) -00:00:01.8495794 TOTAL +00:00:00.1210192 Give To (In Parallel) +00:00:00.0938168 Mixed 90%-Take/10%-Give (In Parallel) +00:00:00.0859916 Mixed 50%-Take/50%-Give (In Parallel) +00:00:00.1097679 Mixed 10%-Take/90%-Give (In Parallel) +00:00:00.0232161 Empty Pool (.TryTake()) +00:00:00.4338116 TOTAL OptimisticArrayObjectPool............................... -00:00:00.5833078 Give To (In Parallel) -00:00:00.4477068 Mixed 90%-Take/10%-Give (In Parallel) -00:00:00.3296106 Mixed 50%-Take/50%-Give (In Parallel) -00:00:00.4408652 Mixed 10%-Take/90%-Give (In Parallel) -00:00:00.3016180 Empty Pool (.TryTake()) -00:00:02.1031084 TOTAL - - -Repeat 32000 for size 100 +00:00:00.1162956 Give To (In Parallel) +00:00:00.1051234 Mixed 90%-Take/10%-Give (In Parallel) +00:00:00.0809849 Mixed 50%-Take/50%-Give (In Parallel) +00:00:00.0920824 Mixed 10%-Take/90%-Give (In Parallel) +00:00:00.0592899 Empty Pool (.TryTake()) +00:00:00.4537762 TOTAL + +InterlockedArrayObjectPool.............................. +00:00:00.1351777 Give To (In Parallel) +00:00:00.1015328 Mixed 90%-Take/10%-Give (In Parallel) +00:00:00.0834721 Mixed 50%-Take/50%-Give (In Parallel) +00:00:00.1115662 Mixed 10%-Take/90%-Give (In Parallel) +00:00:00.0940603 Empty Pool (.TryTake()) +00:00:00.5258091 TOTAL + + +Repeat 9600 for size 100 ------------------------------------ ConcurrentQueueObjectPool............................... -00:00:00.4969536 Give To (In Parallel) -00:00:00.4326518 Mixed 90%-Take/10%-Give (In Parallel) -00:00:00.3199614 Mixed 50%-Take/50%-Give (In Parallel) -00:00:00.4396957 Mixed 10%-Take/90%-Give (In Parallel) -00:00:00.1608001 Empty Pool (.TryTake()) -00:00:01.8500626 TOTAL +00:00:00.1223762 Give To (In Parallel) +00:00:00.0972226 Mixed 90%-Take/10%-Give (In Parallel) +00:00:00.0758479 Mixed 50%-Take/50%-Give (In Parallel) +00:00:00.0921301 Mixed 10%-Take/90%-Give (In Parallel) +00:00:00.0327353 Empty Pool (.TryTake()) +00:00:00.4203121 TOTAL ConcurrentStackObjectPool............................... -00:00:00.5521412 Give To (In Parallel) -00:00:00.4853524 Mixed 90%-Take/10%-Give (In Parallel) -00:00:00.3175640 Mixed 50%-Take/50%-Give (In Parallel) -00:00:00.5159456 Mixed 10%-Take/90%-Give (In Parallel) -00:00:00.1572408 Empty Pool (.TryTake()) -00:00:02.0282440 TOTAL +00:00:00.1517036 Give To (In Parallel) +00:00:00.2118948 Mixed 90%-Take/10%-Give (In Parallel) +00:00:00.1054921 Mixed 50%-Take/50%-Give (In Parallel) +00:00:00.1368268 Mixed 10%-Take/90%-Give (In Parallel) +00:00:00.0473453 Empty Pool (.TryTake()) +00:00:00.6532626 TOTAL OptimisticArrayObjectPool............................... -00:00:00.7719640 Give To (In Parallel) -00:00:00.5488668 Mixed 90%-Take/10%-Give (In Parallel) -00:00:00.2717297 Mixed 50%-Take/50%-Give (In Parallel) -00:00:00.5357692 Mixed 10%-Take/90%-Give (In Parallel) -00:00:00.6489823 Empty Pool (.TryTake()) -00:00:02.7773120 TOTAL - - -Repeat 25600 for size 250 +00:00:00.1073093 Give To (In Parallel) +00:00:00.1045937 Mixed 90%-Take/10%-Give (In Parallel) +00:00:00.0705415 Mixed 50%-Take/50%-Give (In Parallel) +00:00:00.0824673 Mixed 10%-Take/90%-Give (In Parallel) +00:00:00.0956365 Empty Pool (.TryTake()) +00:00:00.4605483 TOTAL + +InterlockedArrayObjectPool.............................. +00:00:00.1494063 Give To (In Parallel) +00:00:00.1077739 Mixed 90%-Take/10%-Give (In Parallel) +00:00:00.0746742 Mixed 50%-Take/50%-Give (In Parallel) +00:00:00.1128479 Mixed 10%-Take/90%-Give (In Parallel) +00:00:00.2238334 Empty Pool (.TryTake()) +00:00:00.6685357 TOTAL + + +Repeat 7680 for size 250 ------------------------------------ ConcurrentQueueObjectPool............................... -00:00:00.7980353 Give To (In Parallel) -00:00:00.6178042 Mixed 90%-Take/10%-Give (In Parallel) -00:00:00.5431682 Mixed 50%-Take/50%-Give (In Parallel) -00:00:00.6892625 Mixed 10%-Take/90%-Give (In Parallel) -00:00:00.2954847 Empty Pool (.TryTake()) -00:00:02.9437549 TOTAL +00:00:00.2123888 Give To (In Parallel) +00:00:00.1725807 Mixed 90%-Take/10%-Give (In Parallel) +00:00:00.1277665 Mixed 50%-Take/50%-Give (In Parallel) +00:00:00.1629961 Mixed 10%-Take/90%-Give (In Parallel) +00:00:00.0699733 Empty Pool (.TryTake()) +00:00:00.7457054 TOTAL ConcurrentStackObjectPool............................... -00:00:00.9389921 Give To (In Parallel) -00:00:00.9667758 Mixed 90%-Take/10%-Give (In Parallel) -00:00:00.5609015 Mixed 50%-Take/50%-Give (In Parallel) -00:00:00.8204988 Mixed 10%-Take/90%-Give (In Parallel) -00:00:00.3075525 Empty Pool (.TryTake()) -00:00:03.5947207 TOTAL +00:00:00.7008323 Give To (In Parallel) +00:00:03.4680108 Mixed 90%-Take/10%-Give (In Parallel) +00:00:00.7312092 Mixed 50%-Take/50%-Give (In Parallel) +00:00:00.7532056 Mixed 10%-Take/90%-Give (In Parallel) +00:00:00.2733349 Empty Pool (.TryTake()) +00:00:05.9265928 TOTAL OptimisticArrayObjectPool............................... -00:00:02.1193418 Give To (In Parallel) -00:00:01.3828499 Mixed 90%-Take/10%-Give (In Parallel) -00:00:00.4523577 Mixed 50%-Take/50%-Give (In Parallel) -00:00:01.3159924 Mixed 10%-Take/90%-Give (In Parallel) -00:00:02.9909843 Empty Pool (.TryTake()) -00:00:08.2615261 TOTAL +00:00:00.2225184 Give To (In Parallel) +00:00:00.2290041 Mixed 90%-Take/10%-Give (In Parallel) +00:00:00.1110785 Mixed 50%-Take/50%-Give (In Parallel) +00:00:00.1400907 Mixed 10%-Take/90%-Give (In Parallel) +00:00:00.3309545 Empty Pool (.TryTake()) +00:00:01.0336462 TOTAL + +InterlockedArrayObjectPool.............................. +00:00:00.4984438 Give To (In Parallel) +00:00:00.2336851 Mixed 90%-Take/10%-Give (In Parallel) +00:00:00.1059643 Mixed 50%-Take/50%-Give (In Parallel) +00:00:00.2670714 Mixed 10%-Take/90%-Give (In Parallel) +00:00:00.8555392 Empty Pool (.TryTake()) +00:00:01.9607038 TOTAL diff --git a/benchmarking/Open.Disposable.ObjectPools.Benchmarking.csproj b/benchmarking/Open.Disposable.ObjectPools.Benchmarking.csproj index 3bcb321..d28050f 100644 --- a/benchmarking/Open.Disposable.ObjectPools.Benchmarking.csproj +++ b/benchmarking/Open.Disposable.ObjectPools.Benchmarking.csproj @@ -21,7 +21,7 @@ - + diff --git a/benchmarking/Program.cs b/benchmarking/Program.cs index 5cc4ba4..380e438 100644 --- a/benchmarking/Program.cs +++ b/benchmarking/Program.cs @@ -28,8 +28,8 @@ static void Main() //report.AddBenchmark("QueueObjectPool", // Note, that this one isn't far off from the following in peformance. // count => () => QueueObjectPool.Create((int)count * 2)); - //report.AddBenchmark("ChannelObjectPool", - // count => () => ChannelObjectPool.Create((int)count * 2)); + report.AddBenchmark("ChannelObjectPool", + count => () => ChannelObjectPool.Create((int)count * 2)); report.AddBenchmark("ConcurrentQueueObjectPool", // Note, that this one isn't far off from the following in peformance, but definitely is faster than LinkedListObjectPool and the rest. count => () => ConcurrentQueueObjectPool.Create((int)count * 2)); diff --git a/source/Channels/ChannelObjectPool.cs b/source/Channels/ChannelObjectPool.cs index 0998e11..a9c3694 100644 --- a/source/Channels/ChannelObjectPool.cs +++ b/source/Channels/ChannelObjectPool.cs @@ -6,18 +6,20 @@ namespace Open.Disposable public sealed class ChannelObjectPool : ObjectPoolBase where T : class { - public ChannelObjectPool(Func factory, Action recycler, int capacity = DEFAULT_CAPACITY) - : base(factory, recycler, capacity) + public ChannelObjectPool(Func factory, Action recycler, Action disposer, int capacity = DEFAULT_CAPACITY) + : base(factory, recycler, disposer, capacity) { Pool = Channel.CreateBounded(new BoundedChannelOptions(capacity) { FullMode = BoundedChannelFullMode.DropWrite }); } public ChannelObjectPool(Func factory, int capacity = DEFAULT_CAPACITY) - : this(factory, null, capacity) + : this(factory, null, null, capacity) { } Channel Pool; + public override int Count => -1; + protected override void OnDispose(bool calledExplicitly) { Pool?.Writer.TryComplete(); @@ -56,7 +58,7 @@ public static ChannelObjectPool Create(Func factory, bool autoRecycle, { Action recycler = null; if (autoRecycle) recycler = Recycler.Recycle; - return new ChannelObjectPool(factory, recycler, capacity); + return new ChannelObjectPool(factory, recycler, null, capacity); } public static ChannelObjectPool Create(bool autoRecycle, int capacity = Constants.DEFAULT_CAPACITY) diff --git a/source/Channels/ChanneledRecycler.cs b/source/Channels/ChanneledRecycler.cs index 5773df4..756cd1a 100644 --- a/source/Channels/ChanneledRecycler.cs +++ b/source/Channels/ChanneledRecycler.cs @@ -18,7 +18,7 @@ public sealed class ChanneledRecycler : RecyclerBase internal ChanneledRecycler( IObjectPool target, Action recycleFunction, - ushort limit = Constants.DEFAULT_CAPACITY) : base(target, recycleFunction, limit) + ushort limit = Constants.DEFAULT_CAPACITY) : base(target, recycleFunction) { var b = _bin = Channel.CreateBounded(new BoundedChannelOptions(limit) { diff --git a/source/Open.Disposable.ObjectPools.csproj b/source/Open.Disposable.ObjectPools.csproj index bf6415c..6b874a1 100644 --- a/source/Open.Disposable.ObjectPools.csproj +++ b/source/Open.Disposable.ObjectPools.csproj @@ -10,7 +10,7 @@ Part of the "Open" set of libraries. electricessence https://github.com/electricessence/Open.Disposable.ObjectPools/blob/master/LISCENSE.md - https://github.com/electricessence/Open.Disposable.ObjectPools/blob/master/LISCENSE.md + https://github.com/electricessence/Open.Disposable.ObjectPools/ https://github.com/electricessence/Open.Disposable.ObjectPools/ git @@ -20,6 +20,7 @@ Part of the "Open" set of libraries. 2.1.3.0 2.1.3.0 + MIT @@ -30,12 +31,6 @@ Part of the "Open" set of libraries. latest - - - - - - From 1a8b81d420aaabdcf3994e56db948ff444e20c1e Mon Sep 17 00:00:00 2001 From: "Oren (electricessence)" Date: Sat, 29 Jun 2019 20:11:10 -0700 Subject: [PATCH 26/83] Benchmark. --- benchmarking/BenchmarkResult.csv | 42 ++-- benchmarking/BenchmarkResult.txt | 250 +++++++++++----------- benchmarking/Program.cs | 8 +- source/Open.Disposable.ObjectPools.csproj | 6 + 4 files changed, 156 insertions(+), 150 deletions(-) diff --git a/benchmarking/BenchmarkResult.csv b/benchmarking/BenchmarkResult.csv index 02d7129..cac72ce 100644 --- a/benchmarking/BenchmarkResult.csv +++ b/benchmarking/BenchmarkResult.csv @@ -1,25 +1,25 @@ Batch,Pool Type," To (In Parallel)",d 90%-Take/10%-Give (In Parallel),d 50%-Take/50%-Give (In Parallel),d 10%-Take/90%-Give (In Parallel),y Pool (.TryTake()),L, -Repeat 120000 for size 4,ConcurrentQueueObjectPool,00:00:00.4328809,00:00:00.4157680,00:00:00.4065244,00:00:00.4045626,00:00:00.0271763,00:00:01.6869122, -Repeat 120000 for size 4,ConcurrentStackObjectPool,00:00:01.0126419,00:00:00.9358291,00:00:00.9317805,00:00:00.9543443,00:00:00.0426221,00:00:03.8772179, -Repeat 120000 for size 4,OptimisticArrayObjectPool,00:00:00.3072837,00:00:00.2791047,00:00:00.2824113,00:00:00.2763940,00:00:00.0181322,00:00:01.1633259, -Repeat 120000 for size 4,InterlockedArrayObjectPool,00:00:00.2849269,00:00:00.2799949,00:00:00.2987070,00:00:00.2980612,00:00:00.0179067,00:00:01.1795967, -Repeat 48000 for size 10,ConcurrentQueueObjectPool,00:00:00.2049617,00:00:00.1879537,00:00:00.1863383,00:00:00.1791973,00:00:00.0187886,00:00:00.7772396, -Repeat 48000 for size 10,ConcurrentStackObjectPool,00:00:00.2312536,00:00:00.2063132,00:00:00.1973173,00:00:00.2137496,00:00:00.0209974,00:00:00.8696311, -Repeat 48000 for size 10,OptimisticArrayObjectPool,00:00:00.1948958,00:00:00.1866085,00:00:00.1851642,00:00:00.1743788,00:00:00.0212318,00:00:00.7622791, -Repeat 48000 for size 10,InterlockedArrayObjectPool,00:00:00.2025915,00:00:00.1947096,00:00:00.1832321,00:00:00.1855078,00:00:00.0213919,00:00:00.7874329, -Repeat 14400 for size 50,ConcurrentQueueObjectPool,00:00:00.1153192,00:00:00.0930024,00:00:00.0847000,00:00:00.0976083,00:00:00.0225625,00:00:00.4131924, -Repeat 14400 for size 50,ConcurrentStackObjectPool,00:00:00.1210192,00:00:00.0938168,00:00:00.0859916,00:00:00.1097679,00:00:00.0232161,00:00:00.4338116, -Repeat 14400 for size 50,OptimisticArrayObjectPool,00:00:00.1162956,00:00:00.1051234,00:00:00.0809849,00:00:00.0920824,00:00:00.0592899,00:00:00.4537762, -Repeat 14400 for size 50,InterlockedArrayObjectPool,00:00:00.1351777,00:00:00.1015328,00:00:00.0834721,00:00:00.1115662,00:00:00.0940603,00:00:00.5258091, -Repeat 9600 for size 100,ConcurrentQueueObjectPool,00:00:00.1223762,00:00:00.0972226,00:00:00.0758479,00:00:00.0921301,00:00:00.0327353,00:00:00.4203121, -Repeat 9600 for size 100,ConcurrentStackObjectPool,00:00:00.1517036,00:00:00.2118948,00:00:00.1054921,00:00:00.1368268,00:00:00.0473453,00:00:00.6532626, -Repeat 9600 for size 100,OptimisticArrayObjectPool,00:00:00.1073093,00:00:00.1045937,00:00:00.0705415,00:00:00.0824673,00:00:00.0956365,00:00:00.4605483, -Repeat 9600 for size 100,InterlockedArrayObjectPool,00:00:00.1494063,00:00:00.1077739,00:00:00.0746742,00:00:00.1128479,00:00:00.2238334,00:00:00.6685357, -Repeat 7680 for size 250,ConcurrentQueueObjectPool,00:00:00.2123888,00:00:00.1725807,00:00:00.1277665,00:00:00.1629961,00:00:00.0699733,00:00:00.7457054, -Repeat 7680 for size 250,ConcurrentStackObjectPool,00:00:00.7008323,00:00:03.4680108,00:00:00.7312092,00:00:00.7532056,00:00:00.2733349,00:00:05.9265928, -Repeat 7680 for size 250,OptimisticArrayObjectPool,00:00:00.2225184,00:00:00.2290041,00:00:00.1110785,00:00:00.1400907,00:00:00.3309545,00:00:01.0336462, -Repeat 7680 for size 250,InterlockedArrayObjectPool,00:00:00.4984438,00:00:00.2336851,00:00:00.1059643,00:00:00.2670714,00:00:00.8555392,00:00:01.9607038, -:51.4640465,00:01:47.3685819, +Repeat 1200000 for size 4,ConcurrentQueueObjectPool,00:00:04.3234623,00:00:04.2859159,00:00:04.0856252,00:00:04.1629271,00:00:00.2383071,00:00:17.0962376, +Repeat 1200000 for size 4,ConcurrentStackObjectPool,00:00:03.4181177,00:00:03.1483734,00:00:03.0326640,00:00:03.2275135,00:00:00.2194885,00:00:13.0461571, +Repeat 1200000 for size 4,OptimisticArrayObjectPool,00:00:02.9567086,00:00:02.9408997,00:00:02.8979050,00:00:02.9429304,00:00:00.1781477,00:00:11.9165914, +Repeat 1200000 for size 4,InterlockedArrayObjectPool,00:00:02.8507860,00:00:02.8378750,00:00:02.8065261,00:00:02.7729287,00:00:00.1748178,00:00:11.4429336, +Repeat 480000 for size 10,ConcurrentQueueObjectPool,00:00:01.9110391,00:00:01.7929113,00:00:01.7431428,00:00:01.7539962,00:00:00.1864032,00:00:07.3874926, +Repeat 480000 for size 10,ConcurrentStackObjectPool,00:00:02.2080509,00:00:01.9085663,00:00:01.7999023,00:00:02.0333784,00:00:00.2014246,00:00:08.1513225, +Repeat 480000 for size 10,OptimisticArrayObjectPool,00:00:01.9464026,00:00:01.8209826,00:00:01.7283101,00:00:01.7703791,00:00:00.2067250,00:00:07.4727994, +Repeat 480000 for size 10,InterlockedArrayObjectPool,00:00:01.9864203,00:00:01.8975615,00:00:01.7755688,00:00:01.8424227,00:00:00.2128414,00:00:07.7148147, +Repeat 144000 for size 50,ConcurrentQueueObjectPool,00:00:01.0865387,00:00:00.9103388,00:00:00.7864308,00:00:00.8607428,00:00:00.2207286,00:00:03.8647797, +Repeat 144000 for size 50,ConcurrentStackObjectPool,00:00:01.2035505,00:00:00.9058416,00:00:00.8623919,00:00:01.0519578,00:00:00.2300882,00:00:04.2538300, +Repeat 144000 for size 50,OptimisticArrayObjectPool,00:00:01.0794476,00:00:00.9707866,00:00:00.7703522,00:00:00.8925395,00:00:00.5686048,00:00:04.2817307, +Repeat 144000 for size 50,InterlockedArrayObjectPool,00:00:01.2918612,00:00:00.9923932,00:00:00.7856951,00:00:01.0361033,00:00:00.8871188,00:00:04.9931716, +Repeat 96000 for size 100,ConcurrentQueueObjectPool,00:00:01.1936583,00:00:00.9884302,00:00:00.7937749,00:00:00.9104213,00:00:00.3325926,00:00:04.2188773, +Repeat 96000 for size 100,ConcurrentStackObjectPool,00:00:01.2954728,00:00:01.5919051,00:00:00.8887785,00:00:01.1267305,00:00:00.3910861,00:00:05.2939730, +Repeat 96000 for size 100,OptimisticArrayObjectPool,00:00:01.0586862,00:00:01.0613279,00:00:00.7160126,00:00:00.8147263,00:00:00.9588449,00:00:04.6095979, +Repeat 96000 for size 100,InterlockedArrayObjectPool,00:00:01.5202602,00:00:01.0914433,00:00:00.7177933,00:00:01.1267538,00:00:02.1815610,00:00:06.6378116, +Repeat 76800 for size 250,ConcurrentQueueObjectPool,00:00:02.1343720,00:00:01.7012785,00:00:01.2752111,00:00:01.6435934,00:00:00.6997324,00:00:07.4541874, +Repeat 76800 for size 250,ConcurrentStackObjectPool,00:00:06.2066697,00:00:27.8900516,00:00:06.5621678,00:00:06.4422102,00:00:02.4804319,00:00:49.5815312, +Repeat 76800 for size 250,OptimisticArrayObjectPool,00:00:02.0446809,00:00:02.2047801,00:00:01.0930008,00:00:01.3709434,00:00:03.2508715,00:00:09.9642767, +Repeat 76800 for size 250,InterlockedArrayObjectPool,00:00:05.1034766,00:00:02.3508106,00:00:01.0738597,00:00:02.7216389,00:00:08.5749945,00:00:19.8247803, +.3685819, , epeat 51200 for size 250,OptimisticArrayObjectPool,00:00:00.7651948,00:00:01.3195712,00:00:01.3758641,00:00:00.6837150,00:00:01.0134658,00:00:01.9616149,00:00:07.1194258, Repeat 51200 for size 250,InterlockedArrayObjectPool,00:00:00.8386708,00:00:02.2800174,00:00:01.4244575,00:00:00.6435472,00:00:01.6845873,00:00:05.4327218,00:00:12.3040020, diff --git a/benchmarking/BenchmarkResult.txt b/benchmarking/BenchmarkResult.txt index c2fa178..1c5c37a 100644 --- a/benchmarking/BenchmarkResult.txt +++ b/benchmarking/BenchmarkResult.txt @@ -1,180 +1,180 @@ -Repeat 120000 for size 4 +Repeat 1200000 for size 4 ------------------------------------ ConcurrentQueueObjectPool............................... -00:00:00.4328809 Give To (In Parallel) -00:00:00.4157680 Mixed 90%-Take/10%-Give (In Parallel) -00:00:00.4065244 Mixed 50%-Take/50%-Give (In Parallel) -00:00:00.4045626 Mixed 10%-Take/90%-Give (In Parallel) -00:00:00.0271763 Empty Pool (.TryTake()) -00:00:01.6869122 TOTAL +00:00:04.3234623 Give To (In Parallel) +00:00:04.2859159 Mixed 90%-Take/10%-Give (In Parallel) +00:00:04.0856252 Mixed 50%-Take/50%-Give (In Parallel) +00:00:04.1629271 Mixed 10%-Take/90%-Give (In Parallel) +00:00:00.2383071 Empty Pool (.TryTake()) +00:00:17.0962376 TOTAL ConcurrentStackObjectPool............................... -00:00:01.0126419 Give To (In Parallel) -00:00:00.9358291 Mixed 90%-Take/10%-Give (In Parallel) -00:00:00.9317805 Mixed 50%-Take/50%-Give (In Parallel) -00:00:00.9543443 Mixed 10%-Take/90%-Give (In Parallel) -00:00:00.0426221 Empty Pool (.TryTake()) -00:00:03.8772179 TOTAL +00:00:03.4181177 Give To (In Parallel) +00:00:03.1483734 Mixed 90%-Take/10%-Give (In Parallel) +00:00:03.0326640 Mixed 50%-Take/50%-Give (In Parallel) +00:00:03.2275135 Mixed 10%-Take/90%-Give (In Parallel) +00:00:00.2194885 Empty Pool (.TryTake()) +00:00:13.0461571 TOTAL OptimisticArrayObjectPool............................... -00:00:00.3072837 Give To (In Parallel) -00:00:00.2791047 Mixed 90%-Take/10%-Give (In Parallel) -00:00:00.2824113 Mixed 50%-Take/50%-Give (In Parallel) -00:00:00.2763940 Mixed 10%-Take/90%-Give (In Parallel) -00:00:00.0181322 Empty Pool (.TryTake()) -00:00:01.1633259 TOTAL +00:00:02.9567086 Give To (In Parallel) +00:00:02.9408997 Mixed 90%-Take/10%-Give (In Parallel) +00:00:02.8979050 Mixed 50%-Take/50%-Give (In Parallel) +00:00:02.9429304 Mixed 10%-Take/90%-Give (In Parallel) +00:00:00.1781477 Empty Pool (.TryTake()) +00:00:11.9165914 TOTAL InterlockedArrayObjectPool.............................. -00:00:00.2849269 Give To (In Parallel) -00:00:00.2799949 Mixed 90%-Take/10%-Give (In Parallel) -00:00:00.2987070 Mixed 50%-Take/50%-Give (In Parallel) -00:00:00.2980612 Mixed 10%-Take/90%-Give (In Parallel) -00:00:00.0179067 Empty Pool (.TryTake()) -00:00:01.1795967 TOTAL +00:00:02.8507860 Give To (In Parallel) +00:00:02.8378750 Mixed 90%-Take/10%-Give (In Parallel) +00:00:02.8065261 Mixed 50%-Take/50%-Give (In Parallel) +00:00:02.7729287 Mixed 10%-Take/90%-Give (In Parallel) +00:00:00.1748178 Empty Pool (.TryTake()) +00:00:11.4429336 TOTAL -Repeat 48000 for size 10 +Repeat 480000 for size 10 ------------------------------------ ConcurrentQueueObjectPool............................... -00:00:00.2049617 Give To (In Parallel) -00:00:00.1879537 Mixed 90%-Take/10%-Give (In Parallel) -00:00:00.1863383 Mixed 50%-Take/50%-Give (In Parallel) -00:00:00.1791973 Mixed 10%-Take/90%-Give (In Parallel) -00:00:00.0187886 Empty Pool (.TryTake()) -00:00:00.7772396 TOTAL +00:00:01.9110391 Give To (In Parallel) +00:00:01.7929113 Mixed 90%-Take/10%-Give (In Parallel) +00:00:01.7431428 Mixed 50%-Take/50%-Give (In Parallel) +00:00:01.7539962 Mixed 10%-Take/90%-Give (In Parallel) +00:00:00.1864032 Empty Pool (.TryTake()) +00:00:07.3874926 TOTAL ConcurrentStackObjectPool............................... -00:00:00.2312536 Give To (In Parallel) -00:00:00.2063132 Mixed 90%-Take/10%-Give (In Parallel) -00:00:00.1973173 Mixed 50%-Take/50%-Give (In Parallel) -00:00:00.2137496 Mixed 10%-Take/90%-Give (In Parallel) -00:00:00.0209974 Empty Pool (.TryTake()) -00:00:00.8696311 TOTAL +00:00:02.2080509 Give To (In Parallel) +00:00:01.9085663 Mixed 90%-Take/10%-Give (In Parallel) +00:00:01.7999023 Mixed 50%-Take/50%-Give (In Parallel) +00:00:02.0333784 Mixed 10%-Take/90%-Give (In Parallel) +00:00:00.2014246 Empty Pool (.TryTake()) +00:00:08.1513225 TOTAL OptimisticArrayObjectPool............................... -00:00:00.1948958 Give To (In Parallel) -00:00:00.1866085 Mixed 90%-Take/10%-Give (In Parallel) -00:00:00.1851642 Mixed 50%-Take/50%-Give (In Parallel) -00:00:00.1743788 Mixed 10%-Take/90%-Give (In Parallel) -00:00:00.0212318 Empty Pool (.TryTake()) -00:00:00.7622791 TOTAL +00:00:01.9464026 Give To (In Parallel) +00:00:01.8209826 Mixed 90%-Take/10%-Give (In Parallel) +00:00:01.7283101 Mixed 50%-Take/50%-Give (In Parallel) +00:00:01.7703791 Mixed 10%-Take/90%-Give (In Parallel) +00:00:00.2067250 Empty Pool (.TryTake()) +00:00:07.4727994 TOTAL InterlockedArrayObjectPool.............................. -00:00:00.2025915 Give To (In Parallel) -00:00:00.1947096 Mixed 90%-Take/10%-Give (In Parallel) -00:00:00.1832321 Mixed 50%-Take/50%-Give (In Parallel) -00:00:00.1855078 Mixed 10%-Take/90%-Give (In Parallel) -00:00:00.0213919 Empty Pool (.TryTake()) -00:00:00.7874329 TOTAL +00:00:01.9864203 Give To (In Parallel) +00:00:01.8975615 Mixed 90%-Take/10%-Give (In Parallel) +00:00:01.7755688 Mixed 50%-Take/50%-Give (In Parallel) +00:00:01.8424227 Mixed 10%-Take/90%-Give (In Parallel) +00:00:00.2128414 Empty Pool (.TryTake()) +00:00:07.7148147 TOTAL -Repeat 14400 for size 50 +Repeat 144000 for size 50 ------------------------------------ ConcurrentQueueObjectPool............................... -00:00:00.1153192 Give To (In Parallel) -00:00:00.0930024 Mixed 90%-Take/10%-Give (In Parallel) -00:00:00.0847000 Mixed 50%-Take/50%-Give (In Parallel) -00:00:00.0976083 Mixed 10%-Take/90%-Give (In Parallel) -00:00:00.0225625 Empty Pool (.TryTake()) -00:00:00.4131924 TOTAL +00:00:01.0865387 Give To (In Parallel) +00:00:00.9103388 Mixed 90%-Take/10%-Give (In Parallel) +00:00:00.7864308 Mixed 50%-Take/50%-Give (In Parallel) +00:00:00.8607428 Mixed 10%-Take/90%-Give (In Parallel) +00:00:00.2207286 Empty Pool (.TryTake()) +00:00:03.8647797 TOTAL ConcurrentStackObjectPool............................... -00:00:00.1210192 Give To (In Parallel) -00:00:00.0938168 Mixed 90%-Take/10%-Give (In Parallel) -00:00:00.0859916 Mixed 50%-Take/50%-Give (In Parallel) -00:00:00.1097679 Mixed 10%-Take/90%-Give (In Parallel) -00:00:00.0232161 Empty Pool (.TryTake()) -00:00:00.4338116 TOTAL +00:00:01.2035505 Give To (In Parallel) +00:00:00.9058416 Mixed 90%-Take/10%-Give (In Parallel) +00:00:00.8623919 Mixed 50%-Take/50%-Give (In Parallel) +00:00:01.0519578 Mixed 10%-Take/90%-Give (In Parallel) +00:00:00.2300882 Empty Pool (.TryTake()) +00:00:04.2538300 TOTAL OptimisticArrayObjectPool............................... -00:00:00.1162956 Give To (In Parallel) -00:00:00.1051234 Mixed 90%-Take/10%-Give (In Parallel) -00:00:00.0809849 Mixed 50%-Take/50%-Give (In Parallel) -00:00:00.0920824 Mixed 10%-Take/90%-Give (In Parallel) -00:00:00.0592899 Empty Pool (.TryTake()) -00:00:00.4537762 TOTAL +00:00:01.0794476 Give To (In Parallel) +00:00:00.9707866 Mixed 90%-Take/10%-Give (In Parallel) +00:00:00.7703522 Mixed 50%-Take/50%-Give (In Parallel) +00:00:00.8925395 Mixed 10%-Take/90%-Give (In Parallel) +00:00:00.5686048 Empty Pool (.TryTake()) +00:00:04.2817307 TOTAL InterlockedArrayObjectPool.............................. -00:00:00.1351777 Give To (In Parallel) -00:00:00.1015328 Mixed 90%-Take/10%-Give (In Parallel) -00:00:00.0834721 Mixed 50%-Take/50%-Give (In Parallel) -00:00:00.1115662 Mixed 10%-Take/90%-Give (In Parallel) -00:00:00.0940603 Empty Pool (.TryTake()) -00:00:00.5258091 TOTAL +00:00:01.2918612 Give To (In Parallel) +00:00:00.9923932 Mixed 90%-Take/10%-Give (In Parallel) +00:00:00.7856951 Mixed 50%-Take/50%-Give (In Parallel) +00:00:01.0361033 Mixed 10%-Take/90%-Give (In Parallel) +00:00:00.8871188 Empty Pool (.TryTake()) +00:00:04.9931716 TOTAL -Repeat 9600 for size 100 +Repeat 96000 for size 100 ------------------------------------ ConcurrentQueueObjectPool............................... -00:00:00.1223762 Give To (In Parallel) -00:00:00.0972226 Mixed 90%-Take/10%-Give (In Parallel) -00:00:00.0758479 Mixed 50%-Take/50%-Give (In Parallel) -00:00:00.0921301 Mixed 10%-Take/90%-Give (In Parallel) -00:00:00.0327353 Empty Pool (.TryTake()) -00:00:00.4203121 TOTAL +00:00:01.1936583 Give To (In Parallel) +00:00:00.9884302 Mixed 90%-Take/10%-Give (In Parallel) +00:00:00.7937749 Mixed 50%-Take/50%-Give (In Parallel) +00:00:00.9104213 Mixed 10%-Take/90%-Give (In Parallel) +00:00:00.3325926 Empty Pool (.TryTake()) +00:00:04.2188773 TOTAL ConcurrentStackObjectPool............................... -00:00:00.1517036 Give To (In Parallel) -00:00:00.2118948 Mixed 90%-Take/10%-Give (In Parallel) -00:00:00.1054921 Mixed 50%-Take/50%-Give (In Parallel) -00:00:00.1368268 Mixed 10%-Take/90%-Give (In Parallel) -00:00:00.0473453 Empty Pool (.TryTake()) -00:00:00.6532626 TOTAL +00:00:01.2954728 Give To (In Parallel) +00:00:01.5919051 Mixed 90%-Take/10%-Give (In Parallel) +00:00:00.8887785 Mixed 50%-Take/50%-Give (In Parallel) +00:00:01.1267305 Mixed 10%-Take/90%-Give (In Parallel) +00:00:00.3910861 Empty Pool (.TryTake()) +00:00:05.2939730 TOTAL OptimisticArrayObjectPool............................... -00:00:00.1073093 Give To (In Parallel) -00:00:00.1045937 Mixed 90%-Take/10%-Give (In Parallel) -00:00:00.0705415 Mixed 50%-Take/50%-Give (In Parallel) -00:00:00.0824673 Mixed 10%-Take/90%-Give (In Parallel) -00:00:00.0956365 Empty Pool (.TryTake()) -00:00:00.4605483 TOTAL +00:00:01.0586862 Give To (In Parallel) +00:00:01.0613279 Mixed 90%-Take/10%-Give (In Parallel) +00:00:00.7160126 Mixed 50%-Take/50%-Give (In Parallel) +00:00:00.8147263 Mixed 10%-Take/90%-Give (In Parallel) +00:00:00.9588449 Empty Pool (.TryTake()) +00:00:04.6095979 TOTAL InterlockedArrayObjectPool.............................. -00:00:00.1494063 Give To (In Parallel) -00:00:00.1077739 Mixed 90%-Take/10%-Give (In Parallel) -00:00:00.0746742 Mixed 50%-Take/50%-Give (In Parallel) -00:00:00.1128479 Mixed 10%-Take/90%-Give (In Parallel) -00:00:00.2238334 Empty Pool (.TryTake()) -00:00:00.6685357 TOTAL +00:00:01.5202602 Give To (In Parallel) +00:00:01.0914433 Mixed 90%-Take/10%-Give (In Parallel) +00:00:00.7177933 Mixed 50%-Take/50%-Give (In Parallel) +00:00:01.1267538 Mixed 10%-Take/90%-Give (In Parallel) +00:00:02.1815610 Empty Pool (.TryTake()) +00:00:06.6378116 TOTAL -Repeat 7680 for size 250 +Repeat 76800 for size 250 ------------------------------------ ConcurrentQueueObjectPool............................... -00:00:00.2123888 Give To (In Parallel) -00:00:00.1725807 Mixed 90%-Take/10%-Give (In Parallel) -00:00:00.1277665 Mixed 50%-Take/50%-Give (In Parallel) -00:00:00.1629961 Mixed 10%-Take/90%-Give (In Parallel) -00:00:00.0699733 Empty Pool (.TryTake()) -00:00:00.7457054 TOTAL +00:00:02.1343720 Give To (In Parallel) +00:00:01.7012785 Mixed 90%-Take/10%-Give (In Parallel) +00:00:01.2752111 Mixed 50%-Take/50%-Give (In Parallel) +00:00:01.6435934 Mixed 10%-Take/90%-Give (In Parallel) +00:00:00.6997324 Empty Pool (.TryTake()) +00:00:07.4541874 TOTAL ConcurrentStackObjectPool............................... -00:00:00.7008323 Give To (In Parallel) -00:00:03.4680108 Mixed 90%-Take/10%-Give (In Parallel) -00:00:00.7312092 Mixed 50%-Take/50%-Give (In Parallel) -00:00:00.7532056 Mixed 10%-Take/90%-Give (In Parallel) -00:00:00.2733349 Empty Pool (.TryTake()) -00:00:05.9265928 TOTAL +00:00:06.2066697 Give To (In Parallel) +00:00:27.8900516 Mixed 90%-Take/10%-Give (In Parallel) +00:00:06.5621678 Mixed 50%-Take/50%-Give (In Parallel) +00:00:06.4422102 Mixed 10%-Take/90%-Give (In Parallel) +00:00:02.4804319 Empty Pool (.TryTake()) +00:00:49.5815312 TOTAL OptimisticArrayObjectPool............................... -00:00:00.2225184 Give To (In Parallel) -00:00:00.2290041 Mixed 90%-Take/10%-Give (In Parallel) -00:00:00.1110785 Mixed 50%-Take/50%-Give (In Parallel) -00:00:00.1400907 Mixed 10%-Take/90%-Give (In Parallel) -00:00:00.3309545 Empty Pool (.TryTake()) -00:00:01.0336462 TOTAL +00:00:02.0446809 Give To (In Parallel) +00:00:02.2047801 Mixed 90%-Take/10%-Give (In Parallel) +00:00:01.0930008 Mixed 50%-Take/50%-Give (In Parallel) +00:00:01.3709434 Mixed 10%-Take/90%-Give (In Parallel) +00:00:03.2508715 Empty Pool (.TryTake()) +00:00:09.9642767 TOTAL InterlockedArrayObjectPool.............................. -00:00:00.4984438 Give To (In Parallel) -00:00:00.2336851 Mixed 90%-Take/10%-Give (In Parallel) -00:00:00.1059643 Mixed 50%-Take/50%-Give (In Parallel) -00:00:00.2670714 Mixed 10%-Take/90%-Give (In Parallel) -00:00:00.8555392 Empty Pool (.TryTake()) -00:00:01.9607038 TOTAL +00:00:05.1034766 Give To (In Parallel) +00:00:02.3508106 Mixed 90%-Take/10%-Give (In Parallel) +00:00:01.0738597 Mixed 50%-Take/50%-Give (In Parallel) +00:00:02.7216389 Mixed 10%-Take/90%-Give (In Parallel) +00:00:08.5749945 Empty Pool (.TryTake()) +00:00:19.8247803 TOTAL diff --git a/benchmarking/Program.cs b/benchmarking/Program.cs index 380e438..d9c582e 100644 --- a/benchmarking/Program.cs +++ b/benchmarking/Program.cs @@ -28,14 +28,14 @@ static void Main() //report.AddBenchmark("QueueObjectPool", // Note, that this one isn't far off from the following in peformance. // count => () => QueueObjectPool.Create((int)count * 2)); - report.AddBenchmark("ChannelObjectPool", - count => () => ChannelObjectPool.Create((int)count * 2)); + //report.AddBenchmark("ChannelObjectPool", + // count => () => ChannelObjectPool.Create((int)count * 2)); report.AddBenchmark("ConcurrentQueueObjectPool", // Note, that this one isn't far off from the following in peformance, but definitely is faster than LinkedListObjectPool and the rest. count => () => ConcurrentQueueObjectPool.Create((int)count * 2)); - report.AddBenchmark("ConcurrentStackObjectPool", // Note, that this one isn't far off from the following in peformance, but definitely is faster than LinkedListObjectPool and the rest. - count => () => ConcurrentStackObjectPool.Create((int)count * 2)); + //report.AddBenchmark("ConcurrentStackObjectPool", // Note, that this one isn't far off from the following in peformance, but definitely is faster than LinkedListObjectPool and the rest. + // count => () => ConcurrentStackObjectPool.Create((int)count * 2)); report.AddBenchmark("OptimisticArrayObjectPool", count => () => OptimisticArrayObjectPool.Create((int)count * 2)); diff --git a/source/Open.Disposable.ObjectPools.csproj b/source/Open.Disposable.ObjectPools.csproj index 6b874a1..1e6ef6c 100644 --- a/source/Open.Disposable.ObjectPools.csproj +++ b/source/Open.Disposable.ObjectPools.csproj @@ -31,6 +31,12 @@ Part of the "Open" set of libraries. latest + + + + + + From cd23d57f23efce797fdfbd82a4174108d3421ac0 Mon Sep 17 00:00:00 2001 From: "Oren (electricessence)" Date: Sat, 29 Jun 2019 21:39:54 -0700 Subject: [PATCH 27/83] Upated versions. --- benchmarking/BenchmarkResult.csv | 32 +-- benchmarking/BenchmarkResult.txt | 220 +++++++----------- ...Disposable.ObjectPools.Benchmarking.csproj | 2 +- source/ObjectPoolAutoTrimmer.cs | 2 +- source/Open.Disposable.ObjectPools.csproj | 10 +- source/RecyclerBase.cs | 2 +- .../Open.Disposable.ObjectPools.Tests.csproj | 8 +- 7 files changed, 118 insertions(+), 158 deletions(-) diff --git a/benchmarking/BenchmarkResult.csv b/benchmarking/BenchmarkResult.csv index cac72ce..820219c 100644 --- a/benchmarking/BenchmarkResult.csv +++ b/benchmarking/BenchmarkResult.csv @@ -1,20 +1,20 @@ Batch,Pool Type," To (In Parallel)",d 90%-Take/10%-Give (In Parallel),d 50%-Take/50%-Give (In Parallel),d 10%-Take/90%-Give (In Parallel),y Pool (.TryTake()),L, -Repeat 1200000 for size 4,ConcurrentQueueObjectPool,00:00:04.3234623,00:00:04.2859159,00:00:04.0856252,00:00:04.1629271,00:00:00.2383071,00:00:17.0962376, -Repeat 1200000 for size 4,ConcurrentStackObjectPool,00:00:03.4181177,00:00:03.1483734,00:00:03.0326640,00:00:03.2275135,00:00:00.2194885,00:00:13.0461571, -Repeat 1200000 for size 4,OptimisticArrayObjectPool,00:00:02.9567086,00:00:02.9408997,00:00:02.8979050,00:00:02.9429304,00:00:00.1781477,00:00:11.9165914, -Repeat 1200000 for size 4,InterlockedArrayObjectPool,00:00:02.8507860,00:00:02.8378750,00:00:02.8065261,00:00:02.7729287,00:00:00.1748178,00:00:11.4429336, -Repeat 480000 for size 10,ConcurrentQueueObjectPool,00:00:01.9110391,00:00:01.7929113,00:00:01.7431428,00:00:01.7539962,00:00:00.1864032,00:00:07.3874926, -Repeat 480000 for size 10,ConcurrentStackObjectPool,00:00:02.2080509,00:00:01.9085663,00:00:01.7999023,00:00:02.0333784,00:00:00.2014246,00:00:08.1513225, -Repeat 480000 for size 10,OptimisticArrayObjectPool,00:00:01.9464026,00:00:01.8209826,00:00:01.7283101,00:00:01.7703791,00:00:00.2067250,00:00:07.4727994, -Repeat 480000 for size 10,InterlockedArrayObjectPool,00:00:01.9864203,00:00:01.8975615,00:00:01.7755688,00:00:01.8424227,00:00:00.2128414,00:00:07.7148147, -Repeat 144000 for size 50,ConcurrentQueueObjectPool,00:00:01.0865387,00:00:00.9103388,00:00:00.7864308,00:00:00.8607428,00:00:00.2207286,00:00:03.8647797, -Repeat 144000 for size 50,ConcurrentStackObjectPool,00:00:01.2035505,00:00:00.9058416,00:00:00.8623919,00:00:01.0519578,00:00:00.2300882,00:00:04.2538300, -Repeat 144000 for size 50,OptimisticArrayObjectPool,00:00:01.0794476,00:00:00.9707866,00:00:00.7703522,00:00:00.8925395,00:00:00.5686048,00:00:04.2817307, -Repeat 144000 for size 50,InterlockedArrayObjectPool,00:00:01.2918612,00:00:00.9923932,00:00:00.7856951,00:00:01.0361033,00:00:00.8871188,00:00:04.9931716, -Repeat 96000 for size 100,ConcurrentQueueObjectPool,00:00:01.1936583,00:00:00.9884302,00:00:00.7937749,00:00:00.9104213,00:00:00.3325926,00:00:04.2188773, -Repeat 96000 for size 100,ConcurrentStackObjectPool,00:00:01.2954728,00:00:01.5919051,00:00:00.8887785,00:00:01.1267305,00:00:00.3910861,00:00:05.2939730, -Repeat 96000 for size 100,OptimisticArrayObjectPool,00:00:01.0586862,00:00:01.0613279,00:00:00.7160126,00:00:00.8147263,00:00:00.9588449,00:00:04.6095979, -Repeat 96000 for size 100,InterlockedArrayObjectPool,00:00:01.5202602,00:00:01.0914433,00:00:00.7177933,00:00:01.1267538,00:00:02.1815610,00:00:06.6378116, +Repeat 1200000 for size 4,ConcurrentQueueObjectPool,00:00:02.8137291,00:00:02.7153391,00:00:02.6985097,00:00:02.7410619,00:00:00.1839002,00:00:11.1525400, +Repeat 1200000 for size 4,OptimisticArrayObjectPool,00:00:02.6651904,00:00:02.6475629,00:00:02.6552883,00:00:02.5890153,00:00:00.1745340,00:00:10.7315909, +Repeat 1200000 for size 4,InterlockedArrayObjectPool,00:00:02.5827382,00:00:02.7023234,00:00:02.5996861,00:00:02.6370871,00:00:00.1624972,00:00:10.6843320, +Repeat 480000 for size 10,ConcurrentQueueObjectPool,00:00:01.7226667,00:00:01.6591525,00:00:01.6255627,00:00:01.6121179,00:00:00.1629193,00:00:06.7824191, +Repeat 480000 for size 10,OptimisticArrayObjectPool,00:00:01.7076846,00:00:01.7039172,00:00:01.6884271,00:00:01.6330203,00:00:00.2135140,00:00:06.9465632, +Repeat 480000 for size 10,InterlockedArrayObjectPool,00:00:01.8557304,00:00:01.7516982,00:00:01.6388447,00:00:01.6915551,00:00:00.2162540,00:00:07.1540824, +Repeat 144000 for size 50,ConcurrentQueueObjectPool,00:00:00.9993787,00:00:00.8644147,00:00:00.7644592,00:00:00.8417858,00:00:00.2118096,00:00:03.6818480, +Repeat 144000 for size 50,OptimisticArrayObjectPool,00:00:01.0304016,00:00:00.9726821,00:00:00.7458965,00:00:00.8811575,00:00:00.6318591,00:00:04.2619968, +Repeat 144000 for size 50,InterlockedArrayObjectPool,00:00:01.2025048,00:00:00.9502443,00:00:00.7301001,00:00:00.9604869,00:00:00.9480492,00:00:04.7913853, +Repeat 96000 for size 100,ConcurrentQueueObjectPool,00:00:01.0717994,00:00:00.9417134,00:00:00.7430992,00:00:00.8641968,00:00:00.2921380,00:00:03.9129468, +Repeat 96000 for size 100,OptimisticArrayObjectPool,00:00:00.9926382,00:00:01.0228171,00:00:00.6834145,00:00:00.7896683,00:00:01.0471828,00:00:04.5357209, +Repeat 96000 for size 100,InterlockedArrayObjectPool,00:00:01.4431922,00:00:01.0450868,00:00:00.6856350,00:00:01.0800545,00:00:02.3318448,00:00:06.5858133, +Repeat 76800 for size 250,ConcurrentQueueObjectPool,00:00:01.9927953,00:00:01.6576482,00:00:01.2314163,00:00:01.5604890,00:00:00.6205257,00:00:07.0628745, +Repeat 76800 for size 250,OptimisticArrayObjectPool,00:00:03.0736505,00:00:02.3286546,00:00:01.0713747,00:00:01.3538321,00:00:04.0564585,00:00:11.8839704, +Repeat 76800 for size 250,InterlockedArrayObjectPool,00:00:04.9460361,00:00:02.3117061,00:00:01.0202632,00:00:02.6333260,00:00:07.5373478,00:00:18.4486792, +peat 96000 for size 100,InterlockedArrayObjectPool,00:00:01.5202602,00:00:01.0914433,00:00:00.7177933,00:00:01.1267538,00:00:02.1815610,00:00:06.6378116, Repeat 76800 for size 250,ConcurrentQueueObjectPool,00:00:02.1343720,00:00:01.7012785,00:00:01.2752111,00:00:01.6435934,00:00:00.6997324,00:00:07.4541874, Repeat 76800 for size 250,ConcurrentStackObjectPool,00:00:06.2066697,00:00:27.8900516,00:00:06.5621678,00:00:06.4422102,00:00:02.4804319,00:00:49.5815312, Repeat 76800 for size 250,OptimisticArrayObjectPool,00:00:02.0446809,00:00:02.2047801,00:00:01.0930008,00:00:01.3709434,00:00:03.2508715,00:00:09.9642767, diff --git a/benchmarking/BenchmarkResult.txt b/benchmarking/BenchmarkResult.txt index 1c5c37a..0cdf9e7 100644 --- a/benchmarking/BenchmarkResult.txt +++ b/benchmarking/BenchmarkResult.txt @@ -2,179 +2,139 @@ Repeat 1200000 for size 4 ------------------------------------ ConcurrentQueueObjectPool............................... -00:00:04.3234623 Give To (In Parallel) -00:00:04.2859159 Mixed 90%-Take/10%-Give (In Parallel) -00:00:04.0856252 Mixed 50%-Take/50%-Give (In Parallel) -00:00:04.1629271 Mixed 10%-Take/90%-Give (In Parallel) -00:00:00.2383071 Empty Pool (.TryTake()) -00:00:17.0962376 TOTAL - -ConcurrentStackObjectPool............................... -00:00:03.4181177 Give To (In Parallel) -00:00:03.1483734 Mixed 90%-Take/10%-Give (In Parallel) -00:00:03.0326640 Mixed 50%-Take/50%-Give (In Parallel) -00:00:03.2275135 Mixed 10%-Take/90%-Give (In Parallel) -00:00:00.2194885 Empty Pool (.TryTake()) -00:00:13.0461571 TOTAL +00:00:02.8137291 Give To (In Parallel) +00:00:02.7153391 Mixed 90%-Take/10%-Give (In Parallel) +00:00:02.6985097 Mixed 50%-Take/50%-Give (In Parallel) +00:00:02.7410619 Mixed 10%-Take/90%-Give (In Parallel) +00:00:00.1839002 Empty Pool (.TryTake()) +00:00:11.1525400 TOTAL OptimisticArrayObjectPool............................... -00:00:02.9567086 Give To (In Parallel) -00:00:02.9408997 Mixed 90%-Take/10%-Give (In Parallel) -00:00:02.8979050 Mixed 50%-Take/50%-Give (In Parallel) -00:00:02.9429304 Mixed 10%-Take/90%-Give (In Parallel) -00:00:00.1781477 Empty Pool (.TryTake()) -00:00:11.9165914 TOTAL +00:00:02.6651904 Give To (In Parallel) +00:00:02.6475629 Mixed 90%-Take/10%-Give (In Parallel) +00:00:02.6552883 Mixed 50%-Take/50%-Give (In Parallel) +00:00:02.5890153 Mixed 10%-Take/90%-Give (In Parallel) +00:00:00.1745340 Empty Pool (.TryTake()) +00:00:10.7315909 TOTAL InterlockedArrayObjectPool.............................. -00:00:02.8507860 Give To (In Parallel) -00:00:02.8378750 Mixed 90%-Take/10%-Give (In Parallel) -00:00:02.8065261 Mixed 50%-Take/50%-Give (In Parallel) -00:00:02.7729287 Mixed 10%-Take/90%-Give (In Parallel) -00:00:00.1748178 Empty Pool (.TryTake()) -00:00:11.4429336 TOTAL +00:00:02.5827382 Give To (In Parallel) +00:00:02.7023234 Mixed 90%-Take/10%-Give (In Parallel) +00:00:02.5996861 Mixed 50%-Take/50%-Give (In Parallel) +00:00:02.6370871 Mixed 10%-Take/90%-Give (In Parallel) +00:00:00.1624972 Empty Pool (.TryTake()) +00:00:10.6843320 TOTAL Repeat 480000 for size 10 ------------------------------------ ConcurrentQueueObjectPool............................... -00:00:01.9110391 Give To (In Parallel) -00:00:01.7929113 Mixed 90%-Take/10%-Give (In Parallel) -00:00:01.7431428 Mixed 50%-Take/50%-Give (In Parallel) -00:00:01.7539962 Mixed 10%-Take/90%-Give (In Parallel) -00:00:00.1864032 Empty Pool (.TryTake()) -00:00:07.3874926 TOTAL - -ConcurrentStackObjectPool............................... -00:00:02.2080509 Give To (In Parallel) -00:00:01.9085663 Mixed 90%-Take/10%-Give (In Parallel) -00:00:01.7999023 Mixed 50%-Take/50%-Give (In Parallel) -00:00:02.0333784 Mixed 10%-Take/90%-Give (In Parallel) -00:00:00.2014246 Empty Pool (.TryTake()) -00:00:08.1513225 TOTAL +00:00:01.7226667 Give To (In Parallel) +00:00:01.6591525 Mixed 90%-Take/10%-Give (In Parallel) +00:00:01.6255627 Mixed 50%-Take/50%-Give (In Parallel) +00:00:01.6121179 Mixed 10%-Take/90%-Give (In Parallel) +00:00:00.1629193 Empty Pool (.TryTake()) +00:00:06.7824191 TOTAL OptimisticArrayObjectPool............................... -00:00:01.9464026 Give To (In Parallel) -00:00:01.8209826 Mixed 90%-Take/10%-Give (In Parallel) -00:00:01.7283101 Mixed 50%-Take/50%-Give (In Parallel) -00:00:01.7703791 Mixed 10%-Take/90%-Give (In Parallel) -00:00:00.2067250 Empty Pool (.TryTake()) -00:00:07.4727994 TOTAL +00:00:01.7076846 Give To (In Parallel) +00:00:01.7039172 Mixed 90%-Take/10%-Give (In Parallel) +00:00:01.6884271 Mixed 50%-Take/50%-Give (In Parallel) +00:00:01.6330203 Mixed 10%-Take/90%-Give (In Parallel) +00:00:00.2135140 Empty Pool (.TryTake()) +00:00:06.9465632 TOTAL InterlockedArrayObjectPool.............................. -00:00:01.9864203 Give To (In Parallel) -00:00:01.8975615 Mixed 90%-Take/10%-Give (In Parallel) -00:00:01.7755688 Mixed 50%-Take/50%-Give (In Parallel) -00:00:01.8424227 Mixed 10%-Take/90%-Give (In Parallel) -00:00:00.2128414 Empty Pool (.TryTake()) -00:00:07.7148147 TOTAL +00:00:01.8557304 Give To (In Parallel) +00:00:01.7516982 Mixed 90%-Take/10%-Give (In Parallel) +00:00:01.6388447 Mixed 50%-Take/50%-Give (In Parallel) +00:00:01.6915551 Mixed 10%-Take/90%-Give (In Parallel) +00:00:00.2162540 Empty Pool (.TryTake()) +00:00:07.1540824 TOTAL Repeat 144000 for size 50 ------------------------------------ ConcurrentQueueObjectPool............................... -00:00:01.0865387 Give To (In Parallel) -00:00:00.9103388 Mixed 90%-Take/10%-Give (In Parallel) -00:00:00.7864308 Mixed 50%-Take/50%-Give (In Parallel) -00:00:00.8607428 Mixed 10%-Take/90%-Give (In Parallel) -00:00:00.2207286 Empty Pool (.TryTake()) -00:00:03.8647797 TOTAL - -ConcurrentStackObjectPool............................... -00:00:01.2035505 Give To (In Parallel) -00:00:00.9058416 Mixed 90%-Take/10%-Give (In Parallel) -00:00:00.8623919 Mixed 50%-Take/50%-Give (In Parallel) -00:00:01.0519578 Mixed 10%-Take/90%-Give (In Parallel) -00:00:00.2300882 Empty Pool (.TryTake()) -00:00:04.2538300 TOTAL +00:00:00.9993787 Give To (In Parallel) +00:00:00.8644147 Mixed 90%-Take/10%-Give (In Parallel) +00:00:00.7644592 Mixed 50%-Take/50%-Give (In Parallel) +00:00:00.8417858 Mixed 10%-Take/90%-Give (In Parallel) +00:00:00.2118096 Empty Pool (.TryTake()) +00:00:03.6818480 TOTAL OptimisticArrayObjectPool............................... -00:00:01.0794476 Give To (In Parallel) -00:00:00.9707866 Mixed 90%-Take/10%-Give (In Parallel) -00:00:00.7703522 Mixed 50%-Take/50%-Give (In Parallel) -00:00:00.8925395 Mixed 10%-Take/90%-Give (In Parallel) -00:00:00.5686048 Empty Pool (.TryTake()) -00:00:04.2817307 TOTAL +00:00:01.0304016 Give To (In Parallel) +00:00:00.9726821 Mixed 90%-Take/10%-Give (In Parallel) +00:00:00.7458965 Mixed 50%-Take/50%-Give (In Parallel) +00:00:00.8811575 Mixed 10%-Take/90%-Give (In Parallel) +00:00:00.6318591 Empty Pool (.TryTake()) +00:00:04.2619968 TOTAL InterlockedArrayObjectPool.............................. -00:00:01.2918612 Give To (In Parallel) -00:00:00.9923932 Mixed 90%-Take/10%-Give (In Parallel) -00:00:00.7856951 Mixed 50%-Take/50%-Give (In Parallel) -00:00:01.0361033 Mixed 10%-Take/90%-Give (In Parallel) -00:00:00.8871188 Empty Pool (.TryTake()) -00:00:04.9931716 TOTAL +00:00:01.2025048 Give To (In Parallel) +00:00:00.9502443 Mixed 90%-Take/10%-Give (In Parallel) +00:00:00.7301001 Mixed 50%-Take/50%-Give (In Parallel) +00:00:00.9604869 Mixed 10%-Take/90%-Give (In Parallel) +00:00:00.9480492 Empty Pool (.TryTake()) +00:00:04.7913853 TOTAL Repeat 96000 for size 100 ------------------------------------ ConcurrentQueueObjectPool............................... -00:00:01.1936583 Give To (In Parallel) -00:00:00.9884302 Mixed 90%-Take/10%-Give (In Parallel) -00:00:00.7937749 Mixed 50%-Take/50%-Give (In Parallel) -00:00:00.9104213 Mixed 10%-Take/90%-Give (In Parallel) -00:00:00.3325926 Empty Pool (.TryTake()) -00:00:04.2188773 TOTAL - -ConcurrentStackObjectPool............................... -00:00:01.2954728 Give To (In Parallel) -00:00:01.5919051 Mixed 90%-Take/10%-Give (In Parallel) -00:00:00.8887785 Mixed 50%-Take/50%-Give (In Parallel) -00:00:01.1267305 Mixed 10%-Take/90%-Give (In Parallel) -00:00:00.3910861 Empty Pool (.TryTake()) -00:00:05.2939730 TOTAL +00:00:01.0717994 Give To (In Parallel) +00:00:00.9417134 Mixed 90%-Take/10%-Give (In Parallel) +00:00:00.7430992 Mixed 50%-Take/50%-Give (In Parallel) +00:00:00.8641968 Mixed 10%-Take/90%-Give (In Parallel) +00:00:00.2921380 Empty Pool (.TryTake()) +00:00:03.9129468 TOTAL OptimisticArrayObjectPool............................... -00:00:01.0586862 Give To (In Parallel) -00:00:01.0613279 Mixed 90%-Take/10%-Give (In Parallel) -00:00:00.7160126 Mixed 50%-Take/50%-Give (In Parallel) -00:00:00.8147263 Mixed 10%-Take/90%-Give (In Parallel) -00:00:00.9588449 Empty Pool (.TryTake()) -00:00:04.6095979 TOTAL +00:00:00.9926382 Give To (In Parallel) +00:00:01.0228171 Mixed 90%-Take/10%-Give (In Parallel) +00:00:00.6834145 Mixed 50%-Take/50%-Give (In Parallel) +00:00:00.7896683 Mixed 10%-Take/90%-Give (In Parallel) +00:00:01.0471828 Empty Pool (.TryTake()) +00:00:04.5357209 TOTAL InterlockedArrayObjectPool.............................. -00:00:01.5202602 Give To (In Parallel) -00:00:01.0914433 Mixed 90%-Take/10%-Give (In Parallel) -00:00:00.7177933 Mixed 50%-Take/50%-Give (In Parallel) -00:00:01.1267538 Mixed 10%-Take/90%-Give (In Parallel) -00:00:02.1815610 Empty Pool (.TryTake()) -00:00:06.6378116 TOTAL +00:00:01.4431922 Give To (In Parallel) +00:00:01.0450868 Mixed 90%-Take/10%-Give (In Parallel) +00:00:00.6856350 Mixed 50%-Take/50%-Give (In Parallel) +00:00:01.0800545 Mixed 10%-Take/90%-Give (In Parallel) +00:00:02.3318448 Empty Pool (.TryTake()) +00:00:06.5858133 TOTAL Repeat 76800 for size 250 ------------------------------------ ConcurrentQueueObjectPool............................... -00:00:02.1343720 Give To (In Parallel) -00:00:01.7012785 Mixed 90%-Take/10%-Give (In Parallel) -00:00:01.2752111 Mixed 50%-Take/50%-Give (In Parallel) -00:00:01.6435934 Mixed 10%-Take/90%-Give (In Parallel) -00:00:00.6997324 Empty Pool (.TryTake()) -00:00:07.4541874 TOTAL - -ConcurrentStackObjectPool............................... -00:00:06.2066697 Give To (In Parallel) -00:00:27.8900516 Mixed 90%-Take/10%-Give (In Parallel) -00:00:06.5621678 Mixed 50%-Take/50%-Give (In Parallel) -00:00:06.4422102 Mixed 10%-Take/90%-Give (In Parallel) -00:00:02.4804319 Empty Pool (.TryTake()) -00:00:49.5815312 TOTAL +00:00:01.9927953 Give To (In Parallel) +00:00:01.6576482 Mixed 90%-Take/10%-Give (In Parallel) +00:00:01.2314163 Mixed 50%-Take/50%-Give (In Parallel) +00:00:01.5604890 Mixed 10%-Take/90%-Give (In Parallel) +00:00:00.6205257 Empty Pool (.TryTake()) +00:00:07.0628745 TOTAL OptimisticArrayObjectPool............................... -00:00:02.0446809 Give To (In Parallel) -00:00:02.2047801 Mixed 90%-Take/10%-Give (In Parallel) -00:00:01.0930008 Mixed 50%-Take/50%-Give (In Parallel) -00:00:01.3709434 Mixed 10%-Take/90%-Give (In Parallel) -00:00:03.2508715 Empty Pool (.TryTake()) -00:00:09.9642767 TOTAL +00:00:03.0736505 Give To (In Parallel) +00:00:02.3286546 Mixed 90%-Take/10%-Give (In Parallel) +00:00:01.0713747 Mixed 50%-Take/50%-Give (In Parallel) +00:00:01.3538321 Mixed 10%-Take/90%-Give (In Parallel) +00:00:04.0564585 Empty Pool (.TryTake()) +00:00:11.8839704 TOTAL InterlockedArrayObjectPool.............................. -00:00:05.1034766 Give To (In Parallel) -00:00:02.3508106 Mixed 90%-Take/10%-Give (In Parallel) -00:00:01.0738597 Mixed 50%-Take/50%-Give (In Parallel) -00:00:02.7216389 Mixed 10%-Take/90%-Give (In Parallel) -00:00:08.5749945 Empty Pool (.TryTake()) -00:00:19.8247803 TOTAL +00:00:04.9460361 Give To (In Parallel) +00:00:02.3117061 Mixed 90%-Take/10%-Give (In Parallel) +00:00:01.0202632 Mixed 50%-Take/50%-Give (In Parallel) +00:00:02.6333260 Mixed 10%-Take/90%-Give (In Parallel) +00:00:07.5373478 Empty Pool (.TryTake()) +00:00:18.4486792 TOTAL diff --git a/benchmarking/Open.Disposable.ObjectPools.Benchmarking.csproj b/benchmarking/Open.Disposable.ObjectPools.Benchmarking.csproj index d28050f..523d20a 100644 --- a/benchmarking/Open.Disposable.ObjectPools.Benchmarking.csproj +++ b/benchmarking/Open.Disposable.ObjectPools.Benchmarking.csproj @@ -2,7 +2,7 @@ Exe - netcoreapp2.1 + netcoreapp3.0 Open.Disposable 1.0.0 diff --git a/source/ObjectPoolAutoTrimmer.cs b/source/ObjectPoolAutoTrimmer.cs index 977e53a..e709f4f 100644 --- a/source/ObjectPoolAutoTrimmer.cs +++ b/source/ObjectPoolAutoTrimmer.cs @@ -41,7 +41,7 @@ public ObjectPoolAutoTrimmer( if (pool is DisposableBase d) { - if (d.IsDisposed) throw new ArgumentException("Cannot trim for an object pool that is already disposed."); + if (d.WasDisposed) throw new ArgumentException("Cannot trim for an object pool that is already disposed."); d.BeforeDispose += Pool_BeforeDispose; } diff --git a/source/Open.Disposable.ObjectPools.csproj b/source/Open.Disposable.ObjectPools.csproj index 1e6ef6c..26e7195 100644 --- a/source/Open.Disposable.ObjectPools.csproj +++ b/source/Open.Disposable.ObjectPools.csproj @@ -1,7 +1,7 @@  - netstandard2.0 + netstandard2.0;netstandard2.1 Open.Disposable A set of variations on ObjectPool implementations with differing underlying collections. @@ -16,9 +16,9 @@ Part of the "Open" set of libraries. git objectpool, dotnet, dotnetcore, cs, idisposable, threadsafe, thread-safe True - 2.1.3 - 2.1.3.0 - 2.1.3.0 + 2.2.0 + 2.2.0.0 + 2.2.0.0 MIT @@ -42,7 +42,7 @@ Part of the "Open" set of libraries. - + diff --git a/source/RecyclerBase.cs b/source/RecyclerBase.cs index cba5c01..b979727 100644 --- a/source/RecyclerBase.cs +++ b/source/RecyclerBase.cs @@ -23,7 +23,7 @@ protected RecyclerBase( Contract.EndContractBlock(); if (!(target is DisposableBase d)) return; - if (d.IsDisposed) throw new ArgumentException("Cannot recycle for an object pool that is already disposed."); + if (d.WasDisposed) throw new ArgumentException("Cannot recycle for an object pool that is already disposed."); d.BeforeDispose += Pool_BeforeDispose; // Could possibly dispose before this line somewhere... But that's just nasty. :P } diff --git a/tests/Open.Disposable.ObjectPools.Tests.csproj b/tests/Open.Disposable.ObjectPools.Tests.csproj index c080ae2..bd7d99a 100644 --- a/tests/Open.Disposable.ObjectPools.Tests.csproj +++ b/tests/Open.Disposable.ObjectPools.Tests.csproj @@ -1,7 +1,7 @@  - netcoreapp2.1 + netcoreapp3.0 Open.Disposable @@ -14,9 +14,9 @@ - - - + + + From c9e97b08c134ddfadf748107398fe77b3d7bffac Mon Sep 17 00:00:00 2001 From: "Oren (electricessence)" Date: Sun, 30 Jun 2019 11:04:01 -0700 Subject: [PATCH 28/83] Release version. --- source/Open.Disposable.ObjectPools.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/Open.Disposable.ObjectPools.csproj b/source/Open.Disposable.ObjectPools.csproj index 26e7195..5d0b7f4 100644 --- a/source/Open.Disposable.ObjectPools.csproj +++ b/source/Open.Disposable.ObjectPools.csproj @@ -42,7 +42,7 @@ Part of the "Open" set of libraries. - + From a39a3c0eb0d7d0a7fb3cbdf6fbf17751e3d28cac Mon Sep 17 00:00:00 2001 From: "Oren (electricessence)" Date: Tue, 2 Jul 2019 19:03:50 -0700 Subject: [PATCH 29/83] Updates to disposable. --- ...Disposable.ObjectPools.Benchmarking.csproj | 4 +-- source/Array/InterlockedArrayObjectPool.cs | 4 +-- source/GlobalSuppressions.cs | 3 ++ source/ObjectPoolAutoTrimmer.cs | 2 +- source/ObjectPoolBase.cs | 5 +-- source/Open.Disposable.ObjectPools.csproj | 8 ++--- source/Recycler.cs | 6 ++-- source/RecyclerBase.cs | 4 +-- source/TrimmableCollectionObjectPoolBase.cs | 31 +++++++++---------- 9 files changed, 34 insertions(+), 33 deletions(-) diff --git a/benchmarking/Open.Disposable.ObjectPools.Benchmarking.csproj b/benchmarking/Open.Disposable.ObjectPools.Benchmarking.csproj index 523d20a..b6ce4ae 100644 --- a/benchmarking/Open.Disposable.ObjectPools.Benchmarking.csproj +++ b/benchmarking/Open.Disposable.ObjectPools.Benchmarking.csproj @@ -20,8 +20,8 @@ - - + + diff --git a/source/Array/InterlockedArrayObjectPool.cs b/source/Array/InterlockedArrayObjectPool.cs index a628665..ed48d83 100644 --- a/source/Array/InterlockedArrayObjectPool.cs +++ b/source/Array/InterlockedArrayObjectPool.cs @@ -71,9 +71,9 @@ protected override T TryRelease() return null; } - protected override void OnDispose(bool calledExplicitly) + protected override void OnDispose() { - base.OnDispose(calledExplicitly); + base.OnDispose(); Pool = null; MaxStored = 0; } diff --git a/source/GlobalSuppressions.cs b/source/GlobalSuppressions.cs index 922b9de..8e19587 100644 --- a/source/GlobalSuppressions.cs +++ b/source/GlobalSuppressions.cs @@ -6,3 +6,6 @@ [assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Performance", "CA1815:Override equals and operator equals on value types", Justification = "", Scope = "type", Target = "~T:Open.Disposable.ReferenceContainer`1")] [assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Performance", "CA1815:Override equals and operator equals on value types", Justification = "", Scope = "type", Target = "~T:Open.Disposable.DualReferenceContainer`1")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Style", "IDE0044:Add readonly modifier", Justification = "", Scope = "member", Target = "~F:Open.Disposable.ObjectPoolAutoTrimmer._trimmedSize")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Style", "IDE0044:Add readonly modifier", Justification = "", Scope = "member", Target = "~F:Open.Disposable.ObjectPoolAutoTrimmer._trimDelay")] + diff --git a/source/ObjectPoolAutoTrimmer.cs b/source/ObjectPoolAutoTrimmer.cs index e709f4f..b6b44d2 100644 --- a/source/ObjectPoolAutoTrimmer.cs +++ b/source/ObjectPoolAutoTrimmer.cs @@ -77,7 +77,7 @@ protected virtual void TrimInternal() _pool?.TrimTo(_trimmedSize); } - protected override void OnDispose(bool calledExplicitly) + protected override void OnDispose() { DisposeOf(ref _trimmer); diff --git a/source/ObjectPoolBase.cs b/source/ObjectPoolBase.cs index 5555895..bfa84bf 100644 --- a/source/ObjectPoolBase.cs +++ b/source/ObjectPoolBase.cs @@ -122,9 +122,10 @@ protected override void OnBeforeDispose() MaxSize = 0; } - protected override void OnDispose(bool calledExplicitly) + protected override void OnDispose() { - if (!calledExplicitly || OnDiscarded == null) return; + if (OnDiscarded == null) return; + T d; while ((d = TryRelease()) != null) OnDiscarded(d); } diff --git a/source/Open.Disposable.ObjectPools.csproj b/source/Open.Disposable.ObjectPools.csproj index 5d0b7f4..1ff25aa 100644 --- a/source/Open.Disposable.ObjectPools.csproj +++ b/source/Open.Disposable.ObjectPools.csproj @@ -16,9 +16,9 @@ Part of the "Open" set of libraries. git objectpool, dotnet, dotnetcore, cs, idisposable, threadsafe, thread-safe True - 2.2.0 - 2.2.0.0 - 2.2.0.0 + 2.3.0 + 2.3.0.0 + 2.3.0.0 MIT @@ -42,7 +42,7 @@ Part of the "Open" set of libraries. - + diff --git a/source/Recycler.cs b/source/Recycler.cs index 7f4e6c6..175dcac 100644 --- a/source/Recycler.cs +++ b/source/Recycler.cs @@ -44,10 +44,10 @@ public override bool Recycle(T item) protected override void OnCloseRequested() => _bin?.Writer.Complete(); - protected override void OnDispose(bool calledExplicitly) + protected override void OnDispose() { - base.OnDispose(calledExplicitly); - if (!calledExplicitly) return; + base.OnDispose(); + _bin.Writer.TryComplete(); _bin = null; } diff --git a/source/RecyclerBase.cs b/source/RecyclerBase.cs index b979727..074b490 100644 --- a/source/RecyclerBase.cs +++ b/source/RecyclerBase.cs @@ -43,10 +43,10 @@ public Task Close() return Completion; } - protected override void OnDispose(bool calledExplicitly) + protected override void OnDispose() { OnCloseRequested(); - if (calledExplicitly) Target = null; + Target = null; } } diff --git a/source/TrimmableCollectionObjectPoolBase.cs b/source/TrimmableCollectionObjectPoolBase.cs index 57efb7e..0bdc25b 100644 --- a/source/TrimmableCollectionObjectPoolBase.cs +++ b/source/TrimmableCollectionObjectPoolBase.cs @@ -19,10 +19,10 @@ protected TrimmableCollectionObjectPoolBase(TCollection pool, Func factory, A public override int Count => (Pool?.Count ?? 0) + PocketCount; - protected override void OnDispose(bool calledExplicitly) + protected override void OnDispose() { - base.OnDispose(calledExplicitly); // Do not call because the following is more optimized. - if (calledExplicitly) Pool = null; + base.OnDispose(); + Pool = null; } } @@ -34,30 +34,27 @@ public abstract class TrimmableGenericCollectionObjectPoolBase : protected TrimmableGenericCollectionObjectPoolBase(TCollection pool, Func factory, Action recycler, Action disposer, int capacity = DEFAULT_CAPACITY, bool countTrackingEnabled = true) : base(factory, recycler, disposer, capacity, countTrackingEnabled) { - Pool = pool; + Pool = pool ?? throw new ArgumentNullException(nameof(pool)); ; } protected TCollection Pool; public override int Count => Pool?.Count ?? 0; - protected override void OnDispose(bool calledExplicitly) + protected override void OnDispose() { - //base.OnDispose(calledExplicitly); // Do not call because the following is more optimized. + //base.OnDispose(); // Do not call because the following is more optimized. - if (calledExplicitly) - { - var p = Pool; - Pool = null; - - if (OnDiscarded != null) - { - foreach (var item in p) - OnDiscarded(item); - } + var p = Pool; + Pool = null; - p.Clear(); + if (OnDiscarded != null) + { + foreach (var item in p) + OnDiscarded(item); } + + p.Clear(); } } From 41dde49ef1429637df0fe7362a10aac5cdbcf0b8 Mon Sep 17 00:00:00 2001 From: "Oren (electricessence)" Date: Wed, 3 Jul 2019 02:33:39 -0700 Subject: [PATCH 30/83] Refresh. --- source/Open.Disposable.ObjectPools.csproj | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/source/Open.Disposable.ObjectPools.csproj b/source/Open.Disposable.ObjectPools.csproj index 1ff25aa..cd9c28b 100644 --- a/source/Open.Disposable.ObjectPools.csproj +++ b/source/Open.Disposable.ObjectPools.csproj @@ -16,9 +16,9 @@ Part of the "Open" set of libraries. git objectpool, dotnet, dotnetcore, cs, idisposable, threadsafe, thread-safe True - 2.3.0 - 2.3.0.0 - 2.3.0.0 + 2.3.2 + 2.3.2.0 + 2.3.2.0 MIT @@ -42,7 +42,7 @@ Part of the "Open" set of libraries. - + From 0ff9e6813058e1cbcb5e7f0806badd49e9093f0b Mon Sep 17 00:00:00 2001 From: "Oren (electricessence)" Date: Thu, 4 Jul 2019 14:24:13 -0700 Subject: [PATCH 31/83] Updated benchmark reference. --- benchmarking/Open.Disposable.ObjectPools.Benchmarking.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/benchmarking/Open.Disposable.ObjectPools.Benchmarking.csproj b/benchmarking/Open.Disposable.ObjectPools.Benchmarking.csproj index b6ce4ae..334a120 100644 --- a/benchmarking/Open.Disposable.ObjectPools.Benchmarking.csproj +++ b/benchmarking/Open.Disposable.ObjectPools.Benchmarking.csproj @@ -21,7 +21,7 @@ - + From 748012983c028437b08b134510e87aa3407846bf Mon Sep 17 00:00:00 2001 From: "Oren (electricessence)" Date: Thu, 4 Jul 2019 15:18:32 -0700 Subject: [PATCH 32/83] Updated reference. --- source/Open.Disposable.ObjectPools.csproj | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/source/Open.Disposable.ObjectPools.csproj b/source/Open.Disposable.ObjectPools.csproj index cd9c28b..2d55a9b 100644 --- a/source/Open.Disposable.ObjectPools.csproj +++ b/source/Open.Disposable.ObjectPools.csproj @@ -16,9 +16,9 @@ Part of the "Open" set of libraries. git objectpool, dotnet, dotnetcore, cs, idisposable, threadsafe, thread-safe True - 2.3.2 - 2.3.2.0 - 2.3.2.0 + 2.3.3 + 2.3.3.0 + 2.3.3.0 MIT @@ -42,7 +42,7 @@ Part of the "Open" set of libraries. - + From effb9abf0d16232793bb0d75e1fcad97990b7ece Mon Sep 17 00:00:00 2001 From: "Oren (electricessence)" Date: Thu, 19 Dec 2019 01:25:40 -0800 Subject: [PATCH 33/83] Updated with improved nullable support. --- source/.editorconfig | 10 ++ source/Array/InterlockedArrayObjectPool.cs | 9 +- source/Array/OptimisticArrayObjectPool.cs | 3 +- .../Collection/CollectionWrapperObjectPool.cs | 8 +- .../Collection/ConcurrentQueueObjectPool.cs | 4 +- .../Collection/ConcurrentStackObjectPool.cs | 4 +- source/Collection/QueueObjectPool.cs | 4 +- source/Collection/StackObjectPool.cs | 4 +- source/GlobalSuppressions.cs | 11 --- source/IObjectPool.cs | 15 ++- source/IRecycler.cs | 2 +- source/ObjectPoolAutoTrimmer.cs | 3 +- source/ObjectPoolBase.cs | 30 +++--- source/Open.Disposable.ObjectPools.csproj | 97 ++++++++++--------- source/Recycler.cs | 4 +- source/RecyclerBase.cs | 6 +- source/ReferenceContainer.cs | 9 +- source/TrimmableCollectionObjectPoolBase.cs | 10 +- source/TrimmableObjectPoolBase.cs | 6 +- 19 files changed, 129 insertions(+), 110 deletions(-) create mode 100644 source/.editorconfig delete mode 100644 source/GlobalSuppressions.cs diff --git a/source/.editorconfig b/source/.editorconfig new file mode 100644 index 0000000..72b2ff2 --- /dev/null +++ b/source/.editorconfig @@ -0,0 +1,10 @@ +[*.cs] + +# CA1707: Identifiers should not contain underscores +dotnet_diagnostic.CA1707.severity = silent + +# CA1303: Do not pass literals as localized parameters +dotnet_diagnostic.CA1303.severity = silent + +# CA1051: Do not declare visible instance fields +dotnet_diagnostic.CA1051.severity = silent diff --git a/source/Array/InterlockedArrayObjectPool.cs b/source/Array/InterlockedArrayObjectPool.cs index ed48d83..00b7b22 100644 --- a/source/Array/InterlockedArrayObjectPool.cs +++ b/source/Array/InterlockedArrayObjectPool.cs @@ -14,7 +14,7 @@ public class InterlockedArrayObjectPool : ObjectPoolBase where T : class { - public InterlockedArrayObjectPool(Func factory, Action recycler, Action disposer, int capacity = DEFAULT_CAPACITY) + public InterlockedArrayObjectPool(Func factory, Action? recycler, Action? disposer, int capacity = DEFAULT_CAPACITY) : base(factory, recycler, disposer, capacity) { Pool = new ReferenceContainer[capacity - 1]; @@ -33,6 +33,7 @@ public InterlockedArrayObjectPool(Func factory, int capacity = DEFAULT_CAPACI public override int Count => Pool.Count(e => e.Value != null) + PocketCount; + [System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1062:Validate arguments of public methods", Justification = "Should never be null.")] protected virtual bool Store(ReferenceContainer[] p, T item, int index) => p[index].TrySave(item); @@ -43,7 +44,7 @@ protected override bool Receive(T item) for (var i = 0; i < len; i++) { - if (Store(elements, item, i)) + if (Store(elements!, item, i)) { var m = MaxStored; if (i >= m) Interlocked.CompareExchange(ref MaxStored, m + MaxStoredIncrement, m); @@ -55,7 +56,7 @@ protected override bool Receive(T item) return false; } - protected override T TryRelease() + protected override T? TryRelease() { // We missed getting the first item or it wasn't there. var elements = Pool; @@ -74,7 +75,7 @@ protected override T TryRelease() protected override void OnDispose() { base.OnDispose(); - Pool = null; + Pool = null!; MaxStored = 0; } diff --git a/source/Array/OptimisticArrayObjectPool.cs b/source/Array/OptimisticArrayObjectPool.cs index 06f09ed..00f8626 100644 --- a/source/Array/OptimisticArrayObjectPool.cs +++ b/source/Array/OptimisticArrayObjectPool.cs @@ -12,7 +12,7 @@ public class OptimisticArrayObjectPool : InterlockedArrayObjectPool where T : class { - public OptimisticArrayObjectPool(Func factory, Action recycler, int capacity = DEFAULT_CAPACITY) + public OptimisticArrayObjectPool(Func factory, Action? recycler, int capacity = DEFAULT_CAPACITY) : base(factory, recycler, null /* disposer not applicable here */, capacity) { } @@ -25,6 +25,7 @@ protected override bool SaveToPocket(T item) => Pocket.SetIfNull(item); // As suggested by Roslyn's implementation, don't worry about interlocking here. It's okay if a few get loose. + [System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1062:Validate arguments of public methods", Justification = "Should never be null.")] protected override bool Store(ReferenceContainer[] p, T item, int index) => p[index].SetIfNull(item); } diff --git a/source/Collection/CollectionWrapperObjectPool.cs b/source/Collection/CollectionWrapperObjectPool.cs index 51ed834..d5be04d 100644 --- a/source/Collection/CollectionWrapperObjectPool.cs +++ b/source/Collection/CollectionWrapperObjectPool.cs @@ -8,7 +8,7 @@ public class CollectionWrapperObjectPool : TrimmableGenericColle where T : class where TCollection : class, ICollection { - public CollectionWrapperObjectPool(TCollection pool, Func factory, Action recycler, Action disposer, int capacity = DEFAULT_CAPACITY, bool countTrackingEnabled = true) + public CollectionWrapperObjectPool(TCollection pool, Func factory, Action? recycler, Action? disposer, int capacity = DEFAULT_CAPACITY, bool countTrackingEnabled = true) : base(pool, factory, recycler, disposer, capacity, countTrackingEnabled) { } @@ -35,9 +35,9 @@ protected override bool Receive(T item) return false; } - protected override T TryRelease() + protected override T? TryRelease() { - retry: + retry: var p = Pool; var item = p?.FirstOrDefault(); @@ -47,7 +47,7 @@ protected override T TryRelease() * This implementation is in place for reference more than practice. Sub classes should override. */ bool wasRemoved; - lock (p) wasRemoved = p.Remove(item); + lock (p!) wasRemoved = p.Remove(item); if (!wasRemoved) goto retry; return item; diff --git a/source/Collection/ConcurrentQueueObjectPool.cs b/source/Collection/ConcurrentQueueObjectPool.cs index ab54acf..3fabfeb 100644 --- a/source/Collection/ConcurrentQueueObjectPool.cs +++ b/source/Collection/ConcurrentQueueObjectPool.cs @@ -7,7 +7,7 @@ public sealed class ConcurrentQueueObjectPool : TrimmableCollectionObjectPool where T : class { - public ConcurrentQueueObjectPool(Func factory, Action recycler, Action disposer, int capacity = DEFAULT_CAPACITY) + public ConcurrentQueueObjectPool(Func factory, Action? recycler, Action? disposer, int capacity = DEFAULT_CAPACITY) : base(new ConcurrentQueue(), factory, recycler, disposer, capacity) { } @@ -31,7 +31,7 @@ protected override bool Receive(T item) return true; } - protected override T TryRelease() + protected override T? TryRelease() { var p = Pool; if (p == null) return null; diff --git a/source/Collection/ConcurrentStackObjectPool.cs b/source/Collection/ConcurrentStackObjectPool.cs index ba8d28c..42c06ca 100644 --- a/source/Collection/ConcurrentStackObjectPool.cs +++ b/source/Collection/ConcurrentStackObjectPool.cs @@ -7,7 +7,7 @@ public sealed class ConcurrentStackObjectPool : TrimmableCollectionObjectPool where T : class { - public ConcurrentStackObjectPool(Func factory, Action recycler, Action disposer, int capacity = DEFAULT_CAPACITY) + public ConcurrentStackObjectPool(Func factory, Action? recycler, Action? disposer, int capacity = DEFAULT_CAPACITY) : base(new ConcurrentStack(), factory, recycler, disposer, capacity) { } @@ -26,7 +26,7 @@ protected override bool Receive(T item) return true; } - protected override T TryRelease() + protected override T? TryRelease() { var p = Pool; if (p == null) return null; diff --git a/source/Collection/QueueObjectPool.cs b/source/Collection/QueueObjectPool.cs index d0556d1..a2264cb 100644 --- a/source/Collection/QueueObjectPool.cs +++ b/source/Collection/QueueObjectPool.cs @@ -7,7 +7,7 @@ public sealed class QueueObjectPool : TrimmableCollectionObjectPoolBase factory, Action recycler, Action disposer, int capacity = DEFAULT_CAPACITY) + public QueueObjectPool(Func factory, Action? recycler, Action? disposer, int capacity = DEFAULT_CAPACITY) : base( new Queue(Math.Min(DEFAULT_CAPACITY, capacity)) /* Very very slight speed improvment when capacity is initially set. */, factory, recycler, disposer, capacity, false) @@ -34,7 +34,7 @@ protected override bool Receive(T item) return false; } - protected override T TryRelease() + protected override T? TryRelease() { var p = Pool; if (p != null && p.Count != 0) diff --git a/source/Collection/StackObjectPool.cs b/source/Collection/StackObjectPool.cs index 9c69d82..30611bd 100644 --- a/source/Collection/StackObjectPool.cs +++ b/source/Collection/StackObjectPool.cs @@ -7,7 +7,7 @@ public sealed class StackObjectPool : TrimmableCollectionObjectPoolBase factory, Action recycler, Action disposer, int capacity = DEFAULT_CAPACITY) + public StackObjectPool(Func factory, Action? recycler, Action? disposer, int capacity = DEFAULT_CAPACITY) : base( new Stack(Math.Min(DEFAULT_CAPACITY, capacity)) /* Very very slight speed improvment when capacity is set. */, factory, recycler, disposer, capacity, false) @@ -34,7 +34,7 @@ protected override bool Receive(T item) return false; } - protected override T TryRelease() + protected override T? TryRelease() { var p = Pool; if (p != null && p.Count != 0) diff --git a/source/GlobalSuppressions.cs b/source/GlobalSuppressions.cs deleted file mode 100644 index 8e19587..0000000 --- a/source/GlobalSuppressions.cs +++ /dev/null @@ -1,11 +0,0 @@ - -// This file is used by Code Analysis to maintain SuppressMessage -// attributes that are applied to this project. -// Project-level suppressions either have no target or are given -// a specific target and scoped to a namespace, type, member, etc. - -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Performance", "CA1815:Override equals and operator equals on value types", Justification = "", Scope = "type", Target = "~T:Open.Disposable.ReferenceContainer`1")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Performance", "CA1815:Override equals and operator equals on value types", Justification = "", Scope = "type", Target = "~T:Open.Disposable.DualReferenceContainer`1")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Style", "IDE0044:Add readonly modifier", Justification = "", Scope = "member", Target = "~F:Open.Disposable.ObjectPoolAutoTrimmer._trimmedSize")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Style", "IDE0044:Add readonly modifier", Justification = "", Scope = "member", Target = "~F:Open.Disposable.ObjectPoolAutoTrimmer._trimDelay")] - diff --git a/source/IObjectPool.cs b/source/IObjectPool.cs index 9c6c1b1..3f2c29c 100644 --- a/source/IObjectPool.cs +++ b/source/IObjectPool.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; +using System.Diagnostics.Contracts; namespace Open.Disposable { @@ -30,13 +31,17 @@ public interface IObjectPool : IDisposable /// /// The item to return if available. Will be null if none avaialable. /// True if an item is provided. - bool TryTake(out T item); +#if NETSTANDARD2_1 + bool TryTake([NotNullWhen(true)] out T? item); +#else + bool TryTake(out T? item); +#endif /// /// If the pool has an item currently avaialable, removes it from the pool and returns it. /// /// The item to return if available. Will be null if none avaialable. - T TryTake(); + T? TryTake(); /// @@ -64,6 +69,9 @@ public static class ObjectPoolExtensions public static void Give(this IObjectPool target, IEnumerable items) where T : class { + if (target is null) throw new ArgumentNullException(nameof(target)); + Contract.EndContractBlock(); + if (items == null) return; foreach (var i in items) target.Give(i); @@ -80,6 +88,9 @@ public static void Give(this IObjectPool target, IEnumerable items) public static void Give(this IObjectPool target, T item1, T item2, params T[] items) where T : class { + if (target is null) throw new ArgumentNullException(nameof(target)); + Contract.EndContractBlock(); + target.Give(item1); target.Give(item2); target.Give(items); diff --git a/source/IRecycler.cs b/source/IRecycler.cs index c6df542..277d1d1 100644 --- a/source/IRecycler.cs +++ b/source/IRecycler.cs @@ -3,7 +3,7 @@ namespace Open.Disposable { - interface IRecycler : IDisposable + public interface IRecycler : IDisposable where T : class { bool Recycle(T item); diff --git a/source/ObjectPoolAutoTrimmer.cs b/source/ObjectPoolAutoTrimmer.cs index b6b44d2..47f50a6 100644 --- a/source/ObjectPoolAutoTrimmer.cs +++ b/source/ObjectPoolAutoTrimmer.cs @@ -5,12 +5,13 @@ namespace Open.Disposable { [SuppressMessage("ReSharper", "FieldCanBeMadeReadOnly.Local")] + [SuppressMessage("Style", "IDE0044:Add readonly modifier", Justification = "Micro-optimization for retrieving this value as read-only is slightly slower")] public class ObjectPoolAutoTrimmer : DisposableBase { ITrimmableObjectPool _pool; ActionRunner _trimmer; - // micro-optimization for retrieving this value as read-only is slightly slower. + // Micro-optimization for retrieving this value as read-only is slightly slower. ushort _trimmedSize; TimeSpan _trimDelay; diff --git a/source/ObjectPoolBase.cs b/source/ObjectPoolBase.cs index bfa84bf..658c1e4 100644 --- a/source/ObjectPoolBase.cs +++ b/source/ObjectPoolBase.cs @@ -1,4 +1,5 @@ using System; +using System.Diagnostics.CodeAnalysis; namespace Open.Disposable { @@ -7,7 +8,7 @@ public abstract class ObjectPoolBase : DisposableBase, IObjectPool { protected const int DEFAULT_CAPACITY = Constants.DEFAULT_CAPACITY; - protected ObjectPoolBase(Func factory, Action recycler, Action disposer, int capacity = DEFAULT_CAPACITY) + protected ObjectPoolBase(Func factory, Action? recycler, Action? disposer, int capacity = DEFAULT_CAPACITY) { if (capacity < 1) throw new ArgumentOutOfRangeException(nameof(capacity), capacity, "Must be at least 1."); @@ -27,8 +28,8 @@ protected ObjectPoolBase(Func factory, Action recycler, Action disposer public abstract int Count { get; } protected int PocketCount => Pocket.Value == null ? 0 : 1; - protected readonly Action Recycler; // Before entering the pool. - protected readonly Action OnDiscarded; // When not able to be used. + protected readonly Action? Recycler; // Before entering the pool. + protected readonly Action? OnDiscarded; // When not able to be used. // Read-only because if Take() is called after disposal, this still facilitates returing an object. @@ -86,26 +87,25 @@ public void Give(T item) #region Release (.Take()) public virtual T Take() - { - return TryTake() ?? Factory(); - } + => TryTake() ?? Factory(); - public bool TryTake(out T item) - { - item = TryTake(); - return item != null; - } +#if NETSTANDARD2_1 + public bool TryTake([NotNullWhen(true)] out T? item) +#else + public bool TryTake(out T? item) +#endif + => (item = TryTake()) != null; protected virtual bool SaveToPocket(T item) => Pocket.TrySave(item); - protected T TakeFromPocket() + protected T? TakeFromPocket() => Pocket.TryRetrieve(); - protected abstract T TryRelease(); + protected abstract T? TryRelease(); - public T TryTake() + public T? TryTake() { var item = TakeFromPocket() ?? TryRelease(); if (item != null) OnReleased(); @@ -126,7 +126,7 @@ protected override void OnDispose() { if (OnDiscarded == null) return; - T d; + T? d; while ((d = TryRelease()) != null) OnDiscarded(d); } } diff --git a/source/Open.Disposable.ObjectPools.csproj b/source/Open.Disposable.ObjectPools.csproj index 2d55a9b..3a1b47f 100644 --- a/source/Open.Disposable.ObjectPools.csproj +++ b/source/Open.Disposable.ObjectPools.csproj @@ -1,50 +1,55 @@  - - netstandard2.0;netstandard2.1 - Open.Disposable - A set of variations on ObjectPool implementations with differing underlying collections. - -Part of the "Open" set of libraries. - electricessence - electricessence - - https://github.com/electricessence/Open.Disposable.ObjectPools/blob/master/LISCENSE.md - - https://github.com/electricessence/Open.Disposable.ObjectPools/ - https://github.com/electricessence/Open.Disposable.ObjectPools/ - git - objectpool, dotnet, dotnetcore, cs, idisposable, threadsafe, thread-safe - True - 2.3.3 - 2.3.3.0 - 2.3.3.0 - - MIT - - - - latest - - - - latest - - - - - - - - - - - - - - - - - + + netstandard2.0;netstandard2.1 + Open.Disposable + + A set of variations on ObjectPool implementations with differing underlying collections. + + Part of the "Open" set of libraries. + + electricessence + electricessence + + https://github.com/electricessence/Open.Disposable.ObjectPools/blob/master/LISCENSE.md + + https://github.com/electricessence/Open.Disposable.ObjectPools/ + https://github.com/electricessence/Open.Disposable.ObjectPools/ + git + objectpool, dotnet, dotnetcore, cs, idisposable, threadsafe, thread-safe + True + 2.4.0 + + MIT + enable + + + + latest + + + + latest + + + + + + + + + + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + \ No newline at end of file diff --git a/source/Recycler.cs b/source/Recycler.cs index 175dcac..47b1236 100644 --- a/source/Recycler.cs +++ b/source/Recycler.cs @@ -49,7 +49,7 @@ protected override void OnDispose() base.OnDispose(); _bin.Writer.TryComplete(); - _bin = null; + _bin = null!; } } @@ -70,7 +70,7 @@ public static Recycler CreateRecycler( => new Recycler(pool, recycleFunction, limit); public static void Recycle(IRecyclable r) - => r.Recycle(); + =>(r ?? throw new ArgumentNullException(nameof(r))).Recycle(); public static Recycler CreateRecycler( this IObjectPool pool, diff --git a/source/RecyclerBase.cs b/source/RecyclerBase.cs index 074b490..4289dc5 100644 --- a/source/RecyclerBase.cs +++ b/source/RecyclerBase.cs @@ -25,7 +25,7 @@ protected RecyclerBase( if (!(target is DisposableBase d)) return; if (d.WasDisposed) throw new ArgumentException("Cannot recycle for an object pool that is already disposed."); d.BeforeDispose += Pool_BeforeDispose; - // Could possibly dispose before this line somewhere... But that's just nasty. :P + // Could possibly dispose before this line somewhere... But that's just nasty. :P } void Pool_BeforeDispose(object sender, EventArgs e) => Dispose(); @@ -35,7 +35,7 @@ protected RecyclerBase( protected abstract void OnCloseRequested(); // ReSharper disable once MemberCanBeProtected.Global - public Task Completion { get; protected set; } + public Task Completion { get; protected set; } = Task.CompletedTask; public Task Close() { @@ -46,7 +46,7 @@ public Task Close() protected override void OnDispose() { OnCloseRequested(); - Target = null; + Target = null!; } } diff --git a/source/ReferenceContainer.cs b/source/ReferenceContainer.cs index 4cabed8..42d4fa5 100644 --- a/source/ReferenceContainer.cs +++ b/source/ReferenceContainer.cs @@ -11,18 +11,19 @@ public interface IReferenceContainer int Capacity { get; } bool SetIfNull(T value); bool TrySave(T value); - T TryRetrieve(); + T? TryRetrieve(); } [DebuggerDisplay("{Value,nq}")] + [SuppressMessage("Performance", "CA1815:Override equals and operator equals on value types", Justification = "Undesired.")] public struct ReferenceContainer : IReferenceContainer where T : class { public int Capacity => 1; - T _value; + T? _value; - public T Value + public T? Value { get => _value; set => _value = value; @@ -39,7 +40,7 @@ public bool TrySave(T value) => _value == null && null == Interlocked.CompareExchange(ref _value, value, null); - public T TryRetrieve() + public T? TryRetrieve() { var item = _value; return (item != null diff --git a/source/TrimmableCollectionObjectPoolBase.cs b/source/TrimmableCollectionObjectPoolBase.cs index 0bdc25b..62c377b 100644 --- a/source/TrimmableCollectionObjectPoolBase.cs +++ b/source/TrimmableCollectionObjectPoolBase.cs @@ -9,7 +9,7 @@ public abstract class TrimmableCollectionObjectPoolBase : Trimma where TCollection : class, ICollection { - protected TrimmableCollectionObjectPoolBase(TCollection pool, Func factory, Action recycler, Action disposer, int capacity = DEFAULT_CAPACITY, bool countTrackingEnabled = true) + protected TrimmableCollectionObjectPoolBase(TCollection pool, Func factory, Action? recycler, Action? disposer, int capacity = DEFAULT_CAPACITY, bool countTrackingEnabled = true) : base(factory, recycler, disposer, capacity, countTrackingEnabled) { Pool = pool ?? throw new ArgumentNullException(nameof(pool)); @@ -22,7 +22,7 @@ protected TrimmableCollectionObjectPoolBase(TCollection pool, Func factory, A protected override void OnDispose() { base.OnDispose(); - Pool = null; + Pool = null!; } } @@ -31,10 +31,10 @@ public abstract class TrimmableGenericCollectionObjectPoolBase : where TCollection : class, ICollection { - protected TrimmableGenericCollectionObjectPoolBase(TCollection pool, Func factory, Action recycler, Action disposer, int capacity = DEFAULT_CAPACITY, bool countTrackingEnabled = true) + protected TrimmableGenericCollectionObjectPoolBase(TCollection pool, Func factory, Action? recycler, Action? disposer, int capacity = DEFAULT_CAPACITY, bool countTrackingEnabled = true) : base(factory, recycler, disposer, capacity, countTrackingEnabled) { - Pool = pool ?? throw new ArgumentNullException(nameof(pool)); ; + Pool = pool ?? throw new ArgumentNullException(nameof(pool)); } protected TCollection Pool; @@ -46,7 +46,7 @@ protected override void OnDispose() //base.OnDispose(); // Do not call because the following is more optimized. var p = Pool; - Pool = null; + Pool = null!; if (OnDiscarded != null) { diff --git a/source/TrimmableObjectPoolBase.cs b/source/TrimmableObjectPoolBase.cs index 8f902d3..2e2b9ee 100644 --- a/source/TrimmableObjectPoolBase.cs +++ b/source/TrimmableObjectPoolBase.cs @@ -8,7 +8,7 @@ namespace Open.Disposable public abstract class TrimmableObjectPoolBase : ObjectPoolBase, ITrimmableObjectPool where T : class { - protected TrimmableObjectPoolBase(Func factory, Action recycler, Action disposer, int capacity, bool countTrackingEnabled = true) + protected TrimmableObjectPoolBase(Func factory, Action? recycler, Action? disposer, int capacity, bool countTrackingEnabled = true) : base(factory, recycler, disposer, capacity) { _countTrackingEnabled = countTrackingEnabled; @@ -24,7 +24,7 @@ protected TrimmableObjectPoolBase(Func factory, Action recycler, Action /// /// Signal for when an item was taken (actually removed) from the pool. /// - public event ObjectPoolResizeEvent Released; + public event ObjectPoolResizeEvent? Released; protected void OnReleased(int newSize) { Debug.Assert(newSize > -2, $"newSize: {newSize}, _count: {_count}"); // Should never get out of control. It may go negative temporarily but should be 100% accounted for. @@ -38,7 +38,7 @@ protected override void OnReleased() /// /// Signal for when an item was given (actually accepted) to the pool. /// - public event ObjectPoolResizeEvent Received; + public event ObjectPoolResizeEvent? Received; protected void OnReceived(int newSize) { Received?.Invoke(newSize); From cd0bac7aa1d16699dc51dd32955a4f5e4f352aa0 Mon Sep 17 00:00:00 2001 From: "Oren (electricessence)" Date: Sat, 4 Jan 2020 01:43:19 -0800 Subject: [PATCH 34/83] Updates and extensions. --- benchmarking/Benchmark.cs | 88 +++++++++---------- ...Disposable.ObjectPools.Benchmarking.csproj | 2 +- source/IObjectPool.cs | 76 ++++++++++++++++ source/Open.Disposable.ObjectPools.csproj | 2 +- source/Recycler.cs | 2 +- 5 files changed, 122 insertions(+), 48 deletions(-) diff --git a/benchmarking/Benchmark.cs b/benchmarking/Benchmark.cs index 3c436fb..455d7ab 100644 --- a/benchmarking/Benchmark.cs +++ b/benchmarking/Benchmark.cs @@ -26,65 +26,63 @@ public Benchmark(uint size, uint repeat, Func> poolFactory) : bas protected override IEnumerable TestOnceInternal() { - using (var pool = Param()) - { - if (pool == null) throw new NullReferenceException(); - //yield return TimedResult.Measure("Take From Empty (In Parallel)", () => - //{ - // Parallel.For(0, TestSize, i => _items[i] = pool.Take()); - //}); + using var pool = Param(); + if (pool == null) throw new NullReferenceException(); + //yield return TimedResult.Measure("Take From Empty (In Parallel)", () => + //{ + // Parallel.For(0, TestSize, i => _items[i] = pool.Take()); + //}); - yield return TimedResult.Measure("Give To (In Parallel)", () => - { - // ReSharper disable once AccessToDisposedClosure - Parallel.For(0, TestSize, i => pool.Give(_items[i])); + yield return TimedResult.Measure("Give To (In Parallel)", () => + { + // ReSharper disable once AccessToDisposedClosure + Parallel.For(0, TestSize, i => pool.Give(_items[i])); #if DEBUG - var count = pool.Count; - Debug.Assert(pool is OptimisticArrayObjectPool || count == TestSize, $"Expected {TestSize}, acutal count: {count}"); + var count = pool.Count; + Debug.Assert(pool is OptimisticArrayObjectPool || count == TestSize, $"Expected {TestSize}, acutal count: {count}"); #endif - }); + }); - yield return TimedResult.Measure("Mixed 90%-Take/10%-Give (In Parallel)", () => + yield return TimedResult.Measure("Mixed 90%-Take/10%-Give (In Parallel)", () => + { + Parallel.For(0, TestSize, i => { - Parallel.For(0, TestSize, i => - { - if (i % 10 == 0) - pool.Give(_items[i]); - else - _items[i] = pool.Take(); - }); + if (i % 10 == 0) + pool.Give(_items[i]); + else + _items[i] = pool.Take(); }); + }); - yield return TimedResult.Measure("Mixed 50%-Take/50%-Give (In Parallel)", () => + yield return TimedResult.Measure("Mixed 50%-Take/50%-Give (In Parallel)", () => + { + Parallel.For(0, TestSize, i => { - Parallel.For(0, TestSize, i => - { - if (i % 2 == 0) - _items[i] = pool.Take(); - else - pool.Give(_items[i]); - }); + if (i % 2 == 0) + _items[i] = pool.Take(); + else + pool.Give(_items[i]); }); + }); - yield return TimedResult.Measure("Mixed 10%-Take/90%-Give (In Parallel)", () => + yield return TimedResult.Measure("Mixed 10%-Take/90%-Give (In Parallel)", () => + { + Parallel.For(0, TestSize, i => { - Parallel.For(0, TestSize, i => - { - if (i % 10 == 0) - _items[i] = pool.Take(); - else - pool.Give(_items[i]); - }); + if (i % 10 == 0) + _items[i] = pool.Take(); + else + pool.Give(_items[i]); }); + }); - yield return TimedResult.Measure("Empty Pool (.TryTake())", () => + yield return TimedResult.Measure("Empty Pool (.TryTake())", () => + { + while (pool.TryTake() != null) { - while (pool.TryTake() != null) - { - // remaining++; - } - }); - } + // remaining++; + } + }); } public static TimedResult[] Results(uint size, uint repeat, Func> poolFactory) diff --git a/benchmarking/Open.Disposable.ObjectPools.Benchmarking.csproj b/benchmarking/Open.Disposable.ObjectPools.Benchmarking.csproj index 334a120..252b857 100644 --- a/benchmarking/Open.Disposable.ObjectPools.Benchmarking.csproj +++ b/benchmarking/Open.Disposable.ObjectPools.Benchmarking.csproj @@ -2,7 +2,7 @@ Exe - netcoreapp3.0 + netcoreapp3.1 Open.Disposable 1.0.0 diff --git a/source/IObjectPool.cs b/source/IObjectPool.cs index 3f2c29c..667b0ed 100644 --- a/source/IObjectPool.cs +++ b/source/IObjectPool.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Diagnostics.Contracts; +using System.Threading.Tasks; namespace Open.Disposable { @@ -96,5 +97,80 @@ public static void Give(this IObjectPool target, T item1, T item2, params target.Give(items); } + /// + /// Provides an item from the pool and returns it when the action sucessfully completes. + /// + /// The object type from the pool. + /// The object pool. + /// The action to execute. + public static void Rent(this IObjectPool source, Action action) + where T : class + { + if (source is null) throw new ArgumentNullException(nameof(source)); + if (action is null) throw new ArgumentNullException(nameof(action)); + Contract.EndContractBlock(); + + var item = source.Take(); + action(item); + source.Give(item); + } + + /// + /// Provides an item from the pool and returns it when the action sucessfully completes. + /// + /// The object type from the pool. + /// The object pool. + /// The action to execute. + /// The value from the action. + public static TResult Rent(this IObjectPool source, Func action) + where T : class + { + if (source is null) throw new ArgumentNullException(nameof(source)); + if (action is null) throw new ArgumentNullException(nameof(action)); + Contract.EndContractBlock(); + + var item = source.Take(); + var result = action(item); + source.Give(item); + return result; + } + + /// + /// Provides an item from the pool and returns it when the action sucessfully completes. + /// + /// The object type from the pool. + /// The object pool. + /// The action to execute. + public static async ValueTask RentAsync(this IObjectPool source, Func action) + where T : class + { + if (source is null) throw new ArgumentNullException(nameof(source)); + if (action is null) throw new ArgumentNullException(nameof(action)); + Contract.EndContractBlock(); + + var item = source.Take(); + await action(item); + source.Give(item); + } + + /// + /// Provides an item from the pool and returns it when the action sucessfully completes. + /// + /// The object type from the pool. + /// The object pool. + /// The action to execute. + /// The value from the action. + public static async ValueTask RentAsync(this IObjectPool source, Func> action) + where T : class + { + if (source is null) throw new ArgumentNullException(nameof(source)); + if (action is null) throw new ArgumentNullException(nameof(action)); + Contract.EndContractBlock(); + + var item = source.Take(); + var result = await action(item); + source.Give(item); + return result; + } } } diff --git a/source/Open.Disposable.ObjectPools.csproj b/source/Open.Disposable.ObjectPools.csproj index 3a1b47f..8457b12 100644 --- a/source/Open.Disposable.ObjectPools.csproj +++ b/source/Open.Disposable.ObjectPools.csproj @@ -18,7 +18,7 @@ git objectpool, dotnet, dotnetcore, cs, idisposable, threadsafe, thread-safe True - 2.4.0 + 2.4.1 MIT enable diff --git a/source/Recycler.cs b/source/Recycler.cs index 47b1236..0a81d14 100644 --- a/source/Recycler.cs +++ b/source/Recycler.cs @@ -70,7 +70,7 @@ public static Recycler CreateRecycler( => new Recycler(pool, recycleFunction, limit); public static void Recycle(IRecyclable r) - =>(r ?? throw new ArgumentNullException(nameof(r))).Recycle(); + => (r ?? throw new ArgumentNullException(nameof(r))).Recycle(); public static Recycler CreateRecycler( this IObjectPool pool, From e4149f405ded8d8b5624589986dcc74bcecce3b4 Mon Sep 17 00:00:00 2001 From: electricessence <5899455+electricessence@users.noreply.github.com> Date: Mon, 6 Apr 2020 20:27:46 -0700 Subject: [PATCH 35/83] Update README.md --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 51153b5..b9ee9ca 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,4 @@ # Open.Disposable.ObjectPools A set of variations on ObjectPool implementations with differing underlying collections. + +[![NuGet](http://img.shields.io/nuget/v/Open.Disposable.ObjectPools.svg)](https://www.nuget.org/packages/Open.Disposable.ObjectPools/) From 7864b99ca62ccf2d029e7cb33d3fc6a53093b363 Mon Sep 17 00:00:00 2001 From: "Oren (electricessence)" Date: Wed, 10 Jun 2020 14:08:23 -0700 Subject: [PATCH 36/83] Updated references. --- source/Open.Disposable.ObjectPools.csproj | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/source/Open.Disposable.ObjectPools.csproj b/source/Open.Disposable.ObjectPools.csproj index 8457b12..b348c53 100644 --- a/source/Open.Disposable.ObjectPools.csproj +++ b/source/Open.Disposable.ObjectPools.csproj @@ -43,13 +43,13 @@ - + all runtime; build; native; contentfiles; analyzers; buildtransitive - + \ No newline at end of file From 1e59d60670362d9d472463de503c119cb75f284c Mon Sep 17 00:00:00 2001 From: "Oren (electricessence)" Date: Sat, 11 Jul 2020 14:13:58 -0700 Subject: [PATCH 37/83] Prep for updated release. --- source/Array/InterlockedArrayObjectPool.cs | 2 +- .../Collection/CollectionWrapperObjectPool.cs | 2 +- .../Collection/ConcurrentQueueObjectPool.cs | 4 ++-- .../Collection/ConcurrentStackObjectPool.cs | 4 ++-- source/IObjectPool.cs | 6 +++--- source/ObjectPoolBase.cs | 4 ++-- source/Open.Disposable.ObjectPools.csproj | 20 +++++++++---------- source/Recycler.cs | 2 +- source/RecyclerBase.cs | 2 +- source/ReferenceContainer.cs | 2 +- source/TrimmableObjectPoolBase.cs | 4 ++-- 11 files changed, 25 insertions(+), 27 deletions(-) diff --git a/source/Array/InterlockedArrayObjectPool.cs b/source/Array/InterlockedArrayObjectPool.cs index 00b7b22..fd26dba 100644 --- a/source/Array/InterlockedArrayObjectPool.cs +++ b/source/Array/InterlockedArrayObjectPool.cs @@ -60,7 +60,7 @@ protected override bool Receive(T item) { // We missed getting the first item or it wasn't there. var elements = Pool; - if (elements == null) return null; + if (elements is null) return null; var len = elements.Length; for (var i = 0; i < MaxStored && i < len; i++) diff --git a/source/Collection/CollectionWrapperObjectPool.cs b/source/Collection/CollectionWrapperObjectPool.cs index d5be04d..21d0c96 100644 --- a/source/Collection/CollectionWrapperObjectPool.cs +++ b/source/Collection/CollectionWrapperObjectPool.cs @@ -41,7 +41,7 @@ protected override bool Receive(T item) var p = Pool; var item = p?.FirstOrDefault(); - if (item == null) return null; + if (item is null) return null; /* Removing the first item is typically horribly inefficient but we can't make assumptions about the implementation here. * It's a trade off between potentially iterating the entire collection before removing the last item, or relying on the underlying implementation. * This implementation is in place for reference more than practice. Sub classes should override. */ diff --git a/source/Collection/ConcurrentQueueObjectPool.cs b/source/Collection/ConcurrentQueueObjectPool.cs index 3fabfeb..4d7060f 100644 --- a/source/Collection/ConcurrentQueueObjectPool.cs +++ b/source/Collection/ConcurrentQueueObjectPool.cs @@ -26,7 +26,7 @@ public ConcurrentQueueObjectPool(Func factory, int capacity = DEFAULT_CAPACIT protected override bool Receive(T item) { var p = Pool; - if (p == null) return false; + if (p is null) return false; p.Enqueue(item); // It's possible that the count could exceed MaxSize here, but the risk is negligble as a few over the limit won't hurt. return true; } @@ -34,7 +34,7 @@ protected override bool Receive(T item) protected override T? TryRelease() { var p = Pool; - if (p == null) return null; + if (p is null) return null; p.TryDequeue(out var item); return item; } diff --git a/source/Collection/ConcurrentStackObjectPool.cs b/source/Collection/ConcurrentStackObjectPool.cs index 42c06ca..e38d9b3 100644 --- a/source/Collection/ConcurrentStackObjectPool.cs +++ b/source/Collection/ConcurrentStackObjectPool.cs @@ -21,7 +21,7 @@ public ConcurrentStackObjectPool(Func factory, int capacity = DEFAULT_CAPACIT protected override bool Receive(T item) { var p = Pool; - if (p == null) return false; + if (p is null) return false; p.Push(item); // It's possible that the count could exceed MaxSize here, but the risk is negligble as a few over the limit won't hurt. return true; } @@ -29,7 +29,7 @@ protected override bool Receive(T item) protected override T? TryRelease() { var p = Pool; - if (p == null) return null; + if (p is null) return null; p.TryPop(out var item); return item; } diff --git a/source/IObjectPool.cs b/source/IObjectPool.cs index 667b0ed..5ef30e9 100644 --- a/source/IObjectPool.cs +++ b/source/IObjectPool.cs @@ -73,7 +73,7 @@ public static void Give(this IObjectPool target, IEnumerable items) if (target is null) throw new ArgumentNullException(nameof(target)); Contract.EndContractBlock(); - if (items == null) return; + if (items is null) return; foreach (var i in items) target.Give(i); } @@ -149,7 +149,7 @@ public static async ValueTask RentAsync(this IObjectPool source, Func RentAsync(this IObjectPool Contract.EndContractBlock(); var item = source.Take(); - var result = await action(item); + var result = await action(item).ConfigureAwait(false); source.Give(item); return result; } diff --git a/source/ObjectPoolBase.cs b/source/ObjectPoolBase.cs index 658c1e4..4642e64 100644 --- a/source/ObjectPoolBase.cs +++ b/source/ObjectPoolBase.cs @@ -26,7 +26,7 @@ protected ObjectPoolBase(Func factory, Action? recycler, Action? dispos /// Total number of items in the pool. /// public abstract int Count { get; } - protected int PocketCount => Pocket.Value == null ? 0 : 1; + protected int PocketCount => Pocket.Value is null ? 0 : 1; protected readonly Action? Recycler; // Before entering the pool. protected readonly Action? OnDiscarded; // When not able to be used. @@ -124,7 +124,7 @@ protected override void OnBeforeDispose() protected override void OnDispose() { - if (OnDiscarded == null) return; + if (OnDiscarded is null) return; T? d; while ((d = TryRelease()) != null) OnDiscarded(d); diff --git a/source/Open.Disposable.ObjectPools.csproj b/source/Open.Disposable.ObjectPools.csproj index b348c53..89d2bcc 100644 --- a/source/Open.Disposable.ObjectPools.csproj +++ b/source/Open.Disposable.ObjectPools.csproj @@ -2,15 +2,14 @@ netstandard2.0;netstandard2.1 + latest + enable Open.Disposable - - A set of variations on ObjectPool implementations with differing underlying collections. - - Part of the "Open" set of libraries. - electricessence - electricessence - + A set of variations on ObjectPool implementations with differing underlying collections. + +Part of the "Open" set of libraries. + https://github.com/electricessence/Open.Disposable.ObjectPools/blob/master/LISCENSE.md https://github.com/electricessence/Open.Disposable.ObjectPools/ @@ -21,7 +20,6 @@ 2.4.1 MIT - enable @@ -43,13 +41,13 @@ + + + all runtime; build; native; contentfiles; analyzers; buildtransitive - - - \ No newline at end of file diff --git a/source/Recycler.cs b/source/Recycler.cs index 0a81d14..00abfd8 100644 --- a/source/Recycler.cs +++ b/source/Recycler.cs @@ -30,7 +30,7 @@ async Task Processor(Action recycleFunction) Target?.Give(item); } } - while (await bin.Reader.WaitToReadAsync()); + while (await bin.Reader.WaitToReadAsync().ConfigureAwait(false)); } internal Recycler( diff --git a/source/RecyclerBase.cs b/source/RecyclerBase.cs index 4289dc5..ab20430 100644 --- a/source/RecyclerBase.cs +++ b/source/RecyclerBase.cs @@ -18,7 +18,7 @@ protected RecyclerBase( IObjectPool target, Action recycleFunction) { - if (recycleFunction == null) throw new ArgumentNullException(nameof(recycleFunction)); + if (recycleFunction is null) throw new ArgumentNullException(nameof(recycleFunction)); Target = target ?? throw new ArgumentNullException(nameof(target)); Contract.EndContractBlock(); diff --git a/source/ReferenceContainer.cs b/source/ReferenceContainer.cs index 42d4fa5..5c53e24 100644 --- a/source/ReferenceContainer.cs +++ b/source/ReferenceContainer.cs @@ -37,7 +37,7 @@ public bool SetIfNull(T value) } public bool TrySave(T value) - => _value == null + => _value is null && null == Interlocked.CompareExchange(ref _value, value, null); public T? TryRetrieve() diff --git a/source/TrimmableObjectPoolBase.cs b/source/TrimmableObjectPoolBase.cs index 2e2b9ee..859e128 100644 --- a/source/TrimmableObjectPoolBase.cs +++ b/source/TrimmableObjectPoolBase.cs @@ -63,7 +63,7 @@ public virtual void TrimTo(int targetSize) i++) { var e = TryRelease(); - if (e == null) break; + if (e is null) break; if (_countTrackingEnabled) { @@ -88,6 +88,6 @@ public virtual void TrimTo(int targetSize) // => Pocket.TryRetrieve() ?? Pocket2.TryRetrieve(); //protected override int PocketCount => - // base.PocketCount + (Pocket2.Value == null ? 0 : 1); + // base.PocketCount + (Pocket2.Value is null ? 0 : 1); } } From 6ed12eedcffed688aa14f7c28416af49a06dbdf3 Mon Sep 17 00:00:00 2001 From: electricessence <5899455+electricessence@users.noreply.github.com> Date: Sat, 11 Jul 2020 14:14:32 -0700 Subject: [PATCH 38/83] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index b9ee9ca..5781a52 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ # Open.Disposable.ObjectPools A set of variations on ObjectPool implementations with differing underlying collections. -[![NuGet](http://img.shields.io/nuget/v/Open.Disposable.ObjectPools.svg)](https://www.nuget.org/packages/Open.Disposable.ObjectPools/) +[![NuGet](https://img.shields.io/nuget/v/Open.Disposable.ObjectPools.svg)](https://www.nuget.org/packages/Open.Disposable.ObjectPools/) From 1f440ae3dbcd0aecd77321cf488077dd3e8e530c Mon Sep 17 00:00:00 2001 From: "Oren (electricessence)" Date: Sat, 11 Jul 2020 14:19:02 -0700 Subject: [PATCH 39/83] Added logo. --- source/Open.Disposable.ObjectPools.csproj | 17 ++++++++++++----- source/logo.png | Bin 0 -> 55915 bytes 2 files changed, 12 insertions(+), 5 deletions(-) create mode 100644 source/logo.png diff --git a/source/Open.Disposable.ObjectPools.csproj b/source/Open.Disposable.ObjectPools.csproj index 89d2bcc..232e3d6 100644 --- a/source/Open.Disposable.ObjectPools.csproj +++ b/source/Open.Disposable.ObjectPools.csproj @@ -5,21 +5,24 @@ latest enable Open.Disposable + True electricessence A set of variations on ObjectPool implementations with differing underlying collections. Part of the "Open" set of libraries. - https://github.com/electricessence/Open.Disposable.ObjectPools/blob/master/LISCENSE.md - - https://github.com/electricessence/Open.Disposable.ObjectPools/ - https://github.com/electricessence/Open.Disposable.ObjectPools/ + https://github.com/Open-NET-Libraries/Open.Disposable.ObjectPools/blob/master/LISCENSE.md + https://github.com/Open-NET-Libraries/Open.Disposable.ObjectPools/ + https://github.com/Open-NET-Libraries/Open.Disposable.ObjectPools/ git objectpool, dotnet, dotnetcore, cs, idisposable, threadsafe, thread-safe - True 2.4.1 MIT + true + true + snupkg + logo.png @@ -34,6 +37,10 @@ Part of the "Open" set of libraries. + + True + + diff --git a/source/logo.png b/source/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..473ec878685a4adea2272b8a27bf0aa654dd3144 GIT binary patch literal 55915 zcmeFZhdBcZq3h58mHQ6b1@Iw)IZBusx zCu{e^`&_M+!kkcbs-tSmo57zHRiO^78dN>yBTvU#cy0 zd(w{gbE_8|f9CpYq^mSLw!{7D<_Ncl?%_QqDdjiy3Vujbd@k)C@|&{x6gTEY|4kaW zF=gy;4C~Glh8Bze*t;f2asI+E$wmMB!~flo|22mHbp(zL{6B*Xez82X;34@lPj6qm z_|LT^J9jM9xvIZuQvf#)k5P`h9WO7h%<|>>|Ni^$b#ZpomO;Yx3H#3V!OHVh_*N;k0Ub6Y^ zsYx>zY~HzZXO7Q68h(E5=FKApqmwZOM?M~D{7?U+PKew+*Do(*R<7(();xaV#0*i< zWHwt1Z%%M;eak;#!t&v)#%UPhMQN$Ar{_aCv)uS|=gv74l)ZXY`N-{?`^eCdaMi;< z-TuXTuC3}`#=ylL@DF)qBq>oQhqlxVnlhOBXJ-y^)MMq~6_yvVFswkkEv ztHVnjqFZXyZ9KkL%7m*O+0okCsp?NCWSL&}{e^OFX(lC0% zd54139Wg;e!^65ZcLKhA{c1L6eXxXPN@b;?n1)aP`x0!yRw;aZ=6;5kVOT~(LqkGS zr`{gyZA&uC^G@8E{x$PCz?p@~C6>)yWBb0<)d9#k9XZIW4tCD2p<#*_YDH&D7>ZXQ&AE#D@w`Csv z{nJ6>vFDGb3|LOlIW8d_Zjs!oRi@`>E4~#wTz6abXZE`DbCivro)&nNGbPm-C+a)! zCFK3%!>RfGO_P4qJukBf}*S{F5=nyNsbeHtv#X9t1?7ZldGp#Gx zhlcu_gWo@}JoC`0Aw^8p)AskzdNSkICEyQZiw^1P?lw4Z;O?#rTbCcve#3*my1G_` z^Lbqz8uAfa<=psw?%=?;$hHqo1&ZOk3~M9jh}Z&2h6!&ozj-JfdT)5R!8dE|+O=D4 zxQunSZL67S?sNOz?ksz(VDQ(=WnbJ?bZxkp#y-vobw2JN7-%h3?Jx{eI#iixB%5jb z=D6|$9=^TV&TDpOIU2dTrag45Pu%<1Q$|y>!Ce?$c80S{nzHce*I~*g_+ZNU2&)%Y zSA_F6&fJsZzWMp{=kM?DJE3&&UDWWO-%0l`I{juC3yvu*NH9#;k<(qOIzvqC-n)0E z+8Z}^8+)=(gs*nn5T@)@eOh2nhGV^on5JKy`!t5xz_DVlgvLgTOvn0Nhu&vwZ>zpH zW5$ey{{H?IJDH$o|DG71x$i08!UYSYWn==gUhy!w928)MboTZdH5Z?sF=tK^UX*E9 zv4M+=3kNmp;*~3IKdf|QO;ReA8+~)YJzC1wV6{dE4<^63CTpWI zku^ufyC=Rnd1rK0l8M91>q}p}e5nvVj$yszOwM!xtmVi1OoNgOVpX@-jju{DD8bYP z-G0`s#Tm1ExB~% zN_1~qO&k2W5@zf1=?QPeW`^0L%%>0I6f{UXuo=51D<`L?ezec<%ZqEeQe2(fz_$1K zzK^djGf%O4dA;@X^DD7e7^Z(O=akj7yu7lTnwo@W&WyWpV-_I#EvC547>K$}bJ(z} zt7})j&!e{$6&Q-?Jj?k^ogs`c$MZ*GcJ`{wgS9&}G&C?`W5KF$y+a=!>P`|~Qa)b7 zhY*=&H#ClnpB2{G-CeQwaD7b5-p7d>gT)hfXQi|*V6x>_uU<_iLDK_dw*30?lBUP8 ziRB6F1E+njOt|9x`{zBpjCcHa+Nh9c&z|Y~_~Z&NGfQ5&`{A=z_fY)QONMrVwO@GF ztoRFJs)>g?K7Rk0ry4EAw8g~4*uA-Yj`SSAeTfZ^P5{~gSasyndBu4u;_Wt-_vTxtMST}|}e{Y_*5m~@T zzdwh3`)Y0QL*_bX+1YEV5{(QR@_m7gb2&ycg)u#4gM4GpDRQtzm6In=#=`}_Hxv}? z-@pH}^Qx^eSA_F0om!j-QZNq=NtO#3tcuw(lXu*>8`!Z_ z15?xZ!ou}=UBz>BHf_@1H?QFZH{&P4S$X6q=T$=R#F61YN!ZH8CMmaYTGB!FgY8s{_c zYxYXpivX@zcqs?3)kpf96L)1Mwu&;WGpb+(W_|-!d5F-tOD6ucRW%(L z`pW%<6MEZg4G0ykHvnh2Ek&%&uJ+09XEK3sYDq`t3 zW)>YE^Sr;_w7GimqJ;`C<9vlPi4ThnV$UJE-(6u@+^KY!VNIQ*>{zK#rWRfQ_;8pW z7gINv?5@#sv0;4FzNe>K1EnJLZEfRet|78ZmQ)7KkX`9F=7C6o^TOQiuUnke0lC`}S-_`>Pu+h$euEl79*%A=Wy;Zr|s4B((}L zo!%S=jCm2VEOqMq4YoZkZx-)VrK zQ|mhU*EX4~t`3JTFs3UNtt;_%GfwQUyJ}yRXjhl*;@$IRqY>f}y+8XNu*dG*{eAB< z9YW~+i)qxx(}wqU8BJfbwS0cC+8q{)p+~(==H{y90X#zGt%AVdu~wD|2NY{!)}G>@ zt?8G0dWu-$w1qmfLG1a%olga4Yxra%23x|`k~v7^7gAoXe_nL;4$IT=505m%Hn^WESNhAIvx54JNZ__w)@}%^F1?zD<+M|NwJ9iLae|Bj;AvSEoVj-P%XF7H_rbNPj z4}xz7&c{h*yt4xWxQg#!hF-Wz%ycQemY&uM6_=)yh{+zmK5q_J@z6mMK|hh(n{ze-RoPYeLfD)r~&G3<{l@WFw3y*c<3eIr-q9Y)ou}5lK#L94P z#&{nQG0my}{1Y{A-RYKEn@A&&gH}=YQbcyd(AJhW(L{JIh^}`1Q3Fy#X$yxm=A4g; zno9#Cq1X;e3nPdDq9$0s4ox&l66TTVlqGw0X)d-K85*e3^Wk9}r;v+X?D3C#Jkk@V zY1dR3m}r)lgSQ1(mRyioxKO*nr~7s0v2MInM^|_FxsiL_t^4Vl78D}^S;RV1VqjO;?DScwRl=|Ad+*RZ~|9l3!} zkhJ;ABKx;@14ES#B|iR{vn8v}^=#{ssCPban%OGu(oR9+S#t^z%UCmsCYkwkY4?7w z+A`(LQ}-#2@**N4U09Xjhlhu%9v<4Q=uo|Nj4g+Nl}7-6qR-A;778E?CUj%Xl-`?@K|hU)^b5^e z_SEvr=g+qItd5S(Tg)lL?VH}0FJEq!9uGsRb`?ev^YGAzMDPInkGYS0e0`CD$XyBz zP24DcWIeKlSOa)D@nVY==H})CvzBWa#BSXVQwGx4EL^s18|^U0y?XWPU64;1DX9{7 zVKqYpj{TOFRgc^#(GDElcCy4xCEWW*X3a{MruBZa#xVuQ=-9Skr+mG?Tr&|85wTO@ z?caM#FV3^$qwk^4IzT1IpC$>rt`Y^|xTJFUXY$Yb$4S~}g*kZ_Z{0om^D21Dx3TxMptcJ1-%l)XuaKHuTJu~!;TJ$d@H>fUbU-j+A( zkRdeJ-tJs=^X5$)e*AU0z4m~zx@qJe*C$MxRB?7$e*Bsfd|kIxvon#1-`knK!J#_& z&Gn_b-Fv?qRcvM}g4b8&{r;Gw;X4=)0-L*2T-@qo?vdH6UGK`7=O3iu_-Q!t(z3E` z$cHPpEX%tKrVUOqa<-ziGQ`|30S9XfD|?@A6U;wpV4Bd+)PuEYV9<|=d;@!)H#9W3 zPwNcJ9~pEY9*g{#v+5JHYQgFUmK*bWt9K`w7v#4JHA?aG^Jg6CwqCk7$56tz=Td2e zx`WEZY?-W&-but4h*cix{u=9s@Qb8ID$-|%g7vHAw2PdB5j2D}Fx;E!ZWA#|SxA1&F}ahzLiOiPF;`91tHA_v_zR@7;EfhRfJr#1DMf1PO<1N19+&Z4(mj zh@(GOpBGmT{r2|Xr9#5O_P{Tqnv{2dlvQljdeHUzw@FRPUPZ)OkJh)RzAbyY)VKS! zG8{H!is&lhGUo^(**s8AhP#6OD?vSHh>JU_j4vmK`R{lM+uAfM4)%z$vjKeHAC!n! zH2!;H^$h+jx7N1{$phgmN?0Z*eQo@D=gyrTkgFu4B;#0v#xF=4G7i?tzm8DfeYCF~ z3_d#sfzhMm<6h!7$U9p_8>OIaL|>Y}@jK>)6-Jp75o(O3U~Xb|M4N|i^2Mj0KtCv* zzjotBJS^M04N-P`pDif`5lsu#}r{7K34XI%mqdmBU|8tNT6TB9`tiNW6; zsWs;QdpXHUV+_&IrDnON={9A?_V(b9>}aE8)9u0PeK&|_K;%Ns&4t}MrJ zfUmSLfp7Mu4d-XuVamWItb5s2oHiJ)K2e>^moI;Yi2TPB%os*Ebb5R>W**%Y3wYOYf@+QNsBEzXc1Kg&^e;vtCy>)J76c;h47=&;T&Sfy{DtCS+ zTa;Y1=v705`O`CkHRPfo8DXWzLK;I=8%LnYDO#`@-3YavpFVxU$4~w0?pE|{H$`&! znm#VX`MC6=MH}Es<%hEBtyOr<_W$YoG1z84ns`mt#PzjU=RZ(m-n@HY+57p*w#CxY z&)lcvmP0F3^zN}GSO$!T5Z#KJb@KG-@03bJj`T=X$aGE%3=9la@kqp(!5y>Lz%W9W z?>mm%W)bNvt2^a4Z{AFvQW~jAyW%*TW~03XT12|_>m8&;#IapZojSGavFAMsXg}UP zKr~}!f_Yys^KM<(`?LOoJAdvgBx6;X4kqMyFv&4ZM1TEa*Ed$IBgiM2* ztF4&gsOEZ}s9v>9H7BH#bH95g%F?hm!HI1u_nOF!ulRp~3ybp)4Bf z2E+yOl)Ab-V2z!R$@!hH%vL%W4`ntHu?gFg06j1dsiF6$Q#0L%`dwgrbz7fbUZ{xJ zr#|rcO72qeB1svU&4Oy3ED|c>7%o}M@Rq{>+SO)j_DWdwIQ`fFvhruy^)9K)qqd;-IM~8oV{P~d;rgtnx zW2E0w_r~%yAX;Ee>=PiVtqE}vARkx2pKVpfU#EPOVk~IWdzkakVnXhe6G$+N&(DdQ zY36mzAX0i-q;KC%O2-yTNg-)${8w`R{N9g8J6vW9Y5yD==mxzDBVt5*!x5|c!(o(h za8+{%l6do^7>G3O?;b$!K(t{`^YingT$NJA?scj{35cEvkUIvyX^w&rFn-sATVXkM z;ZHx6U!1ot!=Xm5cw2TIw14s#MaQ}uUDH`xAQZI4gqZCB%=Y}qY6vYoRz!6M5(RO> zWX3iFI-v(upcoxC%JcT{?f-PfyD3m=l{!xdB*ThxA_|q!8wC&5rfHM(KaJT!rGf6R zuMH5lp!On9y$;T?9cTq?R&x4y5Q(ebaRgqL`En$Z_jYCKWYv3ywF;S9!_!C>;OG~h zKO;+ObL`jjS1{kwL`nROscD4krKHhntGmaVA9g8@gS)YSI4eJdqC z;h@5My%w@L(Yd>K7hqF!P2n)T!Tv+*#)$4&0$#|+&8K;oEVcqrQ+?g9hVl%&hKo!XsV|cKn z$z27+WQqFkI=2_EUQwbs8nZ1@BUJTp3esXA?G6%a3G0(gQo%wSi{h_oRQ=ZrK+1zz zD%jDkT(1YHaNv1K@Rq9k`<`a_{_!L$NrK5Fk?@E)!9KPuT(~d+(i+M?Ko0g67hZ9u zF%wEsPt)0D-z&ChS`D3idBDobt}=dmP*BkRp>d5<^N0I(lMV{o0VCQ%8bhSduRv6n zbmN%L9fTTQF)%}iuBM`~FnBf@9F9O9p^$h*N*L@5gWm%Y+o_>(>)Nq4qV^~>RZ~R6 zDDo6cQ!7%QiG^9e%g?bK@VwOD# z?u9fnUs`$-Vt6$&{8^fxrjbtaE&Q;1iP+)ge%$wqA+aI7l8uRt4aEqK1;d_jvm~Gy zOxSCx9-t<7?OEZa8PLYy{1rgI2Zuf=5!*U5MNA$*t1Y=?N&EX*{Moam)^G_)(I=9> zIw{hRv_XA-$kg85!qq)YfgpTqliAktA)4Trjp~**7V$TYhP zU7AT_{l?}-B2&TQ;^}#<+(6n-vv*&60we=86~CbHpjW8r_q5qproM< zv-n~q;2a1;Z9#peCd?^6;rMZGpmL)Ul9b*z8zmC9-joM$-%QOP)cNrttA?sEQbBY9B3?u<6zxyQDfCgEaYxY+gKkBdo9X#um)q9%=8jCeG}S0Lvl z6_4)+RCt8}JZEsiCCcqgqa?_-(T*LLLHevPft~j)j`2;NH8-vTfX_P9G!A<{fBnXd z4iMnaMP}Q92tt~@d?z|uM3aks%CG;^_)Mn;Gq4Ld`^zE91CZ0{+Ay6K`Ii@kU4XJx zGkK2E*3teFO`&Piutsq?MMb0f9FGdPouTQog|isvObATA$hyD3i5@@t@$?qTsu7Bi zB*Tr^JRH<{Nab(H=k{zC7mVW=7>|JHD*eBSjW=M%*wt0OtnKtm5ir!zs@f!LYI&gxxXF2%@GL8pl0B6%T4{|AAW z^}((_CzCicvMqqY8C3{SgMB}eKsJdardH-2aU;S+N|J%aCD0L%AMcHDm^8u!v*Y6! zXIbPdZ(tZ?dk+uR-oalL{J2?bkg*%0;DWpllAZ8GP!7Dq9XLerJ&z#U@iv6CUD?jJ zt;-^oj?qgCP)FjQD0jGn@00WNf{RvWIhoH862c<20k>qrW5C1+vS)ZPEEhUzREkh^ z1ve3wLmda%uOWT{3}KHdKs!gS^}(HQiQDIAQ|oe@fhzublCKSIGn z=rNMO*Ws!r5CvD7$2Y}(2H$`>S4HYMxE?lx)vynNIj1enf2@4(EE=UYouV_2f4(TS9< zcp!?!SgKkbf=3l78z+n5XQBeYXYBx@KJ`q<+2CLklt7G3pY?EgDbUOJq~NWn!KyYC zHpW8k+44G6(Gu%^NyHJdK_&83oQFKBww3VHtu}Zm1)z}c?;b=$4@x81f}<`lB6r z=OUR#D{3=<9(xn}1y1?(by(c9XPaOulS$&JTL}=R_=u|Auijq8a2`Qp)Cy5=Xn~|* zux8CMfG~@TGH#G=T^qJMa9~%u^?8nD&CBB2b8I|QcNLO&%kb|86C_we7Iw;CdI?q4I@Xhq>admj+20uDpqfjU4{-qFLw+Zfh@RzwnS zl%YHZepylALf|J@3oT{zU->N5RYDI=r`pu1Q!()?kXPYd;&Mxu>aJL^Vz52E49CgT zJp-n?e|;rVR8#~@utI%3A$nsl2FVBZM%{1*<8!V@3FR&dMeC6^!+ZrpE?>Tboiiy3 zhJ_&GGJuRzEiuwLzCz^wRz1B7q+>!}e(TQ9+E10SY&mFyRI-8(BL0ZW{a@)8Q)hZp z-YNsJ5gK3(h=W^y=RTDDWfT-Zs#uc6M5GHoP{IL3E8bR@0fHJ2%)rU98m@+i-#a7W zXA1U)Q@@EQ=m4z`DkY3vXaz<7eqCZmQroKAQVZlHB>X&2ho#%TkwgrQbUHvI!`=*W zo_#+)fQDP4NJ+(dWCPFjloUWd(86~nbV7_GO-GfA(J7>P{)Z;>{byv z#a+8Xf97`Ug=_dC^JEss!{Q+wP2@MD>+z8g?o? z!ijqxrD^mx@(c_PVpjU&y!fQih5!WAg*0spcZBBf6ybsLY=nU8WOWPCvlWeefzqh`IfwjFdz;tnCp%#9K1KpYtDCa8r}m}{ME`V4hNbV=wB{Q83J za-PSBfV&_8MKZEll$gj_(B}rhN$)ScXB8oo--R)TmaBx$G>~*4YZ>MXp+OGi6Cr$+p z7=!A<_qs>vP{9$;n8t;Aw^rRobr9sJ+)`Rx>`ALdr{wj$cQ_a69Oxf8bO2a* zL*BhbpE_Z%KnO0WP>Nr=^RdD{%i5*2PGGdU(EL;Zuu(ktZaGYbU9ip1ud8S#=XFaI z6bwMqs9OdpFbPHIe|UppEOs}04Pnu;Cg^wwu<>I8(lO94kM?)lK}Dz>!a{yWg$YHw zw}tS=t0?rLiE6LrNPjd;MwunsiH+dq<|b_xY<3(n2zEVe-l3)ZHbgWC36oBpJZby? zd0`y`)JG)*1rFnTyJApaM}E764fAz2k&^&~?93~i0(A+p09H zEwsC^H0lWYOLLe4J1Cbp-Eg$n(R-;<2UH%&nb0|xws#WPnaqk6+s7id>0H!hNL>0` zm1Q;DgBmmlTlw7922DXY$GbO?`ruHOhKSRt>TErSSz_>Dzc!L@%GOXks4VdJC%`Wm z08A*_l!^jq4J|C9zLrHUBSt90_>HGc)kIP>3Ys`Nrd+;q1(wk-0MD@qgilJZUcJ+!t(uZ|wcbjDPz~RlR&Z3#51asMTp!C#sh#4Tu zzdi_&sVc|AVRg73&7X@r;VBrZE~HcH?S$>_ht%TzJs}S6$uv&Wi!*5OAI-zLpptZ* zIyVq5Tuqz4?j5Khg;A*)q*}DV@mL#0|gl(_e1ljD$0+D2}?XsT%cA_RLv1^8a0XB zsBMBHL1145p^Y5u5*!JErAu3dVapz{u}MNluRYQj>?_cG2Paw#b*{tSkMPjmuRD3_ z6tF_q{@r~L5>rWJ2auV9`+nBLNYP1V@bKVHw41EwlxKxZmnbP2qi1DhI9H>&&(`s1 zZrY9ZUg}8%CTv9~GTL4#!|hZ7C-p}65_pj-iQh0=nA0dptd4SM0YH|dc3(n{y03sy zvMW}+AzR&$i@vQvP*8kqKZFcY>5*0T&jd<4Th5y|k6N@aTf{U;^!e1F8I7#Og6Ibc z>Tr-csLV5_PTi-%!=g}_eGgT6O47!p&$F*?tu~-YK(>9Z`}gk}yq#Kb;qdQ>)uXx@ zR?6gxJHexsh0p~+(VKPzn{xn^b+96GojSMh|lHNf^4k34p)3ItO z1<#%ehCw}5$YVgu&fblUU5tqKm+d>t!ZnZx-{RK31mwE30CfGi^TftH@(IT$pPX$` zaq@0|P=ZJ*Nd;KsS`fr2f-_kFp`;Vz1ryQC1>1ap)D__I9jXLkNowKlMlQDQ)^L?; zUDM@IK|_CmCUA)H(+25{)eE;*;ki!LCG(MAGb=>?&h;4dauSM^kUX42n+)=ySlZ+| z@iK007rPG+?;-!)ufo%*Lg^fm?>WT)XYk42s5>zI;ZSCJIV6@f(+hezI?WvZd-w#L zKaPTZ$(hKF|9WT)_ZXntYq7F2Xd{T!d^GmKr;Vr}j`|7?EdFb0IDLAtPl*6?+P$q> z7f5e2baD$+y+oU`2vA67PaLu`*Q4A;O{7{Q?T4M2o$AQpVzWRA9nhYH8Nn&fLg6U=4ClRB{P-Z(?G|?m!9|M)sEaQDf|FMC)X)W8&)GEh&$G z;^SVt9}yjk=AtKu*dfG2?3nzAET5E@>*yBhdC(^_eN?q$K6?Hv9tEbzGD>{c|q64q6MFYI{a38EqY@~dxp*jzm*4N z|6UOn{Pyizf=P=QG#OX8bLSR<8m>MxS!f04BV;`o+X~!iwyI|`1uOFNd!IiaKu>vv zJ6|pvMTQjgf8Q)+lBt-aeE>Qr75fp}Z6PxM7r>H#qka2^9xKEPl^{-;h3CHTvNhZ+ z$>sl}d}JK%Fw3;B(m~?~=a73*XuO6742W`Y?U}PwJ(mQVF+mduF^eeM7g4Z=PH_r_ zzEr3o(Djjt0{HuHD+Rp<02!t1Sq;y)1|f%i3WPQq2agUl0%*|FD9xcyg49GrMv?oX zVRYm$(n4^^nbPPmMf9^&;ki$(580bYq_VGCd`)`$6MU8$H}07ie1sUmn0>>B4I(oE zRBkzkUL0cM3QSx7Sdm3-FS_KAs4qf|8qm5V1EZ*S6;7v^+_?+QQq(g*#(|3;$iN-O z*n3jvG_)gcrlb_Qm(YwEL>LAUlJM6Uq(_8WUYW>p5>Tb4v!<&dFc^g6Qg?m}0cf6V z-OjGAH`MzKRwi-4N|HSH>=5qL2fm}46J{b5KR)SEIW)LOgHEv?m zni5gBb6r=&-N3!)XmUlZMd~oEV6ZARO(8xI@uUPrz0hzj-KP`vS$sZ?JQ4v7LtmBO zAZ)h_g_hSnanIuC(mB_Oh&(D5uZLw?#4>H9eX^g zj8=GcHsXbqGI6)2K^c7pD5-9282QnVHa-4QneR`x72Z89?USV=xP&a!1w`Z*q~v^0 zys>=WT2Fr6@0z3Cn$3W=Qu!BIP_>RLzYFM{6FJ;&la9^w02SM}$FtU|R;2P7qPhgs(C4MBJ0epe*i!xK92)xT4r4d}feLEo$*m~w`uW+auQx2y6rya+j*T}xb45=@W~5_uxS&>K z<%8*ZtLBxev?aGsW-Qb*A3iKbIxG@)Fm1CZzhW0SX35tuC-Ijd%gXeQNoHyn797~f z7*_rH_&a}9M{f6>%&KEYyo;?u_6o<{a@FzVW6F8M!^2lN)@8p#btK(j>=+HUsegH} z)%GGz!D0E4U!Sva5_vhBf{!5U+Yhhq0xh=zhV9M3&6^^WvvB^GP_`((Dr2x8QJ_{v zUvy(AV{D0Tysof@q2m6{!L9A>=gR#0(h*OksbnYg`{Pk3Pd>%8BH`VI^UjD~4(HOA zXK@c*tFEr@|FXSALl>c6ztu2Z2W$Jc(C$EulX>tcMLjS0?}-w4M88vyTmhP)m%qd6OKO!%k%#f zF#p1GzqipJy_(OjLjTJ7NTz%oS`uQiTVunptee>0)v8=UmJDxyIt+x@5v*^7EqZhuqT zTJeD?--@dW?fcew*Gp**JfEd#Z|KP<1c|nC*Oo0?ZXq*&fRx21uKe=_^}csrsBe>d zi+7?AL`C&jsK_d33sJXMD=@{$scfFizH6yI3rEUE2Q*<_kK9UEOI^?ygm*AeXC{lT zT5g8uF-y(M8P+B5^fC7x+A=Mn&f$cQ9Gcp-tad6RqS61aVDUEp!3tYDyXQDD^}&{C zmuK_N6hbXYGr+6U&?5A1-x;y}XT&_8>D}Id7KYgiVK>5uydy zQdns48t+nMR83c)4G;>|$}n1jk3V1D0{6X5zJTLsRQ8YZMB(P2kUg%O&&^sPsd{SB z;bIY+Lfp-|6wbx;9ETcS>d@Jf18rU%7c3&%C&$BW^;IbZS;mEXz9@})4*6eIPxVIz ztu|!I&a?#}3pwqR?OWU;|LpPPHUvP#U&Ea`4UOJ}ZFK1GKYVC@Hb3|r+H1-F^IFng z%DvQ!I<4{RQg9(Cd%8l@+sRQ{huR-~i*Uho4%q1PGz?Z}HDTZv02J@qYbIu`pVoQr zE$|x_{l)UI4Gbu4pTLShDwUlLkFZ21Y^}`7N09hd0X}BN0T*IvR$g#}$nZ6<)zHQK zpvrhRuKH~Bx$53xp|A{I;mOl3lDwks6;c~m){vnfT9~ZyGr14m+C(f+s)J(G*;cEoy z{6~jZ_zt=^40AL4L9ZV?ouZ1xDWK^gXz9auk`c&T+lAsqrL>O4X1-SXP)PQGos7!M zQ@^0{T^gdvKU1gH>~YhD_f}R{3vU$jfY};e<`S~iS$Ag2dD)a~N!UdwB9G4x2XnVx zF~+MRv&JkeTuav%E_WU^Dr*}pT;4bq>t4G0TVeHXr*KScsCAov5G@8S6@O6^Rm-mT z4ynNFb#wmM5(C8F5X4j0? zspLJ6dZCETt*I!w`vXGt*QES=(_=Fn5k1K^ds%UPG7TW;zct(i7eLx)?4I#yf& zpCw8k4qgdgtLxJ=Ju(2))LE2yP1`Wqa?24p8S_p^Gi6MBhadB!<#bmJSq%B9*>(mU)hwQgPN7%#OUbx zAGtpRWoJfL!fDOq4!UfGx1xJ`{Y_RJq=YXdS!bGt?{* zJpaNfNu1)q$nOG)zf^-(3W?B4Q@DaL6Bbu>wu0G-}{1t>>J5 zfH|Zo%*Ca&_zbM+!`V|kiRiLetfx8LL(e^b6|Sm`Zmn7z&p~uhK0wzeJQ(Ss{!ZFw z0@7EdQPz4Yt#?gvWrE1aN!&H5#`*u8FEOymllw00FxtR& zbzdlYrTMn3lL)$g{d)W477w_O>#p6qn?ZttQLV6!)1Oa$d1rzVf~=T@9}rC85`E7~ zS!USk#or`GyCj=7m@>76zc8Rk>^fgIAYb3n&h5TQt$1;P1gd^CEYCi7B zKZ+3I-}zlN?>nw31)ztqIL+!MMY=`5DadLJz6rLr(YJ)!;&t#rk#Ma@c@mssakT&F zNLR<`NJsHD?|OXF7Q(l85wf|6aIM#9`&;e#-YyOcF~UOR^SjPFQDz9NzA2lUE1#14 z>jlki5hz$h`#5KTi1(;i#Gl0eN;y9Ia^2R!;Uz*Vov3R2{S!|9+)SP|Zp;xHYNG^~ zW)T12jJWr+Pt)y-ChE_vk6Lr039UGf;2eiR5oT+0vCPox_tvZB_8FkWqaP1H$UOwR z!+aF^@I2ui3dmNpS7>D{swo)hrTLux37xKeT!0`>qIP1r1JEXMw69@g_ZY{hEY}>a z&@94*ZnCl2zr^$t8>3E4d4U~mo)e`Nif+Op9znH#{t{RPw6?#qiwnPlumScKe;ap3 zt0w5=T)1$7_<3&UlS#EQ<;~d5w?mENHJAHcZbB=o60%S`2mm5PdrPCfk`VxoU*Vup z&)hNex?xCn3}Y+HA=HS28(I=>TN3+5JrGto^m}xqA$@w339#Et8RZ%aaaPqFfU74;;v-=h%SF6Qyqb^W(=4U@+tKX||dX z{=6<__e-z8yy!&Z1{{6$Q1H zmVi{BFS0aT63X_;Fpz)%6oWOn7=T&s84T_IVe-{2v|xnTqy!EtlWxEKk)zpY?&U* z5iXHagg`3-jkOdH=Ks2udO(U{Ru(ck*K)hVJFwyOkkY&H8lA^hp7)W8)}3v)Vss9?Cy<=1m5S~ikk z7oe{dCUEdR%+E%O9d?TudoCj*qYy4I1L_YF(qg&1p0_tvSZ<(g;L+a)n^mJ$qNBRps$ux&VN2Z9gzcCfJ$iJgtzg7~a3kxc>S0Gu?%Y$; z#~xe=0ohy>VGIw}q1{jdny~HIgUU{3oMe;dV+Qea!WM<7-PniT>QwWBeh?ecF}HdL z?XUSiV9NTAv>hEJ@~S)^E8o7I2DVy;B?4BfKY)kLMq~P3Am8uw$l+JYX$&j&bH^&f z*D&6j;Y>Yh9?2mn{uPDf_rzc$9HGp0{c!S!OE6Iz`=>}7Su!y@X$r2Cx*;S{)85xp#1p* zubt?Odg4(JQBzS-NmFP<;St%N%eb8aXd+U3c&@)bsGJ=*Zctf!+v6T2AdkP`UU)_Zb7%)DhlU68pu zr6X~-meP6(L|(rh5uzjT#1s{2RUS!6pFh)?_c-}(Ld0nb6(M*JiH{xkjo z6Q?iYpsG*X?uH=Djsk!oAkm(qc5p^5#$~2|#ss(Z>kFrEQ=5vsWIOF8vJN9f)cddu zd~bk#JNX?XP0o6o9ybg-W@KeiyuGWy+F_)}+L3rYz`5ohds{3K_Xe!t7XIVsHK3n& zR@kA3V`>sFGq)bZv6-SvA2IF3U7%E=YaLt z4locp)oTZsn8b&GSdybkS#m2_Yjw{dQ`A%IDr}$ZRpCEe<3FYzW15@kpl~^Tg-a8D z*@D_bWg#iRCG2B7Ffm=5TP_^!zx&M`o1`N&#U+18=$A7q& zGeavT=EcL0-ff^oG#I-zE>e7qE-L$eQ*8(YlO+Rr0UyFjbu5`vi~GnqiGH3hL z(iJSu?>P~-fKT6cEpedCR9~RxM~=gjtVOUn0rB>M^QR~6lTGIX=&(~OZ&NBw$A&YS z1W;#k$aP=Ab@yYBaQC60Kg53^1iw8C9#aUW$+0uYtVTe8dYe3)V$Z>cwbrl(VViId zV|Pza!gHZ7f4A{H#LM*Q7I`9Cw*v{g5S5l$*|2vn*_9z(syRAD2h+5QOXnI13rg>8 zqcOc@7+4bVzz3yWNYEcCX*#mRESIy4RpM*zydY9up{oqT?xmRmP#gQ7%*?-t(*zWX zklsjiGYA(aF1L>}m5nry7Q&#@5JL9DhZoVHNYc)8*coPPhYD=%#QVniJUTLyqsqrY z75m;Jb z#aUP?+5~e*V3GzP)x(MA$`KbF2o*-E4oV0^;jPAcur5f*Dy{q;cn#6 zXDM=S-GAqAx@tip>BRebeEQPm@!O+sjoiH~yu|b$1Q8Muv<#NBc5w4Z(|eB5=Osd8 zFPEas>(zW-nKmaBs?t>p6ixq*@d{)7<^7F^R8&=)LAIY0X`BsseUyxpT9y)Ldd?-!9;vOcB-cMc;rGutpYS#T-q(-J=<)IfGIt|0|QhH!u z=$mH8m&1!&k;k7!+o=lqLo4#X9DEzc7zd)k^aH?oy$8Zy3_|*tjYRbo%}51CF%iM! zVmRYq#hh9zdbIt)3Up_}E5iEvJcA{md|%|0KDer%!+j9Od8zhr%k_m!t;qddxFc-! zDolA9y;CL=p+$>+&5!Fi5)WI7-7s^sw0ai-DcLrpUu;0_4^LM)kWOHHczWC;y5nD;XbngON0M73c6 zvEDS=|4)|xOFf2(=Y^@9!bw9xTikNid}u$-2@?C9zdEtjQeX>Qn)84);Q7KBv#Ea8 zyb-m85{^&DyujJ7Ag++{aB$lddo(ET970||)q{8eyrBrWN7{6Y^T|u}=h5^z3M5Z; z%jGR{fk;{j@G4JFkGi@a*TZ1o8?Zwcsf2-I2_VVgbJu)^^%3v@XrKrbPj*%e%7l3D z&{Ju!tqAUjS{$PjpTaJOWfRQ4fdmwKgNZB@Qh(wSW#_8ytoP zfgqM@i42FZGZQpm0m>S$i2-3wga{AbY?y;+vn!nN6;PH*VT;n_v1g|B{P>_eR(dl{ zA`J=s{m32})-GT)0;Ca5dR0&PI!=iQ?;EQYGg?2e_CT(yiFnVnTh31=(%KPTyOPX@ z7t@6Muy$nMM%cf+gH(S&#$y~Fih1ahIgS0LJ#17*}%IJ4dFRXxi0u^70QWCC_EEV zG?rmeP{HP5vAg*6TNYA;hPzeZdzxmZ=Ki9}HsenlObizf@Ipp9+>X={C#6SUD?qs1 zKM5wDM#sXTl(%R4jC4mLntAL(NrD;)=qQJH5lULAe#@PFolD639qwo}J(U=7#*``m zCR1_IpUk*%*({s`RIzLH5FBbI=>?@M4?t+jW}!JyN*ccrkFqu*L@uN7>WbBC65e7C{=6 zh5kM7XSnI%Fm!hUuB1Ndf*Bl#8Jvq0f^?jSP@rx>1?tb2Hi8d$Kk9kAh1O>(u#9bh zi~mLlOybTEv5GhvgGD&1)uMkTS0-r%J_>Gpxw_i;sXC}S9(}c<1f1R**|*olA3M`WF<3kAej+UN~<6!$G1(`4 zgZQ4N9|4+0*|%nP)a#Vjw3My=Qu`2KFreE$HURDCf@%@`X01wihN^jI;vbb2dTScVy6-a zbUD=%{n5FAqF&n%P9Yu&&!qo3a)H4@DUDA?8-kktAeXTMru^NvmCt$)>-++glyHhK z0H&FU%zk5$SvgigN9WDwLDd+@?9avycqUaE zD03gnVHqu49gHy~I|GayUpHm-g0b-5_T$qTi3{k|A(~LGjRzLf`O-sVN=UyK;(M_? zknu7C&fHgbkX$CBwNL##@|2HgB0{AkBzt5EqrV7vza5$?C&gmZv8juBx7_BmdQCQU z!aebz4~~zK_c1KKbE2!|adfYG4-&V;RF?dE&N{b?AauFI$gvR7j z_vc{Mv;L-G2N{?a7&HLibW*Uy_B(SNv*&^LumqL^D&N^gpNF>p61GWGh>D3>I96WN z3aO{EI#A;v6Q?P24Y3zKIGY+z9@JWgp`ab|DpXN+tp18`KVgl(DE;3wSDdhZB0Lldb-Y>$&(Yr^UWVm!Ey~@vGD0C?4dbTC89H{ z{DrT6LoZz^;4&%Q?_0E~OBYJ_jo-BrM>7eRU6bG%xYFh$F@I_-|mr!!e-F3~hfgDgg?diN zVsk(*t|p>y1Q8XbktFOzz^vsKTB2T!$7m4BGyXDOmLqsX44RK!I8>dEW1b{=a4|<^ zk9tXtWh;lPLx^Li0W_fS^CYR03t+rXjWv!b--AncA>oVw$O!U;fB-=>bduGeOgN@3 z0YT1><)Q*R(e4O-bcyTg`w!4ZEz@5saZV7iVn4onmw1$WGx% z^x~^Pzcyp0V{({%8G!i<*8==79SxuKH0q-aQ~_1LJm_B$rl zktCb=iqW|zhCuTs%ZaM4X~-AUIsGXBd1cqZE0=M#7^Z_PPUqghh*Zq4K~+YpyK8ux zP9J*kVYjmpTJIqZ7;Dgsnn&i#IT&j*R@{LxD^bL3cRy}VJ6m9(f48r%FB^S07MJ8yn|zz=pGs zg*6Q_Rn%*V+o`VPr0S?jCqOUxjswthaBvXCR$)v3scGP1LMs5XmS9Qr1Hrl5J^1wP zZAfPx6NlH0wPxT&Y*lpHknTSIVm#9M%qbdQYLLQy#W*8SoG(553;1O#SycXu;6e*KrKi42}D6 z)6++IBS^ZEg|noQz(zRO^AAFqJpRxpi?B|jw10EVs55Y2*b#kb4!#879H-i;wGQ`A zOA$keEJlr|Iv#c!5Mf%&9!x@qgNT&$p_sw(WNUV();JAa<~K5mB*s1jJ74y&_6PLv*Pz zc0k2oR0KsuELfvxELaet3HFv)5H&Gq6kEht*uQfw^E~_Af5HCn-p73$Np!8b#vJ1s zS2?e8&hg)#k>NvWySD>MryHGDM+tIkvH=Y^Z`C}Y<}0fz5{#z0?54}UpKGiGZzdB0 zaMZ2t(f4+^>iTSkHh ze=SYl3c3H83kW*0_O9w-Kp`?REZ?cjvZ3=iIt&ne*Y=ka+t>+{_=Wz1dPm7LCmy1Pz zVwfq^>FmTG_(LS}ukxECk)|2u+oBDA{=5_5xNCnS{sSQ5#n8lU`enNRz?A`7xROmH zNRQao)DS9}WsIFnCUh_LzqmWveSPFN-hKr2G+d5Il02Uiz(%XFWFKaa**F$A5FoYZ zc~0UV3Y*SYD_)GpZI9=5r8I5}knh^-%&Lmv_kThynxZreug8kgUXG%g`KZzBQc>cv zU#Bdsdywn?9ZqpYZ};zPqP8}nppRyZi717qswI_CS~%&)Ykj#|<^fQ|C_fmv@BI?_ zI;hw@@6-GJQcL=oe;^pZRhiY;ayd$Sq-5Ub+Cqy9VlSHo>c8(HM&wT&7*3{Y9qXFW%1u~IC$P+?AT`Lv9?Gt% zIx#GRxpESG{pN`Cbq_65HJXH5^|soRle9fb5TE={a{Y+5?X*f+&y} zK6Hsx3RmbRgD%)?Jj*5vxKy7t&@dG-O1R466Z~Z+jg2o64|kg4nvxqO5m@Xe$(1yr zy^HrIy(jTc&tZyoeJsTqp>fT3oXfeJX}YMbdr+O2*8FM=G!JZ|%oL*#x$)5`fWmV4 z5cj&|VLoU6jadST8H{Ag5|xLKpFSm;=PdtXR?x?o%QI~@__LY~S7M`{%`MfP68*#3$^V@pOcY|Kl&HP ztA`y@oiic}Nw8NhhR9J|x95;gJJ?ZbORUu2mo@}#Qw zkY+w&1#aZbIPRhd@N@0WY0}HY#>i+nCR?Wsa$k!}#JOFf)3AaSJo=7?5!RFNlUoe_ zI|gjiyYBh6yt4H&yjx8)^QC**S`SIuy2-t~&C>^43K-i8klp_p@La#oTj+k%y}SG~ z+GiQu04E*`epC5B^R+HgGSP9G{f3pTKo~Dwx+I9)yTpV^la?cssr~{tJ zG{H0FxJ26I6LGf2)zf7LO)Q0F<&eI6H@&Q`P95TsJ9r^Pr|8`0(gmYxxsr*hqF16?0t$HD;{yohnX=rbup>AC*!olEY*Nb>CPK z=_XH2xmE4W77w9^RQ8jzKh5~t`_>D`GF6PpO)@_tsNKX{t%s+S&cz~TiD|x@uH0M- zZ`?Diim2j9=3i!B?z58Wb^hrOUa$UVGlru&`sEvA_k%N{$oPhv#E4LwVVPq(Qo zb|>|kz%}bksi}yann;U{v=N;PwTaZ0+eBrB+8|x(USOo0d3I~GU|Q_hDr%;-c%Vrd z$q~&yY1btx<87O9G<7X$M37#t;qVR;G`wNo5`1bOZS$$@DtC8+mb_1zEE=UrACi1fD0L26GrP1rHy zkb895%yG?TSLy8t&T)(S+tT%qy(WQ*?w?3`JQ#5?j3rfM?o04w4+{|~fge8TEA%x< zc~x&BT8D7g$xl}|rRX$fd##K=WzfUbK~w%Bo}pv>k3TkTc4s=h5@Q9yVb{2K73t0lI z&-bxW6G59^fL&^B+{2>xE^QpC7duKnf~nf}e74X`|NFFLz#1_s> z?N_zTN1||?6c>J>9L|I3IL-E%JmQ2klp#$awT0L8mvTxq{u(qP!B zZxz^>4p19TZe6Rdzd8!O&0&q;uujU+G^3m;G(RX1`AFeE(m;LrmV)NhP^p|msR`iC z=gOz2CFbO||H}5Y8!ru_N_1~{Px~ETC!X=)Jh?#$*Go_JblePcvwnTs#{CvKlS--} z|0=(}xLV0qX4(){f2VF%^Rl|KLUm%J<qOsj^3hxbT}3IT8o`q2>S8mhf(uZq5xSa8gnJHavPY&%BB?&&m-o+ z65)Q1_a^_#J5)3xikA(Y&FBt`og7kM=Tg|mr7UIPkMqJ+qA zn3+qq{{5d7QGN=1k5Za-$n2cU-{hgP<*LgZ(~E8f!>F%pCgg> zsLJF0_Y7@|L;xtRwmR+X;{p6Jxz+eT;lL$p&DdJ;JhO0Dp>a5k&Uz-f+U@A+4dfq9 z{hI<(BY-YU79t^Kh7QnlC#nxe=a{urGUZ%DA)R3lM62!-lge}m5 zG~<0=O4M}!?9^!oG`pk8s3U)@ov_crbwTWJ0c$bR*-9D6u`KZP^MSDo+?sv(vknYH zy?|hc8ikCw7RRhr!rysOMrn;nyPyg7(Hi~t_(G{`sbf`acV{9<#a>hm^41B(LI~q7;owb_srUfW5MmOHGwcl&Io1_PlgWACS)UT(pQV1ROaCCa36Ql+(3$| zy}C7r^y5H&VQ_dQM(&Xn!edNq$ssnK{`ex|y2#Wxl?2!1rQQAJ(~K|gKCjrOP1p3H z>5c>T_4$6=qDi;9Z*7}zdz@?gwvmtA_J3dIN!*q=`;)2Lt85A=S*=Rl)=eXpGVC5shqneT-ikT2Ad{ zcF-Y>>o6>bnlQqWKK$$Dy~C^5tT};^4%f?DTIBcjC{xq>eqU)d&^(991ZbJ}%6Z@KA2@g6pf6g@y6P!X#lxE+0uvQRNdBNc8YTTC zUrH%`YQ3>w5Jj}Curq#@W9d`7GlF6-(+VCo+Z``}zmj-@D5g#C$ zGQ8_;V+0v4^ynCyhc}s|&$QZRY;8l<)+KPx!a*?}Q%`+<0|r^>QQ)K-Lu@nM*k3Zi zr*`quynD<4-aSlzu6;*U7?$QD`eH<(7RF`-FcWnTZ$-mZIIhm2tioRU(A6FNdNHq=8RnMVz+0OviW&`E^0d>oc$7w#I-MNq%Ltw81Y$;Ve$BHdKrs)1x83Dn zq)~&Xr!Du1ZM|#RvZ7Bww2Z*{Og5=qvLLV1VkL8e@D255sk|8rKJ-J+yOO4yR$VxM< zeoaXJ$g1w{?t~Ue79Ce1-()|=%7q}SMv@J3)JgOJI9t0K8=c9Gnk!b=Z)8qpF3uhw zBX}8`wp!bQG!T@_&uY!AuP9=UNg;2_DJ4 z?dIci5TS|qyoyrR6I}YX&HYZAl7cW6{_iDCp=-%7ufKFj_Ik`y`)h=h>g$ zjroQW0rqXjo+c-!vgggSFy@Bky<@$={>(~3aRfh%nQ_;zK{*6moO`+f&8=b^%Y-TS zDpgK-+NH0c3^I*-^aTrs&FH@cLjZzad6+JGA!hn%OZa*k#hMjORh8Twb{eMQX|?He zh>nUel6hZeIC(xFU_GuD1FN>|Y;2j>ok@0#y+ca+A*i}yp9O8fljlL}&<)I8yTq-*%XD{eUWh@6 z2ZJ7{+u*v+<71&ifZ8*nAVkn7{*Pq>yalbJX0?A!BZ+n{^9{Ms=0$R?C_Xf1w(|P7 z4nmr!>5?q7>3_>q^@;H;2nnoJr_L|zX0z6_^98olUsq~7ucCk=BjpRt5R5TK4uW(9 zw8SUsYW`D?9?STV|2{N{k%6J> zVLeMZ$y+x6p~bYV3}i!udKKzYcwlEEZx_-f#xVT=)D68iwp*|hn3$1}%o$sT0%qba_{@l8HUA71%3bG^9wY4>Y2 zylql8rW}5Dg74*X{XI9gRAh5pg*1jX_2Ve@vkQxVahYgkEdC{=1Eorr-lUma(575> zPjS-JHRiu(eeC60f=O@TtG@sKY;@-RT2>{vl}S^K32aK|K}K$&hov~g((_B-{?_3< zuaI#SS|xsT(7t{9a7pTLi2J2=(8Xw-gAH$D*gak~&KZVy8(llJ4d{hRH^t0m zgu*lnwR3_TOz&!pI|@;=$dR1$_?jY^V`W#gGQ?{g%}6Y(JJSmLZ0b`ppL*}g06kMZf2sRz@Cbei~(YprKGVL>su^Y zx|A(2KPNj|$h>3682ttF3aTY}6J7{Ea$J5)R0%nFCjK2xTxwi)$9kT9 zsU^`+a*RzsQZJ5poV0!5)y-XKJ*}JBxArICFn7e&yg~8d#2A}gsc|y&pAWs|TyJlY zVd#~`S|&x4KeM|wPXqGt#==pMiOi~;%Uqn556B+HF z5qT-pzNQxwS=YRa>2q+TG+mE(96(n7BE2SPl6(B>h!@4(ZqL)*aLvGuZyBzj{r%uca~ASiCY{?Y7YayJh5 zD5HRDGyR-rV0iUZhcach!V-v>loe&#i$^n3Qh9|?#`oc%V6W<#bc>n4O@{=4U`_J5 z2R}vRvHE!%JGa35a8T~^;gb=rY<%E4t^{VF>>C=mez+2AInTB@@k|Yqi!o(y!nLF? z2Z3$srtV=bA~~5fZhliVNbRS;AG)Bco|U>3AtM&NP632t!F>36MLvj{SB5vgWdg_9 zkNsPp1Y}`uc2WQ_5>yE2kmM8yf8t0~NrEKQ+RiUbRh#6)pp}`uz6htt_Tyq!*EwG` z`(0HnwcX*~^2bWDXVfl8YYRLsSvFQ8(_PMt7zyx2Jao^j~W zp|sYEn1^$JSOwqpECXV>6z8)XB_02WaS_gtEohjzOhpL#*CB^2Kr(pB`tbTEakEC= zqin6Ey{!#O+bb60E{p@O;Tp}pH`TsdO~?j{WOgvBoK@Ou`eBUWS^|H#DS;L)K_KuE zue)yyrT#}_i>HaMg?4pZ@Xw!mes`B?3{z{>)k#2k9N=uL^3 z#Xj>t+mU(BWR9eti$9u)SGk2@j_g5tRgT8#uFZl~=B{O)qY`|4jIq8ZgWpw+!ZCa4 zn9&X%<>hRoJOcRX3s{6)W;CApa^x3Yp%?Y52+j`lsLxqu>Km5xUxOo~1YcFFQA3P^ z$mvVUiWOn{h_cAAbpMI43_jC}+A>bBA5sCu*F87CGk zUw(}Q)o`|?5*yb?$|E`1gGX$FhK&70TDpLf_+8l0YFA^+E844k7}%^|*DYDtJ1%HD zQ;w!?msn|zm%CLIPTYhGlq$P9QbRS_k{HJQ6Bw3d#_-9OaXdCJp6=iZ4Tf5@*%XOk zS&(w?)@=5Zi3<2^k~f1`@EyF{bNGgR4vh6m(ULU`WEIDX5I>=|Ygw{#g#SrZ$P=|<6kcuY{c4;v$eJ`nt9 z;xD(x(Ugl%a07P`t8z&GoTZFXwaSGB?V5aD{YgYg#0>^-#@1|zo*p6+X_*BDuCOG3f~ObjpVjKN6)A4|w;--T`2t=CxaZO()m z>xVJIL^QD>`HcdZc|C&aqQuPCwp7A->uGfss&$2}L=1#%jt4GE;1iUwzkJ91K_)+x z(MH_cupRJ$u9qw2<#vXU1HBkjkWK2_pxjmM6S_63{ty5;bk`8brMogx7$>J%SM^RzMfZpm7LsLZ*Sv`qK?%zA1en37BS1t2jF3#(O5YAPz-{ z$WzVrTo9Yng2GO`=4*~?EX5Ey4KVVgx3me#fMv=n$qdn`TKf9fgDxvYr3W{#p30fs z7<6LxG1hI}=-m|?4*zzEqdN*>Ea~5YdxtO3(cv;Y5r>Cq`%`8rva&>fNf)~Y=7;2u z5Yo<^Im_qIivUr6a1vV_% z+uQ%5r)_dyF(07wq`)*z?l&0EtY14!x$7Ht%1x-8ed|MzG?*Z+ouxafcS8`&PF9#D zWoB;4()3QPrCjm57Y=`Lrj=MB71O?NzMs6vQK(UU#pa9+IV`=n^@ zZ7L_lW1#m&R(i>b^#ruqMIypT`fc}|j}yci;Az+T5L)FJC2h!GAcK@P1m|S7wH|XW zEpa?`zxwD3=dZs zARlZRWEWVe5A);zrz~&RuH9<}LiFc@;wMH0V}Ejt{Jx)4AnWJ@S}}se2bz2wj)l3&t0$#2B`W#D23w?~R8Q zc<^N!3$e>Xa*FG%Rw1d|&8R$QFXi#$X`^KayubxEC>aucs-(8|v=SOgb1EljQZLTfoz`nIe&hRmDeRABw ztDW30awy<9{(B`?8Hi^RN$6#<4WdsgNQI=-=jv<$m6ObC4*fAos5$X4(2EUa9UM-tF6==x z!#bAWCXHjL0rH**NDkTg><=6AyWZmvU&-r&6ct%Dg~Ry)L2f@2Sk3HBt=Uy7Ie;uu zI&+nagyC|h$l=S(C*D>~dq*M%9u77+cDR_WfvSd}<~`b?`5$JJB3D)6i*v77HrUX_?d!zWTNa^Plv1uDye%M zZiIcc7rFVvD#Mf9Io$aY?Oao0Y%47VK*oR}v4-VBz&78uKYjIg*7@h@Gf3P~SSZ%@ zkiteKnm)p+LP{fkcz!*JKDk;@*XTrYeqd%45bqf}yP!%)ff+lDzWN~ByEYh=cZm>} z9iil5SL4j~jGA0!)qDnr8;t{E1^-*8$wPHL0gv@~{1quF_naRHj^6xrr89&Ed!ezS zPpCVC{FU);oH=dAosj4de7l^*;t8iL4&f=G;21>sFstg4Rkcgb#;;)k15Y`KU;(Rb zXQGF27anG~^rhEEEEvkg`pmr$Mw!^ZbhO4ROm{RQfhKcBbtt;27@JA}7i%cd=7eBJ z0Fc`-;zNhy%JyK&e&L<{5R{hKW;&4rrepdTy}8^C zbDM#(;6@&Snm9z^7PwsO1b9}=4PHY*R`B^k? z)^}o&EDM`V`Yqtt*KgVJ3pP&y5Zu{ENxcSF`S9upp78@AIY#?N~KAh|zW7gzU==*$juffY@B3otJu zKIt^P=HA7-G)+*^+yInbh-ptykY#K6Z(c+VB(^@5nNZ6_5sh56zzukO@A(a*41m{- zO!|5I%w)PyADCqIe3C|tD;$MtNvHJ9`!e*T;@0G^Ogc+x-oUNwhSeVodr15y_6mhy zg^h;c8mxZalOc|YY=XzVTXs zP@<=62(<>J+C*7~Q4U(KD`d)3a~FqA%}lX@_Y-;^O9Fcktx1@J*aP@^cS&7{tS%cc zmJ3&+8tm0nl6vAC>nGgd35YHQn};%uC~j7|?qk(*R!TRaIIF`9&FN60xYGq(IWgtXQ#PEgt2AnlDpDYC`w+adHoVCjn+Q zaS1q$9%NdzMDNz`nb*7tPApVM0jL3r;GyU}UlwEL`ovJohIqKp@0~^BO?6SNat~w2 zGKV4#t2mG-rCmFZ_<2V|*a>0fFv{)mv9W~VGK@S;*+L?mB7Aua&Q=2PY=(C5;a&i0 zAA)8v1#!w#F)ZURZ!UD=cO&PEPe%)mJdIv^&5$g8Q7)gQ4V@PYUO2qVVhjXarAk|Q zH&f2!dZC>1l7pkq87~*0bVTD!JIE&gi0@41ifP@=7GQFLnoNsQIBYX|7VguZ%x$t` ze#KRwivUdJ6UWfo;KdvH&zDpC8OJr0cwM8$jhjyT4Olz(7lr~4)m4A#O@>x3^Vwbx z599NQg^bZ;Va%WPF7D{=-vM|!fI635y4+CqiK7nTHG*$3^)QaZc#AkCqeVr4CaNi9 zVGZa@q#e9#Ya5OLwGG>huXKbS75OaQ^~V2pK&1Ubk&OB8Q~`@#-;QT^@JD+KLtmGUD-3Ny_j!wj)59_@xrg?!^1c_ ziEM^o4V;g(f~%Wkc-jW7Bd&w0xHII?2Yu%Df>dp+St5ibswHf@QdrL1!j5i3V{A+O ze(dW*$Y6q1IauO}m6N-XcOg5qjh$-e{wy=%&vbFvn4S%0 zc_3`y5AY{~R*OoG<3;h+RP6`;ur#7&M}v^;0FEw^ThNNUHAfdz2vyfqEiWjF3)}_sUIErR1K<;8MQ8yDD zonyCEGL_tkgwEb(6K?{#$7WWb-tJigSgh_aZG&a*;i_db3&(#nsolbc@-&e}Xg4HP zo!fyK9J!0|CyT#j-s58mKM!SC7UHe-5;ZJ6>uhU^6Q(3^&-B0iKWn|k)`TJleQNjY zH?^%Hl1mw=7|Gu~L6WdPnH9qF6-uwplWu^V>-OoWh8#ai7>SogQs14*kY`--^5lH|69R~}*1Z!|q|i8wQfQ&^%Cd&c==#exf5*r1lEE!rV(197~(jve@*-O0-bA zFE-l4V5I4G+=`3%PFxy0EQ@40CHidm7dt2rBuH`z0e~VdZi~s+=@mkpUv|NJdqe#F zs$A=rfnb`iP4|W!t*8Kr0t!@ouX(qlslfy@VI=6k(d=7j{z`3aU#8=Y_n~0hbWZGD z%91E!#&5dR!;oYmS7x~TLnb^RA!R-Ymq;t|ANq4m6S(5(?Qz&|koC;1<@PclICvlV zBypN)YrJ-KSyAO*>?yjXF520b*oexQop=N{J?#xIYQwx;ps4pJe7uHI;v@ZWszemZ zT+nlT#JE75wYKGqBmj!h=op}AV))okq{pNuYU*r8RPPpbkwrc z{uS=Ue2XUP5n@NZ>Ywp6a^@pDl>MLOFlV62w^L4nVlqNIVN79zKaJXGIOAC$+l)_S zR@tQ2e10PDxNQ5AqM-uZl!dX>l(ka0J30A&er+%wI3*qN)|6ciomF=49y3<|M-p5Y z-utCDCIKR1Q2M(H=zs!i6KGR5>IdG)3$#^EO)4CumcPy^)_PvPhwBd90E2gm!6(}k*guXHTG8gp(s8xF~Zh4@5Y}Qcx={> z=f1Byfoneu=7ljp8$+vcu>n72@0;a~%C@*Ch;=JoJ?>Y{$x4w9P3RmC)1D0o2#T7B)&oG zHF_@l&QFIz@_W^Qh?h6I0`O;kZ9#Kyn*yP(#R|*kDuPC zJ_yAx+_tOI&D8oMFqXWNR0|ilN#bp7vq{^@g7inLh^iAhR$6MtNT}9)4da$d?Ud1NnR^3c@O9K5CAdz z=H9ApM%Y+Rqd9^iNvZsWLjJRs*UK{)-vB^)>@eUbxgu#(dc*q3i0aFENyl~%I+VfR z1=)w$y97mk#`+59fA>?q;32?dO}~1o436>g8h*!aQWFhlSLsKRuP@IOn{~8a+MArD zHRqbyma+vL`R~Oo3l8DEq4vN7+r;q<^oJ5`kNX%;eKJvlSh-W?%7Nlh+rWYGXjC!q z7j6a+so~m7ju^M6w*V40O9%(S_iGj+B^V4_wZ#tK=rc~ajqCgb8b}K14A%?aE?g+^ zX6T0wN!@z%2q2)8OfKO1c-$mWD?=#Nz)~dzdc)u^@xB>vz8l_8oQd`@x{AY?#L+G7 z4-Sk?fgJ&fE0MOX>NY?=R6Zq;Rp0LETJj=c^BY{dfqc7k>C(z>Q@Fz|H-c7EaOH6m zuVN>D0-usnh0rSN8A2CWFW|DrlY&OHDvFhIM_=vX zrbKb^hugmB^)%yO^Z<_p)RVzLUSI@+8O59%R{7M)BFB z47%27S-Fj7-XWzupz0 z3K1k)FAZ|r(S`EavYq9h&#iJpEhz+5y3OXcZ-%ELfP&>34{8RZohQ)#PRWGKi}A8x zo2EuR2XZt+Q;gZ(?UQOla*mTkrqxKPuKqdnWFOf!3WIZRRWQ85W1Wq%znz$Kg3(8U ze&%lo%_>A}LqQG*rWeD+p*i)`FR{#B^}amfR=s*ELKT^o`0;j^XG}YNyswFbtoG3BB zen|l(kqPnmv~osfS^V&z!)*OH9MTW|mcWpIUyoCi#VZIA&h1b6(;NJ$SJR3IcO;{Y z|K4=iseITx@76_3H#gK5`#8vXq1kVK2L4cELLiR{<=S zuHM??hv6uZ1`;4jvvtM49r zG!&F9XAvhf9v{i9yhH0Tt5GTn>!2v2^$T)vkKdjkT+Z)Rg=|BNXUJtlZL)ruK9H2E zx&+^To9_+@Z7rPYzikYu743KeHi) z3;w7t9Os65I?@9$ZscquIdlx?I~z$x42NS5#63^R7;&o+1Zf+$yl0;c-1baRj&xsaf>kdeO}i>We>=v7yxt^qmx%pf?otSgNpf_e;2u zbfqyq+Ri|OY6>q&)JOM(Dr_f!9Q)WBaWr|mSMb8T5EZ`DqLC*$t z)T;*SRWVEyV#!948cIT~n@xEv*JOPa7`?jgpzCaMNx}e37<1zA|Hu^(=7hQ8T+v`g zI&VJ*Ig(8xWpse>qd#C0sN8L(7}rr9K+p(&}k1xshPS2M=q#h<ma(+Xf#yLZ8BRV)CEa zR+T#H8kv`2 zw;y-l8|YwNPA+|DiTFs=s-l-%x6;8q>6>8|*EZFH$ryA6HU!*lgpFD<2NJ#UzaOm2 z4Kq>g*3HQ%kah#_a`9(OCW&%i)sAC)y-4abnS9%WX$^x^N7^FjuqymqTCJ^r4EQwP zkhuloSmriFVSDd{a8|Ynw{JU%8rCbBO1I-lz_bTJlD4W}%r3fnw{DjKh0J19mgcnt zZ@J4#MI)2%cxvF4_D*ONHyy2N2@Ie?Luo~9)stM#6buJFz2Jcf=8s6EgENJ?59kJEa$@dr>ouwB&&(3 zj4(?Nc)z4}IAnM~P!W`NsypziXMuXrkbTQ=LxJL}8rI@QN=?MiQsn8)z3tg%aN@<< zN|X=7go#1$K1+(DOdUvx_(k(Pv2sy}?D>@k7#~)B0T-J#TqYeW~ z?4-*_Vw;q?U9ENxTO)a%+z)Q?5rRWJe;Y#b%U~-BC@*;pVT)?#81-t^uH7AscCh(0$;+f%QgaF$Y>WQ%bN`thTzuN0S^3wwjijRZZ< zk8lRsz5&uN{6Z!b`Lh&gQPLner_RpxYXFel1Tx!wH?~8R79p^N<~HpY+%gszas%2jSi zs-o%S+iOta>i;$N{Y`z?bqW|k2`h7y!sli~;H7~7Il-n1;}F8t)R>LXUFg}f4=#%w zg}d+b86L&?^@lfA+-1#BxU7tfjFU4=_`!Y5EFu7gyv!C%FBZa1=?Xa@Xet>)6;xWB zi+~8V{^khW6a@RB0R7~V2Lw~vh5Mvc5#NvFY?b~xGUIvDgS_3x-bcKe1@U>_ z*sZVMM_(?{^|vauA9enzY{#9EUnaa9(Y)`L4401K&RaS^cY{y2`+O%l{)9re$UdL=l zORyr%7n;zIU-t2;`^XeF--&Cnw^D$uZ&GqTm)yPwC*P?+;&8GUY(ol{zf3Kfs9uDv zgcSBZe4S8M3S)U})Dm#=RHhD&{*W*3HmS6Gzma&YfZxNh~S+`dB7@s8-9H(S5R ztVku%S6+0^G^CDr8}M(o^SQ&EVMi2sJpTz^7k;&ck@Mr>d@gQH`HSV6VOX3|QmnE| z+qP{*86vuutM105QyK^v3YSP0ipeLYU4BiWV|aP#gw{50H$d#W{R*%hPqu0=PtpU} zS(A1aFMqR-joX#r;NUC~X_KhCmfJN^<6dag0jKsp%yc*3Y%x0PDB?-!C-zQ{#Lw$Y zN`C}$p4i(sx1I}?rXlXke(cWgJC-SPmQxu4MIe727eF{1bwpEqp`Ldnw(iDh2J9Nx z6kq)3a*$_p6pbFyl(3x1x99o&{u;7M1ebATWob^^C5*-R_VF zi5KG(+rI@-Xo~G!La5$BU~WQWDL|1W;%Ojb+&jTYX**)BQG7c!;vO0@f{|sP&R(TZ zjowig75vQo!tJg0fPe2tH{}IR)2&XOWxP%&d>3+60Su+G?Mh=-OKGrP3;#4{`h0+) zX#^GSbwdiL+|Wd=RH}9+=5D^qRQcM>8yWKUeEzso!5#8Ig~Yslu78UbEliepNK&1ORcO*BrhTWQnA@qhMn$LM8?(dS!CF6|Qr&uR zr`|+ip;xvM=r;tN(p+*<^wmR(rwnOv|67OQ2|+5IqVd4-aSGj9QChsv+`DIO4sK#Zi|S#s)aH0d^(@iiZE7WflL{*uhGj^Nj%9(r&x zZqqcuQnH7Cp?CR;;46&>ji3(Hj}nZlExhwn9#!3>8K5Yg>FigLty@56^k+4IYbuPL z4YTKPMhVi)I+6GO!~@DE(T8UrJ$jUCv9TLYaGu!PvP?v@r6r(pN%<{N^v+yy_lch%!bK@JX{od)m7d;M$iZX6GF>JfO;iP zvz#IK*cFgXd%-m1nf!=hQb%63e*OAl z%+@n>h{Wu!nqqwO_%~l?J7*er_noA58&15rXDq>fwsWKHm=_m1Fv~R>*@bDME%5P2 ze$S13OHkv`tM$8)kimawpY2#{4gF~i#1^JbnfC|Uc4D?neVn0kYLV-Hr$wB^zF*+f zj-rr4_C{_X0s8Ij+qcWL2p*|eBeC~33utjAanUo`?QWMC+pzXTsuHyh_?7|_5~ro_ zY4!C#X8@cixp=mSjoFf6V<}92W8^vHK8r4aNv2vV?9_uAjUFg3T}Y88@Pf6o$$E%w zPbQZ6JHfo*WMT+jx!)PEz!-EzEnLsY76~z54b+!4n4jJ92H$N6)1sy+?i_r~Go*M< zb@H3e-0P*>7UaFyoGzCPqkNp^0@&WbYq7pT-D{WBW8vAsd zdCXgVHqHZv=_hhHVB=CGXnMrhb?AV|nRbe7rWY@ne6>YDNu9VBz8B%rRT&0}{4bQN z4DJE-_iP+L9%lPH)qf(uqOqhrwFgtPowr3wWOU=DI;r3b_F`&Zkm2UX{R&z5?RqF zK8(6e_~jQ65XH+D^WoYdquG?Q%`V`bx2Bbu+X&wK9qxt|M-ie?m%G zM<=IL$|Qy}C7SSTy_@@seodVQO`bfN+)j0^Pz+XbNkU3HA#zW$a1$5fn!kHh-Z;l7 z1K*9L8ER;5x(A~(dncsadrdm$49jwqJXx=^a{wUZG6qnk^_uhVm9dQF&UUVq%%2gY zR2>eqt63>H=qWcSNvd^JP8Xm@N%S6ucgT2DA$E6JD11H|2D_IcoU2`fyu~dy6_WB! zu*cuNv8vHV)Cg6kCY6t(8`MZnF|eXfHyC?Xh7cfRT=h|dIWrxX^o;mgz=eeDF46)XsKwqjPnLxz$o(0!_{+7Lb->}R5lMHSC9g9!?E&RC z5g=fjedg}o$y;bJJT+_K@@M40RLh-*=hA`uU~>CbqJq` z@=wFq0v4N7eb(D&Um$pS#sdy?1$7si&NfE0iYlM$%u<$0OqF>@?T8qVFrnK+8^HwY|@5$tTHoanuLp~oN(`& zEBwTkB|dPm8+WE+coR1waaHDX)tO~MeYA*tDn`;MPQic5J-@@jk5nV|0Q4wi*5csj z4El)RcGi9Q^5t;+;uiqYTYWY;qa{yg;T-bqXYG7p6d7xu5|_uUtG442sF$Amx<`EZJW z%sW;mcOd)ufP%{<8ko$&)db&*K>Idl%$qWiBE*Kw^UQ^d?z3JtoI4;AQ{r&@1??^P`PrLYU#*73e#P47bIfB!ENv?}|>X;P@$=?C6v19z0KrL@2*kq|MstM7u`HCuD7PRpIuho0bi(#9#1X1 z#s7~cZecCgHJ=jx;lqbqV1_CnDR^TKD8*T>*hEkVC(x5BJHjbG_PzA^0M=8rp zWx{hAgljqdt!{Xe^XAQyZvS)xKVt-0cUdXLnffSs+<4lxag5$H@3n3+GdA+Nyc>gX zEE8)}l`2$WI-_Fl<~;{D{z4X>o#=#liU)RkKjh7m8+oU0;MOb#$@?NVuSMikd4<9X zrp$h|PCJzCY{V^N1hKQX6bE#2cUNWE#5u|`!Gt3hyN~JLGsD;i=`FqoRMrg4L_-{y zQP{ZWjTmSEC|UA9bV?HRM?HM94a?5j?(U1}=jcQLE*t0PpY^AYk<*7RmopR_Wuc9i zcCU9>1+-yR2{D{UBJRt%PMvU{6`%ePfG%<* zKc_s>+a2*3NzH#;Q8KZe<;ME|1CkF&Wh{0@+jHn+daV)vLRN z<(yOikDFgvTh>-G=q7&MFPn1E~;S2x^+8x%rMR| zr^dSlQSX+=)Bx#Ps;0-YfTb{jS*wQR>l7u(MDoy-XzlPcu%-w&o#!m3{jQ-JP5AgS zx)PUB5RytH<8|o^!%bO@@_uBB%fSJpC`13wK8*YY)%QTrf+!L}KG$MEGHWC0BY&Y> zrh+`s%8nL)F-K^p6bn#+vreCypicz|KtLr&kZtJ!HbdoK>5AS;AP7}aSlQU7EVH?nCOtSJWe{wH^s)c ze}B{Sq4c?dNTjqEaU4a-Ql;2{CGz93EAi-dq}9!yLAntl{NySt56_<&D|x>hdyuC~ z#6c=lx7rH3lp6uQz9k79r{nI{xQlbn5g9)tmAU>*v|OY;2IQ(LAhNhIWD{n{49R;lu-eNlDQdJKxFx$8|-bAUsg?Ky3YbPi71E;MjA1eDv_)8S!9KQxi^OPe);sHEl)? zM9XOqx~SlVPY!^RA{L%ItpJZI_2YTRcA z!UX9D=FHxW8jRNfrCL=%74_g4U+qi;v57N5evTi+m~|RFc(C4#Jl+X^3zWh zs(D7y1X=cR&Qs5n;v1i~u3WiN7`qpp_<}9_ll|5=J~3;?7}Xd1y?*-TuBh~g#~2bb z+AD1heg#F~J1gq%PpO6ZB}R1`k2-O|9zZ}84uqN7FwGgjdYdbehojiOM$o^e@|O<7 zh7HqKj_Y?GUH-XdtTVX9GBJ^#^^FT~DqGfs{D_!V5buf`Mzzoxwv=&mqkNxf8nBRZ z;hMzPoe27}tHrG!@Y7e5j$+vwg5#}eJjN#sFJ5BiR-L|@MvpGGFR`3Rxg{gDO6i;6 zYMjowI7M>+LKTR2!qKn#Y0#-dsfICNq+##6U!L%o^;0`de}1UU}i>4BO)y<;&1+Vf1>aKeUWlN4%<0#ZeAKeF>U5m5SQZMatG&hWX#hA;dZJ!zulIv6-gpPRXkB zLLLkik0*Xi-0!;TQ&@}l;C|()SsVQe1JAjVr2&Z&ukgDI^}EH0q^mMBsif$*H4DzC z#iJ-S0%X^5i{>8=`aQD!RjIgJj)?BPLY;RMvmcG9l_EmbtyW~-+owf!J;t;I_M^@T ztR)1v)#j_?Os{x>WU+;Y>uG*7$Re{%MM@RBK@dI`|LD0*;_Y z#7y0dqX^fqx9p2U@5|xxAd1#~oeHOJsu1?*i@mqX=@9-0LvMtBpEw|kezV2G3_~iY zNkZ@KC(FQJ39UHMV3}Zm@BE6dk#yj<%D)nB)SsC2)1_Cq<2_>v(73RUmgRFl;CH5$ z>g-&Ka)jx+#zYYx7hKpuRhs9DMw2lfd(qb9hK<3WqU)*ff<%!Q7+z^oGRq0=JK>kbqphbxm)sEucoT^7RJd!* zd|A!tuW=#wBHT9#c!X{+{QQ9ojr?_abyVam0tWJ+0N`=!^)xax#ok(`>}2Uf%C*nC zcYy(v7ZqU&VG@M(KjA{xkAvKt+Dq1d>ns1JGfivNA*r{=8VVBQn`xB{iL@Gq1BzIp8H8A{Qj zLWhG?&3W^Ly3BpFakrI#;-8HmH`RX&8rMJn?*snt9{gW9`2XKs$g7lHboR!ucb#)|I$b;ZbVzD9 Hbl(318n=aV literal 0 HcmV?d00001 From bd4f2b8d483bb65a8b0bdc4de42a23ad9c351ed4 Mon Sep 17 00:00:00 2001 From: "Oren (electricessence)" Date: Sat, 11 Jul 2020 14:21:55 -0700 Subject: [PATCH 40/83] Updated version. --- source/Open.Disposable.ObjectPools.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/Open.Disposable.ObjectPools.csproj b/source/Open.Disposable.ObjectPools.csproj index 232e3d6..036b594 100644 --- a/source/Open.Disposable.ObjectPools.csproj +++ b/source/Open.Disposable.ObjectPools.csproj @@ -16,7 +16,7 @@ Part of the "Open" set of libraries. https://github.com/Open-NET-Libraries/Open.Disposable.ObjectPools/ git objectpool, dotnet, dotnetcore, cs, idisposable, threadsafe, thread-safe - 2.4.1 + 2.4.2 MIT true From bd270cd4b159350979ab801c4a184880f28534ab Mon Sep 17 00:00:00 2001 From: "Oren (electricessence)" Date: Thu, 16 Jul 2020 16:24:13 -0700 Subject: [PATCH 41/83] Updated refrence and packaging. --- LICENSE | 12 ++++++------ source/Open.Disposable.ObjectPools.csproj | 16 ++++------------ 2 files changed, 10 insertions(+), 18 deletions(-) diff --git a/LICENSE b/LICENSE index 8864d4a..d21a0d2 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ -MIT License +The MIT License (MIT) -Copyright (c) 2017 +Copyright (c) 2020 electricessence (Oren F.) All rights reserved Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -9,13 +9,13 @@ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/source/Open.Disposable.ObjectPools.csproj b/source/Open.Disposable.ObjectPools.csproj index 036b594..9641c8e 100644 --- a/source/Open.Disposable.ObjectPools.csproj +++ b/source/Open.Disposable.ObjectPools.csproj @@ -11,11 +11,11 @@ Part of the "Open" set of libraries. - https://github.com/Open-NET-Libraries/Open.Disposable.ObjectPools/blob/master/LISCENSE.md + © electricessence (Oren F.) All rights reserved. https://github.com/Open-NET-Libraries/Open.Disposable.ObjectPools/ https://github.com/Open-NET-Libraries/Open.Disposable.ObjectPools/ git - objectpool, dotnet, dotnetcore, cs, idisposable, threadsafe, thread-safe + objectpool;idisposable;thread safe 2.4.2 MIT @@ -25,14 +25,6 @@ Part of the "Open" set of libraries. logo.png - - latest - - - - latest - - @@ -48,8 +40,8 @@ Part of the "Open" set of libraries. - - + + all From 3df93ff0e766ce921cd12d36f43b9400b76d05e8 Mon Sep 17 00:00:00 2001 From: "Oren (electricessence)" Date: Thu, 16 Jul 2020 16:25:46 -0700 Subject: [PATCH 42/83] Updated version. --- source/Open.Disposable.ObjectPools.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/Open.Disposable.ObjectPools.csproj b/source/Open.Disposable.ObjectPools.csproj index 9641c8e..f50ddba 100644 --- a/source/Open.Disposable.ObjectPools.csproj +++ b/source/Open.Disposable.ObjectPools.csproj @@ -16,7 +16,7 @@ Part of the "Open" set of libraries. https://github.com/Open-NET-Libraries/Open.Disposable.ObjectPools/ git objectpool;idisposable;thread safe - 2.4.2 + 2.5.0 MIT true From 44fffec75f5152dc940e4565b24cc037ec6a94ee Mon Sep 17 00:00:00 2001 From: electricessence <5899455+electricessence@users.noreply.github.com> Date: Mon, 28 Jun 2021 14:48:44 -0700 Subject: [PATCH 43/83] Added common shared pools. --- .gitignore | 2 + source/.editorconfig | 3 ++ source/Open.Disposable.ObjectPools.csproj | 2 +- source/SharedPools.cs | 59 +++++++++++++++++++++++ 4 files changed, 65 insertions(+), 1 deletion(-) create mode 100644 source/SharedPools.cs diff --git a/.gitignore b/.gitignore index ac9b19b..78e2566 100644 --- a/.gitignore +++ b/.gitignore @@ -298,3 +298,5 @@ __pycache__/ *.btm.cs *.odx.cs *.xsd.cs +.vscode/launch.json +.vscode/tasks.json diff --git a/source/.editorconfig b/source/.editorconfig index 72b2ff2..c22cfef 100644 --- a/source/.editorconfig +++ b/source/.editorconfig @@ -8,3 +8,6 @@ dotnet_diagnostic.CA1303.severity = silent # CA1051: Do not declare visible instance fields dotnet_diagnostic.CA1051.severity = silent + +# CA1000: Do not declare static members on generic types +dotnet_diagnostic.CA1000.severity = silent diff --git a/source/Open.Disposable.ObjectPools.csproj b/source/Open.Disposable.ObjectPools.csproj index f50ddba..46ee335 100644 --- a/source/Open.Disposable.ObjectPools.csproj +++ b/source/Open.Disposable.ObjectPools.csproj @@ -16,7 +16,7 @@ Part of the "Open" set of libraries. https://github.com/Open-NET-Libraries/Open.Disposable.ObjectPools/ git objectpool;idisposable;thread safe - 2.5.0 + 2.6.0 MIT true diff --git a/source/SharedPools.cs b/source/SharedPools.cs new file mode 100644 index 0000000..f00bba5 --- /dev/null +++ b/source/SharedPools.cs @@ -0,0 +1,59 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Open.Disposable +{ + public static class ListPool + { + /// + /// A shared object pool for use with lists. + /// The list is cleared after being returned. + /// The max size of the pool is 128 which should suffice for most use cases. + /// + public static readonly ConcurrentQueueObjectPool> Shared = Create(); + + /// + /// Creates an object pool for use with lists. + /// The list is cleared after being returned. + /// + public static ConcurrentQueueObjectPool> Create(int capacity = Constants.DEFAULT_CAPACITY) => new(() => new List(), h => + { + h.Clear(); + if (h.Capacity > 16) h.Capacity = 16; + }, null, capacity); + } + + public static class HashSetPool + { + /// + /// A shared object pool for use with hash-sets. + /// The hash-set is cleared after being returned. + /// The max size of the pool is 128 which should suffice for most use cases. + /// + public static readonly ConcurrentQueueObjectPool> Shared = Create(); + + /// + /// Creates an object pool for use with hash-sets. + /// The hash-set is cleared after being returned. + /// + public static ConcurrentQueueObjectPool> Create(int capacity = Constants.DEFAULT_CAPACITY) => new(() => new HashSet(), h => h.Clear(), null, capacity); + } + + public static class DictionaryPool + { + /// + /// A shared object pool for use with dictionaries. + /// The dictionary is cleared after being returned. + /// The max size of the pool is 128 which should suffice for most use cases. + /// + public static readonly ConcurrentQueueObjectPool> Shared = Create(); + + /// + /// Creates an object pooll for use with dictionaries. + /// The dictionary is cleared after being returned. + /// + public static ConcurrentQueueObjectPool> Create(int capacity = Constants.DEFAULT_CAPACITY) => new(() => new Dictionary(), h => h.Clear(), null, capacity); + + } +} From daeafe438106cfced795de122572315183a7c769 Mon Sep 17 00:00:00 2001 From: electricessence <5899455+electricessence@users.noreply.github.com> Date: Sat, 3 Jul 2021 08:10:50 -0700 Subject: [PATCH 44/83] Updated tests and diagnosticsl --- .../Open.Disposable.ObjectPools.Benchmarking.csproj | 4 ++-- tests/Open.Disposable.ObjectPools.Tests.csproj | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/benchmarking/Open.Disposable.ObjectPools.Benchmarking.csproj b/benchmarking/Open.Disposable.ObjectPools.Benchmarking.csproj index 252b857..eaeed56 100644 --- a/benchmarking/Open.Disposable.ObjectPools.Benchmarking.csproj +++ b/benchmarking/Open.Disposable.ObjectPools.Benchmarking.csproj @@ -20,8 +20,8 @@ - - + + diff --git a/tests/Open.Disposable.ObjectPools.Tests.csproj b/tests/Open.Disposable.ObjectPools.Tests.csproj index bd7d99a..6ec56f3 100644 --- a/tests/Open.Disposable.ObjectPools.Tests.csproj +++ b/tests/Open.Disposable.ObjectPools.Tests.csproj @@ -1,7 +1,7 @@  - netcoreapp3.0 + net5.0 Open.Disposable @@ -14,9 +14,9 @@ - - - + + + From 9fb92710ebc5bca9967fc801522bbf73c74203ce Mon Sep 17 00:00:00 2001 From: electricessence <5899455+electricessence@users.noreply.github.com> Date: Sat, 3 Jul 2021 08:11:40 -0700 Subject: [PATCH 45/83] Removed unused and obsolete. --- source/Open.Disposable.ObjectPools.csproj | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/source/Open.Disposable.ObjectPools.csproj b/source/Open.Disposable.ObjectPools.csproj index 46ee335..7d3be3d 100644 --- a/source/Open.Disposable.ObjectPools.csproj +++ b/source/Open.Disposable.ObjectPools.csproj @@ -42,11 +42,7 @@ Part of the "Open" set of libraries. - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - + \ No newline at end of file From d83385e7a6d4fdb8da784950a431106bd3426ee0 Mon Sep 17 00:00:00 2001 From: electricessence <5899455+electricessence@users.noreply.github.com> Date: Sat, 3 Jul 2021 12:32:35 -0700 Subject: [PATCH 46/83] Cleanup and simplification. --- benchmarking/Benchmark.cs | 2 -- benchmarking/Program.cs | 2 -- source/Array/InterlockedArrayObjectPool.cs | 1 - source/Array/OptimisticArrayObjectPool.cs | 1 - source/IObjectPool.cs | 1 - source/ObjectPoolAutoTrimmer.cs | 1 - source/Open.Disposable.ObjectPools.csproj | 1 + source/Recycler.cs | 6 +++--- source/RecyclerBase.cs | 2 +- source/ReferenceContainer.cs | 3 --- source/SharedPools.cs | 4 +--- 11 files changed, 6 insertions(+), 18 deletions(-) diff --git a/benchmarking/Benchmark.cs b/benchmarking/Benchmark.cs index 455d7ab..52eab46 100644 --- a/benchmarking/Benchmark.cs +++ b/benchmarking/Benchmark.cs @@ -1,7 +1,6 @@ using Open.Diagnostics; using System; using System.Collections.Generic; -using System.Diagnostics.CodeAnalysis; using System.Threading.Tasks; #if DEBUG @@ -10,7 +9,6 @@ namespace Open.Disposable.ObjectPools { - [SuppressMessage("ReSharper", "AccessToDisposedClosure")] public class Benchmark : BenchmarkBase>> where T : class { diff --git a/benchmarking/Program.cs b/benchmarking/Program.cs index d9c582e..3477af1 100644 --- a/benchmarking/Program.cs +++ b/benchmarking/Program.cs @@ -2,13 +2,11 @@ using Open.Disposable.ObjectPools; using Open.Text.CSV; using System; -using System.Diagnostics.CodeAnalysis; using System.IO; using System.Text; class Program { - [SuppressMessage("ReSharper", "CoVariantArrayConversion")] static void Main() { Console.Write("Initializing..."); diff --git a/source/Array/InterlockedArrayObjectPool.cs b/source/Array/InterlockedArrayObjectPool.cs index fd26dba..df20e5b 100644 --- a/source/Array/InterlockedArrayObjectPool.cs +++ b/source/Array/InterlockedArrayObjectPool.cs @@ -33,7 +33,6 @@ public InterlockedArrayObjectPool(Func factory, int capacity = DEFAULT_CAPACI public override int Count => Pool.Count(e => e.Value != null) + PocketCount; - [System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1062:Validate arguments of public methods", Justification = "Should never be null.")] protected virtual bool Store(ReferenceContainer[] p, T item, int index) => p[index].TrySave(item); diff --git a/source/Array/OptimisticArrayObjectPool.cs b/source/Array/OptimisticArrayObjectPool.cs index 00f8626..1936aba 100644 --- a/source/Array/OptimisticArrayObjectPool.cs +++ b/source/Array/OptimisticArrayObjectPool.cs @@ -25,7 +25,6 @@ protected override bool SaveToPocket(T item) => Pocket.SetIfNull(item); // As suggested by Roslyn's implementation, don't worry about interlocking here. It's okay if a few get loose. - [System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1062:Validate arguments of public methods", Justification = "Should never be null.")] protected override bool Store(ReferenceContainer[] p, T item, int index) => p[index].SetIfNull(item); } diff --git a/source/IObjectPool.cs b/source/IObjectPool.cs index 5ef30e9..c412fca 100644 --- a/source/IObjectPool.cs +++ b/source/IObjectPool.cs @@ -6,7 +6,6 @@ namespace Open.Disposable { - [SuppressMessage("ReSharper", "UnusedMemberInSuper.Global")] public interface IObjectPool : IDisposable where T : class { diff --git a/source/ObjectPoolAutoTrimmer.cs b/source/ObjectPoolAutoTrimmer.cs index 47f50a6..e4d0c51 100644 --- a/source/ObjectPoolAutoTrimmer.cs +++ b/source/ObjectPoolAutoTrimmer.cs @@ -4,7 +4,6 @@ namespace Open.Disposable { - [SuppressMessage("ReSharper", "FieldCanBeMadeReadOnly.Local")] [SuppressMessage("Style", "IDE0044:Add readonly modifier", Justification = "Micro-optimization for retrieving this value as read-only is slightly slower")] public class ObjectPoolAutoTrimmer : DisposableBase { diff --git a/source/Open.Disposable.ObjectPools.csproj b/source/Open.Disposable.ObjectPools.csproj index 7d3be3d..4577182 100644 --- a/source/Open.Disposable.ObjectPools.csproj +++ b/source/Open.Disposable.ObjectPools.csproj @@ -4,6 +4,7 @@ netstandard2.0;netstandard2.1 latest enable + true Open.Disposable True electricessence diff --git a/source/Recycler.cs b/source/Recycler.cs index 00abfd8..29f6c05 100644 --- a/source/Recycler.cs +++ b/source/Recycler.cs @@ -60,14 +60,14 @@ public static Recycler CreateRecycler( Action recycleFunction, ushort limit = Constants.DEFAULT_CAPACITY) where T : class - => new Recycler(pool, recycleFunction, limit); + => new(pool, recycleFunction, limit); public static Recycler CreateRecycler( this IObjectPool pool, ushort limit, Action recycleFunction) where T : class - => new Recycler(pool, recycleFunction, limit); + => new(pool, recycleFunction, limit); public static void Recycle(IRecyclable r) => (r ?? throw new ArgumentNullException(nameof(r))).Recycle(); @@ -76,6 +76,6 @@ public static Recycler CreateRecycler( this IObjectPool pool, ushort limit = Constants.DEFAULT_CAPACITY) where T : class, IRecyclable - => new Recycler(pool, Recycle, limit); + => new(pool, Recycle, limit); } } diff --git a/source/RecyclerBase.cs b/source/RecyclerBase.cs index ab20430..fb565b0 100644 --- a/source/RecyclerBase.cs +++ b/source/RecyclerBase.cs @@ -22,7 +22,7 @@ protected RecyclerBase( Target = target ?? throw new ArgumentNullException(nameof(target)); Contract.EndContractBlock(); - if (!(target is DisposableBase d)) return; + if (target is not DisposableBase d) return; if (d.WasDisposed) throw new ArgumentException("Cannot recycle for an object pool that is already disposed."); d.BeforeDispose += Pool_BeforeDispose; // Could possibly dispose before this line somewhere... But that's just nasty. :P diff --git a/source/ReferenceContainer.cs b/source/ReferenceContainer.cs index 5c53e24..7cefa8d 100644 --- a/source/ReferenceContainer.cs +++ b/source/ReferenceContainer.cs @@ -1,10 +1,8 @@ using System.Diagnostics; -using System.Diagnostics.CodeAnalysis; using System.Threading; namespace Open.Disposable { - [SuppressMessage("ReSharper", "UnusedMemberInSuper.Global")] public interface IReferenceContainer where T : class { @@ -15,7 +13,6 @@ public interface IReferenceContainer } [DebuggerDisplay("{Value,nq}")] - [SuppressMessage("Performance", "CA1815:Override equals and operator equals on value types", Justification = "Undesired.")] public struct ReferenceContainer : IReferenceContainer where T : class { diff --git a/source/SharedPools.cs b/source/SharedPools.cs index f00bba5..b08eefd 100644 --- a/source/SharedPools.cs +++ b/source/SharedPools.cs @@ -1,6 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Text; +using System.Collections.Generic; namespace Open.Disposable { From 6e42db33f4fe06f1587ed8541e9e5ba4480bffa9 Mon Sep 17 00:00:00 2001 From: electricessence <5899455+electricessence@users.noreply.github.com> Date: Sat, 3 Jul 2021 16:34:55 -0700 Subject: [PATCH 47/83] Updated solution and gitignore. --- .gitignore | 1 + Open.Disposable.ObjectPools.sln | 10 ++++++++-- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index 78e2566..857336e 100644 --- a/.gitignore +++ b/.gitignore @@ -300,3 +300,4 @@ __pycache__/ *.xsd.cs .vscode/launch.json .vscode/tasks.json +/source/nuget.config diff --git a/Open.Disposable.ObjectPools.sln b/Open.Disposable.ObjectPools.sln index 4d8472b..5a0fb91 100644 --- a/Open.Disposable.ObjectPools.sln +++ b/Open.Disposable.ObjectPools.sln @@ -1,12 +1,14 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 15 -VisualStudioVersion = 15.0.27130.2036 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.31410.357 MinimumVisualStudioVersion = 10.0.40219.1 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Open.Disposable.ObjectPools.Benchmarking", "benchmarking\Open.Disposable.ObjectPools.Benchmarking.csproj", "{8404875C-ED46-4057-8D97-598B4D7B8040}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Open.Disposable.ObjectPools", "source\Open.Disposable.ObjectPools.csproj", "{7DF5DA35-AB11-479D-BD87-27461AC3F5A8}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Open.Disposable.ObjectPools.Tests", "tests\Open.Disposable.ObjectPools.Tests.csproj", "{507DBB8A-A662-4D16-A375-48470A578B50}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -21,6 +23,10 @@ Global {7DF5DA35-AB11-479D-BD87-27461AC3F5A8}.Debug|Any CPU.Build.0 = Debug|Any CPU {7DF5DA35-AB11-479D-BD87-27461AC3F5A8}.Release|Any CPU.ActiveCfg = Release|Any CPU {7DF5DA35-AB11-479D-BD87-27461AC3F5A8}.Release|Any CPU.Build.0 = Release|Any CPU + {507DBB8A-A662-4D16-A375-48470A578B50}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {507DBB8A-A662-4D16-A375-48470A578B50}.Debug|Any CPU.Build.0 = Debug|Any CPU + {507DBB8A-A662-4D16-A375-48470A578B50}.Release|Any CPU.ActiveCfg = Release|Any CPU + {507DBB8A-A662-4D16-A375-48470A578B50}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE From 3304421f9271f0553d9f4888b6667308a084aad1 Mon Sep 17 00:00:00 2001 From: electricessence <5899455+electricessence@users.noreply.github.com> Date: Sat, 3 Jul 2021 16:36:15 -0700 Subject: [PATCH 48/83] Updated reference. --- source/Open.Disposable.ObjectPools.csproj | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/source/Open.Disposable.ObjectPools.csproj b/source/Open.Disposable.ObjectPools.csproj index 4577182..3610e28 100644 --- a/source/Open.Disposable.ObjectPools.csproj +++ b/source/Open.Disposable.ObjectPools.csproj @@ -17,7 +17,7 @@ Part of the "Open" set of libraries. https://github.com/Open-NET-Libraries/Open.Disposable.ObjectPools/ git objectpool;idisposable;thread safe - 2.6.0 + 2.6.1 MIT true @@ -42,7 +42,7 @@ Part of the "Open" set of libraries. - + From 2410ef5dd2bde17378dd10e0b28a48f47ce35f28 Mon Sep 17 00:00:00 2001 From: electricessence <5899455+electricessence@users.noreply.github.com> Date: Tue, 6 Jul 2021 17:06:15 -0700 Subject: [PATCH 49/83] Swtiched to InterlockedArrayObjectPool as it has better overall performance with small pool sizes. --- benchmarking/Program.cs | 3 --- source/Open.Disposable.ObjectPools.csproj | 2 +- source/SharedPools.cs | 12 ++++++------ 3 files changed, 7 insertions(+), 10 deletions(-) diff --git a/benchmarking/Program.cs b/benchmarking/Program.cs index 3477af1..0fda8a9 100644 --- a/benchmarking/Program.cs +++ b/benchmarking/Program.cs @@ -26,9 +26,6 @@ static void Main() //report.AddBenchmark("QueueObjectPool", // Note, that this one isn't far off from the following in peformance. // count => () => QueueObjectPool.Create((int)count * 2)); - //report.AddBenchmark("ChannelObjectPool", - // count => () => ChannelObjectPool.Create((int)count * 2)); - report.AddBenchmark("ConcurrentQueueObjectPool", // Note, that this one isn't far off from the following in peformance, but definitely is faster than LinkedListObjectPool and the rest. count => () => ConcurrentQueueObjectPool.Create((int)count * 2)); diff --git a/source/Open.Disposable.ObjectPools.csproj b/source/Open.Disposable.ObjectPools.csproj index 3610e28..5818c71 100644 --- a/source/Open.Disposable.ObjectPools.csproj +++ b/source/Open.Disposable.ObjectPools.csproj @@ -17,7 +17,7 @@ Part of the "Open" set of libraries. https://github.com/Open-NET-Libraries/Open.Disposable.ObjectPools/ git objectpool;idisposable;thread safe - 2.6.1 + 2.6.2 MIT true diff --git a/source/SharedPools.cs b/source/SharedPools.cs index b08eefd..7fcde73 100644 --- a/source/SharedPools.cs +++ b/source/SharedPools.cs @@ -9,13 +9,13 @@ public static class ListPool /// The list is cleared after being returned. /// The max size of the pool is 128 which should suffice for most use cases. /// - public static readonly ConcurrentQueueObjectPool> Shared = Create(); + public static readonly InterlockedArrayObjectPool> Shared = Create(); /// /// Creates an object pool for use with lists. /// The list is cleared after being returned. /// - public static ConcurrentQueueObjectPool> Create(int capacity = Constants.DEFAULT_CAPACITY) => new(() => new List(), h => + public static InterlockedArrayObjectPool> Create(int capacity = Constants.DEFAULT_CAPACITY) => new(() => new List(), h => { h.Clear(); if (h.Capacity > 16) h.Capacity = 16; @@ -29,13 +29,13 @@ public static class HashSetPool /// The hash-set is cleared after being returned. /// The max size of the pool is 128 which should suffice for most use cases. /// - public static readonly ConcurrentQueueObjectPool> Shared = Create(); + public static readonly InterlockedArrayObjectPool> Shared = Create(); /// /// Creates an object pool for use with hash-sets. /// The hash-set is cleared after being returned. /// - public static ConcurrentQueueObjectPool> Create(int capacity = Constants.DEFAULT_CAPACITY) => new(() => new HashSet(), h => h.Clear(), null, capacity); + public static InterlockedArrayObjectPool> Create(int capacity = Constants.DEFAULT_CAPACITY) => new(() => new HashSet(), h => h.Clear(), null, capacity); } public static class DictionaryPool @@ -45,13 +45,13 @@ public static class DictionaryPool /// The dictionary is cleared after being returned. /// The max size of the pool is 128 which should suffice for most use cases. /// - public static readonly ConcurrentQueueObjectPool> Shared = Create(); + public static readonly InterlockedArrayObjectPool> Shared = Create(); /// /// Creates an object pooll for use with dictionaries. /// The dictionary is cleared after being returned. /// - public static ConcurrentQueueObjectPool> Create(int capacity = Constants.DEFAULT_CAPACITY) => new(() => new Dictionary(), h => h.Clear(), null, capacity); + public static InterlockedArrayObjectPool> Create(int capacity = Constants.DEFAULT_CAPACITY) => new(() => new Dictionary(), h => h.Clear(), null, capacity); } } From b946798f91722fa4697fdf9eed961b8aff54188e Mon Sep 17 00:00:00 2001 From: electricessence <5899455+electricessence@users.noreply.github.com> Date: Tue, 6 Jul 2021 23:43:06 -0700 Subject: [PATCH 50/83] Introduced "ConcurrentQueueObjectPoolSlim" that doesn't allow trimming. Simpler, and slightly faster. Very optmisitic for capacity. --- benchmarking/BenchmarkResult.csv | 44 ++-- benchmarking/BenchmarkResult.txt | 220 +++++++++++------- benchmarking/Program.cs | 3 + .../ConcurrentQueueObjectPoolSlim.cs | 105 +++++++++ source/Constants.cs | 2 +- source/Open.Disposable.ObjectPools.csproj | 2 +- source/SharedPools.cs | 18 +- 7 files changed, 270 insertions(+), 124 deletions(-) create mode 100644 source/Collection/ConcurrentQueueObjectPoolSlim.cs diff --git a/benchmarking/BenchmarkResult.csv b/benchmarking/BenchmarkResult.csv index 820219c..daaf07c 100644 --- a/benchmarking/BenchmarkResult.csv +++ b/benchmarking/BenchmarkResult.csv @@ -1,27 +1,25 @@ Batch,Pool Type," To (In Parallel)",d 90%-Take/10%-Give (In Parallel),d 50%-Take/50%-Give (In Parallel),d 10%-Take/90%-Give (In Parallel),y Pool (.TryTake()),L, -Repeat 1200000 for size 4,ConcurrentQueueObjectPool,00:00:02.8137291,00:00:02.7153391,00:00:02.6985097,00:00:02.7410619,00:00:00.1839002,00:00:11.1525400, -Repeat 1200000 for size 4,OptimisticArrayObjectPool,00:00:02.6651904,00:00:02.6475629,00:00:02.6552883,00:00:02.5890153,00:00:00.1745340,00:00:10.7315909, -Repeat 1200000 for size 4,InterlockedArrayObjectPool,00:00:02.5827382,00:00:02.7023234,00:00:02.5996861,00:00:02.6370871,00:00:00.1624972,00:00:10.6843320, -Repeat 480000 for size 10,ConcurrentQueueObjectPool,00:00:01.7226667,00:00:01.6591525,00:00:01.6255627,00:00:01.6121179,00:00:00.1629193,00:00:06.7824191, -Repeat 480000 for size 10,OptimisticArrayObjectPool,00:00:01.7076846,00:00:01.7039172,00:00:01.6884271,00:00:01.6330203,00:00:00.2135140,00:00:06.9465632, -Repeat 480000 for size 10,InterlockedArrayObjectPool,00:00:01.8557304,00:00:01.7516982,00:00:01.6388447,00:00:01.6915551,00:00:00.2162540,00:00:07.1540824, -Repeat 144000 for size 50,ConcurrentQueueObjectPool,00:00:00.9993787,00:00:00.8644147,00:00:00.7644592,00:00:00.8417858,00:00:00.2118096,00:00:03.6818480, -Repeat 144000 for size 50,OptimisticArrayObjectPool,00:00:01.0304016,00:00:00.9726821,00:00:00.7458965,00:00:00.8811575,00:00:00.6318591,00:00:04.2619968, -Repeat 144000 for size 50,InterlockedArrayObjectPool,00:00:01.2025048,00:00:00.9502443,00:00:00.7301001,00:00:00.9604869,00:00:00.9480492,00:00:04.7913853, -Repeat 96000 for size 100,ConcurrentQueueObjectPool,00:00:01.0717994,00:00:00.9417134,00:00:00.7430992,00:00:00.8641968,00:00:00.2921380,00:00:03.9129468, -Repeat 96000 for size 100,OptimisticArrayObjectPool,00:00:00.9926382,00:00:01.0228171,00:00:00.6834145,00:00:00.7896683,00:00:01.0471828,00:00:04.5357209, -Repeat 96000 for size 100,InterlockedArrayObjectPool,00:00:01.4431922,00:00:01.0450868,00:00:00.6856350,00:00:01.0800545,00:00:02.3318448,00:00:06.5858133, -Repeat 76800 for size 250,ConcurrentQueueObjectPool,00:00:01.9927953,00:00:01.6576482,00:00:01.2314163,00:00:01.5604890,00:00:00.6205257,00:00:07.0628745, -Repeat 76800 for size 250,OptimisticArrayObjectPool,00:00:03.0736505,00:00:02.3286546,00:00:01.0713747,00:00:01.3538321,00:00:04.0564585,00:00:11.8839704, -Repeat 76800 for size 250,InterlockedArrayObjectPool,00:00:04.9460361,00:00:02.3117061,00:00:01.0202632,00:00:02.6333260,00:00:07.5373478,00:00:18.4486792, -peat 96000 for size 100,InterlockedArrayObjectPool,00:00:01.5202602,00:00:01.0914433,00:00:00.7177933,00:00:01.1267538,00:00:02.1815610,00:00:06.6378116, -Repeat 76800 for size 250,ConcurrentQueueObjectPool,00:00:02.1343720,00:00:01.7012785,00:00:01.2752111,00:00:01.6435934,00:00:00.6997324,00:00:07.4541874, -Repeat 76800 for size 250,ConcurrentStackObjectPool,00:00:06.2066697,00:00:27.8900516,00:00:06.5621678,00:00:06.4422102,00:00:02.4804319,00:00:49.5815312, -Repeat 76800 for size 250,OptimisticArrayObjectPool,00:00:02.0446809,00:00:02.2047801,00:00:01.0930008,00:00:01.3709434,00:00:03.2508715,00:00:09.9642767, -Repeat 76800 for size 250,InterlockedArrayObjectPool,00:00:05.1034766,00:00:02.3508106,00:00:01.0738597,00:00:02.7216389,00:00:08.5749945,00:00:19.8247803, -.3685819, -, -epeat 51200 for size 250,OptimisticArrayObjectPool,00:00:00.7651948,00:00:01.3195712,00:00:01.3758641,00:00:00.6837150,00:00:01.0134658,00:00:01.9616149,00:00:07.1194258, +Repeat 1200000 for size 4,ConcurrentQueueObjectPool,00:00:05.4593208,00:00:05.6641702,00:00:05.5436176,00:00:05.5487469,00:00:00.3664699,00:00:22.5823254, +Repeat 1200000 for size 4,ConcurrentQueueObjectPoolSlim,00:00:05.4322408,00:00:05.5376339,00:00:05.4256461,00:00:05.4504057,00:00:00.3456239,00:00:22.1915504, +Repeat 1200000 for size 4,OptimisticArrayObjectPool,00:00:05.7515713,00:00:05.9020552,00:00:05.7270413,00:00:05.7505300,00:00:00.4225426,00:00:23.5537404, +Repeat 1200000 for size 4,InterlockedArrayObjectPool,00:00:05.7310971,00:00:05.8633343,00:00:05.7558233,00:00:05.7572257,00:00:00.4234853,00:00:23.5309657, +Repeat 480000 for size 10,ConcurrentQueueObjectPool,00:00:02.6559228,00:00:02.8336403,00:00:02.6687352,00:00:02.7110513,00:00:00.2318305,00:00:11.1011801, +Repeat 480000 for size 10,ConcurrentQueueObjectPoolSlim,00:00:02.8013356,00:00:02.9497849,00:00:02.7953925,00:00:02.8494803,00:00:00.2466537,00:00:11.6426470, +Repeat 480000 for size 10,OptimisticArrayObjectPool,00:00:03.1201625,00:00:03.5308652,00:00:03.1032344,00:00:03.1534127,00:00:00.4161196,00:00:13.3237944, +Repeat 480000 for size 10,InterlockedArrayObjectPool,00:00:02.5905872,00:00:02.6907347,00:00:02.3651720,00:00:02.5440796,00:00:00.3139231,00:00:10.5044966, +Repeat 144000 for size 50,ConcurrentQueueObjectPool,00:00:00.8912894,00:00:00.9607579,00:00:00.7788601,00:00:00.9413875,00:00:00.1853197,00:00:03.7576146, +Repeat 144000 for size 50,ConcurrentQueueObjectPoolSlim,00:00:00.7118744,00:00:00.7833912,00:00:00.5933995,00:00:00.7078938,00:00:00.1502073,00:00:02.9467662, +Repeat 144000 for size 50,OptimisticArrayObjectPool,00:00:00.9905638,00:00:00.9864204,00:00:00.6759549,00:00:00.8384916,00:00:00.6304342,00:00:04.1218649, +Repeat 144000 for size 50,InterlockedArrayObjectPool,00:00:01.2005883,00:00:01.0553068,00:00:00.7240912,00:00:00.9785882,00:00:00.7172196,00:00:04.6757941, +Repeat 96000 for size 100,ConcurrentQueueObjectPool,00:00:01.1948956,00:00:01.2436783,00:00:00.7892695,00:00:01.2061012,00:00:00.2451430,00:00:04.6790876, +Repeat 96000 for size 100,ConcurrentQueueObjectPoolSlim,00:00:01.1056454,00:00:01.1291605,00:00:00.6456573,00:00:00.9885465,00:00:00.2333216,00:00:04.1023313, +Repeat 96000 for size 100,OptimisticArrayObjectPool,00:00:01.6250986,00:00:01.1985866,00:00:00.6426214,00:00:00.9037208,00:00:01.4501568,00:00:05.8201842, +Repeat 96000 for size 100,InterlockedArrayObjectPool,00:00:02.0068444,00:00:01.1984315,00:00:00.6897014,00:00:01.1814997,00:00:01.7948454,00:00:06.8713224, +Repeat 76800 for size 250,ConcurrentQueueObjectPool,00:00:02.6729130,00:00:02.7863215,00:00:01.7569907,00:00:02.8007097,00:00:00.4370162,00:00:10.4539511, +Repeat 76800 for size 250,ConcurrentQueueObjectPoolSlim,00:00:02.6410164,00:00:02.6535867,00:00:01.4697118,00:00:02.6904730,00:00:00.4120720,00:00:09.8668599, +Repeat 76800 for size 250,OptimisticArrayObjectPool,00:00:02.9149582,00:00:02.1909359,00:00:01.1199040,00:00:01.5984575,00:00:04.0259771,00:00:11.8502327, +Repeat 76800 for size 250,InterlockedArrayObjectPool,00:00:03.9977467,00:00:02.4777589,00:00:01.2744094,00:00:02.4151544,00:00:06.8796644,00:00:17.0447338, +51200 for size 250,OptimisticArrayObjectPool,00:00:00.7651948,00:00:01.3195712,00:00:01.3758641,00:00:00.6837150,00:00:01.0134658,00:00:01.9616149,00:00:07.1194258, Repeat 51200 for size 250,InterlockedArrayObjectPool,00:00:00.8386708,00:00:02.2800174,00:00:01.4244575,00:00:00.6435472,00:00:01.6845873,00:00:05.4327218,00:00:12.3040020, Repeat 12800 for size 2000,ConcurrentQueueObjectPool,00:00:00.7692018,00:00:03.0317303,00:00:02.0615927,00:00:01.3597065,00:00:02.0767858,00:00:00.6488593,00:00:09.9478764, Repeat 12800 for size 2000,ConcurrentStackObjectPool,00:00:00.6708805,00:00:02.5628201,00:00:04.7978301,00:00:04.7563902,00:00:03.4723738,00:00:00.6475781,00:00:16.9078728, diff --git a/benchmarking/BenchmarkResult.txt b/benchmarking/BenchmarkResult.txt index 0cdf9e7..02c238c 100644 --- a/benchmarking/BenchmarkResult.txt +++ b/benchmarking/BenchmarkResult.txt @@ -2,139 +2,179 @@ Repeat 1200000 for size 4 ------------------------------------ ConcurrentQueueObjectPool............................... -00:00:02.8137291 Give To (In Parallel) -00:00:02.7153391 Mixed 90%-Take/10%-Give (In Parallel) -00:00:02.6985097 Mixed 50%-Take/50%-Give (In Parallel) -00:00:02.7410619 Mixed 10%-Take/90%-Give (In Parallel) -00:00:00.1839002 Empty Pool (.TryTake()) -00:00:11.1525400 TOTAL +00:00:05.4593208 Give To (In Parallel) +00:00:05.6641702 Mixed 90%-Take/10%-Give (In Parallel) +00:00:05.5436176 Mixed 50%-Take/50%-Give (In Parallel) +00:00:05.5487469 Mixed 10%-Take/90%-Give (In Parallel) +00:00:00.3664699 Empty Pool (.TryTake()) +00:00:22.5823254 TOTAL + +ConcurrentQueueObjectPoolSlim........................... +00:00:05.4322408 Give To (In Parallel) +00:00:05.5376339 Mixed 90%-Take/10%-Give (In Parallel) +00:00:05.4256461 Mixed 50%-Take/50%-Give (In Parallel) +00:00:05.4504057 Mixed 10%-Take/90%-Give (In Parallel) +00:00:00.3456239 Empty Pool (.TryTake()) +00:00:22.1915504 TOTAL OptimisticArrayObjectPool............................... -00:00:02.6651904 Give To (In Parallel) -00:00:02.6475629 Mixed 90%-Take/10%-Give (In Parallel) -00:00:02.6552883 Mixed 50%-Take/50%-Give (In Parallel) -00:00:02.5890153 Mixed 10%-Take/90%-Give (In Parallel) -00:00:00.1745340 Empty Pool (.TryTake()) -00:00:10.7315909 TOTAL +00:00:05.7515713 Give To (In Parallel) +00:00:05.9020552 Mixed 90%-Take/10%-Give (In Parallel) +00:00:05.7270413 Mixed 50%-Take/50%-Give (In Parallel) +00:00:05.7505300 Mixed 10%-Take/90%-Give (In Parallel) +00:00:00.4225426 Empty Pool (.TryTake()) +00:00:23.5537404 TOTAL InterlockedArrayObjectPool.............................. -00:00:02.5827382 Give To (In Parallel) -00:00:02.7023234 Mixed 90%-Take/10%-Give (In Parallel) -00:00:02.5996861 Mixed 50%-Take/50%-Give (In Parallel) -00:00:02.6370871 Mixed 10%-Take/90%-Give (In Parallel) -00:00:00.1624972 Empty Pool (.TryTake()) -00:00:10.6843320 TOTAL +00:00:05.7310971 Give To (In Parallel) +00:00:05.8633343 Mixed 90%-Take/10%-Give (In Parallel) +00:00:05.7558233 Mixed 50%-Take/50%-Give (In Parallel) +00:00:05.7572257 Mixed 10%-Take/90%-Give (In Parallel) +00:00:00.4234853 Empty Pool (.TryTake()) +00:00:23.5309657 TOTAL Repeat 480000 for size 10 ------------------------------------ ConcurrentQueueObjectPool............................... -00:00:01.7226667 Give To (In Parallel) -00:00:01.6591525 Mixed 90%-Take/10%-Give (In Parallel) -00:00:01.6255627 Mixed 50%-Take/50%-Give (In Parallel) -00:00:01.6121179 Mixed 10%-Take/90%-Give (In Parallel) -00:00:00.1629193 Empty Pool (.TryTake()) -00:00:06.7824191 TOTAL +00:00:02.6559228 Give To (In Parallel) +00:00:02.8336403 Mixed 90%-Take/10%-Give (In Parallel) +00:00:02.6687352 Mixed 50%-Take/50%-Give (In Parallel) +00:00:02.7110513 Mixed 10%-Take/90%-Give (In Parallel) +00:00:00.2318305 Empty Pool (.TryTake()) +00:00:11.1011801 TOTAL + +ConcurrentQueueObjectPoolSlim........................... +00:00:02.8013356 Give To (In Parallel) +00:00:02.9497849 Mixed 90%-Take/10%-Give (In Parallel) +00:00:02.7953925 Mixed 50%-Take/50%-Give (In Parallel) +00:00:02.8494803 Mixed 10%-Take/90%-Give (In Parallel) +00:00:00.2466537 Empty Pool (.TryTake()) +00:00:11.6426470 TOTAL OptimisticArrayObjectPool............................... -00:00:01.7076846 Give To (In Parallel) -00:00:01.7039172 Mixed 90%-Take/10%-Give (In Parallel) -00:00:01.6884271 Mixed 50%-Take/50%-Give (In Parallel) -00:00:01.6330203 Mixed 10%-Take/90%-Give (In Parallel) -00:00:00.2135140 Empty Pool (.TryTake()) -00:00:06.9465632 TOTAL +00:00:03.1201625 Give To (In Parallel) +00:00:03.5308652 Mixed 90%-Take/10%-Give (In Parallel) +00:00:03.1032344 Mixed 50%-Take/50%-Give (In Parallel) +00:00:03.1534127 Mixed 10%-Take/90%-Give (In Parallel) +00:00:00.4161196 Empty Pool (.TryTake()) +00:00:13.3237944 TOTAL InterlockedArrayObjectPool.............................. -00:00:01.8557304 Give To (In Parallel) -00:00:01.7516982 Mixed 90%-Take/10%-Give (In Parallel) -00:00:01.6388447 Mixed 50%-Take/50%-Give (In Parallel) -00:00:01.6915551 Mixed 10%-Take/90%-Give (In Parallel) -00:00:00.2162540 Empty Pool (.TryTake()) -00:00:07.1540824 TOTAL +00:00:02.5905872 Give To (In Parallel) +00:00:02.6907347 Mixed 90%-Take/10%-Give (In Parallel) +00:00:02.3651720 Mixed 50%-Take/50%-Give (In Parallel) +00:00:02.5440796 Mixed 10%-Take/90%-Give (In Parallel) +00:00:00.3139231 Empty Pool (.TryTake()) +00:00:10.5044966 TOTAL Repeat 144000 for size 50 ------------------------------------ ConcurrentQueueObjectPool............................... -00:00:00.9993787 Give To (In Parallel) -00:00:00.8644147 Mixed 90%-Take/10%-Give (In Parallel) -00:00:00.7644592 Mixed 50%-Take/50%-Give (In Parallel) -00:00:00.8417858 Mixed 10%-Take/90%-Give (In Parallel) -00:00:00.2118096 Empty Pool (.TryTake()) -00:00:03.6818480 TOTAL +00:00:00.8912894 Give To (In Parallel) +00:00:00.9607579 Mixed 90%-Take/10%-Give (In Parallel) +00:00:00.7788601 Mixed 50%-Take/50%-Give (In Parallel) +00:00:00.9413875 Mixed 10%-Take/90%-Give (In Parallel) +00:00:00.1853197 Empty Pool (.TryTake()) +00:00:03.7576146 TOTAL + +ConcurrentQueueObjectPoolSlim........................... +00:00:00.7118744 Give To (In Parallel) +00:00:00.7833912 Mixed 90%-Take/10%-Give (In Parallel) +00:00:00.5933995 Mixed 50%-Take/50%-Give (In Parallel) +00:00:00.7078938 Mixed 10%-Take/90%-Give (In Parallel) +00:00:00.1502073 Empty Pool (.TryTake()) +00:00:02.9467662 TOTAL OptimisticArrayObjectPool............................... -00:00:01.0304016 Give To (In Parallel) -00:00:00.9726821 Mixed 90%-Take/10%-Give (In Parallel) -00:00:00.7458965 Mixed 50%-Take/50%-Give (In Parallel) -00:00:00.8811575 Mixed 10%-Take/90%-Give (In Parallel) -00:00:00.6318591 Empty Pool (.TryTake()) -00:00:04.2619968 TOTAL +00:00:00.9905638 Give To (In Parallel) +00:00:00.9864204 Mixed 90%-Take/10%-Give (In Parallel) +00:00:00.6759549 Mixed 50%-Take/50%-Give (In Parallel) +00:00:00.8384916 Mixed 10%-Take/90%-Give (In Parallel) +00:00:00.6304342 Empty Pool (.TryTake()) +00:00:04.1218649 TOTAL InterlockedArrayObjectPool.............................. -00:00:01.2025048 Give To (In Parallel) -00:00:00.9502443 Mixed 90%-Take/10%-Give (In Parallel) -00:00:00.7301001 Mixed 50%-Take/50%-Give (In Parallel) -00:00:00.9604869 Mixed 10%-Take/90%-Give (In Parallel) -00:00:00.9480492 Empty Pool (.TryTake()) -00:00:04.7913853 TOTAL +00:00:01.2005883 Give To (In Parallel) +00:00:01.0553068 Mixed 90%-Take/10%-Give (In Parallel) +00:00:00.7240912 Mixed 50%-Take/50%-Give (In Parallel) +00:00:00.9785882 Mixed 10%-Take/90%-Give (In Parallel) +00:00:00.7172196 Empty Pool (.TryTake()) +00:00:04.6757941 TOTAL Repeat 96000 for size 100 ------------------------------------ ConcurrentQueueObjectPool............................... -00:00:01.0717994 Give To (In Parallel) -00:00:00.9417134 Mixed 90%-Take/10%-Give (In Parallel) -00:00:00.7430992 Mixed 50%-Take/50%-Give (In Parallel) -00:00:00.8641968 Mixed 10%-Take/90%-Give (In Parallel) -00:00:00.2921380 Empty Pool (.TryTake()) -00:00:03.9129468 TOTAL +00:00:01.1948956 Give To (In Parallel) +00:00:01.2436783 Mixed 90%-Take/10%-Give (In Parallel) +00:00:00.7892695 Mixed 50%-Take/50%-Give (In Parallel) +00:00:01.2061012 Mixed 10%-Take/90%-Give (In Parallel) +00:00:00.2451430 Empty Pool (.TryTake()) +00:00:04.6790876 TOTAL + +ConcurrentQueueObjectPoolSlim........................... +00:00:01.1056454 Give To (In Parallel) +00:00:01.1291605 Mixed 90%-Take/10%-Give (In Parallel) +00:00:00.6456573 Mixed 50%-Take/50%-Give (In Parallel) +00:00:00.9885465 Mixed 10%-Take/90%-Give (In Parallel) +00:00:00.2333216 Empty Pool (.TryTake()) +00:00:04.1023313 TOTAL OptimisticArrayObjectPool............................... -00:00:00.9926382 Give To (In Parallel) -00:00:01.0228171 Mixed 90%-Take/10%-Give (In Parallel) -00:00:00.6834145 Mixed 50%-Take/50%-Give (In Parallel) -00:00:00.7896683 Mixed 10%-Take/90%-Give (In Parallel) -00:00:01.0471828 Empty Pool (.TryTake()) -00:00:04.5357209 TOTAL +00:00:01.6250986 Give To (In Parallel) +00:00:01.1985866 Mixed 90%-Take/10%-Give (In Parallel) +00:00:00.6426214 Mixed 50%-Take/50%-Give (In Parallel) +00:00:00.9037208 Mixed 10%-Take/90%-Give (In Parallel) +00:00:01.4501568 Empty Pool (.TryTake()) +00:00:05.8201842 TOTAL InterlockedArrayObjectPool.............................. -00:00:01.4431922 Give To (In Parallel) -00:00:01.0450868 Mixed 90%-Take/10%-Give (In Parallel) -00:00:00.6856350 Mixed 50%-Take/50%-Give (In Parallel) -00:00:01.0800545 Mixed 10%-Take/90%-Give (In Parallel) -00:00:02.3318448 Empty Pool (.TryTake()) -00:00:06.5858133 TOTAL +00:00:02.0068444 Give To (In Parallel) +00:00:01.1984315 Mixed 90%-Take/10%-Give (In Parallel) +00:00:00.6897014 Mixed 50%-Take/50%-Give (In Parallel) +00:00:01.1814997 Mixed 10%-Take/90%-Give (In Parallel) +00:00:01.7948454 Empty Pool (.TryTake()) +00:00:06.8713224 TOTAL Repeat 76800 for size 250 ------------------------------------ ConcurrentQueueObjectPool............................... -00:00:01.9927953 Give To (In Parallel) -00:00:01.6576482 Mixed 90%-Take/10%-Give (In Parallel) -00:00:01.2314163 Mixed 50%-Take/50%-Give (In Parallel) -00:00:01.5604890 Mixed 10%-Take/90%-Give (In Parallel) -00:00:00.6205257 Empty Pool (.TryTake()) -00:00:07.0628745 TOTAL +00:00:02.6729130 Give To (In Parallel) +00:00:02.7863215 Mixed 90%-Take/10%-Give (In Parallel) +00:00:01.7569907 Mixed 50%-Take/50%-Give (In Parallel) +00:00:02.8007097 Mixed 10%-Take/90%-Give (In Parallel) +00:00:00.4370162 Empty Pool (.TryTake()) +00:00:10.4539511 TOTAL + +ConcurrentQueueObjectPoolSlim........................... +00:00:02.6410164 Give To (In Parallel) +00:00:02.6535867 Mixed 90%-Take/10%-Give (In Parallel) +00:00:01.4697118 Mixed 50%-Take/50%-Give (In Parallel) +00:00:02.6904730 Mixed 10%-Take/90%-Give (In Parallel) +00:00:00.4120720 Empty Pool (.TryTake()) +00:00:09.8668599 TOTAL OptimisticArrayObjectPool............................... -00:00:03.0736505 Give To (In Parallel) -00:00:02.3286546 Mixed 90%-Take/10%-Give (In Parallel) -00:00:01.0713747 Mixed 50%-Take/50%-Give (In Parallel) -00:00:01.3538321 Mixed 10%-Take/90%-Give (In Parallel) -00:00:04.0564585 Empty Pool (.TryTake()) -00:00:11.8839704 TOTAL +00:00:02.9149582 Give To (In Parallel) +00:00:02.1909359 Mixed 90%-Take/10%-Give (In Parallel) +00:00:01.1199040 Mixed 50%-Take/50%-Give (In Parallel) +00:00:01.5984575 Mixed 10%-Take/90%-Give (In Parallel) +00:00:04.0259771 Empty Pool (.TryTake()) +00:00:11.8502327 TOTAL InterlockedArrayObjectPool.............................. -00:00:04.9460361 Give To (In Parallel) -00:00:02.3117061 Mixed 90%-Take/10%-Give (In Parallel) -00:00:01.0202632 Mixed 50%-Take/50%-Give (In Parallel) -00:00:02.6333260 Mixed 10%-Take/90%-Give (In Parallel) -00:00:07.5373478 Empty Pool (.TryTake()) -00:00:18.4486792 TOTAL +00:00:03.9977467 Give To (In Parallel) +00:00:02.4777589 Mixed 90%-Take/10%-Give (In Parallel) +00:00:01.2744094 Mixed 50%-Take/50%-Give (In Parallel) +00:00:02.4151544 Mixed 10%-Take/90%-Give (In Parallel) +00:00:06.8796644 Empty Pool (.TryTake()) +00:00:17.0447338 TOTAL diff --git a/benchmarking/Program.cs b/benchmarking/Program.cs index 0fda8a9..a5c6a6d 100644 --- a/benchmarking/Program.cs +++ b/benchmarking/Program.cs @@ -29,6 +29,9 @@ static void Main() report.AddBenchmark("ConcurrentQueueObjectPool", // Note, that this one isn't far off from the following in peformance, but definitely is faster than LinkedListObjectPool and the rest. count => () => ConcurrentQueueObjectPool.Create((int)count * 2)); + report.AddBenchmark("ConcurrentQueueObjectPoolSlim", // Note, that this one isn't far off from the following in peformance, but definitely is faster than LinkedListObjectPool and the rest. + count => () => ConcurrentQueueObjectPoolSlim.Create((int)count * 2)); + //report.AddBenchmark("ConcurrentStackObjectPool", // Note, that this one isn't far off from the following in peformance, but definitely is faster than LinkedListObjectPool and the rest. // count => () => ConcurrentStackObjectPool.Create((int)count * 2)); diff --git a/source/Collection/ConcurrentQueueObjectPoolSlim.cs b/source/Collection/ConcurrentQueueObjectPoolSlim.cs new file mode 100644 index 0000000..2cd0a34 --- /dev/null +++ b/source/Collection/ConcurrentQueueObjectPoolSlim.cs @@ -0,0 +1,105 @@ +using System; +using System.Collections.Concurrent; +using System.Threading; + +namespace Open.Disposable +{ + public sealed class ConcurrentQueueObjectPoolSlim : ObjectPoolBase + where T : class + { + + public ConcurrentQueueObjectPoolSlim(Func factory, Action? recycler, Action? disposer, int capacity = DEFAULT_CAPACITY - 10) + : base(factory, recycler, disposer, capacity) + { + Pool = new(); + } + + public ConcurrentQueueObjectPoolSlim(Func factory, int capacity = DEFAULT_CAPACITY - 10) + : this(factory, null, null, capacity) + { + + } + + ConcurrentQueue Pool; + int _count; + public override int Count => _count; + + protected override bool CanReceive => _count < Capacity; + + protected override void OnReleased() + { + Interlocked.Decrement(ref _count); + } + protected override void OnReceived() + { + Interlocked.Increment(ref _count); + } + + /* + * NOTE: ConcurrentQueue is very fast and will perform quite well without using the 'Pocket' feature. + * Benchmarking reveals that mixed read/writes (what really matters) are still faster with the pocket enabled so best to keep it so. + */ + + protected override bool Receive(T item) + { + var p = Pool; + if (p is null) return false; + p.Enqueue(item); // It's possible that the count could exceed MaxSize here, but the risk is negligble as a few over the limit won't hurt. + return true; + } + + protected override T? TryRelease() + { + var p = Pool; + if (p is null) return null; + p.TryDequeue(out var item); + return item; + } + + protected override void OnDispose() + { + base.OnDispose(); + Pool = null!; + } + + } + public static class ConcurrentQueueObjectPoolSlim + { + public static ConcurrentQueueObjectPoolSlim Create(Func factory, int capacity = Constants.DEFAULT_CAPACITY) + where T : class + { + return new ConcurrentQueueObjectPoolSlim(factory, capacity); + } + + public static ConcurrentQueueObjectPoolSlim Create(int capacity = Constants.DEFAULT_CAPACITY) + where T : class, new() + { + return Create(() => new T(), capacity); + } + + public static ConcurrentQueueObjectPoolSlim CreateAutoRecycle(Func factory, int capacity = Constants.DEFAULT_CAPACITY) + where T : class, IRecyclable + { + return new ConcurrentQueueObjectPoolSlim(factory, Recycler.Recycle, null, capacity); + } + + public static ConcurrentQueueObjectPoolSlim CreateAutoRecycle(int capacity = Constants.DEFAULT_CAPACITY) + where T : class, IRecyclable, new() + { + return CreateAutoRecycle(() => new T(), capacity); + } + + public static ConcurrentQueueObjectPoolSlim CreateAutoDisposal(Func factory, int capacity = Constants.DEFAULT_CAPACITY) + where T : class, IDisposable + { + return new ConcurrentQueueObjectPoolSlim(factory, null, d => d.Dispose(), capacity); + } + + public static ConcurrentQueueObjectPoolSlim CreateAutoDisposal(int capacity = Constants.DEFAULT_CAPACITY) + where T : class, IDisposable, new() + { + return CreateAutoDisposal(() => new T(), capacity); + } + + } +} diff --git a/source/Constants.cs b/source/Constants.cs index fc50592..5ed6e77 100644 --- a/source/Constants.cs +++ b/source/Constants.cs @@ -2,6 +2,6 @@ { internal static class Constants { - internal const int DEFAULT_CAPACITY = 128; // Should accomodate all object pools without decreasing their effectiveness. + internal const int DEFAULT_CAPACITY = 64; // Should accomodate all object pools without decreasing their effectiveness. } } diff --git a/source/Open.Disposable.ObjectPools.csproj b/source/Open.Disposable.ObjectPools.csproj index 5818c71..4836658 100644 --- a/source/Open.Disposable.ObjectPools.csproj +++ b/source/Open.Disposable.ObjectPools.csproj @@ -17,7 +17,7 @@ Part of the "Open" set of libraries. https://github.com/Open-NET-Libraries/Open.Disposable.ObjectPools/ git objectpool;idisposable;thread safe - 2.6.2 + 2.6.3 MIT true diff --git a/source/SharedPools.cs b/source/SharedPools.cs index 7fcde73..7c1aa12 100644 --- a/source/SharedPools.cs +++ b/source/SharedPools.cs @@ -7,15 +7,15 @@ public static class ListPool /// /// A shared object pool for use with lists. /// The list is cleared after being returned. - /// The max size of the pool is 128 which should suffice for most use cases. + /// The max size of the pool is 64 which should suffice for most use cases. /// - public static readonly InterlockedArrayObjectPool> Shared = Create(); + public static readonly ConcurrentQueueObjectPoolSlim> Shared = Create(); /// /// Creates an object pool for use with lists. /// The list is cleared after being returned. /// - public static InterlockedArrayObjectPool> Create(int capacity = Constants.DEFAULT_CAPACITY) => new(() => new List(), h => + public static ConcurrentQueueObjectPoolSlim> Create(int capacity = Constants.DEFAULT_CAPACITY) => new(() => new List(), h => { h.Clear(); if (h.Capacity > 16) h.Capacity = 16; @@ -27,15 +27,15 @@ public static class HashSetPool /// /// A shared object pool for use with hash-sets. /// The hash-set is cleared after being returned. - /// The max size of the pool is 128 which should suffice for most use cases. + /// The max size of the pool is 64 which should suffice for most use cases. /// - public static readonly InterlockedArrayObjectPool> Shared = Create(); + public static readonly ConcurrentQueueObjectPoolSlim> Shared = Create(); /// /// Creates an object pool for use with hash-sets. /// The hash-set is cleared after being returned. /// - public static InterlockedArrayObjectPool> Create(int capacity = Constants.DEFAULT_CAPACITY) => new(() => new HashSet(), h => h.Clear(), null, capacity); + public static ConcurrentQueueObjectPoolSlim> Create(int capacity = Constants.DEFAULT_CAPACITY) => new(() => new HashSet(), h => h.Clear(), null, capacity); } public static class DictionaryPool @@ -43,15 +43,15 @@ public static class DictionaryPool /// /// A shared object pool for use with dictionaries. /// The dictionary is cleared after being returned. - /// The max size of the pool is 128 which should suffice for most use cases. + /// The max size of the pool is 64 which should suffice for most use cases. /// - public static readonly InterlockedArrayObjectPool> Shared = Create(); + public static readonly ConcurrentQueueObjectPoolSlim> Shared = Create(); /// /// Creates an object pooll for use with dictionaries. /// The dictionary is cleared after being returned. /// - public static InterlockedArrayObjectPool> Create(int capacity = Constants.DEFAULT_CAPACITY) => new(() => new Dictionary(), h => h.Clear(), null, capacity); + public static ConcurrentQueueObjectPoolSlim> Create(int capacity = Constants.DEFAULT_CAPACITY) => new(() => new Dictionary(), h => h.Clear(), null, capacity); } } From f27807dac7e800118f4576eaf53f549f9a4a97bd Mon Sep 17 00:00:00 2001 From: electricessence <5899455+electricessence@users.noreply.github.com> Date: Tue, 6 Jul 2021 23:47:38 -0700 Subject: [PATCH 51/83] Minor adjustment. --- source/Collection/ConcurrentQueueObjectPoolSlim.cs | 2 +- source/Open.Disposable.ObjectPools.csproj | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/source/Collection/ConcurrentQueueObjectPoolSlim.cs b/source/Collection/ConcurrentQueueObjectPoolSlim.cs index 2cd0a34..ed04214 100644 --- a/source/Collection/ConcurrentQueueObjectPoolSlim.cs +++ b/source/Collection/ConcurrentQueueObjectPoolSlim.cs @@ -24,7 +24,7 @@ public ConcurrentQueueObjectPoolSlim(Func factory, int capacity = DEFAULT_CAP int _count; public override int Count => _count; - protected override bool CanReceive => _count < Capacity; + protected override bool CanReceive => _count < MaxSize; protected override void OnReleased() { diff --git a/source/Open.Disposable.ObjectPools.csproj b/source/Open.Disposable.ObjectPools.csproj index 4836658..5541ea8 100644 --- a/source/Open.Disposable.ObjectPools.csproj +++ b/source/Open.Disposable.ObjectPools.csproj @@ -17,7 +17,7 @@ Part of the "Open" set of libraries. https://github.com/Open-NET-Libraries/Open.Disposable.ObjectPools/ git objectpool;idisposable;thread safe - 2.6.3 + 2.6.4 MIT true From 4b406f033c961003a1497c0545dec782b0033df8 Mon Sep 17 00:00:00 2001 From: electricessence <5899455+electricessence@users.noreply.github.com> Date: Wed, 7 Jul 2021 14:07:49 -0700 Subject: [PATCH 52/83] Ensure recycling is working. --- tests/ObjectPoolSmokeTests.cs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/tests/ObjectPoolSmokeTests.cs b/tests/ObjectPoolSmokeTests.cs index 090cf6d..d8b1054 100644 --- a/tests/ObjectPoolSmokeTests.cs +++ b/tests/ObjectPoolSmokeTests.cs @@ -18,5 +18,16 @@ public void OptimisticArrayObjectPool_FactoryTest() Assert.AreEqual(1, pool.Take().ID); } + [TestMethod] + public void ListPool_RecycleTest() + { + var pool = ListPool.Shared; + var list = pool.Take(); + list.Add(1); + pool.Give(list); + Assert.AreEqual(list, pool.Take()); + Assert.AreEqual(0, list.Count); + } + } } From 20ccf7f41b96a1807fb705c8327cfe9c9e5571e6 Mon Sep 17 00:00:00 2001 From: electricessence <5899455+electricessence@users.noreply.github.com> Date: Wed, 7 Jul 2021 14:08:54 -0700 Subject: [PATCH 53/83] Typo fix. --- source/SharedPools.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/SharedPools.cs b/source/SharedPools.cs index 7c1aa12..e61d2f3 100644 --- a/source/SharedPools.cs +++ b/source/SharedPools.cs @@ -48,7 +48,7 @@ public static class DictionaryPool public static readonly ConcurrentQueueObjectPoolSlim> Shared = Create(); /// - /// Creates an object pooll for use with dictionaries. + /// Creates an object pool for use with dictionaries. /// The dictionary is cleared after being returned. /// public static ConcurrentQueueObjectPoolSlim> Create(int capacity = Constants.DEFAULT_CAPACITY) => new(() => new Dictionary(), h => h.Clear(), null, capacity); From cb60fa99ebc196999e55a5974df4692dc60835f9 Mon Sep 17 00:00:00 2001 From: electricessence <5899455+electricessence@users.noreply.github.com> Date: Wed, 7 Jul 2021 14:58:06 -0700 Subject: [PATCH 54/83] Added CountTrackedObjectPoolBase. Some cleanup. --- .../ConcurrentQueueObjectPoolSlim.cs | 15 +-------- source/CountTrackedObjectPoolBase.cs | 33 +++++++++++++++++++ source/ObjectPoolBase.cs | 8 ++--- source/Open.Disposable.ObjectPools.csproj | 2 +- 4 files changed, 37 insertions(+), 21 deletions(-) create mode 100644 source/CountTrackedObjectPoolBase.cs diff --git a/source/Collection/ConcurrentQueueObjectPoolSlim.cs b/source/Collection/ConcurrentQueueObjectPoolSlim.cs index ed04214..88cccbb 100644 --- a/source/Collection/ConcurrentQueueObjectPoolSlim.cs +++ b/source/Collection/ConcurrentQueueObjectPoolSlim.cs @@ -4,7 +4,7 @@ namespace Open.Disposable { - public sealed class ConcurrentQueueObjectPoolSlim : ObjectPoolBase + public sealed class ConcurrentQueueObjectPoolSlim : CountTrackedObjectPoolBase where T : class { @@ -21,19 +21,6 @@ public ConcurrentQueueObjectPoolSlim(Func factory, int capacity = DEFAULT_CAP } ConcurrentQueue Pool; - int _count; - public override int Count => _count; - - protected override bool CanReceive => _count < MaxSize; - - protected override void OnReleased() - { - Interlocked.Decrement(ref _count); - } - protected override void OnReceived() - { - Interlocked.Increment(ref _count); - } /* * NOTE: ConcurrentQueue is very fast and will perform quite well without using the 'Pocket' feature. diff --git a/source/CountTrackedObjectPoolBase.cs b/source/CountTrackedObjectPoolBase.cs new file mode 100644 index 0000000..32eb8f8 --- /dev/null +++ b/source/CountTrackedObjectPoolBase.cs @@ -0,0 +1,33 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Threading; + +namespace Open.Disposable +{ + public abstract class CountTrackedObjectPoolBase : ObjectPoolBase + where T : class + { + + protected CountTrackedObjectPoolBase(Func factory, Action? recycler, Action? disposer, int capacity = DEFAULT_CAPACITY) + : base(factory, recycler, disposer, capacity) + { + + } + + + int _count; + public override int Count => _count; + + protected override bool CanReceive => _count < MaxSize; + + protected override void OnReleased() + { + Interlocked.Decrement(ref _count); + } + protected override void OnReceived() + { + Interlocked.Increment(ref _count); + } + } +} diff --git a/source/ObjectPoolBase.cs b/source/ObjectPoolBase.cs index 4642e64..676eca5 100644 --- a/source/ObjectPoolBase.cs +++ b/source/ObjectPoolBase.cs @@ -18,6 +18,7 @@ protected ObjectPoolBase(Func factory, Action? recycler, Action? dispos OnDiscarded = disposer; } + // Not read-only due to quirk where read-only is slower than not. protected int MaxSize; public int Capacity => MaxSize; @@ -42,7 +43,7 @@ protected ObjectPoolBase(Func factory, Action? recycler, Action? dispos protected ReferenceContainer Pocket; // Default struct constructs itself. #region Receive (.Give(T item)) - protected virtual bool CanReceive => true; // A default of true is acceptable, enabling the Recieve method to do the actual deciding. + protected virtual bool CanReceive => true; // A default of true is acceptable, enabling the Receive method to do the actual deciding. protected bool PrepareToReceive(T item) { @@ -70,11 +71,6 @@ protected virtual void OnReceived() } - protected void OnReceived(bool wasGiven) - { - if (wasGiven) OnReceived(); - } - public void Give(T item) { if (PrepareToReceive(item) diff --git a/source/Open.Disposable.ObjectPools.csproj b/source/Open.Disposable.ObjectPools.csproj index 5541ea8..c5ea7c5 100644 --- a/source/Open.Disposable.ObjectPools.csproj +++ b/source/Open.Disposable.ObjectPools.csproj @@ -17,7 +17,7 @@ Part of the "Open" set of libraries. https://github.com/Open-NET-Libraries/Open.Disposable.ObjectPools/ git objectpool;idisposable;thread safe - 2.6.4 + 2.6.5 MIT true From 36a00db2cafcfdd4d8d38d8494a5e57e34ce2d1b Mon Sep 17 00:00:00 2001 From: electricessence <5899455+electricessence@users.noreply.github.com> Date: Wed, 7 Jul 2021 15:31:27 -0700 Subject: [PATCH 55/83] Benchmark adjustments. --- benchmarking/BenchmarkResult.csv | 42 ++--- benchmarking/BenchmarkResult.txt | 270 +++++++++++++++---------------- benchmarking/Program.cs | 12 +- 3 files changed, 162 insertions(+), 162 deletions(-) diff --git a/benchmarking/BenchmarkResult.csv b/benchmarking/BenchmarkResult.csv index daaf07c..fae1cd8 100644 --- a/benchmarking/BenchmarkResult.csv +++ b/benchmarking/BenchmarkResult.csv @@ -1,25 +1,25 @@ Batch,Pool Type," To (In Parallel)",d 90%-Take/10%-Give (In Parallel),d 50%-Take/50%-Give (In Parallel),d 10%-Take/90%-Give (In Parallel),y Pool (.TryTake()),L, -Repeat 1200000 for size 4,ConcurrentQueueObjectPool,00:00:05.4593208,00:00:05.6641702,00:00:05.5436176,00:00:05.5487469,00:00:00.3664699,00:00:22.5823254, -Repeat 1200000 for size 4,ConcurrentQueueObjectPoolSlim,00:00:05.4322408,00:00:05.5376339,00:00:05.4256461,00:00:05.4504057,00:00:00.3456239,00:00:22.1915504, -Repeat 1200000 for size 4,OptimisticArrayObjectPool,00:00:05.7515713,00:00:05.9020552,00:00:05.7270413,00:00:05.7505300,00:00:00.4225426,00:00:23.5537404, -Repeat 1200000 for size 4,InterlockedArrayObjectPool,00:00:05.7310971,00:00:05.8633343,00:00:05.7558233,00:00:05.7572257,00:00:00.4234853,00:00:23.5309657, -Repeat 480000 for size 10,ConcurrentQueueObjectPool,00:00:02.6559228,00:00:02.8336403,00:00:02.6687352,00:00:02.7110513,00:00:00.2318305,00:00:11.1011801, -Repeat 480000 for size 10,ConcurrentQueueObjectPoolSlim,00:00:02.8013356,00:00:02.9497849,00:00:02.7953925,00:00:02.8494803,00:00:00.2466537,00:00:11.6426470, -Repeat 480000 for size 10,OptimisticArrayObjectPool,00:00:03.1201625,00:00:03.5308652,00:00:03.1032344,00:00:03.1534127,00:00:00.4161196,00:00:13.3237944, -Repeat 480000 for size 10,InterlockedArrayObjectPool,00:00:02.5905872,00:00:02.6907347,00:00:02.3651720,00:00:02.5440796,00:00:00.3139231,00:00:10.5044966, -Repeat 144000 for size 50,ConcurrentQueueObjectPool,00:00:00.8912894,00:00:00.9607579,00:00:00.7788601,00:00:00.9413875,00:00:00.1853197,00:00:03.7576146, -Repeat 144000 for size 50,ConcurrentQueueObjectPoolSlim,00:00:00.7118744,00:00:00.7833912,00:00:00.5933995,00:00:00.7078938,00:00:00.1502073,00:00:02.9467662, -Repeat 144000 for size 50,OptimisticArrayObjectPool,00:00:00.9905638,00:00:00.9864204,00:00:00.6759549,00:00:00.8384916,00:00:00.6304342,00:00:04.1218649, -Repeat 144000 for size 50,InterlockedArrayObjectPool,00:00:01.2005883,00:00:01.0553068,00:00:00.7240912,00:00:00.9785882,00:00:00.7172196,00:00:04.6757941, -Repeat 96000 for size 100,ConcurrentQueueObjectPool,00:00:01.1948956,00:00:01.2436783,00:00:00.7892695,00:00:01.2061012,00:00:00.2451430,00:00:04.6790876, -Repeat 96000 for size 100,ConcurrentQueueObjectPoolSlim,00:00:01.1056454,00:00:01.1291605,00:00:00.6456573,00:00:00.9885465,00:00:00.2333216,00:00:04.1023313, -Repeat 96000 for size 100,OptimisticArrayObjectPool,00:00:01.6250986,00:00:01.1985866,00:00:00.6426214,00:00:00.9037208,00:00:01.4501568,00:00:05.8201842, -Repeat 96000 for size 100,InterlockedArrayObjectPool,00:00:02.0068444,00:00:01.1984315,00:00:00.6897014,00:00:01.1814997,00:00:01.7948454,00:00:06.8713224, -Repeat 76800 for size 250,ConcurrentQueueObjectPool,00:00:02.6729130,00:00:02.7863215,00:00:01.7569907,00:00:02.8007097,00:00:00.4370162,00:00:10.4539511, -Repeat 76800 for size 250,ConcurrentQueueObjectPoolSlim,00:00:02.6410164,00:00:02.6535867,00:00:01.4697118,00:00:02.6904730,00:00:00.4120720,00:00:09.8668599, -Repeat 76800 for size 250,OptimisticArrayObjectPool,00:00:02.9149582,00:00:02.1909359,00:00:01.1199040,00:00:01.5984575,00:00:04.0259771,00:00:11.8502327, -Repeat 76800 for size 250,InterlockedArrayObjectPool,00:00:03.9977467,00:00:02.4777589,00:00:01.2744094,00:00:02.4151544,00:00:06.8796644,00:00:17.0447338, -51200 for size 250,OptimisticArrayObjectPool,00:00:00.7651948,00:00:01.3195712,00:00:01.3758641,00:00:00.6837150,00:00:01.0134658,00:00:01.9616149,00:00:07.1194258, +Repeat 2400000 for size 4,ConcurrentQueueObjectPoolSlim,00:00:10.2379694,00:00:10.4369939,00:00:10.2027024,00:00:10.2029412,00:00:00.6764481,00:00:41.7570550, +Repeat 2400000 for size 4,ConcurrentQueueObjectPool,00:00:10.3263208,00:00:10.6676992,00:00:10.3146522,00:00:10.3892049,00:00:00.8041729,00:00:42.5020500, +Repeat 2400000 for size 4,OptimisticArrayObjectPool,00:00:10.0751346,00:00:10.5956764,00:00:10.1015581,00:00:10.1791619,00:00:00.8202931,00:00:41.7718241, +Repeat 2400000 for size 4,InterlockedArrayObjectPool,00:00:10.1135907,00:00:10.5975168,00:00:10.2069357,00:00:10.2776009,00:00:00.7987301,00:00:41.9943742, +Repeat 960000 for size 10,ConcurrentQueueObjectPoolSlim,00:00:03.9787914,00:00:04.2003072,00:00:04.0901023,00:00:04.1125416,00:00:00.3234760,00:00:16.7052185, +Repeat 960000 for size 10,ConcurrentQueueObjectPool,00:00:02.6705738,00:00:02.9303461,00:00:02.6527993,00:00:02.7590313,00:00:00.2448799,00:00:11.2576304, +Repeat 960000 for size 10,OptimisticArrayObjectPool,00:00:02.7080938,00:00:03.1857486,00:00:02.7267450,00:00:02.8387716,00:00:00.3469802,00:00:11.8063392, +Repeat 960000 for size 10,InterlockedArrayObjectPool,00:00:03.1543776,00:00:03.3044990,00:00:02.9074669,00:00:03.1888732,00:00:00.3640288,00:00:12.9192455, +Repeat 288000 for size 50,ConcurrentQueueObjectPoolSlim,00:00:01.6453853,00:00:01.7359093,00:00:01.3652902,00:00:01.6812617,00:00:00.3263226,00:00:06.7541691, +Repeat 288000 for size 50,ConcurrentQueueObjectPool,00:00:01.7333532,00:00:01.8169673,00:00:01.4589195,00:00:01.7403590,00:00:00.3740043,00:00:07.1236033, +Repeat 288000 for size 50,OptimisticArrayObjectPool,00:00:02.0335210,00:00:02.0578942,00:00:01.4266376,00:00:01.7863439,00:00:01.3344072,00:00:08.6388039, +Repeat 288000 for size 50,InterlockedArrayObjectPool,00:00:02.5295986,00:00:02.2228966,00:00:01.5291152,00:00:02.0956115,00:00:01.5541699,00:00:09.9313918, +Repeat 192000 for size 100,ConcurrentQueueObjectPoolSlim,00:00:02.3422135,00:00:02.4589825,00:00:01.4419367,00:00:02.2890798,00:00:00.4853905,00:00:09.0176030, +Repeat 192000 for size 100,ConcurrentQueueObjectPool,00:00:02.5509913,00:00:02.6460844,00:00:01.6833011,00:00:02.5321679,00:00:00.5554094,00:00:09.9679541, +Repeat 192000 for size 100,OptimisticArrayObjectPool,00:00:03.3738400,00:00:02.4423141,00:00:01.3292480,00:00:01.8814529,00:00:02.8973500,00:00:11.9242050, +Repeat 192000 for size 100,InterlockedArrayObjectPool,00:00:04.0758376,00:00:02.4918027,00:00:01.4167009,00:00:02.4431298,00:00:03.5020819,00:00:13.9295529, +Repeat 153600 for size 250,ConcurrentQueueObjectPoolSlim,00:00:05.3605084,00:00:05.3904152,00:00:02.9076538,00:00:05.4448936,00:00:00.8048672,00:00:19.9083382, +Repeat 153600 for size 250,ConcurrentQueueObjectPool,00:00:05.3745739,00:00:05.6739715,00:00:03.5889170,00:00:05.5923650,00:00:00.9855414,00:00:21.2153688, +Repeat 153600 for size 250,OptimisticArrayObjectPool,00:00:05.9226589,00:00:04.5202656,00:00:02.2839784,00:00:03.2966061,00:00:08.1874870,00:00:24.2109960, +Repeat 153600 for size 250,InterlockedArrayObjectPool,00:00:22.5836030,00:00:12.0998476,00:00:06.3335435,00:00:12.1356510,00:00:38.4403391,00:01:31.5929842, +r size 250,OptimisticArrayObjectPool,00:00:00.7651948,00:00:01.3195712,00:00:01.3758641,00:00:00.6837150,00:00:01.0134658,00:00:01.9616149,00:00:07.1194258, Repeat 51200 for size 250,InterlockedArrayObjectPool,00:00:00.8386708,00:00:02.2800174,00:00:01.4244575,00:00:00.6435472,00:00:01.6845873,00:00:05.4327218,00:00:12.3040020, Repeat 12800 for size 2000,ConcurrentQueueObjectPool,00:00:00.7692018,00:00:03.0317303,00:00:02.0615927,00:00:01.3597065,00:00:02.0767858,00:00:00.6488593,00:00:09.9478764, Repeat 12800 for size 2000,ConcurrentStackObjectPool,00:00:00.6708805,00:00:02.5628201,00:00:04.7978301,00:00:04.7563902,00:00:03.4723738,00:00:00.6475781,00:00:16.9078728, diff --git a/benchmarking/BenchmarkResult.txt b/benchmarking/BenchmarkResult.txt index 02c238c..03565e3 100644 --- a/benchmarking/BenchmarkResult.txt +++ b/benchmarking/BenchmarkResult.txt @@ -1,180 +1,180 @@ -Repeat 1200000 for size 4 +Repeat 2400000 for size 4 ------------------------------------ -ConcurrentQueueObjectPool............................... -00:00:05.4593208 Give To (In Parallel) -00:00:05.6641702 Mixed 90%-Take/10%-Give (In Parallel) -00:00:05.5436176 Mixed 50%-Take/50%-Give (In Parallel) -00:00:05.5487469 Mixed 10%-Take/90%-Give (In Parallel) -00:00:00.3664699 Empty Pool (.TryTake()) -00:00:22.5823254 TOTAL - ConcurrentQueueObjectPoolSlim........................... -00:00:05.4322408 Give To (In Parallel) -00:00:05.5376339 Mixed 90%-Take/10%-Give (In Parallel) -00:00:05.4256461 Mixed 50%-Take/50%-Give (In Parallel) -00:00:05.4504057 Mixed 10%-Take/90%-Give (In Parallel) -00:00:00.3456239 Empty Pool (.TryTake()) -00:00:22.1915504 TOTAL +00:00:10.2379694 Give To (In Parallel) +00:00:10.4369939 Mixed 90%-Take/10%-Give (In Parallel) +00:00:10.2027024 Mixed 50%-Take/50%-Give (In Parallel) +00:00:10.2029412 Mixed 10%-Take/90%-Give (In Parallel) +00:00:00.6764481 Empty Pool (.TryTake()) +00:00:41.7570550 TOTAL + +ConcurrentQueueObjectPool............................... +00:00:10.3263208 Give To (In Parallel) +00:00:10.6676992 Mixed 90%-Take/10%-Give (In Parallel) +00:00:10.3146522 Mixed 50%-Take/50%-Give (In Parallel) +00:00:10.3892049 Mixed 10%-Take/90%-Give (In Parallel) +00:00:00.8041729 Empty Pool (.TryTake()) +00:00:42.5020500 TOTAL OptimisticArrayObjectPool............................... -00:00:05.7515713 Give To (In Parallel) -00:00:05.9020552 Mixed 90%-Take/10%-Give (In Parallel) -00:00:05.7270413 Mixed 50%-Take/50%-Give (In Parallel) -00:00:05.7505300 Mixed 10%-Take/90%-Give (In Parallel) -00:00:00.4225426 Empty Pool (.TryTake()) -00:00:23.5537404 TOTAL +00:00:10.0751346 Give To (In Parallel) +00:00:10.5956764 Mixed 90%-Take/10%-Give (In Parallel) +00:00:10.1015581 Mixed 50%-Take/50%-Give (In Parallel) +00:00:10.1791619 Mixed 10%-Take/90%-Give (In Parallel) +00:00:00.8202931 Empty Pool (.TryTake()) +00:00:41.7718241 TOTAL InterlockedArrayObjectPool.............................. -00:00:05.7310971 Give To (In Parallel) -00:00:05.8633343 Mixed 90%-Take/10%-Give (In Parallel) -00:00:05.7558233 Mixed 50%-Take/50%-Give (In Parallel) -00:00:05.7572257 Mixed 10%-Take/90%-Give (In Parallel) -00:00:00.4234853 Empty Pool (.TryTake()) -00:00:23.5309657 TOTAL +00:00:10.1135907 Give To (In Parallel) +00:00:10.5975168 Mixed 90%-Take/10%-Give (In Parallel) +00:00:10.2069357 Mixed 50%-Take/50%-Give (In Parallel) +00:00:10.2776009 Mixed 10%-Take/90%-Give (In Parallel) +00:00:00.7987301 Empty Pool (.TryTake()) +00:00:41.9943742 TOTAL -Repeat 480000 for size 10 +Repeat 960000 for size 10 ------------------------------------ -ConcurrentQueueObjectPool............................... -00:00:02.6559228 Give To (In Parallel) -00:00:02.8336403 Mixed 90%-Take/10%-Give (In Parallel) -00:00:02.6687352 Mixed 50%-Take/50%-Give (In Parallel) -00:00:02.7110513 Mixed 10%-Take/90%-Give (In Parallel) -00:00:00.2318305 Empty Pool (.TryTake()) -00:00:11.1011801 TOTAL - ConcurrentQueueObjectPoolSlim........................... -00:00:02.8013356 Give To (In Parallel) -00:00:02.9497849 Mixed 90%-Take/10%-Give (In Parallel) -00:00:02.7953925 Mixed 50%-Take/50%-Give (In Parallel) -00:00:02.8494803 Mixed 10%-Take/90%-Give (In Parallel) -00:00:00.2466537 Empty Pool (.TryTake()) -00:00:11.6426470 TOTAL +00:00:03.9787914 Give To (In Parallel) +00:00:04.2003072 Mixed 90%-Take/10%-Give (In Parallel) +00:00:04.0901023 Mixed 50%-Take/50%-Give (In Parallel) +00:00:04.1125416 Mixed 10%-Take/90%-Give (In Parallel) +00:00:00.3234760 Empty Pool (.TryTake()) +00:00:16.7052185 TOTAL + +ConcurrentQueueObjectPool............................... +00:00:02.6705738 Give To (In Parallel) +00:00:02.9303461 Mixed 90%-Take/10%-Give (In Parallel) +00:00:02.6527993 Mixed 50%-Take/50%-Give (In Parallel) +00:00:02.7590313 Mixed 10%-Take/90%-Give (In Parallel) +00:00:00.2448799 Empty Pool (.TryTake()) +00:00:11.2576304 TOTAL OptimisticArrayObjectPool............................... -00:00:03.1201625 Give To (In Parallel) -00:00:03.5308652 Mixed 90%-Take/10%-Give (In Parallel) -00:00:03.1032344 Mixed 50%-Take/50%-Give (In Parallel) -00:00:03.1534127 Mixed 10%-Take/90%-Give (In Parallel) -00:00:00.4161196 Empty Pool (.TryTake()) -00:00:13.3237944 TOTAL +00:00:02.7080938 Give To (In Parallel) +00:00:03.1857486 Mixed 90%-Take/10%-Give (In Parallel) +00:00:02.7267450 Mixed 50%-Take/50%-Give (In Parallel) +00:00:02.8387716 Mixed 10%-Take/90%-Give (In Parallel) +00:00:00.3469802 Empty Pool (.TryTake()) +00:00:11.8063392 TOTAL InterlockedArrayObjectPool.............................. -00:00:02.5905872 Give To (In Parallel) -00:00:02.6907347 Mixed 90%-Take/10%-Give (In Parallel) -00:00:02.3651720 Mixed 50%-Take/50%-Give (In Parallel) -00:00:02.5440796 Mixed 10%-Take/90%-Give (In Parallel) -00:00:00.3139231 Empty Pool (.TryTake()) -00:00:10.5044966 TOTAL +00:00:03.1543776 Give To (In Parallel) +00:00:03.3044990 Mixed 90%-Take/10%-Give (In Parallel) +00:00:02.9074669 Mixed 50%-Take/50%-Give (In Parallel) +00:00:03.1888732 Mixed 10%-Take/90%-Give (In Parallel) +00:00:00.3640288 Empty Pool (.TryTake()) +00:00:12.9192455 TOTAL -Repeat 144000 for size 50 +Repeat 288000 for size 50 ------------------------------------ -ConcurrentQueueObjectPool............................... -00:00:00.8912894 Give To (In Parallel) -00:00:00.9607579 Mixed 90%-Take/10%-Give (In Parallel) -00:00:00.7788601 Mixed 50%-Take/50%-Give (In Parallel) -00:00:00.9413875 Mixed 10%-Take/90%-Give (In Parallel) -00:00:00.1853197 Empty Pool (.TryTake()) -00:00:03.7576146 TOTAL - ConcurrentQueueObjectPoolSlim........................... -00:00:00.7118744 Give To (In Parallel) -00:00:00.7833912 Mixed 90%-Take/10%-Give (In Parallel) -00:00:00.5933995 Mixed 50%-Take/50%-Give (In Parallel) -00:00:00.7078938 Mixed 10%-Take/90%-Give (In Parallel) -00:00:00.1502073 Empty Pool (.TryTake()) -00:00:02.9467662 TOTAL +00:00:01.6453853 Give To (In Parallel) +00:00:01.7359093 Mixed 90%-Take/10%-Give (In Parallel) +00:00:01.3652902 Mixed 50%-Take/50%-Give (In Parallel) +00:00:01.6812617 Mixed 10%-Take/90%-Give (In Parallel) +00:00:00.3263226 Empty Pool (.TryTake()) +00:00:06.7541691 TOTAL + +ConcurrentQueueObjectPool............................... +00:00:01.7333532 Give To (In Parallel) +00:00:01.8169673 Mixed 90%-Take/10%-Give (In Parallel) +00:00:01.4589195 Mixed 50%-Take/50%-Give (In Parallel) +00:00:01.7403590 Mixed 10%-Take/90%-Give (In Parallel) +00:00:00.3740043 Empty Pool (.TryTake()) +00:00:07.1236033 TOTAL OptimisticArrayObjectPool............................... -00:00:00.9905638 Give To (In Parallel) -00:00:00.9864204 Mixed 90%-Take/10%-Give (In Parallel) -00:00:00.6759549 Mixed 50%-Take/50%-Give (In Parallel) -00:00:00.8384916 Mixed 10%-Take/90%-Give (In Parallel) -00:00:00.6304342 Empty Pool (.TryTake()) -00:00:04.1218649 TOTAL +00:00:02.0335210 Give To (In Parallel) +00:00:02.0578942 Mixed 90%-Take/10%-Give (In Parallel) +00:00:01.4266376 Mixed 50%-Take/50%-Give (In Parallel) +00:00:01.7863439 Mixed 10%-Take/90%-Give (In Parallel) +00:00:01.3344072 Empty Pool (.TryTake()) +00:00:08.6388039 TOTAL InterlockedArrayObjectPool.............................. -00:00:01.2005883 Give To (In Parallel) -00:00:01.0553068 Mixed 90%-Take/10%-Give (In Parallel) -00:00:00.7240912 Mixed 50%-Take/50%-Give (In Parallel) -00:00:00.9785882 Mixed 10%-Take/90%-Give (In Parallel) -00:00:00.7172196 Empty Pool (.TryTake()) -00:00:04.6757941 TOTAL +00:00:02.5295986 Give To (In Parallel) +00:00:02.2228966 Mixed 90%-Take/10%-Give (In Parallel) +00:00:01.5291152 Mixed 50%-Take/50%-Give (In Parallel) +00:00:02.0956115 Mixed 10%-Take/90%-Give (In Parallel) +00:00:01.5541699 Empty Pool (.TryTake()) +00:00:09.9313918 TOTAL -Repeat 96000 for size 100 +Repeat 192000 for size 100 ------------------------------------ -ConcurrentQueueObjectPool............................... -00:00:01.1948956 Give To (In Parallel) -00:00:01.2436783 Mixed 90%-Take/10%-Give (In Parallel) -00:00:00.7892695 Mixed 50%-Take/50%-Give (In Parallel) -00:00:01.2061012 Mixed 10%-Take/90%-Give (In Parallel) -00:00:00.2451430 Empty Pool (.TryTake()) -00:00:04.6790876 TOTAL - ConcurrentQueueObjectPoolSlim........................... -00:00:01.1056454 Give To (In Parallel) -00:00:01.1291605 Mixed 90%-Take/10%-Give (In Parallel) -00:00:00.6456573 Mixed 50%-Take/50%-Give (In Parallel) -00:00:00.9885465 Mixed 10%-Take/90%-Give (In Parallel) -00:00:00.2333216 Empty Pool (.TryTake()) -00:00:04.1023313 TOTAL +00:00:02.3422135 Give To (In Parallel) +00:00:02.4589825 Mixed 90%-Take/10%-Give (In Parallel) +00:00:01.4419367 Mixed 50%-Take/50%-Give (In Parallel) +00:00:02.2890798 Mixed 10%-Take/90%-Give (In Parallel) +00:00:00.4853905 Empty Pool (.TryTake()) +00:00:09.0176030 TOTAL + +ConcurrentQueueObjectPool............................... +00:00:02.5509913 Give To (In Parallel) +00:00:02.6460844 Mixed 90%-Take/10%-Give (In Parallel) +00:00:01.6833011 Mixed 50%-Take/50%-Give (In Parallel) +00:00:02.5321679 Mixed 10%-Take/90%-Give (In Parallel) +00:00:00.5554094 Empty Pool (.TryTake()) +00:00:09.9679541 TOTAL OptimisticArrayObjectPool............................... -00:00:01.6250986 Give To (In Parallel) -00:00:01.1985866 Mixed 90%-Take/10%-Give (In Parallel) -00:00:00.6426214 Mixed 50%-Take/50%-Give (In Parallel) -00:00:00.9037208 Mixed 10%-Take/90%-Give (In Parallel) -00:00:01.4501568 Empty Pool (.TryTake()) -00:00:05.8201842 TOTAL +00:00:03.3738400 Give To (In Parallel) +00:00:02.4423141 Mixed 90%-Take/10%-Give (In Parallel) +00:00:01.3292480 Mixed 50%-Take/50%-Give (In Parallel) +00:00:01.8814529 Mixed 10%-Take/90%-Give (In Parallel) +00:00:02.8973500 Empty Pool (.TryTake()) +00:00:11.9242050 TOTAL InterlockedArrayObjectPool.............................. -00:00:02.0068444 Give To (In Parallel) -00:00:01.1984315 Mixed 90%-Take/10%-Give (In Parallel) -00:00:00.6897014 Mixed 50%-Take/50%-Give (In Parallel) -00:00:01.1814997 Mixed 10%-Take/90%-Give (In Parallel) -00:00:01.7948454 Empty Pool (.TryTake()) -00:00:06.8713224 TOTAL +00:00:04.0758376 Give To (In Parallel) +00:00:02.4918027 Mixed 90%-Take/10%-Give (In Parallel) +00:00:01.4167009 Mixed 50%-Take/50%-Give (In Parallel) +00:00:02.4431298 Mixed 10%-Take/90%-Give (In Parallel) +00:00:03.5020819 Empty Pool (.TryTake()) +00:00:13.9295529 TOTAL -Repeat 76800 for size 250 +Repeat 153600 for size 250 ------------------------------------ -ConcurrentQueueObjectPool............................... -00:00:02.6729130 Give To (In Parallel) -00:00:02.7863215 Mixed 90%-Take/10%-Give (In Parallel) -00:00:01.7569907 Mixed 50%-Take/50%-Give (In Parallel) -00:00:02.8007097 Mixed 10%-Take/90%-Give (In Parallel) -00:00:00.4370162 Empty Pool (.TryTake()) -00:00:10.4539511 TOTAL - ConcurrentQueueObjectPoolSlim........................... -00:00:02.6410164 Give To (In Parallel) -00:00:02.6535867 Mixed 90%-Take/10%-Give (In Parallel) -00:00:01.4697118 Mixed 50%-Take/50%-Give (In Parallel) -00:00:02.6904730 Mixed 10%-Take/90%-Give (In Parallel) -00:00:00.4120720 Empty Pool (.TryTake()) -00:00:09.8668599 TOTAL +00:00:05.3605084 Give To (In Parallel) +00:00:05.3904152 Mixed 90%-Take/10%-Give (In Parallel) +00:00:02.9076538 Mixed 50%-Take/50%-Give (In Parallel) +00:00:05.4448936 Mixed 10%-Take/90%-Give (In Parallel) +00:00:00.8048672 Empty Pool (.TryTake()) +00:00:19.9083382 TOTAL + +ConcurrentQueueObjectPool............................... +00:00:05.3745739 Give To (In Parallel) +00:00:05.6739715 Mixed 90%-Take/10%-Give (In Parallel) +00:00:03.5889170 Mixed 50%-Take/50%-Give (In Parallel) +00:00:05.5923650 Mixed 10%-Take/90%-Give (In Parallel) +00:00:00.9855414 Empty Pool (.TryTake()) +00:00:21.2153688 TOTAL OptimisticArrayObjectPool............................... -00:00:02.9149582 Give To (In Parallel) -00:00:02.1909359 Mixed 90%-Take/10%-Give (In Parallel) -00:00:01.1199040 Mixed 50%-Take/50%-Give (In Parallel) -00:00:01.5984575 Mixed 10%-Take/90%-Give (In Parallel) -00:00:04.0259771 Empty Pool (.TryTake()) -00:00:11.8502327 TOTAL +00:00:05.9226589 Give To (In Parallel) +00:00:04.5202656 Mixed 90%-Take/10%-Give (In Parallel) +00:00:02.2839784 Mixed 50%-Take/50%-Give (In Parallel) +00:00:03.2966061 Mixed 10%-Take/90%-Give (In Parallel) +00:00:08.1874870 Empty Pool (.TryTake()) +00:00:24.2109960 TOTAL InterlockedArrayObjectPool.............................. -00:00:03.9977467 Give To (In Parallel) -00:00:02.4777589 Mixed 90%-Take/10%-Give (In Parallel) -00:00:01.2744094 Mixed 50%-Take/50%-Give (In Parallel) -00:00:02.4151544 Mixed 10%-Take/90%-Give (In Parallel) -00:00:06.8796644 Empty Pool (.TryTake()) -00:00:17.0447338 TOTAL +00:00:22.5836030 Give To (In Parallel) +00:00:12.0998476 Mixed 90%-Take/10%-Give (In Parallel) +00:00:06.3335435 Mixed 50%-Take/50%-Give (In Parallel) +00:00:12.1356510 Mixed 10%-Take/90%-Give (In Parallel) +00:00:38.4403391 Empty Pool (.TryTake()) +00:01:31.5929842 TOTAL diff --git a/benchmarking/Program.cs b/benchmarking/Program.cs index a5c6a6d..4bf86e1 100644 --- a/benchmarking/Program.cs +++ b/benchmarking/Program.cs @@ -26,13 +26,13 @@ static void Main() //report.AddBenchmark("QueueObjectPool", // Note, that this one isn't far off from the following in peformance. // count => () => QueueObjectPool.Create((int)count * 2)); - report.AddBenchmark("ConcurrentQueueObjectPool", // Note, that this one isn't far off from the following in peformance, but definitely is faster than LinkedListObjectPool and the rest. - count => () => ConcurrentQueueObjectPool.Create((int)count * 2)); - - report.AddBenchmark("ConcurrentQueueObjectPoolSlim", // Note, that this one isn't far off from the following in peformance, but definitely is faster than LinkedListObjectPool and the rest. + report.AddBenchmark("ConcurrentQueueObjectPoolSlim", count => () => ConcurrentQueueObjectPoolSlim.Create((int)count * 2)); - //report.AddBenchmark("ConcurrentStackObjectPool", // Note, that this one isn't far off from the following in peformance, but definitely is faster than LinkedListObjectPool and the rest. + report.AddBenchmark("ConcurrentQueueObjectPool", + count => () => ConcurrentQueueObjectPool.Create((int)count * 2)); + + //report.AddBenchmark("ConcurrentStackObjectPool", // count => () => ConcurrentStackObjectPool.Create((int)count * 2)); report.AddBenchmark("OptimisticArrayObjectPool", @@ -46,7 +46,7 @@ static void Main() Console.SetCursorPosition(0, Console.CursorTop); - const int loopMultiple = 6; + const int loopMultiple = 12; report.Test(4, 8 * loopMultiple); report.Test(10, 8 * loopMultiple); report.Test(50, 12 * loopMultiple); From 21820630982d3c585af9822db7ea3289b6dea2a4 Mon Sep 17 00:00:00 2001 From: electricessence <5899455+electricessence@users.noreply.github.com> Date: Sun, 11 Jul 2021 09:50:26 -0700 Subject: [PATCH 56/83] Added disposable RecycleHelper. --- source/IObjectPool.cs | 11 ++++++++ source/Open.Disposable.ObjectPools.csproj | 2 +- source/RecycleHelper.cs | 34 +++++++++++++++++++++++ 3 files changed, 46 insertions(+), 1 deletion(-) create mode 100644 source/RecycleHelper.cs diff --git a/source/IObjectPool.cs b/source/IObjectPool.cs index c412fca..3fcdc3d 100644 --- a/source/IObjectPool.cs +++ b/source/IObjectPool.cs @@ -96,6 +96,17 @@ public static void Give(this IObjectPool target, T item1, T item2, params target.Give(items); } + /// + /// Provides a disposable RecycleHelper that contains an item from the pool. + /// Will be returned to the pool once .Dispose() is called. + /// + /// The object type from the pool. + /// The object pool. + /// A RecycleHelper containing an item from the pool. + public static RecycleHelper Rent(this IObjectPool source) + where T : class + => new(source); + /// /// Provides an item from the pool and returns it when the action sucessfully completes. /// diff --git a/source/Open.Disposable.ObjectPools.csproj b/source/Open.Disposable.ObjectPools.csproj index c5ea7c5..a005dfb 100644 --- a/source/Open.Disposable.ObjectPools.csproj +++ b/source/Open.Disposable.ObjectPools.csproj @@ -17,7 +17,7 @@ Part of the "Open" set of libraries. https://github.com/Open-NET-Libraries/Open.Disposable.ObjectPools/ git objectpool;idisposable;thread safe - 2.6.5 + 2.6.6 MIT true diff --git a/source/RecycleHelper.cs b/source/RecycleHelper.cs new file mode 100644 index 0000000..54aab48 --- /dev/null +++ b/source/RecycleHelper.cs @@ -0,0 +1,34 @@ +using System; + +namespace Open.Disposable +{ + public struct RecycleHelper : IDisposable + where T : class + { + private IObjectPool? _pool; + + private RecycleHelper(IObjectPool pool, T item) + { + _pool = pool; + _item = item; + } + + public RecycleHelper(IObjectPool pool) + : this(pool ?? throw new ArgumentNullException(nameof(pool)), pool.Take()) + { + + } + + private T? _item; + public T Item => _item ?? throw new ObjectDisposedException(GetType().ToString()); + + public void Dispose() + { + var i = Item; + _item = null; + var p = _pool ?? throw new ObjectDisposedException(GetType().ToString()); + _pool = null; + p.Give(i); + } + } +} From f3e005fa29c75d186f5bb439dd5d89beaa7b7ab5 Mon Sep 17 00:00:00 2001 From: electricessence <5899455+electricessence@users.noreply.github.com> Date: Sun, 11 Jul 2021 10:25:20 -0700 Subject: [PATCH 57/83] Allow for multiple dispose calls but retain exception for access to the item. --- source/Open.Disposable.ObjectPools.csproj | 2 +- source/RecycleHelper.cs | 7 +++---- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/source/Open.Disposable.ObjectPools.csproj b/source/Open.Disposable.ObjectPools.csproj index a005dfb..68b3153 100644 --- a/source/Open.Disposable.ObjectPools.csproj +++ b/source/Open.Disposable.ObjectPools.csproj @@ -17,7 +17,7 @@ Part of the "Open" set of libraries. https://github.com/Open-NET-Libraries/Open.Disposable.ObjectPools/ git objectpool;idisposable;thread safe - 2.6.6 + 2.6.7 MIT true diff --git a/source/RecycleHelper.cs b/source/RecycleHelper.cs index 54aab48..bc70aff 100644 --- a/source/RecycleHelper.cs +++ b/source/RecycleHelper.cs @@ -24,11 +24,10 @@ public RecycleHelper(IObjectPool pool) public void Dispose() { - var i = Item; + var i = _item; _item = null; - var p = _pool ?? throw new ObjectDisposedException(GetType().ToString()); - _pool = null; - p.Give(i); + if (i == null) return; + _pool?.Give(i); } } } From cb547e2738620ee1e9a19a810b77022eff8e9066 Mon Sep 17 00:00:00 2001 From: electricessence <5899455+electricessence@users.noreply.github.com> Date: Mon, 6 Sep 2021 09:58:26 -0700 Subject: [PATCH 58/83] Reference updates. --- source/Open.Disposable.ObjectPools.csproj | 2 +- tests/Open.Disposable.ObjectPools.Tests.csproj | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/source/Open.Disposable.ObjectPools.csproj b/source/Open.Disposable.ObjectPools.csproj index 68b3153..4851ad0 100644 --- a/source/Open.Disposable.ObjectPools.csproj +++ b/source/Open.Disposable.ObjectPools.csproj @@ -41,7 +41,7 @@ Part of the "Open" set of libraries. - + diff --git a/tests/Open.Disposable.ObjectPools.Tests.csproj b/tests/Open.Disposable.ObjectPools.Tests.csproj index 6ec56f3..51814b6 100644 --- a/tests/Open.Disposable.ObjectPools.Tests.csproj +++ b/tests/Open.Disposable.ObjectPools.Tests.csproj @@ -14,9 +14,9 @@ - - - + + + From 8e915cf4efd91707fb88209d98d42bfd57bbb0f2 Mon Sep 17 00:00:00 2001 From: electricessence <5899455+electricessence@users.noreply.github.com> Date: Mon, 20 Sep 2021 19:44:46 -0700 Subject: [PATCH 59/83] Cleanup. --- source/Collection/ConcurrentQueueObjectPoolSlim.cs | 1 - source/CountTrackedObjectPoolBase.cs | 2 -- 2 files changed, 3 deletions(-) diff --git a/source/Collection/ConcurrentQueueObjectPoolSlim.cs b/source/Collection/ConcurrentQueueObjectPoolSlim.cs index 88cccbb..6b695d1 100644 --- a/source/Collection/ConcurrentQueueObjectPoolSlim.cs +++ b/source/Collection/ConcurrentQueueObjectPoolSlim.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Concurrent; -using System.Threading; namespace Open.Disposable { diff --git a/source/CountTrackedObjectPoolBase.cs b/source/CountTrackedObjectPoolBase.cs index 32eb8f8..306b04e 100644 --- a/source/CountTrackedObjectPoolBase.cs +++ b/source/CountTrackedObjectPoolBase.cs @@ -1,6 +1,4 @@ using System; -using System.Collections.Generic; -using System.Text; using System.Threading; namespace Open.Disposable From ee903ca2b9f92078241df1c5355a04431e6f4f2d Mon Sep 17 00:00:00 2001 From: electricessence <5899455+electricessence@users.noreply.github.com> Date: Mon, 27 Sep 2021 21:16:17 -0700 Subject: [PATCH 60/83] Updated RecycleHelper. --- source/RecycleHelper.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/source/RecycleHelper.cs b/source/RecycleHelper.cs index bc70aff..443c86b 100644 --- a/source/RecycleHelper.cs +++ b/source/RecycleHelper.cs @@ -5,7 +5,7 @@ namespace Open.Disposable public struct RecycleHelper : IDisposable where T : class { - private IObjectPool? _pool; + private readonly IObjectPool _pool; private RecycleHelper(IObjectPool pool, T item) { @@ -27,7 +27,7 @@ public void Dispose() var i = _item; _item = null; if (i == null) return; - _pool?.Give(i); + _pool.Give(i); } } } From cffab2780731ff65d0abefeadefa363453df4325 Mon Sep 17 00:00:00 2001 From: electricessence <5899455+electricessence@users.noreply.github.com> Date: Mon, 27 Sep 2021 21:16:37 -0700 Subject: [PATCH 61/83] Updated version. --- source/Open.Disposable.ObjectPools.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/Open.Disposable.ObjectPools.csproj b/source/Open.Disposable.ObjectPools.csproj index 4851ad0..1893871 100644 --- a/source/Open.Disposable.ObjectPools.csproj +++ b/source/Open.Disposable.ObjectPools.csproj @@ -17,7 +17,7 @@ Part of the "Open" set of libraries. https://github.com/Open-NET-Libraries/Open.Disposable.ObjectPools/ git objectpool;idisposable;thread safe - 2.6.7 + 2.6.8 MIT true From 6635e51920a5958279a62ff568989dc1cc470606 Mon Sep 17 00:00:00 2001 From: electricessence <5899455+electricessence@users.noreply.github.com> Date: Mon, 27 Sep 2021 22:02:43 -0700 Subject: [PATCH 62/83] Using Microsoft.Extensions.ObjectPool as baseline. Should be similar to OptimisticArrayPool. --- benchmarking/Benchmark.cs | 4 +- benchmarking/DefaultObjectPool.cs | 64 +++++++++++++++++++ ...Disposable.ObjectPools.Benchmarking.csproj | 3 +- benchmarking/Program.cs | 5 +- 4 files changed, 73 insertions(+), 3 deletions(-) create mode 100644 benchmarking/DefaultObjectPool.cs diff --git a/benchmarking/Benchmark.cs b/benchmarking/Benchmark.cs index 52eab46..82f8a01 100644 --- a/benchmarking/Benchmark.cs +++ b/benchmarking/Benchmark.cs @@ -36,6 +36,7 @@ protected override IEnumerable TestOnceInternal() // ReSharper disable once AccessToDisposedClosure Parallel.For(0, TestSize, i => pool.Give(_items[i])); #if DEBUG + if (pool is DefaultObjectPool) return; var count = pool.Count; Debug.Assert(pool is OptimisticArrayObjectPool || count == TestSize, $"Expected {TestSize}, acutal count: {count}"); #endif @@ -76,7 +77,8 @@ protected override IEnumerable TestOnceInternal() yield return TimedResult.Measure("Empty Pool (.TryTake())", () => { - while (pool.TryTake() != null) + var count = TestSize; + while (pool.TryTake() != null && --count>0) { // remaining++; } diff --git a/benchmarking/DefaultObjectPool.cs b/benchmarking/DefaultObjectPool.cs new file mode 100644 index 0000000..b8ab6f7 --- /dev/null +++ b/benchmarking/DefaultObjectPool.cs @@ -0,0 +1,64 @@ +using Microsoft.Extensions.ObjectPool; +using System; +using System.Diagnostics.CodeAnalysis; + +namespace Open.Disposable.ObjectPools +{ + public class DefaultObjectPool : Microsoft.Extensions.ObjectPool.DefaultObjectPool, IObjectPool + where T : class + { + private readonly IPooledObjectPolicy _policy; + + + class Policy : IPooledObjectPolicy + { + private readonly Func factory; + private readonly Action recycler; + + public Policy(Func factory, Action? recycler) + { + if (factory is null) + throw new ArgumentNullException(nameof(factory)); + + + this.factory = factory; + this.recycler = recycler; + } + public T Create() => factory(); + + public bool Return(T obj) + { + recycler?.Invoke(obj); + return true; + } + } + + public DefaultObjectPool(Func factory, Action? recycler, int capacity = 64) + : base(new Policy(factory, recycler), capacity) + { + + } + + public int Capacity { get; } + + public int Count => throw new NotImplementedException(); + + public void Dispose() + { + } + + public T Generate() => _policy.Create(); + + public void Give(T item) => Return(item); + + public T Take() => Get(); + + public bool TryTake([NotNullWhen(true)] out T item) + { + item = Get(); + return true; + } + + public T? TryTake() => Get(); + } +} diff --git a/benchmarking/Open.Disposable.ObjectPools.Benchmarking.csproj b/benchmarking/Open.Disposable.ObjectPools.Benchmarking.csproj index eaeed56..871c99b 100644 --- a/benchmarking/Open.Disposable.ObjectPools.Benchmarking.csproj +++ b/benchmarking/Open.Disposable.ObjectPools.Benchmarking.csproj @@ -20,8 +20,9 @@ + - + diff --git a/benchmarking/Program.cs b/benchmarking/Program.cs index 4bf86e1..16fa7ed 100644 --- a/benchmarking/Program.cs +++ b/benchmarking/Program.cs @@ -26,6 +26,9 @@ static void Main() //report.AddBenchmark("QueueObjectPool", // Note, that this one isn't far off from the following in peformance. // count => () => QueueObjectPool.Create((int)count * 2)); + report.AddBenchmark("Microsoft.Extensions.ObjectPool.DefaultObjectPool", + count => () => new DefaultObjectPool(() => new object(), null, (int)count * 2)); + report.AddBenchmark("ConcurrentQueueObjectPoolSlim", count => () => ConcurrentQueueObjectPoolSlim.Create((int)count * 2)); @@ -46,7 +49,7 @@ static void Main() Console.SetCursorPosition(0, Console.CursorTop); - const int loopMultiple = 12; + const int loopMultiple = 2; report.Test(4, 8 * loopMultiple); report.Test(10, 8 * loopMultiple); report.Test(50, 12 * loopMultiple); From 78445e4fd70ef7ddc8f9d1ec7d297b6894427563 Mon Sep 17 00:00:00 2001 From: electricessence <5899455+electricessence@users.noreply.github.com> Date: Mon, 27 Sep 2021 22:06:35 -0700 Subject: [PATCH 63/83] Adjusted unusable benchmark. --- benchmarking/Benchmark.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/benchmarking/Benchmark.cs b/benchmarking/Benchmark.cs index 82f8a01..4403e5b 100644 --- a/benchmarking/Benchmark.cs +++ b/benchmarking/Benchmark.cs @@ -75,10 +75,11 @@ protected override IEnumerable TestOnceInternal() }); }); + if (pool is DefaultObjectPool) yield break; yield return TimedResult.Measure("Empty Pool (.TryTake())", () => { - var count = TestSize; - while (pool.TryTake() != null && --count>0) + + while (pool.TryTake() != null) { // remaining++; } From 787aa3f724d262306cf95fa40eb97da083178495 Mon Sep 17 00:00:00 2001 From: electricessence <5899455+electricessence@users.noreply.github.com> Date: Mon, 27 Sep 2021 22:13:41 -0700 Subject: [PATCH 64/83] Added a StringBuilder pool. --- source/SharedPools.cs | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/source/SharedPools.cs b/source/SharedPools.cs index e61d2f3..d25fc7e 100644 --- a/source/SharedPools.cs +++ b/source/SharedPools.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using System.Text; namespace Open.Disposable { @@ -15,7 +16,7 @@ public static class ListPool /// Creates an object pool for use with lists. /// The list is cleared after being returned. /// - public static ConcurrentQueueObjectPoolSlim> Create(int capacity = Constants.DEFAULT_CAPACITY) => new(() => new List(), h => + public static ConcurrentQueueObjectPoolSlim> Create(int capacity = Constants.DEFAULT_CAPACITY) => new(() => new (), h => { h.Clear(); if (h.Capacity > 16) h.Capacity = 16; @@ -35,7 +36,23 @@ public static class HashSetPool /// Creates an object pool for use with hash-sets. /// The hash-set is cleared after being returned. /// - public static ConcurrentQueueObjectPoolSlim> Create(int capacity = Constants.DEFAULT_CAPACITY) => new(() => new HashSet(), h => h.Clear(), null, capacity); + public static ConcurrentQueueObjectPoolSlim> Create(int capacity = Constants.DEFAULT_CAPACITY) => new(() => new(), h => h.Clear(), null, capacity); + } + + public static class StringBuilderPool + { + /// + /// A shared object pool for use with StringBuilders. + /// The StringBuilder is cleared after being returned. + /// The max size of the pool is 64 which should suffice for most use cases. + /// + public static readonly ConcurrentQueueObjectPoolSlim Shared = Create(); + + /// + /// Creates an object pool for use with StringBuilders. + /// The StringBuilder is cleared after being returned. + /// + public static ConcurrentQueueObjectPoolSlim Create(int capacity = Constants.DEFAULT_CAPACITY) => new(() => new(), sb => sb.Clear(), null, capacity); } public static class DictionaryPool From 8b4862723683f05fcfed283f77103ee3df3159a1 Mon Sep 17 00:00:00 2001 From: electricessence <5899455+electricessence@users.noreply.github.com> Date: Mon, 27 Sep 2021 23:02:27 -0700 Subject: [PATCH 65/83] Tweaks and improvements. --- benchmarking/Benchmark.cs | 18 +- benchmarking/BenchmarkResult.csv | 51 ++-- benchmarking/BenchmarkResult.txt | 255 +++++++++--------- benchmarking/DefaultObjectPool.cs | 21 +- benchmarking/Program.cs | 4 +- source/Array/InterlockedArrayObjectPool.cs | 6 +- source/Array/OptimisticArrayObjectPool.cs | 5 + .../Collection/CollectionWrapperObjectPool.cs | 2 +- .../Collection/ConcurrentQueueObjectPool.cs | 3 + .../ConcurrentQueueObjectPoolSlim.cs | 3 + source/Collection/QueueObjectPool.cs | 4 +- source/Collection/StackObjectPool.cs | 4 +- source/CountTrackedObjectPoolBase.cs | 4 + source/ObjectPoolBase.cs | 14 +- source/Open.Disposable.ObjectPools.csproj | 2 +- source/RecycleHelper.cs | 2 +- source/ReferenceContainer.cs | 8 +- source/TrimmableCollectionObjectPoolBase.cs | 2 +- source/TrimmableObjectPoolBase.cs | 6 + 19 files changed, 232 insertions(+), 182 deletions(-) diff --git a/benchmarking/Benchmark.cs b/benchmarking/Benchmark.cs index 4403e5b..dc08b4e 100644 --- a/benchmarking/Benchmark.cs +++ b/benchmarking/Benchmark.cs @@ -25,7 +25,7 @@ public Benchmark(uint size, uint repeat, Func> poolFactory) : bas protected override IEnumerable TestOnceInternal() { using var pool = Param(); - if (pool == null) throw new NullReferenceException(); + if (pool is null) throw new NullReferenceException(); //yield return TimedResult.Measure("Take From Empty (In Parallel)", () => //{ // Parallel.For(0, TestSize, i => _items[i] = pool.Take()); @@ -75,15 +75,15 @@ protected override IEnumerable TestOnceInternal() }); }); - if (pool is DefaultObjectPool) yield break; - yield return TimedResult.Measure("Empty Pool (.TryTake())", () => - { + //if (pool is DefaultObjectPool) yield break; + //yield return TimedResult.Measure("Empty Pool (.TryTake())", () => + //{ - while (pool.TryTake() != null) - { - // remaining++; - } - }); + // while (pool.TryTake() is not null) + // { + // // remaining++; + // } + //}); } public static TimedResult[] Results(uint size, uint repeat, Func> poolFactory) diff --git a/benchmarking/BenchmarkResult.csv b/benchmarking/BenchmarkResult.csv index fae1cd8..6c86167 100644 --- a/benchmarking/BenchmarkResult.csv +++ b/benchmarking/BenchmarkResult.csv @@ -1,27 +1,30 @@ -Batch,Pool Type," To (In Parallel)",d 90%-Take/10%-Give (In Parallel),d 50%-Take/50%-Give (In Parallel),d 10%-Take/90%-Give (In Parallel),y Pool (.TryTake()),L, -Repeat 2400000 for size 4,ConcurrentQueueObjectPoolSlim,00:00:10.2379694,00:00:10.4369939,00:00:10.2027024,00:00:10.2029412,00:00:00.6764481,00:00:41.7570550, -Repeat 2400000 for size 4,ConcurrentQueueObjectPool,00:00:10.3263208,00:00:10.6676992,00:00:10.3146522,00:00:10.3892049,00:00:00.8041729,00:00:42.5020500, -Repeat 2400000 for size 4,OptimisticArrayObjectPool,00:00:10.0751346,00:00:10.5956764,00:00:10.1015581,00:00:10.1791619,00:00:00.8202931,00:00:41.7718241, -Repeat 2400000 for size 4,InterlockedArrayObjectPool,00:00:10.1135907,00:00:10.5975168,00:00:10.2069357,00:00:10.2776009,00:00:00.7987301,00:00:41.9943742, -Repeat 960000 for size 10,ConcurrentQueueObjectPoolSlim,00:00:03.9787914,00:00:04.2003072,00:00:04.0901023,00:00:04.1125416,00:00:00.3234760,00:00:16.7052185, -Repeat 960000 for size 10,ConcurrentQueueObjectPool,00:00:02.6705738,00:00:02.9303461,00:00:02.6527993,00:00:02.7590313,00:00:00.2448799,00:00:11.2576304, -Repeat 960000 for size 10,OptimisticArrayObjectPool,00:00:02.7080938,00:00:03.1857486,00:00:02.7267450,00:00:02.8387716,00:00:00.3469802,00:00:11.8063392, -Repeat 960000 for size 10,InterlockedArrayObjectPool,00:00:03.1543776,00:00:03.3044990,00:00:02.9074669,00:00:03.1888732,00:00:00.3640288,00:00:12.9192455, -Repeat 288000 for size 50,ConcurrentQueueObjectPoolSlim,00:00:01.6453853,00:00:01.7359093,00:00:01.3652902,00:00:01.6812617,00:00:00.3263226,00:00:06.7541691, -Repeat 288000 for size 50,ConcurrentQueueObjectPool,00:00:01.7333532,00:00:01.8169673,00:00:01.4589195,00:00:01.7403590,00:00:00.3740043,00:00:07.1236033, -Repeat 288000 for size 50,OptimisticArrayObjectPool,00:00:02.0335210,00:00:02.0578942,00:00:01.4266376,00:00:01.7863439,00:00:01.3344072,00:00:08.6388039, -Repeat 288000 for size 50,InterlockedArrayObjectPool,00:00:02.5295986,00:00:02.2228966,00:00:01.5291152,00:00:02.0956115,00:00:01.5541699,00:00:09.9313918, -Repeat 192000 for size 100,ConcurrentQueueObjectPoolSlim,00:00:02.3422135,00:00:02.4589825,00:00:01.4419367,00:00:02.2890798,00:00:00.4853905,00:00:09.0176030, -Repeat 192000 for size 100,ConcurrentQueueObjectPool,00:00:02.5509913,00:00:02.6460844,00:00:01.6833011,00:00:02.5321679,00:00:00.5554094,00:00:09.9679541, -Repeat 192000 for size 100,OptimisticArrayObjectPool,00:00:03.3738400,00:00:02.4423141,00:00:01.3292480,00:00:01.8814529,00:00:02.8973500,00:00:11.9242050, -Repeat 192000 for size 100,InterlockedArrayObjectPool,00:00:04.0758376,00:00:02.4918027,00:00:01.4167009,00:00:02.4431298,00:00:03.5020819,00:00:13.9295529, -Repeat 153600 for size 250,ConcurrentQueueObjectPoolSlim,00:00:05.3605084,00:00:05.3904152,00:00:02.9076538,00:00:05.4448936,00:00:00.8048672,00:00:19.9083382, -Repeat 153600 for size 250,ConcurrentQueueObjectPool,00:00:05.3745739,00:00:05.6739715,00:00:03.5889170,00:00:05.5923650,00:00:00.9855414,00:00:21.2153688, -Repeat 153600 for size 250,OptimisticArrayObjectPool,00:00:05.9226589,00:00:04.5202656,00:00:02.2839784,00:00:03.2966061,00:00:08.1874870,00:00:24.2109960, -Repeat 153600 for size 250,InterlockedArrayObjectPool,00:00:22.5836030,00:00:12.0998476,00:00:06.3335435,00:00:12.1356510,00:00:38.4403391,00:01:31.5929842, -r size 250,OptimisticArrayObjectPool,00:00:00.7651948,00:00:01.3195712,00:00:01.3758641,00:00:00.6837150,00:00:01.0134658,00:00:01.9616149,00:00:07.1194258, -Repeat 51200 for size 250,InterlockedArrayObjectPool,00:00:00.8386708,00:00:02.2800174,00:00:01.4244575,00:00:00.6435472,00:00:01.6845873,00:00:05.4327218,00:00:12.3040020, -Repeat 12800 for size 2000,ConcurrentQueueObjectPool,00:00:00.7692018,00:00:03.0317303,00:00:02.0615927,00:00:01.3597065,00:00:02.0767858,00:00:00.6488593,00:00:09.9478764, +Batch,Pool Type," To (In Parallel)",d 90%-Take/10%-Give (In Parallel),d 50%-Take/50%-Give (In Parallel),d 10%-Take/90%-Give (In Parallel),L, +Repeat 2400000 for size 4,Microsoft.Extensions.ObjectPool.DefaultObjectPool,00:00:10.2988122,00:00:10.4798439,00:00:10.3993411,00:00:10.3464322,00:00:41.5244294, +Repeat 2400000 for size 4,ConcurrentQueueObjectPoolSlim,00:00:11.1870930,00:00:11.5168981,00:00:11.1280664,00:00:11.2224827,00:00:45.0545402, +Repeat 2400000 for size 4,ConcurrentQueueObjectPool,00:00:09.1399563,00:00:09.3180086,00:00:09.2816926,00:00:09.1941966,00:00:36.9338541, +Repeat 2400000 for size 4,OptimisticArrayObjectPool,00:00:10.8894648,00:00:11.4317863,00:00:11.0345582,00:00:11.0905596,00:00:44.4463689, +Repeat 2400000 for size 4,InterlockedArrayObjectPool,00:00:11.4199709,00:00:11.9546985,00:00:11.4750506,00:00:11.6023244,00:00:46.4520444, +Repeat 960000 for size 10,Microsoft.Extensions.ObjectPool.DefaultObjectPool,00:00:04.2160006,00:00:03.7725744,00:00:03.5597736,00:00:04.0179846,00:00:15.5663332, +Repeat 960000 for size 10,ConcurrentQueueObjectPoolSlim,00:00:04.4151484,00:00:04.6938482,00:00:04.4135618,00:00:04.5366021,00:00:18.0591605, +Repeat 960000 for size 10,ConcurrentQueueObjectPool,00:00:03.4561782,00:00:03.8400098,00:00:03.4414185,00:00:03.5460435,00:00:14.2836500, +Repeat 960000 for size 10,OptimisticArrayObjectPool,00:00:03.7549698,00:00:04.2279702,00:00:03.6004288,00:00:03.7115653,00:00:15.2949341, +Repeat 960000 for size 10,InterlockedArrayObjectPool,00:00:03.3626513,00:00:03.4878549,00:00:02.9997860,00:00:03.3175440,00:00:13.1678362, +Repeat 288000 for size 50,Microsoft.Extensions.ObjectPool.DefaultObjectPool,00:00:04.7460719,00:00:01.7401188,00:00:01.3147629,00:00:03.6584918,00:00:11.4594454, +Repeat 288000 for size 50,ConcurrentQueueObjectPoolSlim,00:00:01.6226510,00:00:01.7854581,00:00:01.3020626,00:00:01.6743924,00:00:06.3845641, +Repeat 288000 for size 50,ConcurrentQueueObjectPool,00:00:01.7477988,00:00:01.8965112,00:00:01.4379041,00:00:01.7960447,00:00:06.8782588, +Repeat 288000 for size 50,OptimisticArrayObjectPool,00:00:02.0226269,00:00:02.0758868,00:00:01.4867332,00:00:01.9249519,00:00:07.5101988, +Repeat 288000 for size 50,InterlockedArrayObjectPool,00:00:02.3024578,00:00:02.0728807,00:00:01.4972879,00:00:02.0989055,00:00:07.9715319, +Repeat 192000 for size 100,Microsoft.Extensions.ObjectPool.DefaultObjectPool,00:00:08.7588527,00:00:01.8885618,00:00:01.2955575,00:00:06.7774603,00:00:18.7204323, +Repeat 192000 for size 100,ConcurrentQueueObjectPoolSlim,00:00:02.5878519,00:00:02.6535733,00:00:01.4588720,00:00:02.4315543,00:00:09.1318515, +Repeat 192000 for size 100,ConcurrentQueueObjectPool,00:00:02.8200448,00:00:02.8029050,00:00:01.7456810,00:00:02.7047082,00:00:10.0733390, +Repeat 192000 for size 100,OptimisticArrayObjectPool,00:00:02.2072395,00:00:02.4041567,00:00:01.4230022,00:00:01.8949625,00:00:07.9293609, +Repeat 192000 for size 100,InterlockedArrayObjectPool,00:00:03.1657165,00:00:02.5525702,00:00:01.5274323,00:00:02.6791879,00:00:09.9249069, +Repeat 153600 for size 250,Microsoft.Extensions.ObjectPool.DefaultObjectPool,00:00:27.5101753,00:00:03.2947973,00:00:02.3024871,00:00:20.5458083,00:00:53.6532680, +Repeat 153600 for size 250,ConcurrentQueueObjectPoolSlim,00:00:06.4921520,00:00:05.6483503,00:00:02.9681300,00:00:05.5662083,00:00:20.6748406, +Repeat 153600 for size 250,ConcurrentQueueObjectPool,00:00:06.6973676,00:00:05.8383753,00:00:03.5723233,00:00:05.8146242,00:00:21.9226904, +Repeat 153600 for size 250,OptimisticArrayObjectPool,00:00:04.7849466,00:00:05.0781449,00:00:02.4908361,00:00:03.6097713,00:00:15.9636989, +Repeat 153600 for size 250,InterlockedArrayObjectPool,00:00:07.3464588,00:00:05.3703708,00:00:02.8338082,00:00:05.5183809,00:00:21.0690187, +0:02.0767858,00:00:00.6488593,00:00:09.9478764, Repeat 12800 for size 2000,ConcurrentStackObjectPool,00:00:00.6708805,00:00:02.5628201,00:00:04.7978301,00:00:04.7563902,00:00:03.4723738,00:00:00.6475781,00:00:16.9078728, Repeat 12800 for size 2000,OptimisticArrayObjectPool,00:00:00.7184635,00:00:07.2485577,00:00:07.4304114,00:00:01.1244839,00:00:04.7572699,00:00:15.8850338,00:00:37.1642202, Repeat 12800 for size 2000,InterlockedArrayObjectPool,00:00:01.2202062,00:00:19.5837353,00:00:08.9766760,00:00:01.0202604,00:00:12.8356061,00:01:23.7957059,00:02:07.4321899, diff --git a/benchmarking/BenchmarkResult.txt b/benchmarking/BenchmarkResult.txt index 03565e3..65f3c7b 100644 --- a/benchmarking/BenchmarkResult.txt +++ b/benchmarking/BenchmarkResult.txt @@ -1,180 +1,195 @@ Repeat 2400000 for size 4 ------------------------------------ +Microsoft.Extensions.ObjectPool.DefaultObjectPool....... +00:00:10.2988122 Give To (In Parallel) +00:00:10.4798439 Mixed 90%-Take/10%-Give (In Parallel) +00:00:10.3993411 Mixed 50%-Take/50%-Give (In Parallel) +00:00:10.3464322 Mixed 10%-Take/90%-Give (In Parallel) +00:00:41.5244294 TOTAL + ConcurrentQueueObjectPoolSlim........................... -00:00:10.2379694 Give To (In Parallel) -00:00:10.4369939 Mixed 90%-Take/10%-Give (In Parallel) -00:00:10.2027024 Mixed 50%-Take/50%-Give (In Parallel) -00:00:10.2029412 Mixed 10%-Take/90%-Give (In Parallel) -00:00:00.6764481 Empty Pool (.TryTake()) -00:00:41.7570550 TOTAL +00:00:11.1870930 Give To (In Parallel) +00:00:11.5168981 Mixed 90%-Take/10%-Give (In Parallel) +00:00:11.1280664 Mixed 50%-Take/50%-Give (In Parallel) +00:00:11.2224827 Mixed 10%-Take/90%-Give (In Parallel) +00:00:45.0545402 TOTAL ConcurrentQueueObjectPool............................... -00:00:10.3263208 Give To (In Parallel) -00:00:10.6676992 Mixed 90%-Take/10%-Give (In Parallel) -00:00:10.3146522 Mixed 50%-Take/50%-Give (In Parallel) -00:00:10.3892049 Mixed 10%-Take/90%-Give (In Parallel) -00:00:00.8041729 Empty Pool (.TryTake()) -00:00:42.5020500 TOTAL +00:00:09.1399563 Give To (In Parallel) +00:00:09.3180086 Mixed 90%-Take/10%-Give (In Parallel) +00:00:09.2816926 Mixed 50%-Take/50%-Give (In Parallel) +00:00:09.1941966 Mixed 10%-Take/90%-Give (In Parallel) +00:00:36.9338541 TOTAL OptimisticArrayObjectPool............................... -00:00:10.0751346 Give To (In Parallel) -00:00:10.5956764 Mixed 90%-Take/10%-Give (In Parallel) -00:00:10.1015581 Mixed 50%-Take/50%-Give (In Parallel) -00:00:10.1791619 Mixed 10%-Take/90%-Give (In Parallel) -00:00:00.8202931 Empty Pool (.TryTake()) -00:00:41.7718241 TOTAL +00:00:10.8894648 Give To (In Parallel) +00:00:11.4317863 Mixed 90%-Take/10%-Give (In Parallel) +00:00:11.0345582 Mixed 50%-Take/50%-Give (In Parallel) +00:00:11.0905596 Mixed 10%-Take/90%-Give (In Parallel) +00:00:44.4463689 TOTAL InterlockedArrayObjectPool.............................. -00:00:10.1135907 Give To (In Parallel) -00:00:10.5975168 Mixed 90%-Take/10%-Give (In Parallel) -00:00:10.2069357 Mixed 50%-Take/50%-Give (In Parallel) -00:00:10.2776009 Mixed 10%-Take/90%-Give (In Parallel) -00:00:00.7987301 Empty Pool (.TryTake()) -00:00:41.9943742 TOTAL +00:00:11.4199709 Give To (In Parallel) +00:00:11.9546985 Mixed 90%-Take/10%-Give (In Parallel) +00:00:11.4750506 Mixed 50%-Take/50%-Give (In Parallel) +00:00:11.6023244 Mixed 10%-Take/90%-Give (In Parallel) +00:00:46.4520444 TOTAL Repeat 960000 for size 10 ------------------------------------ +Microsoft.Extensions.ObjectPool.DefaultObjectPool....... +00:00:04.2160006 Give To (In Parallel) +00:00:03.7725744 Mixed 90%-Take/10%-Give (In Parallel) +00:00:03.5597736 Mixed 50%-Take/50%-Give (In Parallel) +00:00:04.0179846 Mixed 10%-Take/90%-Give (In Parallel) +00:00:15.5663332 TOTAL + ConcurrentQueueObjectPoolSlim........................... -00:00:03.9787914 Give To (In Parallel) -00:00:04.2003072 Mixed 90%-Take/10%-Give (In Parallel) -00:00:04.0901023 Mixed 50%-Take/50%-Give (In Parallel) -00:00:04.1125416 Mixed 10%-Take/90%-Give (In Parallel) -00:00:00.3234760 Empty Pool (.TryTake()) -00:00:16.7052185 TOTAL +00:00:04.4151484 Give To (In Parallel) +00:00:04.6938482 Mixed 90%-Take/10%-Give (In Parallel) +00:00:04.4135618 Mixed 50%-Take/50%-Give (In Parallel) +00:00:04.5366021 Mixed 10%-Take/90%-Give (In Parallel) +00:00:18.0591605 TOTAL ConcurrentQueueObjectPool............................... -00:00:02.6705738 Give To (In Parallel) -00:00:02.9303461 Mixed 90%-Take/10%-Give (In Parallel) -00:00:02.6527993 Mixed 50%-Take/50%-Give (In Parallel) -00:00:02.7590313 Mixed 10%-Take/90%-Give (In Parallel) -00:00:00.2448799 Empty Pool (.TryTake()) -00:00:11.2576304 TOTAL +00:00:03.4561782 Give To (In Parallel) +00:00:03.8400098 Mixed 90%-Take/10%-Give (In Parallel) +00:00:03.4414185 Mixed 50%-Take/50%-Give (In Parallel) +00:00:03.5460435 Mixed 10%-Take/90%-Give (In Parallel) +00:00:14.2836500 TOTAL OptimisticArrayObjectPool............................... -00:00:02.7080938 Give To (In Parallel) -00:00:03.1857486 Mixed 90%-Take/10%-Give (In Parallel) -00:00:02.7267450 Mixed 50%-Take/50%-Give (In Parallel) -00:00:02.8387716 Mixed 10%-Take/90%-Give (In Parallel) -00:00:00.3469802 Empty Pool (.TryTake()) -00:00:11.8063392 TOTAL +00:00:03.7549698 Give To (In Parallel) +00:00:04.2279702 Mixed 90%-Take/10%-Give (In Parallel) +00:00:03.6004288 Mixed 50%-Take/50%-Give (In Parallel) +00:00:03.7115653 Mixed 10%-Take/90%-Give (In Parallel) +00:00:15.2949341 TOTAL InterlockedArrayObjectPool.............................. -00:00:03.1543776 Give To (In Parallel) -00:00:03.3044990 Mixed 90%-Take/10%-Give (In Parallel) -00:00:02.9074669 Mixed 50%-Take/50%-Give (In Parallel) -00:00:03.1888732 Mixed 10%-Take/90%-Give (In Parallel) -00:00:00.3640288 Empty Pool (.TryTake()) -00:00:12.9192455 TOTAL +00:00:03.3626513 Give To (In Parallel) +00:00:03.4878549 Mixed 90%-Take/10%-Give (In Parallel) +00:00:02.9997860 Mixed 50%-Take/50%-Give (In Parallel) +00:00:03.3175440 Mixed 10%-Take/90%-Give (In Parallel) +00:00:13.1678362 TOTAL Repeat 288000 for size 50 ------------------------------------ +Microsoft.Extensions.ObjectPool.DefaultObjectPool....... +00:00:04.7460719 Give To (In Parallel) +00:00:01.7401188 Mixed 90%-Take/10%-Give (In Parallel) +00:00:01.3147629 Mixed 50%-Take/50%-Give (In Parallel) +00:00:03.6584918 Mixed 10%-Take/90%-Give (In Parallel) +00:00:11.4594454 TOTAL + ConcurrentQueueObjectPoolSlim........................... -00:00:01.6453853 Give To (In Parallel) -00:00:01.7359093 Mixed 90%-Take/10%-Give (In Parallel) -00:00:01.3652902 Mixed 50%-Take/50%-Give (In Parallel) -00:00:01.6812617 Mixed 10%-Take/90%-Give (In Parallel) -00:00:00.3263226 Empty Pool (.TryTake()) -00:00:06.7541691 TOTAL +00:00:01.6226510 Give To (In Parallel) +00:00:01.7854581 Mixed 90%-Take/10%-Give (In Parallel) +00:00:01.3020626 Mixed 50%-Take/50%-Give (In Parallel) +00:00:01.6743924 Mixed 10%-Take/90%-Give (In Parallel) +00:00:06.3845641 TOTAL ConcurrentQueueObjectPool............................... -00:00:01.7333532 Give To (In Parallel) -00:00:01.8169673 Mixed 90%-Take/10%-Give (In Parallel) -00:00:01.4589195 Mixed 50%-Take/50%-Give (In Parallel) -00:00:01.7403590 Mixed 10%-Take/90%-Give (In Parallel) -00:00:00.3740043 Empty Pool (.TryTake()) -00:00:07.1236033 TOTAL +00:00:01.7477988 Give To (In Parallel) +00:00:01.8965112 Mixed 90%-Take/10%-Give (In Parallel) +00:00:01.4379041 Mixed 50%-Take/50%-Give (In Parallel) +00:00:01.7960447 Mixed 10%-Take/90%-Give (In Parallel) +00:00:06.8782588 TOTAL OptimisticArrayObjectPool............................... -00:00:02.0335210 Give To (In Parallel) -00:00:02.0578942 Mixed 90%-Take/10%-Give (In Parallel) -00:00:01.4266376 Mixed 50%-Take/50%-Give (In Parallel) -00:00:01.7863439 Mixed 10%-Take/90%-Give (In Parallel) -00:00:01.3344072 Empty Pool (.TryTake()) -00:00:08.6388039 TOTAL +00:00:02.0226269 Give To (In Parallel) +00:00:02.0758868 Mixed 90%-Take/10%-Give (In Parallel) +00:00:01.4867332 Mixed 50%-Take/50%-Give (In Parallel) +00:00:01.9249519 Mixed 10%-Take/90%-Give (In Parallel) +00:00:07.5101988 TOTAL InterlockedArrayObjectPool.............................. -00:00:02.5295986 Give To (In Parallel) -00:00:02.2228966 Mixed 90%-Take/10%-Give (In Parallel) -00:00:01.5291152 Mixed 50%-Take/50%-Give (In Parallel) -00:00:02.0956115 Mixed 10%-Take/90%-Give (In Parallel) -00:00:01.5541699 Empty Pool (.TryTake()) -00:00:09.9313918 TOTAL +00:00:02.3024578 Give To (In Parallel) +00:00:02.0728807 Mixed 90%-Take/10%-Give (In Parallel) +00:00:01.4972879 Mixed 50%-Take/50%-Give (In Parallel) +00:00:02.0989055 Mixed 10%-Take/90%-Give (In Parallel) +00:00:07.9715319 TOTAL Repeat 192000 for size 100 ------------------------------------ +Microsoft.Extensions.ObjectPool.DefaultObjectPool....... +00:00:08.7588527 Give To (In Parallel) +00:00:01.8885618 Mixed 90%-Take/10%-Give (In Parallel) +00:00:01.2955575 Mixed 50%-Take/50%-Give (In Parallel) +00:00:06.7774603 Mixed 10%-Take/90%-Give (In Parallel) +00:00:18.7204323 TOTAL + ConcurrentQueueObjectPoolSlim........................... -00:00:02.3422135 Give To (In Parallel) -00:00:02.4589825 Mixed 90%-Take/10%-Give (In Parallel) -00:00:01.4419367 Mixed 50%-Take/50%-Give (In Parallel) -00:00:02.2890798 Mixed 10%-Take/90%-Give (In Parallel) -00:00:00.4853905 Empty Pool (.TryTake()) -00:00:09.0176030 TOTAL +00:00:02.5878519 Give To (In Parallel) +00:00:02.6535733 Mixed 90%-Take/10%-Give (In Parallel) +00:00:01.4588720 Mixed 50%-Take/50%-Give (In Parallel) +00:00:02.4315543 Mixed 10%-Take/90%-Give (In Parallel) +00:00:09.1318515 TOTAL ConcurrentQueueObjectPool............................... -00:00:02.5509913 Give To (In Parallel) -00:00:02.6460844 Mixed 90%-Take/10%-Give (In Parallel) -00:00:01.6833011 Mixed 50%-Take/50%-Give (In Parallel) -00:00:02.5321679 Mixed 10%-Take/90%-Give (In Parallel) -00:00:00.5554094 Empty Pool (.TryTake()) -00:00:09.9679541 TOTAL +00:00:02.8200448 Give To (In Parallel) +00:00:02.8029050 Mixed 90%-Take/10%-Give (In Parallel) +00:00:01.7456810 Mixed 50%-Take/50%-Give (In Parallel) +00:00:02.7047082 Mixed 10%-Take/90%-Give (In Parallel) +00:00:10.0733390 TOTAL OptimisticArrayObjectPool............................... -00:00:03.3738400 Give To (In Parallel) -00:00:02.4423141 Mixed 90%-Take/10%-Give (In Parallel) -00:00:01.3292480 Mixed 50%-Take/50%-Give (In Parallel) -00:00:01.8814529 Mixed 10%-Take/90%-Give (In Parallel) -00:00:02.8973500 Empty Pool (.TryTake()) -00:00:11.9242050 TOTAL +00:00:02.2072395 Give To (In Parallel) +00:00:02.4041567 Mixed 90%-Take/10%-Give (In Parallel) +00:00:01.4230022 Mixed 50%-Take/50%-Give (In Parallel) +00:00:01.8949625 Mixed 10%-Take/90%-Give (In Parallel) +00:00:07.9293609 TOTAL InterlockedArrayObjectPool.............................. -00:00:04.0758376 Give To (In Parallel) -00:00:02.4918027 Mixed 90%-Take/10%-Give (In Parallel) -00:00:01.4167009 Mixed 50%-Take/50%-Give (In Parallel) -00:00:02.4431298 Mixed 10%-Take/90%-Give (In Parallel) -00:00:03.5020819 Empty Pool (.TryTake()) -00:00:13.9295529 TOTAL +00:00:03.1657165 Give To (In Parallel) +00:00:02.5525702 Mixed 90%-Take/10%-Give (In Parallel) +00:00:01.5274323 Mixed 50%-Take/50%-Give (In Parallel) +00:00:02.6791879 Mixed 10%-Take/90%-Give (In Parallel) +00:00:09.9249069 TOTAL Repeat 153600 for size 250 ------------------------------------ +Microsoft.Extensions.ObjectPool.DefaultObjectPool....... +00:00:27.5101753 Give To (In Parallel) +00:00:03.2947973 Mixed 90%-Take/10%-Give (In Parallel) +00:00:02.3024871 Mixed 50%-Take/50%-Give (In Parallel) +00:00:20.5458083 Mixed 10%-Take/90%-Give (In Parallel) +00:00:53.6532680 TOTAL + ConcurrentQueueObjectPoolSlim........................... -00:00:05.3605084 Give To (In Parallel) -00:00:05.3904152 Mixed 90%-Take/10%-Give (In Parallel) -00:00:02.9076538 Mixed 50%-Take/50%-Give (In Parallel) -00:00:05.4448936 Mixed 10%-Take/90%-Give (In Parallel) -00:00:00.8048672 Empty Pool (.TryTake()) -00:00:19.9083382 TOTAL +00:00:06.4921520 Give To (In Parallel) +00:00:05.6483503 Mixed 90%-Take/10%-Give (In Parallel) +00:00:02.9681300 Mixed 50%-Take/50%-Give (In Parallel) +00:00:05.5662083 Mixed 10%-Take/90%-Give (In Parallel) +00:00:20.6748406 TOTAL ConcurrentQueueObjectPool............................... -00:00:05.3745739 Give To (In Parallel) -00:00:05.6739715 Mixed 90%-Take/10%-Give (In Parallel) -00:00:03.5889170 Mixed 50%-Take/50%-Give (In Parallel) -00:00:05.5923650 Mixed 10%-Take/90%-Give (In Parallel) -00:00:00.9855414 Empty Pool (.TryTake()) -00:00:21.2153688 TOTAL +00:00:06.6973676 Give To (In Parallel) +00:00:05.8383753 Mixed 90%-Take/10%-Give (In Parallel) +00:00:03.5723233 Mixed 50%-Take/50%-Give (In Parallel) +00:00:05.8146242 Mixed 10%-Take/90%-Give (In Parallel) +00:00:21.9226904 TOTAL OptimisticArrayObjectPool............................... -00:00:05.9226589 Give To (In Parallel) -00:00:04.5202656 Mixed 90%-Take/10%-Give (In Parallel) -00:00:02.2839784 Mixed 50%-Take/50%-Give (In Parallel) -00:00:03.2966061 Mixed 10%-Take/90%-Give (In Parallel) -00:00:08.1874870 Empty Pool (.TryTake()) -00:00:24.2109960 TOTAL +00:00:04.7849466 Give To (In Parallel) +00:00:05.0781449 Mixed 90%-Take/10%-Give (In Parallel) +00:00:02.4908361 Mixed 50%-Take/50%-Give (In Parallel) +00:00:03.6097713 Mixed 10%-Take/90%-Give (In Parallel) +00:00:15.9636989 TOTAL InterlockedArrayObjectPool.............................. -00:00:22.5836030 Give To (In Parallel) -00:00:12.0998476 Mixed 90%-Take/10%-Give (In Parallel) -00:00:06.3335435 Mixed 50%-Take/50%-Give (In Parallel) -00:00:12.1356510 Mixed 10%-Take/90%-Give (In Parallel) -00:00:38.4403391 Empty Pool (.TryTake()) -00:01:31.5929842 TOTAL +00:00:07.3464588 Give To (In Parallel) +00:00:05.3703708 Mixed 90%-Take/10%-Give (In Parallel) +00:00:02.8338082 Mixed 50%-Take/50%-Give (In Parallel) +00:00:05.5183809 Mixed 10%-Take/90%-Give (In Parallel) +00:00:21.0690187 TOTAL diff --git a/benchmarking/DefaultObjectPool.cs b/benchmarking/DefaultObjectPool.cs index b8ab6f7..1d41237 100644 --- a/benchmarking/DefaultObjectPool.cs +++ b/benchmarking/DefaultObjectPool.cs @@ -2,41 +2,42 @@ using System; using System.Diagnostics.CodeAnalysis; +#nullable enable + namespace Open.Disposable.ObjectPools { public class DefaultObjectPool : Microsoft.Extensions.ObjectPool.DefaultObjectPool, IObjectPool where T : class { - private readonly IPooledObjectPolicy _policy; - + private readonly Func _factory; class Policy : IPooledObjectPolicy { private readonly Func factory; - private readonly Action recycler; +// private readonly Action? recycler; - public Policy(Func factory, Action? recycler) + public Policy(Func factory)//, Action? recycler) { if (factory is null) throw new ArgumentNullException(nameof(factory)); this.factory = factory; - this.recycler = recycler; + //this.recycler = recycler; } public T Create() => factory(); public bool Return(T obj) { - recycler?.Invoke(obj); + // recycler?.Invoke(obj); return true; } } - public DefaultObjectPool(Func factory, Action? recycler, int capacity = 64) - : base(new Policy(factory, recycler), capacity) + public DefaultObjectPool(Func factory, int capacity = 64) + : base(new Policy(factory), capacity) { - + _factory = factory; } public int Capacity { get; } @@ -47,7 +48,7 @@ public void Dispose() { } - public T Generate() => _policy.Create(); + public T Generate() => _factory(); public void Give(T item) => Return(item); diff --git a/benchmarking/Program.cs b/benchmarking/Program.cs index 16fa7ed..7c15d5f 100644 --- a/benchmarking/Program.cs +++ b/benchmarking/Program.cs @@ -27,7 +27,7 @@ static void Main() // count => () => QueueObjectPool.Create((int)count * 2)); report.AddBenchmark("Microsoft.Extensions.ObjectPool.DefaultObjectPool", - count => () => new DefaultObjectPool(() => new object(), null, (int)count * 2)); + count => () => new DefaultObjectPool(() => new object(), (int)count * 2)); report.AddBenchmark("ConcurrentQueueObjectPoolSlim", count => () => ConcurrentQueueObjectPoolSlim.Create((int)count * 2)); @@ -49,7 +49,7 @@ static void Main() Console.SetCursorPosition(0, Console.CursorTop); - const int loopMultiple = 2; + const int loopMultiple = 12; report.Test(4, 8 * loopMultiple); report.Test(10, 8 * loopMultiple); report.Test(50, 12 * loopMultiple); diff --git a/source/Array/InterlockedArrayObjectPool.cs b/source/Array/InterlockedArrayObjectPool.cs index df20e5b..e6e80f9 100644 --- a/source/Array/InterlockedArrayObjectPool.cs +++ b/source/Array/InterlockedArrayObjectPool.cs @@ -2,6 +2,7 @@ using System; using System.Linq; +using System.Runtime.CompilerServices; using System.Threading; namespace Open.Disposable @@ -31,8 +32,9 @@ public InterlockedArrayObjectPool(Func factory, int capacity = DEFAULT_CAPACI protected const int MaxStoredIncrement = 5; // Instead of every one. public override int Count - => Pool.Count(e => e.Value != null) + PocketCount; + => Pool.Count(e => e.Value is not null) + PocketCount; + [MethodImpl(MethodImplOptions.AggressiveInlining)] protected virtual bool Store(ReferenceContainer[] p, T item, int index) => p[index].TrySave(item); @@ -65,7 +67,7 @@ protected override bool Receive(T item) for (var i = 0; i < MaxStored && i < len; i++) { var item = elements[i].TryRetrieve(); - if (item != null) return item; + if (item is not null) return item; } return null; diff --git a/source/Array/OptimisticArrayObjectPool.cs b/source/Array/OptimisticArrayObjectPool.cs index 1936aba..d9bb7d4 100644 --- a/source/Array/OptimisticArrayObjectPool.cs +++ b/source/Array/OptimisticArrayObjectPool.cs @@ -1,6 +1,7 @@ /* Based on Roslyn's ObjectPool */ using System; +using System.Runtime.CompilerServices; namespace Open.Disposable { @@ -21,10 +22,14 @@ public OptimisticArrayObjectPool(Func factory, int capacity = DEFAULT_CAPACIT { } // As suggested by Roslyn's implementation, don't worry about interlocking here. It's okay if a few get loose. + + [MethodImpl(MethodImplOptions.AggressiveInlining)] protected override bool SaveToPocket(T item) => Pocket.SetIfNull(item); // As suggested by Roslyn's implementation, don't worry about interlocking here. It's okay if a few get loose. + + [MethodImpl(MethodImplOptions.AggressiveInlining)] protected override bool Store(ReferenceContainer[] p, T item, int index) => p[index].SetIfNull(item); } diff --git a/source/Collection/CollectionWrapperObjectPool.cs b/source/Collection/CollectionWrapperObjectPool.cs index 21d0c96..96c164b 100644 --- a/source/Collection/CollectionWrapperObjectPool.cs +++ b/source/Collection/CollectionWrapperObjectPool.cs @@ -21,7 +21,7 @@ public CollectionWrapperObjectPool(TCollection pool, Func factory, int capaci protected override bool Receive(T item) { var p = Pool; - if (p != null) + if (p is not null) { lock (p) { diff --git a/source/Collection/ConcurrentQueueObjectPool.cs b/source/Collection/ConcurrentQueueObjectPool.cs index 4d7060f..c2be433 100644 --- a/source/Collection/ConcurrentQueueObjectPool.cs +++ b/source/Collection/ConcurrentQueueObjectPool.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Concurrent; +using System.Runtime.CompilerServices; namespace Open.Disposable { @@ -23,6 +24,7 @@ public ConcurrentQueueObjectPool(Func factory, int capacity = DEFAULT_CAPACIT * Benchmarking reveals that mixed read/writes (what really matters) are still faster with the pocket enabled so best to keep it so. */ + [MethodImpl(MethodImplOptions.AggressiveInlining)] protected override bool Receive(T item) { var p = Pool; @@ -31,6 +33,7 @@ protected override bool Receive(T item) return true; } + [MethodImpl(MethodImplOptions.AggressiveInlining)] protected override T? TryRelease() { var p = Pool; diff --git a/source/Collection/ConcurrentQueueObjectPoolSlim.cs b/source/Collection/ConcurrentQueueObjectPoolSlim.cs index 6b695d1..82db7b2 100644 --- a/source/Collection/ConcurrentQueueObjectPoolSlim.cs +++ b/source/Collection/ConcurrentQueueObjectPoolSlim.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Concurrent; +using System.Runtime.CompilerServices; namespace Open.Disposable { @@ -26,6 +27,7 @@ public ConcurrentQueueObjectPoolSlim(Func factory, int capacity = DEFAULT_CAP * Benchmarking reveals that mixed read/writes (what really matters) are still faster with the pocket enabled so best to keep it so. */ + [MethodImpl(MethodImplOptions.AggressiveInlining)] protected override bool Receive(T item) { var p = Pool; @@ -34,6 +36,7 @@ protected override bool Receive(T item) return true; } + [MethodImpl(MethodImplOptions.AggressiveInlining)] protected override T? TryRelease() { var p = Pool; diff --git a/source/Collection/QueueObjectPool.cs b/source/Collection/QueueObjectPool.cs index a2264cb..2c64376 100644 --- a/source/Collection/QueueObjectPool.cs +++ b/source/Collection/QueueObjectPool.cs @@ -23,7 +23,7 @@ public QueueObjectPool(Func factory, int capacity = DEFAULT_CAPACITY) protected override bool Receive(T item) { var p = Pool; - if (p != null) + if (p is not null) { // It's possible that the count could exceed MaxSize here, but the risk is negligble as a few over the limit won't hurt. // The lock operation should be quick enough to not pile up too many items. @@ -37,7 +37,7 @@ protected override bool Receive(T item) protected override T? TryRelease() { var p = Pool; - if (p != null && p.Count != 0) + if (p is not null && p.Count != 0) { lock (p) { diff --git a/source/Collection/StackObjectPool.cs b/source/Collection/StackObjectPool.cs index 30611bd..a17244e 100644 --- a/source/Collection/StackObjectPool.cs +++ b/source/Collection/StackObjectPool.cs @@ -23,7 +23,7 @@ public StackObjectPool(Func factory, int capacity = DEFAULT_CAPACITY) protected override bool Receive(T item) { var p = Pool; - if (p != null) + if (p is not null) { // It's possible that the count could exceed MaxSize here, but the risk is negligble as a few over the limit won't hurt. // The lock operation should be quick enough to not pile up too many items. @@ -37,7 +37,7 @@ protected override bool Receive(T item) protected override T? TryRelease() { var p = Pool; - if (p != null && p.Count != 0) + if (p is not null && p.Count != 0) { lock (p) { diff --git a/source/CountTrackedObjectPoolBase.cs b/source/CountTrackedObjectPoolBase.cs index 306b04e..c69d291 100644 --- a/source/CountTrackedObjectPoolBase.cs +++ b/source/CountTrackedObjectPoolBase.cs @@ -1,4 +1,5 @@ using System; +using System.Runtime.CompilerServices; using System.Threading; namespace Open.Disposable @@ -19,10 +20,13 @@ protected CountTrackedObjectPoolBase(Func factory, Action? recycler, Actio protected override bool CanReceive => _count < MaxSize; + [MethodImpl(MethodImplOptions.AggressiveInlining)] protected override void OnReleased() { Interlocked.Decrement(ref _count); } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] protected override void OnReceived() { Interlocked.Increment(ref _count); diff --git a/source/ObjectPoolBase.cs b/source/ObjectPoolBase.cs index 676eca5..655cf5e 100644 --- a/source/ObjectPoolBase.cs +++ b/source/ObjectPoolBase.cs @@ -1,5 +1,6 @@ using System; using System.Diagnostics.CodeAnalysis; +using System.Runtime.CompilerServices; namespace Open.Disposable { @@ -47,10 +48,10 @@ protected ObjectPoolBase(Func factory, Action? recycler, Action? dispos protected bool PrepareToReceive(T item) { - if (item != null && CanReceive) + if (item is not null && CanReceive) { var r = Recycler; - if (r != null) + if (r is not null) { r(item); // Did the recycle take so long that the state changed? @@ -90,11 +91,13 @@ public bool TryTake([NotNullWhen(true)] out T? item) #else public bool TryTake(out T? item) #endif - => (item = TryTake()) != null; + => (item = TryTake()) is not null; + [MethodImpl(MethodImplOptions.AggressiveInlining)] protected virtual bool SaveToPocket(T item) => Pocket.TrySave(item); + [MethodImpl(MethodImplOptions.AggressiveInlining)] protected T? TakeFromPocket() => Pocket.TryRetrieve(); @@ -104,10 +107,11 @@ protected virtual bool SaveToPocket(T item) public T? TryTake() { var item = TakeFromPocket() ?? TryRelease(); - if (item != null) OnReleased(); + if (item is not null) OnReleased(); return item; } + [MethodImpl(MethodImplOptions.AggressiveInlining)] protected virtual void OnReleased() { } @@ -123,7 +127,7 @@ protected override void OnDispose() if (OnDiscarded is null) return; T? d; - while ((d = TryRelease()) != null) OnDiscarded(d); + while ((d = TryRelease()) is not null) OnDiscarded(d); } } } diff --git a/source/Open.Disposable.ObjectPools.csproj b/source/Open.Disposable.ObjectPools.csproj index 1893871..ad6dc04 100644 --- a/source/Open.Disposable.ObjectPools.csproj +++ b/source/Open.Disposable.ObjectPools.csproj @@ -17,7 +17,7 @@ Part of the "Open" set of libraries. https://github.com/Open-NET-Libraries/Open.Disposable.ObjectPools/ git objectpool;idisposable;thread safe - 2.6.8 + 2.7.0 MIT true diff --git a/source/RecycleHelper.cs b/source/RecycleHelper.cs index 443c86b..5426a48 100644 --- a/source/RecycleHelper.cs +++ b/source/RecycleHelper.cs @@ -26,7 +26,7 @@ public void Dispose() { var i = _item; _item = null; - if (i == null) return; + if (i is null) return; _pool.Give(i); } } diff --git a/source/ReferenceContainer.cs b/source/ReferenceContainer.cs index 7cefa8d..96c73a7 100644 --- a/source/ReferenceContainer.cs +++ b/source/ReferenceContainer.cs @@ -1,4 +1,5 @@ using System.Diagnostics; +using System.Runtime.CompilerServices; using System.Threading; namespace Open.Disposable @@ -26,21 +27,24 @@ public T? Value set => _value = value; } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool SetIfNull(T value) { - if (_value != null) return false; + if (_value is not null) return false; _value = value; return true; } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool TrySave(T value) => _value is null && null == Interlocked.CompareExchange(ref _value, value, null); + [MethodImpl(MethodImplOptions.AggressiveInlining)] public T? TryRetrieve() { var item = _value; - return (item != null + return (item is not null && item == Interlocked.CompareExchange(ref _value, null, item)) ? item : null; diff --git a/source/TrimmableCollectionObjectPoolBase.cs b/source/TrimmableCollectionObjectPoolBase.cs index 62c377b..8f00737 100644 --- a/source/TrimmableCollectionObjectPoolBase.cs +++ b/source/TrimmableCollectionObjectPoolBase.cs @@ -48,7 +48,7 @@ protected override void OnDispose() var p = Pool; Pool = null!; - if (OnDiscarded != null) + if (OnDiscarded is not null) { foreach (var item in p) OnDiscarded(item); diff --git a/source/TrimmableObjectPoolBase.cs b/source/TrimmableObjectPoolBase.cs index 859e128..f5a7067 100644 --- a/source/TrimmableObjectPoolBase.cs +++ b/source/TrimmableObjectPoolBase.cs @@ -1,5 +1,6 @@ using System; using System.Diagnostics; +using System.Runtime.CompilerServices; using System.Threading; namespace Open.Disposable @@ -30,6 +31,8 @@ protected void OnReleased(int newSize) Debug.Assert(newSize > -2, $"newSize: {newSize}, _count: {_count}"); // Should never get out of control. It may go negative temporarily but should be 100% accounted for. Released?.Invoke(newSize); } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] protected override void OnReleased() { OnReleased(_countTrackingEnabled ? Interlocked.Decrement(ref _count) : Count); @@ -39,11 +42,14 @@ protected override void OnReleased() /// Signal for when an item was given (actually accepted) to the pool. /// public event ObjectPoolResizeEvent? Received; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] protected void OnReceived(int newSize) { Received?.Invoke(newSize); } + [MethodImpl(MethodImplOptions.AggressiveInlining)] protected override void OnReceived() { OnReceived(_countTrackingEnabled ? Interlocked.Increment(ref _count) : Count); From bf31418a554204495e4aca718db5ed3144651c56 Mon Sep 17 00:00:00 2001 From: electricessence <5899455+electricessence@users.noreply.github.com> Date: Mon, 27 Sep 2021 23:33:02 -0700 Subject: [PATCH 66/83] Improved docs. --- benchmarking/BenchmarkResult.csv | 50 ++-- benchmarking/BenchmarkResult.txt | 250 ++++++++++---------- source/CountTrackedObjectPoolBase.cs | 1 + source/IRecyclable.cs | 3 + source/IRecycler.cs | 7 + source/ObjectPoolBase.cs | 8 +- source/RecycleHelper.cs | 3 + source/Recycler.cs | 1 + source/RecyclerBase.cs | 1 + source/ReferenceContainer.cs | 15 ++ source/TrimmableCollectionObjectPoolBase.cs | 2 + source/TrimmableObjectPoolBase.cs | 1 + 12 files changed, 190 insertions(+), 152 deletions(-) diff --git a/benchmarking/BenchmarkResult.csv b/benchmarking/BenchmarkResult.csv index 6c86167..d40e6c3 100644 --- a/benchmarking/BenchmarkResult.csv +++ b/benchmarking/BenchmarkResult.csv @@ -1,29 +1,29 @@ Batch,Pool Type," To (In Parallel)",d 90%-Take/10%-Give (In Parallel),d 50%-Take/50%-Give (In Parallel),d 10%-Take/90%-Give (In Parallel),L, -Repeat 2400000 for size 4,Microsoft.Extensions.ObjectPool.DefaultObjectPool,00:00:10.2988122,00:00:10.4798439,00:00:10.3993411,00:00:10.3464322,00:00:41.5244294, -Repeat 2400000 for size 4,ConcurrentQueueObjectPoolSlim,00:00:11.1870930,00:00:11.5168981,00:00:11.1280664,00:00:11.2224827,00:00:45.0545402, -Repeat 2400000 for size 4,ConcurrentQueueObjectPool,00:00:09.1399563,00:00:09.3180086,00:00:09.2816926,00:00:09.1941966,00:00:36.9338541, -Repeat 2400000 for size 4,OptimisticArrayObjectPool,00:00:10.8894648,00:00:11.4317863,00:00:11.0345582,00:00:11.0905596,00:00:44.4463689, -Repeat 2400000 for size 4,InterlockedArrayObjectPool,00:00:11.4199709,00:00:11.9546985,00:00:11.4750506,00:00:11.6023244,00:00:46.4520444, -Repeat 960000 for size 10,Microsoft.Extensions.ObjectPool.DefaultObjectPool,00:00:04.2160006,00:00:03.7725744,00:00:03.5597736,00:00:04.0179846,00:00:15.5663332, -Repeat 960000 for size 10,ConcurrentQueueObjectPoolSlim,00:00:04.4151484,00:00:04.6938482,00:00:04.4135618,00:00:04.5366021,00:00:18.0591605, -Repeat 960000 for size 10,ConcurrentQueueObjectPool,00:00:03.4561782,00:00:03.8400098,00:00:03.4414185,00:00:03.5460435,00:00:14.2836500, -Repeat 960000 for size 10,OptimisticArrayObjectPool,00:00:03.7549698,00:00:04.2279702,00:00:03.6004288,00:00:03.7115653,00:00:15.2949341, -Repeat 960000 for size 10,InterlockedArrayObjectPool,00:00:03.3626513,00:00:03.4878549,00:00:02.9997860,00:00:03.3175440,00:00:13.1678362, -Repeat 288000 for size 50,Microsoft.Extensions.ObjectPool.DefaultObjectPool,00:00:04.7460719,00:00:01.7401188,00:00:01.3147629,00:00:03.6584918,00:00:11.4594454, -Repeat 288000 for size 50,ConcurrentQueueObjectPoolSlim,00:00:01.6226510,00:00:01.7854581,00:00:01.3020626,00:00:01.6743924,00:00:06.3845641, -Repeat 288000 for size 50,ConcurrentQueueObjectPool,00:00:01.7477988,00:00:01.8965112,00:00:01.4379041,00:00:01.7960447,00:00:06.8782588, -Repeat 288000 for size 50,OptimisticArrayObjectPool,00:00:02.0226269,00:00:02.0758868,00:00:01.4867332,00:00:01.9249519,00:00:07.5101988, -Repeat 288000 for size 50,InterlockedArrayObjectPool,00:00:02.3024578,00:00:02.0728807,00:00:01.4972879,00:00:02.0989055,00:00:07.9715319, -Repeat 192000 for size 100,Microsoft.Extensions.ObjectPool.DefaultObjectPool,00:00:08.7588527,00:00:01.8885618,00:00:01.2955575,00:00:06.7774603,00:00:18.7204323, -Repeat 192000 for size 100,ConcurrentQueueObjectPoolSlim,00:00:02.5878519,00:00:02.6535733,00:00:01.4588720,00:00:02.4315543,00:00:09.1318515, -Repeat 192000 for size 100,ConcurrentQueueObjectPool,00:00:02.8200448,00:00:02.8029050,00:00:01.7456810,00:00:02.7047082,00:00:10.0733390, -Repeat 192000 for size 100,OptimisticArrayObjectPool,00:00:02.2072395,00:00:02.4041567,00:00:01.4230022,00:00:01.8949625,00:00:07.9293609, -Repeat 192000 for size 100,InterlockedArrayObjectPool,00:00:03.1657165,00:00:02.5525702,00:00:01.5274323,00:00:02.6791879,00:00:09.9249069, -Repeat 153600 for size 250,Microsoft.Extensions.ObjectPool.DefaultObjectPool,00:00:27.5101753,00:00:03.2947973,00:00:02.3024871,00:00:20.5458083,00:00:53.6532680, -Repeat 153600 for size 250,ConcurrentQueueObjectPoolSlim,00:00:06.4921520,00:00:05.6483503,00:00:02.9681300,00:00:05.5662083,00:00:20.6748406, -Repeat 153600 for size 250,ConcurrentQueueObjectPool,00:00:06.6973676,00:00:05.8383753,00:00:03.5723233,00:00:05.8146242,00:00:21.9226904, -Repeat 153600 for size 250,OptimisticArrayObjectPool,00:00:04.7849466,00:00:05.0781449,00:00:02.4908361,00:00:03.6097713,00:00:15.9636989, -Repeat 153600 for size 250,InterlockedArrayObjectPool,00:00:07.3464588,00:00:05.3703708,00:00:02.8338082,00:00:05.5183809,00:00:21.0690187, +Repeat 2400000 for size 4,Microsoft.Extensions.ObjectPool.DefaultObjectPool,00:00:08.7536436,00:00:08.8078884,00:00:08.7786113,00:00:08.7696869,00:00:35.1098302, +Repeat 2400000 for size 4,ConcurrentQueueObjectPoolSlim,00:00:10.9719702,00:00:11.2012940,00:00:10.9157990,00:00:10.9839675,00:00:44.0730307, +Repeat 2400000 for size 4,ConcurrentQueueObjectPool,00:00:11.9357472,00:00:12.0596338,00:00:11.8062979,00:00:11.9269534,00:00:47.7286323, +Repeat 2400000 for size 4,OptimisticArrayObjectPool,00:00:11.3448787,00:00:11.9320120,00:00:11.6168340,00:00:11.5151251,00:00:46.4088498, +Repeat 2400000 for size 4,InterlockedArrayObjectPool,00:00:11.4076831,00:00:12.0350556,00:00:11.5814744,00:00:11.4369017,00:00:46.4611148, +Repeat 960000 for size 10,Microsoft.Extensions.ObjectPool.DefaultObjectPool,00:00:03.1998733,00:00:02.8702785,00:00:02.6837168,00:00:02.9629105,00:00:11.7167791, +Repeat 960000 for size 10,ConcurrentQueueObjectPoolSlim,00:00:02.9492963,00:00:03.2428289,00:00:03.0176898,00:00:03.0319249,00:00:12.2417399, +Repeat 960000 for size 10,ConcurrentQueueObjectPool,00:00:02.8828210,00:00:03.2011413,00:00:02.8822661,00:00:02.9505638,00:00:11.9167922, +Repeat 960000 for size 10,OptimisticArrayObjectPool,00:00:03.0700174,00:00:03.5310316,00:00:02.9952085,00:00:03.0371614,00:00:12.6334189, +Repeat 960000 for size 10,InterlockedArrayObjectPool,00:00:03.2483005,00:00:03.3170740,00:00:02.9369241,00:00:03.2100109,00:00:12.7123095, +Repeat 288000 for size 50,Microsoft.Extensions.ObjectPool.DefaultObjectPool,00:00:04.6794305,00:00:01.7429937,00:00:01.3513779,00:00:03.6357603,00:00:11.4095624, +Repeat 288000 for size 50,ConcurrentQueueObjectPoolSlim,00:00:01.6163124,00:00:01.7762298,00:00:01.3300174,00:00:01.6749817,00:00:06.3975413, +Repeat 288000 for size 50,ConcurrentQueueObjectPool,00:00:01.7832340,00:00:01.9323201,00:00:01.4793899,00:00:01.8423448,00:00:07.0372888, +Repeat 288000 for size 50,OptimisticArrayObjectPool,00:00:01.8823951,00:00:01.9428592,00:00:01.4112225,00:00:01.7877122,00:00:07.0241890, +Repeat 288000 for size 50,InterlockedArrayObjectPool,00:00:02.2154790,00:00:02.0187594,00:00:01.4717288,00:00:02.0234332,00:00:07.7294004, +Repeat 192000 for size 100,Microsoft.Extensions.ObjectPool.DefaultObjectPool,00:00:08.6239380,00:00:01.7883047,00:00:01.2339147,00:00:06.6098240,00:00:18.2559814, +Repeat 192000 for size 100,ConcurrentQueueObjectPoolSlim,00:00:02.6089351,00:00:02.6653084,00:00:01.4554945,00:00:02.4419762,00:00:09.1717142, +Repeat 192000 for size 100,ConcurrentQueueObjectPool,00:00:02.8683847,00:00:02.8497468,00:00:01.7801622,00:00:02.7221468,00:00:10.2204405, +Repeat 192000 for size 100,OptimisticArrayObjectPool,00:00:02.2792049,00:00:02.4557422,00:00:01.4629279,00:00:01.9573853,00:00:08.1552603, +Repeat 192000 for size 100,InterlockedArrayObjectPool,00:00:03.1284602,00:00:02.5353265,00:00:01.5067879,00:00:02.6633642,00:00:09.8339388, +Repeat 153600 for size 250,Microsoft.Extensions.ObjectPool.DefaultObjectPool,00:00:27.6249504,00:00:03.3819394,00:00:02.3777699,00:00:20.7230291,00:00:54.1076888, +Repeat 153600 for size 250,ConcurrentQueueObjectPoolSlim,00:00:06.4197135,00:00:05.5706983,00:00:02.9047885,00:00:05.5148018,00:00:20.4100021, +Repeat 153600 for size 250,ConcurrentQueueObjectPool,00:00:06.6929502,00:00:05.8257810,00:00:03.6463958,00:00:05.7592677,00:00:21.9243947, +Repeat 153600 for size 250,OptimisticArrayObjectPool,00:00:04.5394559,00:00:04.9284349,00:00:02.4790010,00:00:03.4449412,00:00:15.3918330, +Repeat 153600 for size 250,InterlockedArrayObjectPool,00:00:07.2010676,00:00:05.3582282,00:00:02.8324961,00:00:05.4306754,00:00:20.8224673, 0:02.0767858,00:00:00.6488593,00:00:09.9478764, Repeat 12800 for size 2000,ConcurrentStackObjectPool,00:00:00.6708805,00:00:02.5628201,00:00:04.7978301,00:00:04.7563902,00:00:03.4723738,00:00:00.6475781,00:00:16.9078728, Repeat 12800 for size 2000,OptimisticArrayObjectPool,00:00:00.7184635,00:00:07.2485577,00:00:07.4304114,00:00:01.1244839,00:00:04.7572699,00:00:15.8850338,00:00:37.1642202, diff --git a/benchmarking/BenchmarkResult.txt b/benchmarking/BenchmarkResult.txt index 65f3c7b..1d2080b 100644 --- a/benchmarking/BenchmarkResult.txt +++ b/benchmarking/BenchmarkResult.txt @@ -2,194 +2,194 @@ Repeat 2400000 for size 4 ------------------------------------ Microsoft.Extensions.ObjectPool.DefaultObjectPool....... -00:00:10.2988122 Give To (In Parallel) -00:00:10.4798439 Mixed 90%-Take/10%-Give (In Parallel) -00:00:10.3993411 Mixed 50%-Take/50%-Give (In Parallel) -00:00:10.3464322 Mixed 10%-Take/90%-Give (In Parallel) -00:00:41.5244294 TOTAL +00:00:08.7536436 Give To (In Parallel) +00:00:08.8078884 Mixed 90%-Take/10%-Give (In Parallel) +00:00:08.7786113 Mixed 50%-Take/50%-Give (In Parallel) +00:00:08.7696869 Mixed 10%-Take/90%-Give (In Parallel) +00:00:35.1098302 TOTAL ConcurrentQueueObjectPoolSlim........................... -00:00:11.1870930 Give To (In Parallel) -00:00:11.5168981 Mixed 90%-Take/10%-Give (In Parallel) -00:00:11.1280664 Mixed 50%-Take/50%-Give (In Parallel) -00:00:11.2224827 Mixed 10%-Take/90%-Give (In Parallel) -00:00:45.0545402 TOTAL +00:00:10.9719702 Give To (In Parallel) +00:00:11.2012940 Mixed 90%-Take/10%-Give (In Parallel) +00:00:10.9157990 Mixed 50%-Take/50%-Give (In Parallel) +00:00:10.9839675 Mixed 10%-Take/90%-Give (In Parallel) +00:00:44.0730307 TOTAL ConcurrentQueueObjectPool............................... -00:00:09.1399563 Give To (In Parallel) -00:00:09.3180086 Mixed 90%-Take/10%-Give (In Parallel) -00:00:09.2816926 Mixed 50%-Take/50%-Give (In Parallel) -00:00:09.1941966 Mixed 10%-Take/90%-Give (In Parallel) -00:00:36.9338541 TOTAL +00:00:11.9357472 Give To (In Parallel) +00:00:12.0596338 Mixed 90%-Take/10%-Give (In Parallel) +00:00:11.8062979 Mixed 50%-Take/50%-Give (In Parallel) +00:00:11.9269534 Mixed 10%-Take/90%-Give (In Parallel) +00:00:47.7286323 TOTAL OptimisticArrayObjectPool............................... -00:00:10.8894648 Give To (In Parallel) -00:00:11.4317863 Mixed 90%-Take/10%-Give (In Parallel) -00:00:11.0345582 Mixed 50%-Take/50%-Give (In Parallel) -00:00:11.0905596 Mixed 10%-Take/90%-Give (In Parallel) -00:00:44.4463689 TOTAL +00:00:11.3448787 Give To (In Parallel) +00:00:11.9320120 Mixed 90%-Take/10%-Give (In Parallel) +00:00:11.6168340 Mixed 50%-Take/50%-Give (In Parallel) +00:00:11.5151251 Mixed 10%-Take/90%-Give (In Parallel) +00:00:46.4088498 TOTAL InterlockedArrayObjectPool.............................. -00:00:11.4199709 Give To (In Parallel) -00:00:11.9546985 Mixed 90%-Take/10%-Give (In Parallel) -00:00:11.4750506 Mixed 50%-Take/50%-Give (In Parallel) -00:00:11.6023244 Mixed 10%-Take/90%-Give (In Parallel) -00:00:46.4520444 TOTAL +00:00:11.4076831 Give To (In Parallel) +00:00:12.0350556 Mixed 90%-Take/10%-Give (In Parallel) +00:00:11.5814744 Mixed 50%-Take/50%-Give (In Parallel) +00:00:11.4369017 Mixed 10%-Take/90%-Give (In Parallel) +00:00:46.4611148 TOTAL Repeat 960000 for size 10 ------------------------------------ Microsoft.Extensions.ObjectPool.DefaultObjectPool....... -00:00:04.2160006 Give To (In Parallel) -00:00:03.7725744 Mixed 90%-Take/10%-Give (In Parallel) -00:00:03.5597736 Mixed 50%-Take/50%-Give (In Parallel) -00:00:04.0179846 Mixed 10%-Take/90%-Give (In Parallel) -00:00:15.5663332 TOTAL +00:00:03.1998733 Give To (In Parallel) +00:00:02.8702785 Mixed 90%-Take/10%-Give (In Parallel) +00:00:02.6837168 Mixed 50%-Take/50%-Give (In Parallel) +00:00:02.9629105 Mixed 10%-Take/90%-Give (In Parallel) +00:00:11.7167791 TOTAL ConcurrentQueueObjectPoolSlim........................... -00:00:04.4151484 Give To (In Parallel) -00:00:04.6938482 Mixed 90%-Take/10%-Give (In Parallel) -00:00:04.4135618 Mixed 50%-Take/50%-Give (In Parallel) -00:00:04.5366021 Mixed 10%-Take/90%-Give (In Parallel) -00:00:18.0591605 TOTAL +00:00:02.9492963 Give To (In Parallel) +00:00:03.2428289 Mixed 90%-Take/10%-Give (In Parallel) +00:00:03.0176898 Mixed 50%-Take/50%-Give (In Parallel) +00:00:03.0319249 Mixed 10%-Take/90%-Give (In Parallel) +00:00:12.2417399 TOTAL ConcurrentQueueObjectPool............................... -00:00:03.4561782 Give To (In Parallel) -00:00:03.8400098 Mixed 90%-Take/10%-Give (In Parallel) -00:00:03.4414185 Mixed 50%-Take/50%-Give (In Parallel) -00:00:03.5460435 Mixed 10%-Take/90%-Give (In Parallel) -00:00:14.2836500 TOTAL +00:00:02.8828210 Give To (In Parallel) +00:00:03.2011413 Mixed 90%-Take/10%-Give (In Parallel) +00:00:02.8822661 Mixed 50%-Take/50%-Give (In Parallel) +00:00:02.9505638 Mixed 10%-Take/90%-Give (In Parallel) +00:00:11.9167922 TOTAL OptimisticArrayObjectPool............................... -00:00:03.7549698 Give To (In Parallel) -00:00:04.2279702 Mixed 90%-Take/10%-Give (In Parallel) -00:00:03.6004288 Mixed 50%-Take/50%-Give (In Parallel) -00:00:03.7115653 Mixed 10%-Take/90%-Give (In Parallel) -00:00:15.2949341 TOTAL +00:00:03.0700174 Give To (In Parallel) +00:00:03.5310316 Mixed 90%-Take/10%-Give (In Parallel) +00:00:02.9952085 Mixed 50%-Take/50%-Give (In Parallel) +00:00:03.0371614 Mixed 10%-Take/90%-Give (In Parallel) +00:00:12.6334189 TOTAL InterlockedArrayObjectPool.............................. -00:00:03.3626513 Give To (In Parallel) -00:00:03.4878549 Mixed 90%-Take/10%-Give (In Parallel) -00:00:02.9997860 Mixed 50%-Take/50%-Give (In Parallel) -00:00:03.3175440 Mixed 10%-Take/90%-Give (In Parallel) -00:00:13.1678362 TOTAL +00:00:03.2483005 Give To (In Parallel) +00:00:03.3170740 Mixed 90%-Take/10%-Give (In Parallel) +00:00:02.9369241 Mixed 50%-Take/50%-Give (In Parallel) +00:00:03.2100109 Mixed 10%-Take/90%-Give (In Parallel) +00:00:12.7123095 TOTAL Repeat 288000 for size 50 ------------------------------------ Microsoft.Extensions.ObjectPool.DefaultObjectPool....... -00:00:04.7460719 Give To (In Parallel) -00:00:01.7401188 Mixed 90%-Take/10%-Give (In Parallel) -00:00:01.3147629 Mixed 50%-Take/50%-Give (In Parallel) -00:00:03.6584918 Mixed 10%-Take/90%-Give (In Parallel) -00:00:11.4594454 TOTAL +00:00:04.6794305 Give To (In Parallel) +00:00:01.7429937 Mixed 90%-Take/10%-Give (In Parallel) +00:00:01.3513779 Mixed 50%-Take/50%-Give (In Parallel) +00:00:03.6357603 Mixed 10%-Take/90%-Give (In Parallel) +00:00:11.4095624 TOTAL ConcurrentQueueObjectPoolSlim........................... -00:00:01.6226510 Give To (In Parallel) -00:00:01.7854581 Mixed 90%-Take/10%-Give (In Parallel) -00:00:01.3020626 Mixed 50%-Take/50%-Give (In Parallel) -00:00:01.6743924 Mixed 10%-Take/90%-Give (In Parallel) -00:00:06.3845641 TOTAL +00:00:01.6163124 Give To (In Parallel) +00:00:01.7762298 Mixed 90%-Take/10%-Give (In Parallel) +00:00:01.3300174 Mixed 50%-Take/50%-Give (In Parallel) +00:00:01.6749817 Mixed 10%-Take/90%-Give (In Parallel) +00:00:06.3975413 TOTAL ConcurrentQueueObjectPool............................... -00:00:01.7477988 Give To (In Parallel) -00:00:01.8965112 Mixed 90%-Take/10%-Give (In Parallel) -00:00:01.4379041 Mixed 50%-Take/50%-Give (In Parallel) -00:00:01.7960447 Mixed 10%-Take/90%-Give (In Parallel) -00:00:06.8782588 TOTAL +00:00:01.7832340 Give To (In Parallel) +00:00:01.9323201 Mixed 90%-Take/10%-Give (In Parallel) +00:00:01.4793899 Mixed 50%-Take/50%-Give (In Parallel) +00:00:01.8423448 Mixed 10%-Take/90%-Give (In Parallel) +00:00:07.0372888 TOTAL OptimisticArrayObjectPool............................... -00:00:02.0226269 Give To (In Parallel) -00:00:02.0758868 Mixed 90%-Take/10%-Give (In Parallel) -00:00:01.4867332 Mixed 50%-Take/50%-Give (In Parallel) -00:00:01.9249519 Mixed 10%-Take/90%-Give (In Parallel) -00:00:07.5101988 TOTAL +00:00:01.8823951 Give To (In Parallel) +00:00:01.9428592 Mixed 90%-Take/10%-Give (In Parallel) +00:00:01.4112225 Mixed 50%-Take/50%-Give (In Parallel) +00:00:01.7877122 Mixed 10%-Take/90%-Give (In Parallel) +00:00:07.0241890 TOTAL InterlockedArrayObjectPool.............................. -00:00:02.3024578 Give To (In Parallel) -00:00:02.0728807 Mixed 90%-Take/10%-Give (In Parallel) -00:00:01.4972879 Mixed 50%-Take/50%-Give (In Parallel) -00:00:02.0989055 Mixed 10%-Take/90%-Give (In Parallel) -00:00:07.9715319 TOTAL +00:00:02.2154790 Give To (In Parallel) +00:00:02.0187594 Mixed 90%-Take/10%-Give (In Parallel) +00:00:01.4717288 Mixed 50%-Take/50%-Give (In Parallel) +00:00:02.0234332 Mixed 10%-Take/90%-Give (In Parallel) +00:00:07.7294004 TOTAL Repeat 192000 for size 100 ------------------------------------ Microsoft.Extensions.ObjectPool.DefaultObjectPool....... -00:00:08.7588527 Give To (In Parallel) -00:00:01.8885618 Mixed 90%-Take/10%-Give (In Parallel) -00:00:01.2955575 Mixed 50%-Take/50%-Give (In Parallel) -00:00:06.7774603 Mixed 10%-Take/90%-Give (In Parallel) -00:00:18.7204323 TOTAL +00:00:08.6239380 Give To (In Parallel) +00:00:01.7883047 Mixed 90%-Take/10%-Give (In Parallel) +00:00:01.2339147 Mixed 50%-Take/50%-Give (In Parallel) +00:00:06.6098240 Mixed 10%-Take/90%-Give (In Parallel) +00:00:18.2559814 TOTAL ConcurrentQueueObjectPoolSlim........................... -00:00:02.5878519 Give To (In Parallel) -00:00:02.6535733 Mixed 90%-Take/10%-Give (In Parallel) -00:00:01.4588720 Mixed 50%-Take/50%-Give (In Parallel) -00:00:02.4315543 Mixed 10%-Take/90%-Give (In Parallel) -00:00:09.1318515 TOTAL +00:00:02.6089351 Give To (In Parallel) +00:00:02.6653084 Mixed 90%-Take/10%-Give (In Parallel) +00:00:01.4554945 Mixed 50%-Take/50%-Give (In Parallel) +00:00:02.4419762 Mixed 10%-Take/90%-Give (In Parallel) +00:00:09.1717142 TOTAL ConcurrentQueueObjectPool............................... -00:00:02.8200448 Give To (In Parallel) -00:00:02.8029050 Mixed 90%-Take/10%-Give (In Parallel) -00:00:01.7456810 Mixed 50%-Take/50%-Give (In Parallel) -00:00:02.7047082 Mixed 10%-Take/90%-Give (In Parallel) -00:00:10.0733390 TOTAL +00:00:02.8683847 Give To (In Parallel) +00:00:02.8497468 Mixed 90%-Take/10%-Give (In Parallel) +00:00:01.7801622 Mixed 50%-Take/50%-Give (In Parallel) +00:00:02.7221468 Mixed 10%-Take/90%-Give (In Parallel) +00:00:10.2204405 TOTAL OptimisticArrayObjectPool............................... -00:00:02.2072395 Give To (In Parallel) -00:00:02.4041567 Mixed 90%-Take/10%-Give (In Parallel) -00:00:01.4230022 Mixed 50%-Take/50%-Give (In Parallel) -00:00:01.8949625 Mixed 10%-Take/90%-Give (In Parallel) -00:00:07.9293609 TOTAL +00:00:02.2792049 Give To (In Parallel) +00:00:02.4557422 Mixed 90%-Take/10%-Give (In Parallel) +00:00:01.4629279 Mixed 50%-Take/50%-Give (In Parallel) +00:00:01.9573853 Mixed 10%-Take/90%-Give (In Parallel) +00:00:08.1552603 TOTAL InterlockedArrayObjectPool.............................. -00:00:03.1657165 Give To (In Parallel) -00:00:02.5525702 Mixed 90%-Take/10%-Give (In Parallel) -00:00:01.5274323 Mixed 50%-Take/50%-Give (In Parallel) -00:00:02.6791879 Mixed 10%-Take/90%-Give (In Parallel) -00:00:09.9249069 TOTAL +00:00:03.1284602 Give To (In Parallel) +00:00:02.5353265 Mixed 90%-Take/10%-Give (In Parallel) +00:00:01.5067879 Mixed 50%-Take/50%-Give (In Parallel) +00:00:02.6633642 Mixed 10%-Take/90%-Give (In Parallel) +00:00:09.8339388 TOTAL Repeat 153600 for size 250 ------------------------------------ Microsoft.Extensions.ObjectPool.DefaultObjectPool....... -00:00:27.5101753 Give To (In Parallel) -00:00:03.2947973 Mixed 90%-Take/10%-Give (In Parallel) -00:00:02.3024871 Mixed 50%-Take/50%-Give (In Parallel) -00:00:20.5458083 Mixed 10%-Take/90%-Give (In Parallel) -00:00:53.6532680 TOTAL +00:00:27.6249504 Give To (In Parallel) +00:00:03.3819394 Mixed 90%-Take/10%-Give (In Parallel) +00:00:02.3777699 Mixed 50%-Take/50%-Give (In Parallel) +00:00:20.7230291 Mixed 10%-Take/90%-Give (In Parallel) +00:00:54.1076888 TOTAL ConcurrentQueueObjectPoolSlim........................... -00:00:06.4921520 Give To (In Parallel) -00:00:05.6483503 Mixed 90%-Take/10%-Give (In Parallel) -00:00:02.9681300 Mixed 50%-Take/50%-Give (In Parallel) -00:00:05.5662083 Mixed 10%-Take/90%-Give (In Parallel) -00:00:20.6748406 TOTAL +00:00:06.4197135 Give To (In Parallel) +00:00:05.5706983 Mixed 90%-Take/10%-Give (In Parallel) +00:00:02.9047885 Mixed 50%-Take/50%-Give (In Parallel) +00:00:05.5148018 Mixed 10%-Take/90%-Give (In Parallel) +00:00:20.4100021 TOTAL ConcurrentQueueObjectPool............................... -00:00:06.6973676 Give To (In Parallel) -00:00:05.8383753 Mixed 90%-Take/10%-Give (In Parallel) -00:00:03.5723233 Mixed 50%-Take/50%-Give (In Parallel) -00:00:05.8146242 Mixed 10%-Take/90%-Give (In Parallel) -00:00:21.9226904 TOTAL +00:00:06.6929502 Give To (In Parallel) +00:00:05.8257810 Mixed 90%-Take/10%-Give (In Parallel) +00:00:03.6463958 Mixed 50%-Take/50%-Give (In Parallel) +00:00:05.7592677 Mixed 10%-Take/90%-Give (In Parallel) +00:00:21.9243947 TOTAL OptimisticArrayObjectPool............................... -00:00:04.7849466 Give To (In Parallel) -00:00:05.0781449 Mixed 90%-Take/10%-Give (In Parallel) -00:00:02.4908361 Mixed 50%-Take/50%-Give (In Parallel) -00:00:03.6097713 Mixed 10%-Take/90%-Give (In Parallel) -00:00:15.9636989 TOTAL +00:00:04.5394559 Give To (In Parallel) +00:00:04.9284349 Mixed 90%-Take/10%-Give (In Parallel) +00:00:02.4790010 Mixed 50%-Take/50%-Give (In Parallel) +00:00:03.4449412 Mixed 10%-Take/90%-Give (In Parallel) +00:00:15.3918330 TOTAL InterlockedArrayObjectPool.............................. -00:00:07.3464588 Give To (In Parallel) -00:00:05.3703708 Mixed 90%-Take/10%-Give (In Parallel) -00:00:02.8338082 Mixed 50%-Take/50%-Give (In Parallel) -00:00:05.5183809 Mixed 10%-Take/90%-Give (In Parallel) -00:00:21.0690187 TOTAL +00:00:07.2010676 Give To (In Parallel) +00:00:05.3582282 Mixed 90%-Take/10%-Give (In Parallel) +00:00:02.8324961 Mixed 50%-Take/50%-Give (In Parallel) +00:00:05.4306754 Mixed 10%-Take/90%-Give (In Parallel) +00:00:20.8224673 TOTAL diff --git a/source/CountTrackedObjectPoolBase.cs b/source/CountTrackedObjectPoolBase.cs index c69d291..6faffba 100644 --- a/source/CountTrackedObjectPoolBase.cs +++ b/source/CountTrackedObjectPoolBase.cs @@ -16,6 +16,7 @@ protected CountTrackedObjectPoolBase(Func factory, Action? recycler, Actio int _count; + /// public override int Count => _count; protected override bool CanReceive => _count < MaxSize; diff --git a/source/IRecyclable.cs b/source/IRecyclable.cs index a87e734..3de903d 100644 --- a/source/IRecyclable.cs +++ b/source/IRecyclable.cs @@ -2,6 +2,9 @@ { public interface IRecyclable { + /// + /// Signals the item should be recycled. + /// void Recycle(); } } diff --git a/source/IRecycler.cs b/source/IRecycler.cs index 277d1d1..79a8e66 100644 --- a/source/IRecycler.cs +++ b/source/IRecycler.cs @@ -6,7 +6,14 @@ namespace Open.Disposable public interface IRecycler : IDisposable where T : class { + /// + /// Recycles the item. + /// bool Recycle(T item); + + /// + /// Closes the recycling. No more should be recycled. + /// Task Close(); } } diff --git a/source/ObjectPoolBase.cs b/source/ObjectPoolBase.cs index 655cf5e..94b8f5a 100644 --- a/source/ObjectPoolBase.cs +++ b/source/ObjectPoolBase.cs @@ -23,10 +23,10 @@ protected ObjectPoolBase(Func factory, Action? recycler, Action? dispos protected int MaxSize; public int Capacity => MaxSize; - /// /// /// Total number of items in the pool. /// + /// public abstract int Count { get; } protected int PocketCount => Pocket.Value is null ? 0 : 1; @@ -48,7 +48,7 @@ protected ObjectPoolBase(Func factory, Action? recycler, Action? dispos protected bool PrepareToReceive(T item) { - if (item is not null && CanReceive) + if (CanReceive && item is not null) { var r = Recycler; if (r is not null) @@ -72,6 +72,7 @@ protected virtual void OnReceived() } + /// public void Give(T item) { if (PrepareToReceive(item) @@ -83,9 +84,11 @@ public void Give(T item) #endregion #region Release (.Take()) + /// public virtual T Take() => TryTake() ?? Factory(); + /// #if NETSTANDARD2_1 public bool TryTake([NotNullWhen(true)] out T? item) #else @@ -104,6 +107,7 @@ protected virtual bool SaveToPocket(T item) protected abstract T? TryRelease(); + /// public T? TryTake() { var item = TakeFromPocket() ?? TryRelease(); diff --git a/source/RecycleHelper.cs b/source/RecycleHelper.cs index 5426a48..ec44c76 100644 --- a/source/RecycleHelper.cs +++ b/source/RecycleHelper.cs @@ -2,6 +2,9 @@ namespace Open.Disposable { + /// + /// Allows for the 'using' syntax to be used with any object to return it to the pool. + /// public struct RecycleHelper : IDisposable where T : class { diff --git a/source/Recycler.cs b/source/Recycler.cs index 29f6c05..4886c70 100644 --- a/source/Recycler.cs +++ b/source/Recycler.cs @@ -38,6 +38,7 @@ internal Recycler( IObjectPool pool, Action recycleFunction) : this(pool, recycleFunction, limit) { } + /// public override bool Recycle(T item) => _bin?.Writer.TryWrite(item) ?? false; diff --git a/source/RecyclerBase.cs b/source/RecyclerBase.cs index fb565b0..c3f655a 100644 --- a/source/RecyclerBase.cs +++ b/source/RecyclerBase.cs @@ -37,6 +37,7 @@ protected RecyclerBase( // ReSharper disable once MemberCanBeProtected.Global public Task Completion { get; protected set; } = Task.CompletedTask; + /// public Task Close() { OnCloseRequested(); diff --git a/source/ReferenceContainer.cs b/source/ReferenceContainer.cs index 96c73a7..f502367 100644 --- a/source/ReferenceContainer.cs +++ b/source/ReferenceContainer.cs @@ -21,12 +21,19 @@ public struct ReferenceContainer : IReferenceContainer T? _value; + /// + /// The value contained. + /// public T? Value { get => _value; set => _value = value; } + /// + /// Sets the value if it is currently null without interlocking. + /// + /// true if the value was set; otherwise false. [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool SetIfNull(T value) { @@ -35,11 +42,19 @@ public bool SetIfNull(T value) return true; } + /// + /// Tries to atomically store the value. + /// + /// true if the value was set; otherwise false. [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool TrySave(T value) => _value is null && null == Interlocked.CompareExchange(ref _value, value, null); + /// + /// Tries to atomically retrieve the value. + /// + /// The value retrieved; otherwise null. [MethodImpl(MethodImplOptions.AggressiveInlining)] public T? TryRetrieve() { diff --git a/source/TrimmableCollectionObjectPoolBase.cs b/source/TrimmableCollectionObjectPoolBase.cs index 8f00737..b45869e 100644 --- a/source/TrimmableCollectionObjectPoolBase.cs +++ b/source/TrimmableCollectionObjectPoolBase.cs @@ -17,6 +17,7 @@ protected TrimmableCollectionObjectPoolBase(TCollection pool, Func factory, A protected TCollection Pool; + /// public override int Count => (Pool?.Count ?? 0) + PocketCount; protected override void OnDispose() @@ -39,6 +40,7 @@ protected TrimmableGenericCollectionObjectPoolBase(TCollection pool, Func fac protected TCollection Pool; + /// public override int Count => Pool?.Count ?? 0; protected override void OnDispose() diff --git a/source/TrimmableObjectPoolBase.cs b/source/TrimmableObjectPoolBase.cs index f5a7067..d54b24a 100644 --- a/source/TrimmableObjectPoolBase.cs +++ b/source/TrimmableObjectPoolBase.cs @@ -55,6 +55,7 @@ protected override void OnReceived() OnReceived(_countTrackingEnabled ? Interlocked.Increment(ref _count) : Count); } + /// public virtual void TrimTo(int targetSize) { if (targetSize < 0) return; // Possible upstream math hiccup or default -1. Silently dismiss. From 45e159668934faf68bf1af2963c92291da48a87f Mon Sep 17 00:00:00 2001 From: electricessence <5899455+electricessence@users.noreply.github.com> Date: Tue, 28 Sep 2021 20:25:26 -0700 Subject: [PATCH 67/83] Reformat and added non-disposable SharedPool --- .editorconfig | 219 +++++++++++ Open.Disposable.ObjectPools.sln | 9 +- source/.editorconfig | 13 - source/Array/InterlockedArrayObjectPool.cs | 165 ++++----- source/Array/OptimisticArrayObjectPool.cs | 85 ++--- .../Collection/CollectionWrapperObjectPool.cs | 103 +++--- .../Collection/ConcurrentQueueObjectPool.cs | 117 +++--- .../ConcurrentQueueObjectPoolSlim.cs | 112 ++---- .../ConcurrentQueueObjectPoolSlimBase.cs | 50 +++ .../Collection/ConcurrentStackObjectPool.cs | 103 +++--- source/Collection/QueueObjectPool.cs | 123 +++---- source/Collection/StackObjectPool.cs | 124 +++---- source/Constants.cs | 9 +- source/CountTrackedObjectPoolBase.cs | 44 +-- source/IObjectPool.cs | 340 +++++++++--------- source/IRecyclable.cs | 15 +- source/IRecycler.cs | 26 +- source/ITrimmableObjectPool.cs | 13 +- source/ObjectPoolAutoTrimmer.cs | 135 ++++--- source/ObjectPoolBase.cs | 234 ++++++------ source/ObjectPoolCompatibilityExtensions.cs | 16 + source/ObjectPoolResizeEvent.cs | 7 +- source/Open.Disposable.ObjectPools.csproj | 2 +- source/RecycleHelper.cs | 51 ++- source/Recycler.cs | 113 +++--- source/RecyclerBase.cs | 88 +++-- source/ReferenceContainer.cs | 133 +++---- source/SharedPools.cs | 139 +++---- source/TrimmableCollectionObjectPoolBase.cs | 99 ++--- source/TrimmableObjectPoolBase.cs | 136 ++++--- 30 files changed, 1457 insertions(+), 1366 deletions(-) create mode 100644 .editorconfig delete mode 100644 source/.editorconfig create mode 100644 source/Collection/ConcurrentQueueObjectPoolSlimBase.cs create mode 100644 source/ObjectPoolCompatibilityExtensions.cs diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..880dfa3 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,219 @@ +# Remove the line below if you want to inherit .editorconfig settings from higher directories +root = true + +# C# files +[*.cs] + +#### Core EditorConfig Options #### + +# Indentation and spacing +indent_size = 4 +indent_style = tab +tab_width = 4 + +# New line preferences +end_of_line = crlf +insert_final_newline = false + +#### .NET Coding Conventions #### + +# Organize usings +dotnet_separate_import_directive_groups = false +dotnet_sort_system_directives_first = false +file_header_template = unset + +# this. and Me. preferences +dotnet_style_qualification_for_event = false +dotnet_style_qualification_for_field = false +dotnet_style_qualification_for_method = false +dotnet_style_qualification_for_property = false + +# Language keywords vs BCL types preferences +dotnet_style_predefined_type_for_locals_parameters_members = true +dotnet_style_predefined_type_for_member_access = true + +# Parentheses preferences +dotnet_style_parentheses_in_arithmetic_binary_operators = never_if_unnecessary +dotnet_style_parentheses_in_other_binary_operators = never_if_unnecessary +dotnet_style_parentheses_in_other_operators = never_if_unnecessary +dotnet_style_parentheses_in_relational_binary_operators = always_for_clarity + +# Modifier preferences +dotnet_style_require_accessibility_modifiers = for_non_interface_members + +# Expression-level preferences +dotnet_style_coalesce_expression = true +dotnet_style_collection_initializer = true +dotnet_style_explicit_tuple_names = true +dotnet_style_namespace_match_folder = true +dotnet_style_null_propagation = true +dotnet_style_object_initializer = true +dotnet_style_operator_placement_when_wrapping = beginning_of_line +dotnet_style_prefer_auto_properties = true +dotnet_style_prefer_compound_assignment = true +dotnet_style_prefer_conditional_expression_over_assignment = true +dotnet_style_prefer_conditional_expression_over_return = true +dotnet_style_prefer_inferred_anonymous_type_member_names = true +dotnet_style_prefer_inferred_tuple_names = true +dotnet_style_prefer_is_null_check_over_reference_equality_method = true +dotnet_style_prefer_simplified_boolean_expressions = true +dotnet_style_prefer_simplified_interpolation = true + +# Field preferences +dotnet_style_readonly_field = true + +# Parameter preferences +dotnet_code_quality_unused_parameters = all + +# Suppression preferences +dotnet_remove_unnecessary_suppression_exclusions = none + +# New line preferences +dotnet_style_allow_multiple_blank_lines_experimental = true +dotnet_style_allow_statement_immediately_after_block_experimental = true + +#### C# Coding Conventions #### + +# var preferences +csharp_style_var_elsewhere = true +csharp_style_var_for_built_in_types = true +csharp_style_var_when_type_is_apparent = true + +# Expression-bodied members +csharp_style_expression_bodied_accessors = true +csharp_style_expression_bodied_constructors = when_on_single_line +csharp_style_expression_bodied_indexers = true +csharp_style_expression_bodied_lambdas = true +csharp_style_expression_bodied_local_functions = true +csharp_style_expression_bodied_methods = true +csharp_style_expression_bodied_operators = when_on_single_line +csharp_style_expression_bodied_properties = true + +# Pattern matching preferences +csharp_style_pattern_matching_over_as_with_null_check = true +csharp_style_pattern_matching_over_is_with_cast_check = true +csharp_style_prefer_not_pattern = true +csharp_style_prefer_pattern_matching = true +csharp_style_prefer_switch_expression = true + +# Null-checking preferences +csharp_style_conditional_delegate_call = true + +# Modifier preferences +csharp_prefer_static_local_function = true +csharp_preferred_modifier_order = public,private,protected,internal,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,volatile,async + +# Code-block preferences +csharp_prefer_braces = when_multiline +csharp_prefer_simple_using_statement = true +csharp_style_namespace_declarations = file_scoped + +# Expression-level preferences +csharp_prefer_simple_default_expression = true +csharp_style_deconstructed_variable_declaration = true +csharp_style_implicit_object_creation_when_type_is_apparent = true +csharp_style_inlined_variable_declaration = true +csharp_style_pattern_local_over_anonymous_function = true +csharp_style_prefer_index_operator = true +csharp_style_prefer_null_check_over_type_check = true +csharp_style_prefer_range_operator = true +csharp_style_throw_expression = true +csharp_style_unused_value_assignment_preference = discard_variable +csharp_style_unused_value_expression_statement_preference = discard_variable + +# 'using' directive preferences +csharp_using_directive_placement = outside_namespace + +# New line preferences +csharp_style_allow_blank_line_after_colon_in_constructor_initializer_experimental = true +csharp_style_allow_blank_lines_between_consecutive_braces_experimental = true +csharp_style_allow_embedded_statements_on_same_line_experimental = true + +#### C# Formatting Rules #### + +# New line preferences +csharp_new_line_before_catch = true +csharp_new_line_before_else = true +csharp_new_line_before_finally = true +csharp_new_line_before_members_in_anonymous_types = true +csharp_new_line_before_members_in_object_initializers = true +csharp_new_line_before_open_brace = all +csharp_new_line_between_query_expression_clauses = true + +# Indentation preferences +csharp_indent_block_contents = true +csharp_indent_braces = false +csharp_indent_case_contents = true +csharp_indent_case_contents_when_block = true +csharp_indent_labels = one_less_than_current +csharp_indent_switch_labels = true + +# Space preferences +csharp_space_after_cast = false +csharp_space_after_colon_in_inheritance_clause = true +csharp_space_after_comma = true +csharp_space_after_dot = false +csharp_space_after_keywords_in_control_flow_statements = true +csharp_space_after_semicolon_in_for_statement = true +csharp_space_around_binary_operators = before_and_after +csharp_space_around_declaration_statements = false +csharp_space_before_colon_in_inheritance_clause = true +csharp_space_before_comma = false +csharp_space_before_dot = false +csharp_space_before_open_square_brackets = false +csharp_space_before_semicolon_in_for_statement = false +csharp_space_between_empty_square_brackets = false +csharp_space_between_method_call_empty_parameter_list_parentheses = false +csharp_space_between_method_call_name_and_opening_parenthesis = false +csharp_space_between_method_call_parameter_list_parentheses = false +csharp_space_between_method_declaration_empty_parameter_list_parentheses = false +csharp_space_between_method_declaration_name_and_open_parenthesis = false +csharp_space_between_method_declaration_parameter_list_parentheses = false +csharp_space_between_parentheses = false +csharp_space_between_square_brackets = false + +# Wrapping preferences +csharp_preserve_single_line_blocks = true +csharp_preserve_single_line_statements = true + +#### Naming styles #### + +# Naming rules + +dotnet_naming_rule.interface_should_be_begins_with_i.severity = suggestion +dotnet_naming_rule.interface_should_be_begins_with_i.symbols = interface +dotnet_naming_rule.interface_should_be_begins_with_i.style = begins_with_i + +dotnet_naming_rule.types_should_be_pascal_case.severity = suggestion +dotnet_naming_rule.types_should_be_pascal_case.symbols = types +dotnet_naming_rule.types_should_be_pascal_case.style = pascal_case + +dotnet_naming_rule.non_field_members_should_be_pascal_case.severity = suggestion +dotnet_naming_rule.non_field_members_should_be_pascal_case.symbols = non_field_members +dotnet_naming_rule.non_field_members_should_be_pascal_case.style = pascal_case + +# Symbol specifications + +dotnet_naming_symbols.interface.applicable_kinds = interface +dotnet_naming_symbols.interface.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected +dotnet_naming_symbols.interface.required_modifiers = + +dotnet_naming_symbols.types.applicable_kinds = class, struct, interface, enum +dotnet_naming_symbols.types.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected +dotnet_naming_symbols.types.required_modifiers = + +dotnet_naming_symbols.non_field_members.applicable_kinds = property, event, method +dotnet_naming_symbols.non_field_members.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected +dotnet_naming_symbols.non_field_members.required_modifiers = + +# Naming styles + +dotnet_naming_style.pascal_case.required_prefix = +dotnet_naming_style.pascal_case.required_suffix = +dotnet_naming_style.pascal_case.word_separator = +dotnet_naming_style.pascal_case.capitalization = pascal_case + +dotnet_naming_style.begins_with_i.required_prefix = I +dotnet_naming_style.begins_with_i.required_suffix = +dotnet_naming_style.begins_with_i.word_separator = +dotnet_naming_style.begins_with_i.capitalization = pascal_case diff --git a/Open.Disposable.ObjectPools.sln b/Open.Disposable.ObjectPools.sln index 5a0fb91..6de8138 100644 --- a/Open.Disposable.ObjectPools.sln +++ b/Open.Disposable.ObjectPools.sln @@ -1,7 +1,7 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 16 -VisualStudioVersion = 16.0.31410.357 +# Visual Studio Version 17 +VisualStudioVersion = 17.0.31717.71 MinimumVisualStudioVersion = 10.0.40219.1 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Open.Disposable.ObjectPools.Benchmarking", "benchmarking\Open.Disposable.ObjectPools.Benchmarking.csproj", "{8404875C-ED46-4057-8D97-598B4D7B8040}" EndProject @@ -9,6 +9,11 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Open.Disposable.ObjectPools EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Open.Disposable.ObjectPools.Tests", "tests\Open.Disposable.ObjectPools.Tests.csproj", "{507DBB8A-A662-4D16-A375-48470A578B50}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{4E2B0ECD-63D2-42CF-91D2-53917748B856}" + ProjectSection(SolutionItems) = preProject + .editorconfig = .editorconfig + EndProjectSection +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU diff --git a/source/.editorconfig b/source/.editorconfig deleted file mode 100644 index c22cfef..0000000 --- a/source/.editorconfig +++ /dev/null @@ -1,13 +0,0 @@ -[*.cs] - -# CA1707: Identifiers should not contain underscores -dotnet_diagnostic.CA1707.severity = silent - -# CA1303: Do not pass literals as localized parameters -dotnet_diagnostic.CA1303.severity = silent - -# CA1051: Do not declare visible instance fields -dotnet_diagnostic.CA1051.severity = silent - -# CA1000: Do not declare static members on generic types -dotnet_diagnostic.CA1000.severity = silent diff --git a/source/Array/InterlockedArrayObjectPool.cs b/source/Array/InterlockedArrayObjectPool.cs index e6e80f9..20367a0 100644 --- a/source/Array/InterlockedArrayObjectPool.cs +++ b/source/Array/InterlockedArrayObjectPool.cs @@ -5,120 +5,101 @@ using System.Runtime.CompilerServices; using System.Threading; -namespace Open.Disposable +namespace Open.Disposable; + +/// +/// An extremely fast ObjectPool when the capacity is in the low 100s. +/// +/// +public class InterlockedArrayObjectPool + : ObjectPoolBase + where T : class { - /// - /// An extremely fast ObjectPool when the capacity is in the low 100s. - /// - /// - public class InterlockedArrayObjectPool : ObjectPoolBase - where T : class - { + public InterlockedArrayObjectPool( + Func factory, + Action? recycler, + Action? disposer, + int capacity = DEFAULT_CAPACITY) + : base(factory, recycler, disposer, capacity) + => Pool = new ReferenceContainer[capacity - 1]; - public InterlockedArrayObjectPool(Func factory, Action? recycler, Action? disposer, int capacity = DEFAULT_CAPACITY) - : base(factory, recycler, disposer, capacity) - { - Pool = new ReferenceContainer[capacity - 1]; - } + public InterlockedArrayObjectPool( + Func factory, + int capacity = DEFAULT_CAPACITY) + : this(factory, null, null, capacity) { } - public InterlockedArrayObjectPool(Func factory, int capacity = DEFAULT_CAPACITY) - : this(factory, null, null, capacity) - { } + protected ReferenceContainer[] Pool; - protected ReferenceContainer[] Pool; + // Sets a limit on what has been stored yet to prevent over searching the array unnecessarily.. + protected int MaxStored; + protected const int MaxStoredIncrement = 5; // Instead of every one. - // Sets a limit on what has been stored yet to prevent over searching the array unnecessarily.. - protected int MaxStored; - protected const int MaxStoredIncrement = 5; // Instead of every one. + public override int Count + => Pool.Count(e => e.Value is not null) + PocketCount; - public override int Count - => Pool.Count(e => e.Value is not null) + PocketCount; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + protected virtual bool Store(ReferenceContainer[] p, T item, int index) + => p[index].TrySave(item); - [MethodImpl(MethodImplOptions.AggressiveInlining)] - protected virtual bool Store(ReferenceContainer[] p, T item, int index) - => p[index].TrySave(item); + protected override bool Receive(T item) + { + var elements = Pool; + var len = elements?.Length ?? 0; - protected override bool Receive(T item) + for (var i = 0; i < len; i++) { - var elements = Pool; - var len = elements?.Length ?? 0; - - for (var i = 0; i < len; i++) - { - if (Store(elements!, item, i)) - { - var m = MaxStored; - if (i >= m) Interlocked.CompareExchange(ref MaxStored, m + MaxStoredIncrement, m); - - return true; - } - } - - return false; + if (!Store(elements!, item, i)) continue; + var m = MaxStored; + if (i >= m) Interlocked.CompareExchange(ref MaxStored, m + MaxStoredIncrement, m); + return true; } - protected override T? TryRelease() - { - // We missed getting the first item or it wasn't there. - var elements = Pool; - if (elements is null) return null; - - var len = elements.Length; - for (var i = 0; i < MaxStored && i < len; i++) - { - var item = elements[i].TryRetrieve(); - if (item is not null) return item; - } - - return null; - } + return false; + } + + protected override T? TryRelease() + { + // We missed getting the first item or it wasn't there. + var elements = Pool; + if (elements is null) return null; - protected override void OnDispose() + var len = elements.Length; + for (var i = 0; i < MaxStored && i < len; i++) { - base.OnDispose(); - Pool = null!; - MaxStored = 0; + var item = elements[i].TryRetrieve(); + if (item is not null) return item; } + return null; } - public static class InterlockedArrayObjectPool + protected override void OnDispose() { - public static InterlockedArrayObjectPool Create(Func factory, int capacity = Constants.DEFAULT_CAPACITY) - where T : class - { - return new InterlockedArrayObjectPool(factory, capacity); - } + base.OnDispose(); + Pool = null!; + MaxStored = 0; + } - public static InterlockedArrayObjectPool Create(int capacity = Constants.DEFAULT_CAPACITY) - where T : class, new() - { - return Create(() => new T(), capacity); - } +} - public static InterlockedArrayObjectPool CreateAutoRecycle(Func factory, int capacity = Constants.DEFAULT_CAPACITY) - where T : class, IRecyclable - { - return new InterlockedArrayObjectPool(factory, Recycler.Recycle, null, capacity); - } +public static class InterlockedArrayObjectPool +{ + public static InterlockedArrayObjectPool Create(Func factory, int capacity = Constants.DEFAULT_CAPACITY) + where T : class => new(factory, capacity); - public static InterlockedArrayObjectPool CreateAutoRecycle(int capacity = Constants.DEFAULT_CAPACITY) - where T : class, IRecyclable, new() - { - return CreateAutoRecycle(() => new T(), capacity); - } + public static InterlockedArrayObjectPool Create(int capacity = Constants.DEFAULT_CAPACITY) + where T : class, new() => Create(() => new T(), capacity); - public static InterlockedArrayObjectPool CreateAutoDisposal(Func factory, int capacity = Constants.DEFAULT_CAPACITY) - where T : class, IDisposable - { - return new InterlockedArrayObjectPool(factory, null, d => d.Dispose(), capacity); - } + public static InterlockedArrayObjectPool CreateAutoRecycle(Func factory, int capacity = Constants.DEFAULT_CAPACITY) + where T : class, IRecyclable => new(factory, Recycler.Recycle, null, capacity); - public static InterlockedArrayObjectPool CreateAutoDisposal(int capacity = Constants.DEFAULT_CAPACITY) - where T : class, IDisposable, new() - { - return CreateAutoDisposal(() => new T(), capacity); - } + public static InterlockedArrayObjectPool CreateAutoRecycle(int capacity = Constants.DEFAULT_CAPACITY) + where T : class, IRecyclable, new() => CreateAutoRecycle(() => new T(), capacity); + + public static InterlockedArrayObjectPool CreateAutoDisposal(Func factory, int capacity = Constants.DEFAULT_CAPACITY) + where T : class, IDisposable => new(factory, null, d => d.Dispose(), capacity); + + public static InterlockedArrayObjectPool CreateAutoDisposal(int capacity = Constants.DEFAULT_CAPACITY) + where T : class, IDisposable, new() => CreateAutoDisposal(() => new T(), capacity); - } } diff --git a/source/Array/OptimisticArrayObjectPool.cs b/source/Array/OptimisticArrayObjectPool.cs index d9bb7d4..eb25e0a 100644 --- a/source/Array/OptimisticArrayObjectPool.cs +++ b/source/Array/OptimisticArrayObjectPool.cs @@ -3,62 +3,51 @@ using System; using System.Runtime.CompilerServices; -namespace Open.Disposable +namespace Open.Disposable; + +/// +/// An extremely fast ObjectPool when the capacity is in the low 100s. +/// +/// +public class OptimisticArrayObjectPool + : InterlockedArrayObjectPool + where T : class { - /// - /// An extremely fast ObjectPool when the capacity is in the low 100s. - /// - /// - public class OptimisticArrayObjectPool : InterlockedArrayObjectPool - where T : class - { + public OptimisticArrayObjectPool( + Func factory, + Action? recycler, + int capacity = DEFAULT_CAPACITY) + : base(factory, recycler, null /* disposer not applicable here */, capacity) { } - public OptimisticArrayObjectPool(Func factory, Action? recycler, int capacity = DEFAULT_CAPACITY) - : base(factory, recycler, null /* disposer not applicable here */, capacity) - { } + public OptimisticArrayObjectPool( + Func factory, + int capacity = DEFAULT_CAPACITY) + : this(factory, null, capacity) { } - public OptimisticArrayObjectPool(Func factory, int capacity = DEFAULT_CAPACITY) - : this(factory, null, capacity) - { } + // As suggested by Roslyn's implementation, don't worry about interlocking here. It's okay if a few get loose. - // As suggested by Roslyn's implementation, don't worry about interlocking here. It's okay if a few get loose. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + protected override bool SaveToPocket(T item) + => Pocket.SetIfNull(item); - [MethodImpl(MethodImplOptions.AggressiveInlining)] - protected override bool SaveToPocket(T item) - => Pocket.SetIfNull(item); + // As suggested by Roslyn's implementation, don't worry about interlocking here. It's okay if a few get loose. - // As suggested by Roslyn's implementation, don't worry about interlocking here. It's okay if a few get loose. - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - protected override bool Store(ReferenceContainer[] p, T item, int index) - => p[index].SetIfNull(item); - } - - public static class OptimisticArrayObjectPool - { - public static OptimisticArrayObjectPool Create(Func factory, int capacity = Constants.DEFAULT_CAPACITY) - where T : class - { - return new OptimisticArrayObjectPool(factory, capacity); - } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + protected override bool Store(ReferenceContainer[] p, T item, int index) + => p[index].SetIfNull(item); +} - public static OptimisticArrayObjectPool Create(int capacity = Constants.DEFAULT_CAPACITY) - where T : class, new() - { - return Create(() => new T(), capacity); - } +public static class OptimisticArrayObjectPool +{ + public static OptimisticArrayObjectPool Create(Func factory, int capacity = Constants.DEFAULT_CAPACITY) + where T : class => new(factory, capacity); - public static OptimisticArrayObjectPool CreateAutoRecycle(Func factory, int capacity = Constants.DEFAULT_CAPACITY) - where T : class, IRecyclable - { - return new OptimisticArrayObjectPool(factory, Recycler.Recycle, capacity); - } + public static OptimisticArrayObjectPool Create(int capacity = Constants.DEFAULT_CAPACITY) + where T : class, new() => Create(() => new T(), capacity); - public static OptimisticArrayObjectPool CreateAutoRecycle(int capacity = Constants.DEFAULT_CAPACITY) - where T : class, IRecyclable, new() - { - return CreateAutoRecycle(() => new T(), capacity); - } + public static OptimisticArrayObjectPool CreateAutoRecycle(Func factory, int capacity = Constants.DEFAULT_CAPACITY) + where T : class, IRecyclable => new(factory, Recycler.Recycle, capacity); - } + public static OptimisticArrayObjectPool CreateAutoRecycle(int capacity = Constants.DEFAULT_CAPACITY) + where T : class, IRecyclable, new() => CreateAutoRecycle(() => new T(), capacity); } diff --git a/source/Collection/CollectionWrapperObjectPool.cs b/source/Collection/CollectionWrapperObjectPool.cs index 96c164b..efd7b98 100644 --- a/source/Collection/CollectionWrapperObjectPool.cs +++ b/source/Collection/CollectionWrapperObjectPool.cs @@ -2,64 +2,69 @@ using System.Collections.Generic; using System.Linq; -namespace Open.Disposable +namespace Open.Disposable; + +public class CollectionWrapperObjectPool + : TrimmableGenericCollectionObjectPoolBase + where T : class + where TCollection : class, ICollection { - public class CollectionWrapperObjectPool : TrimmableGenericCollectionObjectPoolBase - where T : class - where TCollection : class, ICollection - { - public CollectionWrapperObjectPool(TCollection pool, Func factory, Action? recycler, Action? disposer, int capacity = DEFAULT_CAPACITY, bool countTrackingEnabled = true) - : base(pool, factory, recycler, disposer, capacity, countTrackingEnabled) - { - } + public CollectionWrapperObjectPool( + TCollection pool, + Func factory, + Action? recycler, + Action? disposer, + int capacity = DEFAULT_CAPACITY, + bool countTrackingEnabled = true) + : base(pool, factory, recycler, disposer, capacity, countTrackingEnabled) { } - public CollectionWrapperObjectPool(TCollection pool, Func factory, int capacity = DEFAULT_CAPACITY, bool countTrackingEnabled = true) - : this(pool, factory, null, null, capacity, countTrackingEnabled) - { - } + public CollectionWrapperObjectPool( + TCollection pool, + Func factory, + int capacity = DEFAULT_CAPACITY, + bool countTrackingEnabled = true) + : this(pool, factory, null, null, capacity, countTrackingEnabled) { } - protected override bool Receive(T item) + protected override bool Receive(T item) + { + var p = Pool; + if (p is null) return false; + lock (p) { - var p = Pool; - if (p is not null) - { - lock (p) - { - // It's possible that the count could exceed MaxSize here, but the risk is negligble as a few over the limit won't hurt. - // The lock operation should be quick enough to not pile up too many items. - p.Add(item); - return true; - } - } - - return false; + // It's possible that the count could exceed MaxSize here, but the risk is negligble as a few over the limit won't hurt. + // The lock operation should be quick enough to not pile up too many items. + p.Add(item); } + return true; + } - protected override T? TryRelease() - { - retry: - - var p = Pool; - var item = p?.FirstOrDefault(); - if (item is null) return null; - /* Removing the first item is typically horribly inefficient but we can't make assumptions about the implementation here. - * It's a trade off between potentially iterating the entire collection before removing the last item, or relying on the underlying implementation. - * This implementation is in place for reference more than practice. Sub classes should override. */ + protected override T? TryRelease() + { + retry: - bool wasRemoved; - lock (p!) wasRemoved = p.Remove(item); - if (!wasRemoved) goto retry; + var p = Pool; + var item = p?.FirstOrDefault(); + if (item is null) return null; + /* Removing the first item is typically horribly inefficient but we can't make assumptions about the implementation here. + * It's a trade off between potentially iterating the entire collection before removing the last item, or relying on the underlying implementation. + * This implementation is in place for reference more than practice. Sub classes should override. */ - return item; - } + bool wasRemoved; + lock (p!) wasRemoved = p.Remove(item); + if (!wasRemoved) goto retry; + return item; } - public class CollectionWrapperObjectPool : CollectionWrapperObjectPool> - where T : class - { - public CollectionWrapperObjectPool(ICollection pool, Func factory, int capacity = DEFAULT_CAPACITY) : base(pool, factory, capacity) - { - } - } +} + +public class CollectionWrapperObjectPool + : CollectionWrapperObjectPool> + where T : class +{ + public CollectionWrapperObjectPool( + ICollection pool, + Func factory, + int capacity = DEFAULT_CAPACITY) + : base(pool, factory, capacity) { } } diff --git a/source/Collection/ConcurrentQueueObjectPool.cs b/source/Collection/ConcurrentQueueObjectPool.cs index c2be433..ccbcdd0 100644 --- a/source/Collection/ConcurrentQueueObjectPool.cs +++ b/source/Collection/ConcurrentQueueObjectPool.cs @@ -2,84 +2,67 @@ using System.Collections.Concurrent; using System.Runtime.CompilerServices; -namespace Open.Disposable -{ - public sealed class ConcurrentQueueObjectPool : TrimmableCollectionObjectPoolBase> - where T : class - { - - public ConcurrentQueueObjectPool(Func factory, Action? recycler, Action? disposer, int capacity = DEFAULT_CAPACITY) - : base(new ConcurrentQueue(), factory, recycler, disposer, capacity) - { - } +namespace Open.Disposable; - public ConcurrentQueueObjectPool(Func factory, int capacity = DEFAULT_CAPACITY) - : this(factory, null, null, capacity) - { - - } +public class ConcurrentQueueObjectPool + : TrimmableCollectionObjectPoolBase> + where T : class +{ - /* - * NOTE: ConcurrentQueue is very fast and will perform quite well without using the 'Pocket' feature. - * Benchmarking reveals that mixed read/writes (what really matters) are still faster with the pocket enabled so best to keep it so. - */ + public ConcurrentQueueObjectPool( + Func factory, + Action? recycler, + Action? disposer, + int capacity = DEFAULT_CAPACITY) + : base(new ConcurrentQueue(), factory, recycler, disposer, capacity) { } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - protected override bool Receive(T item) - { - var p = Pool; - if (p is null) return false; - p.Enqueue(item); // It's possible that the count could exceed MaxSize here, but the risk is negligble as a few over the limit won't hurt. - return true; - } + public ConcurrentQueueObjectPool( + Func factory, + int capacity = DEFAULT_CAPACITY) + : this(factory, null, null, capacity) { } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - protected override T? TryRelease() - { - var p = Pool; - if (p is null) return null; - p.TryDequeue(out var item); - return item; - } + /* + * NOTE: ConcurrentQueue is very fast and will perform quite well without using the 'Pocket' feature. + * Benchmarking reveals that mixed read/writes (what really matters) are still faster with the pocket enabled so best to keep it so. + */ + [MethodImpl(MethodImplOptions.AggressiveInlining)] + protected override bool Receive(T item) + { + var p = Pool; + if (p is null) return false; + p.Enqueue(item); // It's possible that the count could exceed MaxSize here, but the risk is negligble as a few over the limit won't hurt. + return true; } - public static class ConcurrentQueueObjectPool + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + protected override T? TryRelease() { - public static ConcurrentQueueObjectPool Create(Func factory, int capacity = Constants.DEFAULT_CAPACITY) - where T : class - { - return new ConcurrentQueueObjectPool(factory, capacity); - } + var p = Pool; + if (p is null) return null; + p.TryDequeue(out var item); + return item; + } + +} +public static class ConcurrentQueueObjectPool +{ + public static ConcurrentQueueObjectPool Create(Func factory, int capacity = Constants.DEFAULT_CAPACITY) + where T : class => new(factory, capacity); - public static ConcurrentQueueObjectPool Create(int capacity = Constants.DEFAULT_CAPACITY) - where T : class, new() - { - return Create(() => new T(), capacity); - } + public static ConcurrentQueueObjectPool Create(int capacity = Constants.DEFAULT_CAPACITY) + where T : class, new() => Create(() => new T(), capacity); - public static ConcurrentQueueObjectPool CreateAutoRecycle(Func factory, int capacity = Constants.DEFAULT_CAPACITY) - where T : class, IRecyclable - { - return new ConcurrentQueueObjectPool(factory, Recycler.Recycle, null, capacity); - } + public static ConcurrentQueueObjectPool CreateAutoRecycle(Func factory, int capacity = Constants.DEFAULT_CAPACITY) + where T : class, IRecyclable => new(factory, Recycler.Recycle, null, capacity); - public static ConcurrentQueueObjectPool CreateAutoRecycle(int capacity = Constants.DEFAULT_CAPACITY) - where T : class, IRecyclable, new() - { - return CreateAutoRecycle(() => new T(), capacity); - } + public static ConcurrentQueueObjectPool CreateAutoRecycle(int capacity = Constants.DEFAULT_CAPACITY) + where T : class, IRecyclable, new() => CreateAutoRecycle(() => new T(), capacity); - public static ConcurrentQueueObjectPool CreateAutoDisposal(Func factory, int capacity = Constants.DEFAULT_CAPACITY) - where T : class, IDisposable - { - return new ConcurrentQueueObjectPool(factory, null, d => d.Dispose(), capacity); - } + public static ConcurrentQueueObjectPool CreateAutoDisposal(Func factory, int capacity = Constants.DEFAULT_CAPACITY) + where T : class, IDisposable => new(factory, null, d => d.Dispose(), capacity); - public static ConcurrentQueueObjectPool CreateAutoDisposal(int capacity = Constants.DEFAULT_CAPACITY) - where T : class, IDisposable, new() - { - return CreateAutoDisposal(() => new T(), capacity); - } + public static ConcurrentQueueObjectPool CreateAutoDisposal(int capacity = Constants.DEFAULT_CAPACITY) + where T : class, IDisposable, new() => CreateAutoDisposal(() => new T(), capacity); - } } diff --git a/source/Collection/ConcurrentQueueObjectPoolSlim.cs b/source/Collection/ConcurrentQueueObjectPoolSlim.cs index 82db7b2..7ddc6a6 100644 --- a/source/Collection/ConcurrentQueueObjectPoolSlim.cs +++ b/source/Collection/ConcurrentQueueObjectPoolSlim.cs @@ -2,93 +2,43 @@ using System.Collections.Concurrent; using System.Runtime.CompilerServices; -namespace Open.Disposable -{ - public sealed class ConcurrentQueueObjectPoolSlim : CountTrackedObjectPoolBase - where T : class - { - - public ConcurrentQueueObjectPoolSlim(Func factory, Action? recycler, Action? disposer, int capacity = DEFAULT_CAPACITY - 10) - : base(factory, recycler, disposer, capacity) - { - Pool = new(); - } - - public ConcurrentQueueObjectPoolSlim(Func factory, int capacity = DEFAULT_CAPACITY - 10) - : this(factory, null, null, capacity) - { - - } - - ConcurrentQueue Pool; - - /* - * NOTE: ConcurrentQueue is very fast and will perform quite well without using the 'Pocket' feature. - * Benchmarking reveals that mixed read/writes (what really matters) are still faster with the pocket enabled so best to keep it so. - */ +namespace Open.Disposable; - [MethodImpl(MethodImplOptions.AggressiveInlining)] - protected override bool Receive(T item) - { - var p = Pool; - if (p is null) return false; - p.Enqueue(item); // It's possible that the count could exceed MaxSize here, but the risk is negligble as a few over the limit won't hurt. - return true; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - protected override T? TryRelease() - { - var p = Pool; - if (p is null) return null; - p.TryDequeue(out var item); - return item; - } - - protected override void OnDispose() - { - base.OnDispose(); - Pool = null!; - } +public sealed class ConcurrentQueueObjectPoolSlim + : ConcurrentQueueObjectPoolSlimBase + where T : class +{ + public ConcurrentQueueObjectPoolSlim( + Func factory, + Action? recycler, + Action? disposer, + int capacity = DEFAULT_CAPACITY - 10) + : base(factory, recycler, disposer, capacity) { } + + public ConcurrentQueueObjectPoolSlim( + Func factory, + int capacity = DEFAULT_CAPACITY - 10) + : this(factory, null, null, capacity) { } +} - } - public static class ConcurrentQueueObjectPoolSlim - { - public static ConcurrentQueueObjectPoolSlim Create(Func factory, int capacity = Constants.DEFAULT_CAPACITY) - where T : class - { - return new ConcurrentQueueObjectPoolSlim(factory, capacity); - } +public static class ConcurrentQueueObjectPoolSlim +{ + public static ConcurrentQueueObjectPoolSlim Create(Func factory, int capacity = Constants.DEFAULT_CAPACITY) + where T : class => new(factory, capacity); - public static ConcurrentQueueObjectPoolSlim Create(int capacity = Constants.DEFAULT_CAPACITY) - where T : class, new() - { - return Create(() => new T(), capacity); - } + public static ConcurrentQueueObjectPoolSlim Create(int capacity = Constants.DEFAULT_CAPACITY) + where T : class, new() => Create(() => new T(), capacity); - public static ConcurrentQueueObjectPoolSlim CreateAutoRecycle(Func factory, int capacity = Constants.DEFAULT_CAPACITY) - where T : class, IRecyclable - { - return new ConcurrentQueueObjectPoolSlim(factory, Recycler.Recycle, null, capacity); - } + public static ConcurrentQueueObjectPoolSlim CreateAutoRecycle(Func factory, int capacity = Constants.DEFAULT_CAPACITY) + where T : class, IRecyclable => new(factory, Recycler.Recycle, null, capacity); - public static ConcurrentQueueObjectPoolSlim CreateAutoRecycle(int capacity = Constants.DEFAULT_CAPACITY) - where T : class, IRecyclable, new() - { - return CreateAutoRecycle(() => new T(), capacity); - } + public static ConcurrentQueueObjectPoolSlim CreateAutoRecycle(int capacity = Constants.DEFAULT_CAPACITY) + where T : class, IRecyclable, new() => CreateAutoRecycle(() => new T(), capacity); - public static ConcurrentQueueObjectPoolSlim CreateAutoDisposal(Func factory, int capacity = Constants.DEFAULT_CAPACITY) - where T : class, IDisposable - { - return new ConcurrentQueueObjectPoolSlim(factory, null, d => d.Dispose(), capacity); - } + public static ConcurrentQueueObjectPoolSlim CreateAutoDisposal(Func factory, int capacity = Constants.DEFAULT_CAPACITY) + where T : class, IDisposable => new(factory, null, d => d.Dispose(), capacity); - public static ConcurrentQueueObjectPoolSlim CreateAutoDisposal(int capacity = Constants.DEFAULT_CAPACITY) - where T : class, IDisposable, new() - { - return CreateAutoDisposal(() => new T(), capacity); - } + public static ConcurrentQueueObjectPoolSlim CreateAutoDisposal(int capacity = Constants.DEFAULT_CAPACITY) + where T : class, IDisposable, new() => CreateAutoDisposal(() => new T(), capacity); - } } diff --git a/source/Collection/ConcurrentQueueObjectPoolSlimBase.cs b/source/Collection/ConcurrentQueueObjectPoolSlimBase.cs new file mode 100644 index 0000000..6bbd402 --- /dev/null +++ b/source/Collection/ConcurrentQueueObjectPoolSlimBase.cs @@ -0,0 +1,50 @@ +using System; +using System.Collections.Concurrent; +using System.Runtime.CompilerServices; + +namespace Open.Disposable; + +public abstract class ConcurrentQueueObjectPoolSlimBase + : CountTrackedObjectPoolBase + where T : class +{ + protected ConcurrentQueueObjectPoolSlimBase( + Func factory, + Action? recycler, + Action? disposer, + int capacity = DEFAULT_CAPACITY - 10) + : base(factory, recycler, disposer, capacity) + => Pool = new(); + + ConcurrentQueue Pool; + + /* + * NOTE: ConcurrentQueue is very fast and will perform quite well without using the 'Pocket' feature. + * Benchmarking reveals that mixed read/writes (what really matters) are still faster with the pocket enabled so best to keep it so. + */ + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + protected override bool Receive(T item) + { + var p = Pool; + if (p is null) return false; + p.Enqueue(item); // It's possible that the count could exceed MaxSize here, but the risk is negligble as a few over the limit won't hurt. + return true; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + protected override T? TryRelease() + { + var p = Pool; + if (p is null) return null; + p.TryDequeue(out var item); + return item; + } + + protected override void OnDispose() + { + base.OnDispose(); + Pool = null!; + } + +} diff --git a/source/Collection/ConcurrentStackObjectPool.cs b/source/Collection/ConcurrentStackObjectPool.cs index e38d9b3..356b857 100644 --- a/source/Collection/ConcurrentStackObjectPool.cs +++ b/source/Collection/ConcurrentStackObjectPool.cs @@ -1,78 +1,59 @@ using System; using System.Collections.Concurrent; -namespace Open.Disposable -{ - public sealed class ConcurrentStackObjectPool : TrimmableCollectionObjectPoolBase> - where T : class - { - - public ConcurrentStackObjectPool(Func factory, Action? recycler, Action? disposer, int capacity = DEFAULT_CAPACITY) - : base(new ConcurrentStack(), factory, recycler, disposer, capacity) - { - } +namespace Open.Disposable; - public ConcurrentStackObjectPool(Func factory, int capacity = DEFAULT_CAPACITY) - : this(factory, null, null, capacity) - { - - } +public sealed class ConcurrentStackObjectPool + : TrimmableCollectionObjectPoolBase> + where T : class +{ - protected override bool Receive(T item) - { - var p = Pool; - if (p is null) return false; - p.Push(item); // It's possible that the count could exceed MaxSize here, but the risk is negligble as a few over the limit won't hurt. - return true; - } + public ConcurrentStackObjectPool( + Func factory, + Action? recycler, + Action? disposer, + int capacity = DEFAULT_CAPACITY) + : base(new ConcurrentStack(), factory, recycler, disposer, capacity) { } - protected override T? TryRelease() - { - var p = Pool; - if (p is null) return null; - p.TryPop(out var item); - return item; - } + public ConcurrentStackObjectPool(Func factory, int capacity = DEFAULT_CAPACITY) + : this(factory, null, null, capacity) { } + protected override bool Receive(T item) + { + var p = Pool; + if (p is null) return false; + p.Push(item); // It's possible that the count could exceed MaxSize here, but the risk is negligble as a few over the limit won't hurt. + return true; } - public static class ConcurrentStackObjectPool + protected override T? TryRelease() { - public static ConcurrentStackObjectPool Create(Func factory, int capacity = Constants.DEFAULT_CAPACITY) - where T : class - { - return new ConcurrentStackObjectPool(factory, capacity); - } + var p = Pool; + if (p is null) return null; + p.TryPop(out var item); + return item; + } + +} - public static ConcurrentStackObjectPool Create(int capacity = Constants.DEFAULT_CAPACITY) - where T : class, new() - { - return Create(() => new T(), capacity); - } +public static class ConcurrentStackObjectPool +{ + public static ConcurrentStackObjectPool Create(Func factory, int capacity = Constants.DEFAULT_CAPACITY) + where T : class => new(factory, capacity); - public static ConcurrentStackObjectPool CreateAutoRecycle(Func factory, int capacity = Constants.DEFAULT_CAPACITY) - where T : class, IRecyclable - { - return new ConcurrentStackObjectPool(factory, Recycler.Recycle, null, capacity); - } + public static ConcurrentStackObjectPool Create(int capacity = Constants.DEFAULT_CAPACITY) + where T : class, new() => Create(() => new T(), capacity); - public static ConcurrentStackObjectPool CreateAutoRecycle(int capacity = Constants.DEFAULT_CAPACITY) - where T : class, IRecyclable, new() - { - return CreateAutoRecycle(() => new T(), capacity); - } + public static ConcurrentStackObjectPool CreateAutoRecycle(Func factory, int capacity = Constants.DEFAULT_CAPACITY) + where T : class, IRecyclable => new(factory, Recycler.Recycle, null, capacity); - public static ConcurrentStackObjectPool CreateAutoDisposal(Func factory, int capacity = Constants.DEFAULT_CAPACITY) - where T : class, IDisposable - { - return new ConcurrentStackObjectPool(factory, null, d => d.Dispose(), capacity); - } + public static ConcurrentStackObjectPool CreateAutoRecycle(int capacity = Constants.DEFAULT_CAPACITY) + where T : class, IRecyclable, new() => CreateAutoRecycle(() => new T(), capacity); - public static ConcurrentStackObjectPool CreateAutoDisposal(int capacity = Constants.DEFAULT_CAPACITY) - where T : class, IDisposable, new() - { - return CreateAutoDisposal(() => new T(), capacity); - } + public static ConcurrentStackObjectPool CreateAutoDisposal(Func factory, int capacity = Constants.DEFAULT_CAPACITY) + where T : class, IDisposable => new(factory, null, d => d.Dispose(), capacity); + + public static ConcurrentStackObjectPool CreateAutoDisposal(int capacity = Constants.DEFAULT_CAPACITY) + where T : class, IDisposable, new() => CreateAutoDisposal(() => new T(), capacity); - } } diff --git a/source/Collection/QueueObjectPool.cs b/source/Collection/QueueObjectPool.cs index 2c64376..784f2cc 100644 --- a/source/Collection/QueueObjectPool.cs +++ b/source/Collection/QueueObjectPool.cs @@ -1,94 +1,77 @@ using System; using System.Collections.Generic; -namespace Open.Disposable +namespace Open.Disposable; + +public sealed class QueueObjectPool + : TrimmableCollectionObjectPoolBase> + where T : class { - public sealed class QueueObjectPool : TrimmableCollectionObjectPoolBase> - where T : class + public QueueObjectPool( + Func factory, + Action? recycler, + Action? disposer, + int capacity = DEFAULT_CAPACITY) + : base( + new Queue(Math.Min(DEFAULT_CAPACITY, capacity)) /* Very very slight speed improvment when capacity is initially set. */, + factory, recycler, disposer, capacity, false) + { } + + public QueueObjectPool( + Func factory, + int capacity = DEFAULT_CAPACITY) + : this(factory, null, null, capacity) { } + + protected override bool Receive(T item) { - - public QueueObjectPool(Func factory, Action? recycler, Action? disposer, int capacity = DEFAULT_CAPACITY) - : base( - new Queue(Math.Min(DEFAULT_CAPACITY, capacity)) /* Very very slight speed improvment when capacity is initially set. */, - factory, recycler, disposer, capacity, false) - { - } - - public QueueObjectPool(Func factory, int capacity = DEFAULT_CAPACITY) - : this(factory, null, null, capacity) + var p = Pool; + if (p is not null) { - + // It's possible that the count could exceed MaxSize here, but the risk is negligble as a few over the limit won't hurt. + // The lock operation should be quick enough to not pile up too many items. + lock (p) p.Enqueue(item); + return true; } - protected override bool Receive(T item) - { - var p = Pool; - if (p is not null) - { - // It's possible that the count could exceed MaxSize here, but the risk is negligble as a few over the limit won't hurt. - // The lock operation should be quick enough to not pile up too many items. - lock (p) p.Enqueue(item); - return true; - } - - return false; - } + return false; + } - protected override T? TryRelease() + protected override T? TryRelease() + { + var p = Pool; + if (p is not null && p.Count != 0) { - var p = Pool; - if (p is not null && p.Count != 0) + lock (p) { - lock (p) - { - if (p.Count != 0) - return p.Dequeue(); - } - + if (p.Count != 0) + return p.Dequeue(); } - return null; } + return null; } - public static class QueueObjectPool - { - public static QueueObjectPool Create(Func factory, int capacity = Constants.DEFAULT_CAPACITY) - where T : class - { - return new QueueObjectPool(factory, capacity); - } +} - public static QueueObjectPool Create(int capacity = Constants.DEFAULT_CAPACITY) - where T : class, new() - { - return Create(() => new T(), capacity); - } +public static class QueueObjectPool +{ + public static QueueObjectPool Create(Func factory, int capacity = Constants.DEFAULT_CAPACITY) + where T : class => new(factory, capacity); - public static QueueObjectPool CreateAutoRecycle(Func factory, int capacity = Constants.DEFAULT_CAPACITY) - where T : class, IRecyclable - { - return new QueueObjectPool(factory, Recycler.Recycle, null, capacity); - } + public static QueueObjectPool Create(int capacity = Constants.DEFAULT_CAPACITY) + where T : class, new() => Create(() => new T(), capacity); - public static QueueObjectPool CreateAutoRecycle(int capacity = Constants.DEFAULT_CAPACITY) - where T : class, IRecyclable, new() - { - return CreateAutoRecycle(() => new T(), capacity); - } + public static QueueObjectPool CreateAutoRecycle(Func factory, int capacity = Constants.DEFAULT_CAPACITY) + where T : class, IRecyclable => new(factory, Recycler.Recycle, null, capacity); - public static QueueObjectPool CreateAutoDisposal(Func factory, int capacity = Constants.DEFAULT_CAPACITY) - where T : class, IDisposable - { - return new QueueObjectPool(factory, null, d => d.Dispose(), capacity); - } + public static QueueObjectPool CreateAutoRecycle(int capacity = Constants.DEFAULT_CAPACITY) + where T : class, IRecyclable, new() => CreateAutoRecycle(() => new T(), capacity); - public static QueueObjectPool CreateAutoDisposal(int capacity = Constants.DEFAULT_CAPACITY) - where T : class, IDisposable, new() - { - return CreateAutoDisposal(() => new T(), capacity); - } + public static QueueObjectPool CreateAutoDisposal(Func factory, int capacity = Constants.DEFAULT_CAPACITY) + where T : class, IDisposable => new(factory, null, d => d.Dispose(), capacity); + + public static QueueObjectPool CreateAutoDisposal(int capacity = Constants.DEFAULT_CAPACITY) + where T : class, IDisposable, new() => CreateAutoDisposal(() => new T(), capacity); - } } diff --git a/source/Collection/StackObjectPool.cs b/source/Collection/StackObjectPool.cs index a17244e..aa07249 100644 --- a/source/Collection/StackObjectPool.cs +++ b/source/Collection/StackObjectPool.cs @@ -1,94 +1,76 @@ using System; using System.Collections.Generic; -namespace Open.Disposable +namespace Open.Disposable; + +public sealed class StackObjectPool + : TrimmableCollectionObjectPoolBase> + where T : class { - public sealed class StackObjectPool : TrimmableCollectionObjectPoolBase> - where T : class - { - public StackObjectPool(Func factory, Action? recycler, Action? disposer, int capacity = DEFAULT_CAPACITY) - : base( - new Stack(Math.Min(DEFAULT_CAPACITY, capacity)) /* Very very slight speed improvment when capacity is set. */, - factory, recycler, disposer, capacity, false) + public StackObjectPool( + Func factory, + Action? recycler, + Action? disposer, + int capacity = DEFAULT_CAPACITY) + : base( + new Stack(Math.Min(DEFAULT_CAPACITY, capacity)) /* Very very slight speed improvment when capacity is set. */, + factory, recycler, disposer, capacity, false) { } + + public StackObjectPool( + Func factory, + int capacity = DEFAULT_CAPACITY) + : this(factory, null, null, capacity) { } + + protected override bool Receive(T item) + { + var p = Pool; + if (p is not null) { + // It's possible that the count could exceed MaxSize here, but the risk is negligble as a few over the limit won't hurt. + // The lock operation should be quick enough to not pile up too many items. + lock (p) p.Push(item); + return true; } - public StackObjectPool(Func factory, int capacity = DEFAULT_CAPACITY) - : this(factory, null, null, capacity) - { + return false; + } - } + protected override T? TryRelease() + { + var p = Pool; + if (p is null || p.Count == 0) + return null; - protected override bool Receive(T item) + lock (p) { - var p = Pool; - if (p is not null) - { - // It's possible that the count could exceed MaxSize here, but the risk is negligble as a few over the limit won't hurt. - // The lock operation should be quick enough to not pile up too many items. - lock (p) p.Push(item); - return true; - } - - return false; + if (p.Count != 0) + return p.Pop(); } - protected override T? TryRelease() - { - var p = Pool; - if (p is not null && p.Count != 0) - { - lock (p) - { - if (p.Count != 0) - return p.Pop(); - } - - } - - return null; - } + return null; } +} - public static class StackObjectPool - { - public static StackObjectPool Create(Func factory, int capacity = Constants.DEFAULT_CAPACITY) - where T : class - { - return new StackObjectPool(factory, capacity); - } +public static class StackObjectPool +{ + public static StackObjectPool Create(Func factory, int capacity = Constants.DEFAULT_CAPACITY) + where T : class => new(factory, capacity); - public static StackObjectPool Create(int capacity = Constants.DEFAULT_CAPACITY) - where T : class, new() - { - return Create(() => new T(), capacity); - } + public static StackObjectPool Create(int capacity = Constants.DEFAULT_CAPACITY) + where T : class, new() => Create(() => new T(), capacity); - public static StackObjectPool CreateAutoRecycle(Func factory, int capacity = Constants.DEFAULT_CAPACITY) - where T : class, IRecyclable - { - return new StackObjectPool(factory, Recycler.Recycle, null, capacity); - } + public static StackObjectPool CreateAutoRecycle(Func factory, int capacity = Constants.DEFAULT_CAPACITY) + where T : class, IRecyclable => new(factory, Recycler.Recycle, null, capacity); - public static StackObjectPool CreateAutoRecycle(int capacity = Constants.DEFAULT_CAPACITY) - where T : class, IRecyclable, new() - { - return CreateAutoRecycle(() => new T(), capacity); - } + public static StackObjectPool CreateAutoRecycle(int capacity = Constants.DEFAULT_CAPACITY) + where T : class, IRecyclable, new() => CreateAutoRecycle(() => new T(), capacity); - public static StackObjectPool CreateAutoDisposal(Func factory, int capacity = Constants.DEFAULT_CAPACITY) - where T : class, IDisposable - { - return new StackObjectPool(factory, null, d => d.Dispose(), capacity); - } + public static StackObjectPool CreateAutoDisposal(Func factory, int capacity = Constants.DEFAULT_CAPACITY) + where T : class, IDisposable => new(factory, null, d => d.Dispose(), capacity); - public static StackObjectPool CreateAutoDisposal(int capacity = Constants.DEFAULT_CAPACITY) - where T : class, IDisposable, new() - { - return CreateAutoDisposal(() => new T(), capacity); - } + public static StackObjectPool CreateAutoDisposal(int capacity = Constants.DEFAULT_CAPACITY) + where T : class, IDisposable, new() => CreateAutoDisposal(() => new T(), capacity); - } } diff --git a/source/Constants.cs b/source/Constants.cs index 5ed6e77..343a47e 100644 --- a/source/Constants.cs +++ b/source/Constants.cs @@ -1,7 +1,6 @@ -namespace Open.Disposable +namespace Open.Disposable; + +internal static class Constants { - internal static class Constants - { - internal const int DEFAULT_CAPACITY = 64; // Should accomodate all object pools without decreasing their effectiveness. - } + internal const int DEFAULT_CAPACITY = 64; // Should accomodate all object pools without decreasing their effectiveness. } diff --git a/source/CountTrackedObjectPoolBase.cs b/source/CountTrackedObjectPoolBase.cs index 6faffba..1e83b36 100644 --- a/source/CountTrackedObjectPoolBase.cs +++ b/source/CountTrackedObjectPoolBase.cs @@ -2,35 +2,29 @@ using System.Runtime.CompilerServices; using System.Threading; -namespace Open.Disposable -{ - public abstract class CountTrackedObjectPoolBase : ObjectPoolBase - where T : class - { - - protected CountTrackedObjectPoolBase(Func factory, Action? recycler, Action? disposer, int capacity = DEFAULT_CAPACITY) - : base(factory, recycler, disposer, capacity) - { +namespace Open.Disposable; - } +public abstract class CountTrackedObjectPoolBase + : ObjectPoolBase + where T : class +{ + protected CountTrackedObjectPoolBase( + Func factory, + Action? recycler, + Action? disposer, + int capacity = DEFAULT_CAPACITY) + : base(factory, recycler, disposer, capacity) { } - int _count; - /// - public override int Count => _count; + int _count; + /// + public override int Count => _count; - protected override bool CanReceive => _count < MaxSize; + protected override bool CanReceive => _count < MaxSize; - [MethodImpl(MethodImplOptions.AggressiveInlining)] - protected override void OnReleased() - { - Interlocked.Decrement(ref _count); - } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + protected override void OnReleased() => Interlocked.Decrement(ref _count); - [MethodImpl(MethodImplOptions.AggressiveInlining)] - protected override void OnReceived() - { - Interlocked.Increment(ref _count); - } - } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + protected override void OnReceived() => Interlocked.Increment(ref _count); } diff --git a/source/IObjectPool.cs b/source/IObjectPool.cs index 3fcdc3d..e14f461 100644 --- a/source/IObjectPool.cs +++ b/source/IObjectPool.cs @@ -1,186 +1,184 @@ using System; using System.Collections.Generic; -using System.Diagnostics.CodeAnalysis; using System.Diagnostics.Contracts; using System.Threading.Tasks; -namespace Open.Disposable +namespace Open.Disposable; + +public interface IObjectPool + : IDisposable + where T : class { - public interface IObjectPool : IDisposable - where T : class - { - /// - /// Defines the maximum at which the pool can grow. - /// - int Capacity { get; } - - /// - /// Directly calls the underlying factory that generates the items. (No pool interaction.) - /// - T Generate(); - - /// - /// Receives an item and adds it to the pool. Ignores null references. - /// WARNING: The item is considered 'dead' but resurrectable so be sure not to hold on to the item's reference. - /// - /// The item to give up to the pool. - void Give(T item); - - /// - /// If the pool has an item currently avaialable, removes it from the pool and provides it as the out parameter. - /// - /// The item to return if available. Will be null if none avaialable. - /// True if an item is provided. + /// + /// Defines the maximum at which the pool can grow. + /// + int Capacity { get; } + + /// + /// Directly calls the underlying factory that generates the items. (No pool interaction.) + /// + T Generate(); + + /// + /// Receives an item and adds it to the pool. Ignores null references.
+ /// WARNING: The item is considered 'dead' but resurrectable so be sure not to hold on to the item's reference. + ///
+ /// The item to give up to the pool. + void Give(T item); + + /// + /// If the pool has an item currently avaialable, removes it from the pool and provides it as the out parameter. + /// + /// The item to return if available. Will be null if none avaialable. + /// True if an item is provided. #if NETSTANDARD2_1 - bool TryTake([NotNullWhen(true)] out T? item); + bool TryTake([System.Diagnostics.CodeAnalysis.NotNullWhen(true)] out T? item); #else - bool TryTake(out T? item); + bool TryTake(out T? item); #endif - /// - /// If the pool has an item currently avaialable, removes it from the pool and returns it. - /// - /// The item to return if available. Will be null if none avaialable. - T? TryTake(); - - - /// - /// If the pool has an item currently avaialable, removes it from the pool and returns it. - /// If none is available, it generates one. - /// - /// An item removed from the pool or generated. Should never be null. - T Take(); - - /// - /// Total number of items in the pool. - /// Depending on the implementation could be an O(n) operation and should only be used for debugging. - /// - int Count { get; } + /// + /// If the pool has an item currently avaialable, removes it from the pool and returns it. + /// + /// The item to return if available. Will be null if none avaialable. + T? TryTake(); + + + /// + /// If the pool has an item currently avaialable, removes it from the pool and returns it. + /// If none is available, it generates one. + /// + /// An item removed from the pool or generated. Should never be null. + T Take(); + + /// + /// Total number of items in the pool.
+ /// Depending on the implementation could be an O(n) operation and should only be used for debugging. + ///
+ int Count { get; } +} + +public static class ObjectPoolExtensions +{ + /// + /// Receives items and iteratively adds them to the pool.
+ /// WARNING: These items are considered 'dead' but resurrectable so be sure not to hold on to their reference. + ///
+ /// The pool to give to. + /// The items to give up to the pool. + public static void Give(this IObjectPool target, IEnumerable items) + where T : class + { + if (target is null) throw new ArgumentNullException(nameof(target)); + Contract.EndContractBlock(); + + if (items is null) return; + foreach (var i in items) + target.Give(i); } - public static class ObjectPoolExtensions + /// + /// Receives items and iteratively adds them to the pool.
+ /// WARNING: These items are considered 'dead' but resurrectable so be sure not to hold on to their reference. + ///
+ /// The pool to give to. + /// The first item to give up to the pool. + /// The second item to give up to the pool. + /// The remaining items to give up to the pool. + public static void Give(this IObjectPool target, T item1, T item2, params T[] items) + where T : class + { + if (target is null) throw new ArgumentNullException(nameof(target)); + Contract.EndContractBlock(); + + target.Give(item1); + target.Give(item2); + target.Give(items); + } + + /// + /// Provides a disposable RecycleHelper that contains an item from the pool.
+ /// Will be returned to the pool once .Dispose() is called. + ///
+ /// The object type from the pool. + /// The object pool. + /// A RecycleHelper containing an item from the pool. + public static RecycleHelper Rent(this IObjectPool source) + where T : class => new(source); + + /// + /// Provides an item from the pool and returns it when the action sucessfully completes. + /// + /// The object type from the pool. + /// The object pool. + /// The action to execute. + public static void Rent(this IObjectPool source, Action action) + where T : class + { + if (source is null) throw new ArgumentNullException(nameof(source)); + if (action is null) throw new ArgumentNullException(nameof(action)); + Contract.EndContractBlock(); + + var item = source.Take(); + action(item); + source.Give(item); + } + + /// + /// Provides an item from the pool and returns it when the action sucessfully completes. + /// + /// The object type from the pool. + /// The object pool. + /// The action to execute. + /// The value from the action. + public static TResult Rent(this IObjectPool source, Func action) + where T : class + { + if (source is null) throw new ArgumentNullException(nameof(source)); + if (action is null) throw new ArgumentNullException(nameof(action)); + Contract.EndContractBlock(); + + var item = source.Take(); + var result = action(item); + source.Give(item); + return result; + } + + /// + /// Provides an item from the pool and returns it when the action sucessfully completes. + /// + /// The object type from the pool. + /// The object pool. + /// The action to execute. + public static async ValueTask RentAsync(this IObjectPool source, Func action) + where T : class + { + if (source is null) throw new ArgumentNullException(nameof(source)); + if (action is null) throw new ArgumentNullException(nameof(action)); + Contract.EndContractBlock(); + + var item = source.Take(); + await action(item).ConfigureAwait(false); + source.Give(item); + } + + /// + /// Provides an item from the pool and returns it when the action sucessfully completes. + /// + /// The object type from the pool. + /// The object pool. + /// The action to execute. + /// The value from the action. + public static async ValueTask RentAsync(this IObjectPool source, Func> action) + where T : class { - /// - /// Receives items and iteratively adds them to the pool. - /// WARNING: These items are considered 'dead' but resurrectable so be sure not to hold on to their reference. - /// - /// The pool to give to. - /// The items to give up to the pool. - public static void Give(this IObjectPool target, IEnumerable items) - where T : class - { - if (target is null) throw new ArgumentNullException(nameof(target)); - Contract.EndContractBlock(); - - if (items is null) return; - foreach (var i in items) - target.Give(i); - } - - /// - /// Receives items and iteratively adds them to the pool. - /// WARNING: These items are considered 'dead' but resurrectable so be sure not to hold on to their reference. - /// - /// The pool to give to. - /// The first item to give up to the pool. - /// The second item to give up to the pool. - /// The remaining items to give up to the pool. - public static void Give(this IObjectPool target, T item1, T item2, params T[] items) - where T : class - { - if (target is null) throw new ArgumentNullException(nameof(target)); - Contract.EndContractBlock(); - - target.Give(item1); - target.Give(item2); - target.Give(items); - } - - /// - /// Provides a disposable RecycleHelper that contains an item from the pool. - /// Will be returned to the pool once .Dispose() is called. - /// - /// The object type from the pool. - /// The object pool. - /// A RecycleHelper containing an item from the pool. - public static RecycleHelper Rent(this IObjectPool source) - where T : class - => new(source); - - /// - /// Provides an item from the pool and returns it when the action sucessfully completes. - /// - /// The object type from the pool. - /// The object pool. - /// The action to execute. - public static void Rent(this IObjectPool source, Action action) - where T : class - { - if (source is null) throw new ArgumentNullException(nameof(source)); - if (action is null) throw new ArgumentNullException(nameof(action)); - Contract.EndContractBlock(); - - var item = source.Take(); - action(item); - source.Give(item); - } - - /// - /// Provides an item from the pool and returns it when the action sucessfully completes. - /// - /// The object type from the pool. - /// The object pool. - /// The action to execute. - /// The value from the action. - public static TResult Rent(this IObjectPool source, Func action) - where T : class - { - if (source is null) throw new ArgumentNullException(nameof(source)); - if (action is null) throw new ArgumentNullException(nameof(action)); - Contract.EndContractBlock(); - - var item = source.Take(); - var result = action(item); - source.Give(item); - return result; - } - - /// - /// Provides an item from the pool and returns it when the action sucessfully completes. - /// - /// The object type from the pool. - /// The object pool. - /// The action to execute. - public static async ValueTask RentAsync(this IObjectPool source, Func action) - where T : class - { - if (source is null) throw new ArgumentNullException(nameof(source)); - if (action is null) throw new ArgumentNullException(nameof(action)); - Contract.EndContractBlock(); - - var item = source.Take(); - await action(item).ConfigureAwait(false); - source.Give(item); - } - - /// - /// Provides an item from the pool and returns it when the action sucessfully completes. - /// - /// The object type from the pool. - /// The object pool. - /// The action to execute. - /// The value from the action. - public static async ValueTask RentAsync(this IObjectPool source, Func> action) - where T : class - { - if (source is null) throw new ArgumentNullException(nameof(source)); - if (action is null) throw new ArgumentNullException(nameof(action)); - Contract.EndContractBlock(); - - var item = source.Take(); - var result = await action(item).ConfigureAwait(false); - source.Give(item); - return result; - } + if (source is null) throw new ArgumentNullException(nameof(source)); + if (action is null) throw new ArgumentNullException(nameof(action)); + Contract.EndContractBlock(); + + var item = source.Take(); + var result = await action(item).ConfigureAwait(false); + source.Give(item); + return result; } } diff --git a/source/IRecyclable.cs b/source/IRecyclable.cs index 3de903d..f192ea1 100644 --- a/source/IRecyclable.cs +++ b/source/IRecyclable.cs @@ -1,10 +1,9 @@ -namespace Open.Disposable +namespace Open.Disposable; + +public interface IRecyclable { - public interface IRecyclable - { - /// - /// Signals the item should be recycled. - /// - void Recycle(); - } + /// + /// Signals the item should be recycled. + /// + void Recycle(); } diff --git a/source/IRecycler.cs b/source/IRecycler.cs index 79a8e66..034309f 100644 --- a/source/IRecycler.cs +++ b/source/IRecycler.cs @@ -1,19 +1,19 @@ using System; using System.Threading.Tasks; -namespace Open.Disposable +namespace Open.Disposable; + +public interface IRecycler + : IDisposable + where T : class { - public interface IRecycler : IDisposable - where T : class - { - /// - /// Recycles the item. - /// - bool Recycle(T item); + /// + /// Recycles the item. + /// + bool Recycle(T item); - /// - /// Closes the recycling. No more should be recycled. - /// - Task Close(); - } + /// + /// Closes the recycling. No more should be recycled. + /// + Task Close(); } diff --git a/source/ITrimmableObjectPool.cs b/source/ITrimmableObjectPool.cs index 351fc7a..913dcf9 100644 --- a/source/ITrimmableObjectPool.cs +++ b/source/ITrimmableObjectPool.cs @@ -1,10 +1,9 @@ -namespace Open.Disposable +namespace Open.Disposable; + +public interface ITrimmableObjectPool { - public interface ITrimmableObjectPool - { - event ObjectPoolResizeEvent Received; - event ObjectPoolResizeEvent Released; + event ObjectPoolResizeEvent Received; + event ObjectPoolResizeEvent Released; - void TrimTo(int targetSize); - } + void TrimTo(int targetSize); } diff --git a/source/ObjectPoolAutoTrimmer.cs b/source/ObjectPoolAutoTrimmer.cs index e4d0c51..866f45c 100644 --- a/source/ObjectPoolAutoTrimmer.cs +++ b/source/ObjectPoolAutoTrimmer.cs @@ -2,88 +2,85 @@ using System; using System.Diagnostics.CodeAnalysis; -namespace Open.Disposable +namespace Open.Disposable; + +[SuppressMessage("Style", "IDE0044:Add readonly modifier", Justification = "Micro-optimization for retrieving this value as read-only is slightly slower")] +public class ObjectPoolAutoTrimmer + : DisposableBase { - [SuppressMessage("Style", "IDE0044:Add readonly modifier", Justification = "Micro-optimization for retrieving this value as read-only is slightly slower")] - public class ObjectPoolAutoTrimmer : DisposableBase - { - ITrimmableObjectPool _pool; - ActionRunner _trimmer; - - // Micro-optimization for retrieving this value as read-only is slightly slower. - ushort _trimmedSize; - TimeSpan _trimDelay; - - /// - /// Max size that trimming will allow. - /// - // ReSharper disable once NotAccessedField.Global - public readonly ushort TrimmedSize; - - /// - /// Time to wait/defer trimming. Default is 500 milliSeconds. - /// - // ReSharper disable once NotAccessedField.Global - public readonly TimeSpan TrimDelay; - - /// - /// Constructs an auto-trimming ObjectPool helper. - /// - /// The governable object pool to maintain. - /// The target size to limit to after a half second timeout. Allowing the pool to still grow to the max size until the trim occurs. - /// The amount of time to wait/defer trimming. - public ObjectPoolAutoTrimmer( - ushort trimmedSize, - ITrimmableObjectPool pool, - TimeSpan? trimDelay = null) - { - _pool = pool ?? throw new ArgumentNullException(nameof(pool)); + ITrimmableObjectPool _pool; + ActionRunner _trimmer; - if (pool is DisposableBase d) - { - if (d.WasDisposed) throw new ArgumentException("Cannot trim for an object pool that is already disposed."); - d.BeforeDispose += Pool_BeforeDispose; - } + // Micro-optimization for retrieving this value as read-only is slightly slower. + ushort _trimmedSize; + TimeSpan _trimDelay; - TrimmedSize = _trimmedSize = trimmedSize; - TrimDelay = _trimDelay = trimDelay ?? TimeSpan.FromMilliseconds(500); + /// + /// Max size that trimming will allow. + /// + // ReSharper disable once NotAccessedField.Global + public readonly ushort TrimmedSize; - _trimmer = new ActionRunner(TrimInternal); + /// + /// Time to wait/defer trimming. Default is 500 milliSeconds. + /// + // ReSharper disable once NotAccessedField.Global + public readonly TimeSpan TrimDelay; - pool.Received += Target_GivenTo; - pool.Released += Target_TakenFrom; - } + /// + /// Constructs an auto-trimming ObjectPool helper. + /// + /// The governable object pool to maintain. + /// The target size to limit to after a half second timeout. Allowing the pool to still grow to the max size until the trim occurs. + /// The amount of time to wait/defer trimming. + public ObjectPoolAutoTrimmer( + ushort trimmedSize, + ITrimmableObjectPool pool, + TimeSpan? trimDelay = null) + { + _pool = pool ?? throw new ArgumentNullException(nameof(pool)); - protected virtual void Target_GivenTo(int newSize) + if (pool is DisposableBase d) { - if (newSize >= 0 && newSize > _trimmedSize) - _trimmer?.Defer(_trimDelay, false); + if (d.WasDisposed) throw new ArgumentException("Cannot trim for an object pool that is already disposed."); + d.BeforeDispose += Pool_BeforeDispose; } - protected virtual void Target_TakenFrom(int newSize) - { - if (newSize >= 0 && newSize <= _trimmedSize) - _trimmer?.Cancel(); - } + TrimmedSize = _trimmedSize = trimmedSize; + TrimDelay = _trimDelay = trimDelay ?? TimeSpan.FromMilliseconds(500); - void Pool_BeforeDispose(object sender, EventArgs e) - { - Dispose(); - } + _trimmer = new ActionRunner(TrimInternal); - protected virtual void TrimInternal() - { + pool.Received += Target_GivenTo; + pool.Released += Target_TakenFrom; + } + + protected virtual void Target_GivenTo(int newSize) + { + if (newSize >= 0 && newSize > _trimmedSize) + _trimmer?.Defer(_trimDelay, false); + } + + protected virtual void Target_TakenFrom(int newSize) + { + if (newSize >= 0 && newSize <= _trimmedSize) _trimmer?.Cancel(); - _pool?.TrimTo(_trimmedSize); - } + } - protected override void OnDispose() - { - DisposeOf(ref _trimmer); + void Pool_BeforeDispose(object sender, EventArgs e) => Dispose(); - var target = Nullify(ref _pool); - target.Received -= Target_GivenTo; - target.Released -= Target_TakenFrom; - } + protected virtual void TrimInternal() + { + _trimmer?.Cancel(); + _pool?.TrimTo(_trimmedSize); + } + + protected override void OnDispose() + { + DisposeOf(ref _trimmer); + + var target = Nullify(ref _pool); + target.Received -= Target_GivenTo; + target.Released -= Target_TakenFrom; } } diff --git a/source/ObjectPoolBase.cs b/source/ObjectPoolBase.cs index 94b8f5a..e647357 100644 --- a/source/ObjectPoolBase.cs +++ b/source/ObjectPoolBase.cs @@ -1,137 +1,131 @@ using System; -using System.Diagnostics.CodeAnalysis; +using System.Diagnostics; using System.Runtime.CompilerServices; -namespace Open.Disposable +namespace Open.Disposable; + +public abstract class ObjectPoolBase + : DisposableBase, IObjectPool + where T : class { - public abstract class ObjectPoolBase : DisposableBase, IObjectPool - where T : class + protected const int DEFAULT_CAPACITY = Constants.DEFAULT_CAPACITY; + + protected ObjectPoolBase( + Func factory, + Action? recycler, + Action? disposer, + int capacity = DEFAULT_CAPACITY) + { + if (capacity < 1) + throw new ArgumentOutOfRangeException(nameof(capacity), capacity, "Must be at least 1."); + Factory = factory ?? throw new ArgumentNullException(nameof(factory)); + MaxSize = capacity; + Recycler = recycler; + OnDiscarded = disposer; + } + + // Not read-only due to quirk where read-only is slower than not. + protected int MaxSize; + public int Capacity => MaxSize; + + /// + /// Total number of items in the pool. + /// + /// + public abstract int Count { get; } + protected int PocketCount => Pocket.Value is null ? 0 : 1; + + protected readonly Action? Recycler; // Before entering the pool. + protected readonly Action? OnDiscarded; // When not able to be used. + + + // Read-only because if Take() is called after disposal, this still facilitates returing an object. + // Allow the GC to do the final cleanup after dispose. + protected readonly Func Factory; + + public T Generate() => Factory(); + + // ReSharper disable once UnassignedField.Global + protected ReferenceContainer Pocket; // Default struct constructs itself. + + #region Receive (.Give(T item)) + protected virtual bool CanReceive => true; // A default of true is acceptable, enabling the Receive method to do the actual deciding. + + protected bool PrepareToReceive(T item) + { + if (!CanReceive) return false; + + Debug.Assert(item is not null); + var r = Recycler; + if (r is null) return true; + + r(item!); + // Did the recycle take so long that the state changed? + return CanReceive; + } + + // Contract should be that no item can be null here. + protected abstract bool Receive(T item); + + protected virtual void OnReceived() + { + + } + + /// + public void Give(T item) { - protected const int DEFAULT_CAPACITY = Constants.DEFAULT_CAPACITY; - - protected ObjectPoolBase(Func factory, Action? recycler, Action? disposer, int capacity = DEFAULT_CAPACITY) - { - if (capacity < 1) - throw new ArgumentOutOfRangeException(nameof(capacity), capacity, "Must be at least 1."); - Factory = factory ?? throw new ArgumentNullException(nameof(factory)); - MaxSize = capacity; - Recycler = recycler; - OnDiscarded = disposer; - } - - // Not read-only due to quirk where read-only is slower than not. - protected int MaxSize; - public int Capacity => MaxSize; - - /// - /// Total number of items in the pool. - /// - /// - public abstract int Count { get; } - protected int PocketCount => Pocket.Value is null ? 0 : 1; - - protected readonly Action? Recycler; // Before entering the pool. - protected readonly Action? OnDiscarded; // When not able to be used. - - - // Read-only because if Take() is called after disposal, this still facilitates returing an object. - // Allow the GC to do the final cleanup after dispose. - protected readonly Func Factory; - - public T Generate() => Factory(); - - // ReSharper disable once UnassignedField.Global - protected ReferenceContainer Pocket; // Default struct constructs itself. - - #region Receive (.Give(T item)) - protected virtual bool CanReceive => true; // A default of true is acceptable, enabling the Receive method to do the actual deciding. - - protected bool PrepareToReceive(T item) - { - if (CanReceive && item is not null) - { - var r = Recycler; - if (r is not null) - { - r(item); - // Did the recycle take so long that the state changed? - if (!CanReceive) return false; - } - - return true; - } - - return false; - } - - // Contract should be that no item can be null here. - protected abstract bool Receive(T item); - - protected virtual void OnReceived() - { - - } - - /// - public void Give(T item) - { - if (PrepareToReceive(item) - && (SaveToPocket(item) || Receive(item))) - OnReceived(); - else - OnDiscarded?.Invoke(item); - } - #endregion - - #region Release (.Take()) - /// - public virtual T Take() - => TryTake() ?? Factory(); - - /// -#if NETSTANDARD2_1 - public bool TryTake([NotNullWhen(true)] out T? item) + if (item is null) return; + if (PrepareToReceive(item) + && (SaveToPocket(item) + || Receive(item))) + OnReceived(); + else + OnDiscarded?.Invoke(item); + } + #endregion + + #region Release (.Take()) + /// + public virtual T Take() => TryTake() ?? Factory(); + + /// +#if NETSTANDARD2_1_OR_GREATER + public bool TryTake([System.Diagnostics.CodeAnalysis.NotNullWhen(true)] out T? item) #else - public bool TryTake(out T? item) + public bool TryTake(out T? item) #endif - => (item = TryTake()) is not null; + => (item = TryTake()) is not null; - [MethodImpl(MethodImplOptions.AggressiveInlining)] - protected virtual bool SaveToPocket(T item) - => Pocket.TrySave(item); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + protected virtual bool SaveToPocket(T item) => Pocket.TrySave(item); - [MethodImpl(MethodImplOptions.AggressiveInlining)] - protected T? TakeFromPocket() - => Pocket.TryRetrieve(); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + protected T? TakeFromPocket() => Pocket.TryRetrieve(); + protected abstract T? TryRelease(); - protected abstract T? TryRelease(); - - /// - public T? TryTake() - { - var item = TakeFromPocket() ?? TryRelease(); - if (item is not null) OnReleased(); - return item; - } + /// + public T? TryTake() + { + var item = TakeFromPocket() ?? TryRelease(); + if (item is not null) OnReleased(); + return item; + } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - protected virtual void OnReleased() - { - } - #endregion + [MethodImpl(MethodImplOptions.AggressiveInlining)] + protected virtual void OnReleased() + { + } + #endregion - protected override void OnBeforeDispose() - { - MaxSize = 0; - } + protected override void OnBeforeDispose() => MaxSize = 0; - protected override void OnDispose() - { - if (OnDiscarded is null) return; + protected override void OnDispose() + { + if (OnDiscarded is null) return; - T? d; - while ((d = TryRelease()) is not null) OnDiscarded(d); - } + T? d; + while ((d = TryRelease()) is not null) OnDiscarded(d); } } diff --git a/source/ObjectPoolCompatibilityExtensions.cs b/source/ObjectPoolCompatibilityExtensions.cs new file mode 100644 index 0000000..4263ce7 --- /dev/null +++ b/source/ObjectPoolCompatibilityExtensions.cs @@ -0,0 +1,16 @@ +namespace Open.Disposable.ObjectPoolCompatibility; + +/// +/// Provided as means for compatiibility with object pools +/// that have the same interface as the Microsoft Extensions version. +/// +public static class ObjectPoolCompatibilityExtensions +{ + /// + public static T Get(this IObjectPool pool) + where T : class => pool.Take(); + + /// + public static void Return(this IObjectPool pool, T item) + where T : class => pool.Give(item); +} diff --git a/source/ObjectPoolResizeEvent.cs b/source/ObjectPoolResizeEvent.cs index 71b92ff..78659d9 100644 --- a/source/ObjectPoolResizeEvent.cs +++ b/source/ObjectPoolResizeEvent.cs @@ -1,4 +1,3 @@ -namespace Open.Disposable -{ - public delegate void ObjectPoolResizeEvent(int newSize); -} +namespace Open.Disposable; + +public delegate void ObjectPoolResizeEvent(int newSize); diff --git a/source/Open.Disposable.ObjectPools.csproj b/source/Open.Disposable.ObjectPools.csproj index ad6dc04..e816676 100644 --- a/source/Open.Disposable.ObjectPools.csproj +++ b/source/Open.Disposable.ObjectPools.csproj @@ -17,7 +17,7 @@ Part of the "Open" set of libraries. https://github.com/Open-NET-Libraries/Open.Disposable.ObjectPools/ git objectpool;idisposable;thread safe - 2.7.0 + 2.8.0 MIT true diff --git a/source/RecycleHelper.cs b/source/RecycleHelper.cs index ec44c76..9085250 100644 --- a/source/RecycleHelper.cs +++ b/source/RecycleHelper.cs @@ -1,36 +1,35 @@ using System; -namespace Open.Disposable +namespace Open.Disposable; + +/// +/// Allows for the 'using' syntax to be used with any object to return it to the pool. +/// +public struct RecycleHelper : IDisposable + where T : class { - /// - /// Allows for the 'using' syntax to be used with any object to return it to the pool. - /// - public struct RecycleHelper : IDisposable - where T : class - { - private readonly IObjectPool _pool; + private readonly IObjectPool _pool; - private RecycleHelper(IObjectPool pool, T item) - { - _pool = pool; - _item = item; - } + private RecycleHelper(IObjectPool pool, T item) + { + _pool = pool; + _item = item; + } - public RecycleHelper(IObjectPool pool) - : this(pool ?? throw new ArgumentNullException(nameof(pool)), pool.Take()) - { + public RecycleHelper(IObjectPool pool) + : this(pool ?? throw new ArgumentNullException(nameof(pool)), pool.Take()) + { - } + } - private T? _item; - public T Item => _item ?? throw new ObjectDisposedException(GetType().ToString()); + private T? _item; + public T Item => _item ?? throw new ObjectDisposedException(GetType().ToString()); - public void Dispose() - { - var i = _item; - _item = null; - if (i is null) return; - _pool.Give(i); - } + public void Dispose() + { + var i = _item; + _item = null; + if (i is null) return; + _pool.Give(i); } } diff --git a/source/Recycler.cs b/source/Recycler.cs index 4886c70..1f776b3 100644 --- a/source/Recycler.cs +++ b/source/Recycler.cs @@ -2,81 +2,74 @@ using System.Threading.Channels; using System.Threading.Tasks; -namespace Open.Disposable +namespace Open.Disposable; + +/// +public class Recycler : RecyclerBase + where T : class { - /// - public class Recycler : RecyclerBase - where T : class - { - Channel _bin; + Channel _bin; - internal Recycler( - IObjectPool target, - Action recycleFunction, - ushort limit = Constants.DEFAULT_CAPACITY) : base(target, recycleFunction) - { - _bin = Channel.CreateBounded(limit); - Completion = Processor(recycleFunction); - } + internal Recycler( + IObjectPool target, + Action recycleFunction, + ushort limit = Constants.DEFAULT_CAPACITY) : base(target, recycleFunction) + { + _bin = Channel.CreateBounded(limit); + Completion = Processor(recycleFunction); + } - async Task Processor(Action recycleFunction) + async Task Processor(Action recycleFunction) + { + var bin = _bin; + do { - var bin = _bin; - do + while (bin.Reader.TryRead(out var item)) { - while (bin.Reader.TryRead(out var item)) - { - recycleFunction(item); - Target?.Give(item); - } + recycleFunction(item); + Target?.Give(item); } - while (await bin.Reader.WaitToReadAsync().ConfigureAwait(false)); } + while (await bin.Reader.WaitToReadAsync().ConfigureAwait(false)); + } - internal Recycler( - ushort limit, - IObjectPool pool, - Action recycleFunction) : this(pool, recycleFunction, limit) { } + internal Recycler( + ushort limit, + IObjectPool pool, + Action recycleFunction) : this(pool, recycleFunction, limit) { } - /// - public override bool Recycle(T item) - => _bin?.Writer.TryWrite(item) ?? false; + /// + public override bool Recycle(T item) => _bin?.Writer.TryWrite(item) ?? false; - protected override void OnCloseRequested() - => _bin?.Writer.Complete(); + protected override void OnCloseRequested() => _bin?.Writer.Complete(); - protected override void OnDispose() - { - base.OnDispose(); + protected override void OnDispose() + { + base.OnDispose(); - _bin.Writer.TryComplete(); - _bin = null!; - } + _bin.Writer.TryComplete(); + _bin = null!; } +} - public static class Recycler - { - public static Recycler CreateRecycler( - this IObjectPool pool, - Action recycleFunction, - ushort limit = Constants.DEFAULT_CAPACITY) - where T : class - => new(pool, recycleFunction, limit); +public static class Recycler +{ + public static Recycler CreateRecycler( + this IObjectPool pool, + Action recycleFunction, + ushort limit = Constants.DEFAULT_CAPACITY) + where T : class => new(pool, recycleFunction, limit); - public static Recycler CreateRecycler( - this IObjectPool pool, - ushort limit, - Action recycleFunction) - where T : class - => new(pool, recycleFunction, limit); + public static Recycler CreateRecycler( + this IObjectPool pool, + ushort limit, + Action recycleFunction) + where T : class => new(pool, recycleFunction, limit); - public static void Recycle(IRecyclable r) - => (r ?? throw new ArgumentNullException(nameof(r))).Recycle(); + public static void Recycle(IRecyclable r) => (r ?? throw new ArgumentNullException(nameof(r))).Recycle(); - public static Recycler CreateRecycler( - this IObjectPool pool, - ushort limit = Constants.DEFAULT_CAPACITY) - where T : class, IRecyclable - => new(pool, Recycle, limit); - } + public static Recycler CreateRecycler( + this IObjectPool pool, + ushort limit = Constants.DEFAULT_CAPACITY) + where T : class, IRecyclable => new(pool, Recycle, limit); } diff --git a/source/RecyclerBase.cs b/source/RecyclerBase.cs index c3f655a..26024d4 100644 --- a/source/RecyclerBase.cs +++ b/source/RecyclerBase.cs @@ -2,53 +2,51 @@ using System.Diagnostics.Contracts; using System.Threading.Tasks; -namespace Open.Disposable +namespace Open.Disposable; + +/// +/// This class is provided as an asynchronous queue for recycling instead of using a recycle delegate with an object pool and calling GiveAsync() which could pile up unnecessarily. +/// So if recycling an object takes extra time, this might be a good way to toss objects away and not have to worry about the heavy cost as they will one by one be processed back into the target pool. +/// +// ReSharper disable once InheritdocConsiderUsage +public abstract class RecyclerBase : DisposableBase, IRecycler + where T : class { - /// - /// This class is provided as an asynchronous queue for recycling instead of using a recycle delegate with an object pool and calling GiveAsync() which could pile up unnecessarily. - /// So if recycling an object takes extra time, this might be a good way to toss objects away and not have to worry about the heavy cost as they will one by one be processed back into the target pool. - /// - // ReSharper disable once InheritdocConsiderUsage - public abstract class RecyclerBase : DisposableBase, IRecycler - where T : class + protected IObjectPool Target; + + protected RecyclerBase( + IObjectPool target, + Action recycleFunction) + { + if (recycleFunction is null) throw new ArgumentNullException(nameof(recycleFunction)); + Target = target ?? throw new ArgumentNullException(nameof(target)); + Contract.EndContractBlock(); + + if (target is not DisposableBase d) return; + if (d.WasDisposed) throw new ArgumentException("Cannot recycle for an object pool that is already disposed."); + d.BeforeDispose += Pool_BeforeDispose; + // Could possibly dispose before this line somewhere... But that's just nasty. :P + } + + void Pool_BeforeDispose(object sender, EventArgs e) => Dispose(); + + public abstract bool Recycle(T item); + + protected abstract void OnCloseRequested(); + + // ReSharper disable once MemberCanBeProtected.Global + public Task Completion { get; protected set; } = Task.CompletedTask; + + /// + public Task Close() { - protected IObjectPool Target; - - protected RecyclerBase( - IObjectPool target, - Action recycleFunction) - { - if (recycleFunction is null) throw new ArgumentNullException(nameof(recycleFunction)); - Target = target ?? throw new ArgumentNullException(nameof(target)); - Contract.EndContractBlock(); - - if (target is not DisposableBase d) return; - if (d.WasDisposed) throw new ArgumentException("Cannot recycle for an object pool that is already disposed."); - d.BeforeDispose += Pool_BeforeDispose; - // Could possibly dispose before this line somewhere... But that's just nasty. :P - } - - void Pool_BeforeDispose(object sender, EventArgs e) => Dispose(); - - public abstract bool Recycle(T item); - - protected abstract void OnCloseRequested(); - - // ReSharper disable once MemberCanBeProtected.Global - public Task Completion { get; protected set; } = Task.CompletedTask; - - /// - public Task Close() - { - OnCloseRequested(); - return Completion; - } - - protected override void OnDispose() - { - OnCloseRequested(); - Target = null!; - } + OnCloseRequested(); + return Completion; } + protected override void OnDispose() + { + OnCloseRequested(); + Target = null!; + } } diff --git a/source/ReferenceContainer.cs b/source/ReferenceContainer.cs index f502367..b0e9c9f 100644 --- a/source/ReferenceContainer.cs +++ b/source/ReferenceContainer.cs @@ -2,92 +2,65 @@ using System.Runtime.CompilerServices; using System.Threading; -namespace Open.Disposable -{ - public interface IReferenceContainer - where T : class - { - int Capacity { get; } - bool SetIfNull(T value); - bool TrySave(T value); - T? TryRetrieve(); - } - - [DebuggerDisplay("{Value,nq}")] - public struct ReferenceContainer : IReferenceContainer - where T : class - { - public int Capacity => 1; +namespace Open.Disposable; - T? _value; - - /// - /// The value contained. - /// - public T? Value - { - get => _value; - set => _value = value; - } +public interface IReferenceContainer + where T : class +{ + int Capacity { get; } + bool SetIfNull(T value); + bool TrySave(T value); + T? TryRetrieve(); +} - /// - /// Sets the value if it is currently null without interlocking. - /// - /// true if the value was set; otherwise false. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool SetIfNull(T value) - { - if (_value is not null) return false; - _value = value; - return true; - } +[DebuggerDisplay("{Value,nq}")] +public struct ReferenceContainer : IReferenceContainer + where T : class +{ + public int Capacity => 1; - /// - /// Tries to atomically store the value. - /// - /// true if the value was set; otherwise false. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool TrySave(T value) - => _value is null - && null == Interlocked.CompareExchange(ref _value, value, null); + T? _value; - /// - /// Tries to atomically retrieve the value. - /// - /// The value retrieved; otherwise null. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public T? TryRetrieve() - { - var item = _value; - return (item is not null - && item == Interlocked.CompareExchange(ref _value, null, item)) - ? item - : null; - } + /// + /// The value contained. + /// + public T? Value + { + get => _value; + set => _value = value; } - //public struct DualReferenceContainer : IReferenceContainer - // where T : class - //{ - // public int Capacity => 2; - - // ReferenceContainer _1; - // ReferenceContainer _2; - - // public bool SetIfNull(T value) - // { - // return _1.SetIfNull(value) || _2.SetIfNull(value); - // } - - // public T TryRetrieve() - // { - // return _1.TryRetrieve() ?? _2.TryRetrieve(); - // } + /// + /// Sets the value if it is currently null without interlocking. + /// + /// true if the value was set; otherwise false. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool SetIfNull(T value) + { + if (_value is not null) return false; + _value = value; + return true; + } - // public bool TrySave(T value) - // { - // return _1.TrySave(value) || _2.TrySave(value); - // } - //} + /// + /// Tries to atomically store the value. + /// + /// true if the value was set; otherwise false. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool TrySave(T value) + => _value is null && null == Interlocked.CompareExchange(ref _value, value, null); + /// + /// Tries to atomically retrieve the value. + /// + /// The value retrieved; otherwise null. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public T? TryRetrieve() + { + var item = _value; + return (item is not null + && item == Interlocked.CompareExchange(ref _value, null, item)) + ? item + : null; + } } diff --git a/source/SharedPools.cs b/source/SharedPools.cs index d25fc7e..0fff6b1 100644 --- a/source/SharedPools.cs +++ b/source/SharedPools.cs @@ -1,74 +1,93 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using System.Text; -namespace Open.Disposable +namespace Open.Disposable; + +public class SharedPool : ConcurrentQueueObjectPoolSlimBase + where T : class { - public static class ListPool - { - /// - /// A shared object pool for use with lists. - /// The list is cleared after being returned. - /// The max size of the pool is 64 which should suffice for most use cases. - /// - public static readonly ConcurrentQueueObjectPoolSlim> Shared = Create(); + public SharedPool( + Func factory, + Action? recycler, + Action? disposer, + int capacity) + : base(factory, recycler, disposer, capacity) { } - /// - /// Creates an object pool for use with lists. - /// The list is cleared after being returned. - /// - public static ConcurrentQueueObjectPoolSlim> Create(int capacity = Constants.DEFAULT_CAPACITY) => new(() => new (), h => - { - h.Clear(); - if (h.Capacity > 16) h.Capacity = 16; - }, null, capacity); + public new void Dispose() + { + throw new NotImplementedException("Shared pools cannot be disposed."); } +} - public static class HashSetPool +public static class ListPool +{ + /// + /// A shared object pool for use with lists. + /// The list is cleared after being returned. + /// The max size of the pool is 64 which should suffice for most use cases. + /// + public static readonly SharedPool> Shared = Create(); + + /// + /// Creates an object pool for use with lists. + /// The list is cleared after being returned. + /// + public static SharedPool> Create(int capacity = Constants.DEFAULT_CAPACITY) => new(() => new(), h => { - /// - /// A shared object pool for use with hash-sets. - /// The hash-set is cleared after being returned. - /// The max size of the pool is 64 which should suffice for most use cases. - /// - public static readonly ConcurrentQueueObjectPoolSlim> Shared = Create(); + h.Clear(); + if (h.Capacity > 16) h.Capacity = 16; + }, null, capacity); +} - /// - /// Creates an object pool for use with hash-sets. - /// The hash-set is cleared after being returned. - /// - public static ConcurrentQueueObjectPoolSlim> Create(int capacity = Constants.DEFAULT_CAPACITY) => new(() => new(), h => h.Clear(), null, capacity); - } +public static class HashSetPool +{ + /// + /// A shared object pool for use with hash-sets. + /// The hash-set is cleared after being returned. + /// The max size of the pool is 64 which should suffice for most use cases. + /// + public static readonly SharedPool> Shared = Create(); - public static class StringBuilderPool - { - /// - /// A shared object pool for use with StringBuilders. - /// The StringBuilder is cleared after being returned. - /// The max size of the pool is 64 which should suffice for most use cases. - /// - public static readonly ConcurrentQueueObjectPoolSlim Shared = Create(); + /// + /// Creates an object pool for use with hash-sets. + /// The hash-set is cleared after being returned. + /// + public static SharedPool> Create(int capacity = Constants.DEFAULT_CAPACITY) + => new(() => new(), h => h.Clear(), null, capacity); +} - /// - /// Creates an object pool for use with StringBuilders. - /// The StringBuilder is cleared after being returned. - /// - public static ConcurrentQueueObjectPoolSlim Create(int capacity = Constants.DEFAULT_CAPACITY) => new(() => new(), sb => sb.Clear(), null, capacity); - } +public static class StringBuilderPool +{ + /// + /// A shared object pool for use with StringBuilders. + /// The StringBuilder is cleared after being returned. + /// The max size of the pool is 64 which should suffice for most use cases. + /// + public static readonly SharedPool Shared = Create(); - public static class DictionaryPool - { - /// - /// A shared object pool for use with dictionaries. - /// The dictionary is cleared after being returned. - /// The max size of the pool is 64 which should suffice for most use cases. - /// - public static readonly ConcurrentQueueObjectPoolSlim> Shared = Create(); + /// + /// Creates an object pool for use with StringBuilders. + /// The StringBuilder is cleared after being returned. + /// + public static SharedPool Create(int capacity = Constants.DEFAULT_CAPACITY) + => new(() => new(), sb => sb.Clear(), null, capacity); +} + +public static class DictionaryPool +{ + /// + /// A shared object pool for use with dictionaries. + /// The dictionary is cleared after being returned. + /// The max size of the pool is 64 which should suffice for most use cases. + /// + public static readonly SharedPool> Shared = Create(); - /// - /// Creates an object pool for use with dictionaries. - /// The dictionary is cleared after being returned. - /// - public static ConcurrentQueueObjectPoolSlim> Create(int capacity = Constants.DEFAULT_CAPACITY) => new(() => new Dictionary(), h => h.Clear(), null, capacity); + /// + /// Creates an object pool for use with dictionaries. + /// The dictionary is cleared after being returned. + /// + public static SharedPool> Create(int capacity = Constants.DEFAULT_CAPACITY) + => new(() => new Dictionary(), h => h.Clear(), null, capacity); - } } diff --git a/source/TrimmableCollectionObjectPoolBase.cs b/source/TrimmableCollectionObjectPoolBase.cs index b45869e..e4ae5f7 100644 --- a/source/TrimmableCollectionObjectPoolBase.cs +++ b/source/TrimmableCollectionObjectPoolBase.cs @@ -2,62 +2,75 @@ using System.Collections; using System.Collections.Generic; -namespace Open.Disposable -{ - public abstract class TrimmableCollectionObjectPoolBase : TrimmableObjectPoolBase - where T : class - where TCollection : class, ICollection - { +namespace Open.Disposable; - protected TrimmableCollectionObjectPoolBase(TCollection pool, Func factory, Action? recycler, Action? disposer, int capacity = DEFAULT_CAPACITY, bool countTrackingEnabled = true) - : base(factory, recycler, disposer, capacity, countTrackingEnabled) - { - Pool = pool ?? throw new ArgumentNullException(nameof(pool)); - } +/* + * There are two class varations here because ICollection and ICollection do not overlap. + * 'Count' is the common property used in both classes. + */ + +public abstract class TrimmableCollectionObjectPoolBase + : TrimmableObjectPoolBase + where T : class + where TCollection : class, ICollection +{ - protected TCollection Pool; + protected TrimmableCollectionObjectPoolBase( + TCollection pool, + Func factory, + Action? recycler, + Action? disposer, + int capacity = DEFAULT_CAPACITY, + bool countTrackingEnabled = true) + : base(factory, recycler, disposer, capacity, countTrackingEnabled) + => Pool = pool ?? throw new ArgumentNullException(nameof(pool)); - /// - public override int Count => (Pool?.Count ?? 0) + PocketCount; + protected TCollection Pool; - protected override void OnDispose() - { - base.OnDispose(); - Pool = null!; - } - } + /// + public override int Count => (Pool?.Count ?? 0) + PocketCount; - public abstract class TrimmableGenericCollectionObjectPoolBase : TrimmableObjectPoolBase - where T : class - where TCollection : class, ICollection + protected override void OnDispose() { + base.OnDispose(); + Pool = null!; + } +} - protected TrimmableGenericCollectionObjectPoolBase(TCollection pool, Func factory, Action? recycler, Action? disposer, int capacity = DEFAULT_CAPACITY, bool countTrackingEnabled = true) - : base(factory, recycler, disposer, capacity, countTrackingEnabled) - { - Pool = pool ?? throw new ArgumentNullException(nameof(pool)); - } +public abstract class TrimmableGenericCollectionObjectPoolBase + : TrimmableObjectPoolBase + where T : class + where TCollection : class, ICollection +{ - protected TCollection Pool; + protected TrimmableGenericCollectionObjectPoolBase( + TCollection pool, + Func factory, + Action? recycler, + Action? disposer, + int capacity = DEFAULT_CAPACITY, + bool countTrackingEnabled = true) + : base(factory, recycler, disposer, capacity, countTrackingEnabled) + => Pool = pool ?? throw new ArgumentNullException(nameof(pool)); - /// - public override int Count => Pool?.Count ?? 0; + protected TCollection Pool; - protected override void OnDispose() - { - //base.OnDispose(); // Do not call because the following is more optimized. + /// + public override int Count => (Pool?.Count ?? 0) + PocketCount; - var p = Pool; - Pool = null!; + protected override void OnDispose() + { + //base.OnDispose(); // Do not call because the following is more optimized. - if (OnDiscarded is not null) - { - foreach (var item in p) - OnDiscarded(item); - } + var p = Pool; + Pool = null!; - p.Clear(); + if (OnDiscarded is not null) + { + foreach (var item in p) + OnDiscarded(item); } - } + p.Clear(); + } } diff --git a/source/TrimmableObjectPoolBase.cs b/source/TrimmableObjectPoolBase.cs index d54b24a..ce29307 100644 --- a/source/TrimmableObjectPoolBase.cs +++ b/source/TrimmableObjectPoolBase.cs @@ -3,98 +3,84 @@ using System.Runtime.CompilerServices; using System.Threading; -namespace Open.Disposable +namespace Open.Disposable; + +[DebuggerDisplay("Count = {Count}")] +public abstract class TrimmableObjectPoolBase : ObjectPoolBase, ITrimmableObjectPool + where T : class { - [DebuggerDisplay("Count = {Count}")] - public abstract class TrimmableObjectPoolBase : ObjectPoolBase, ITrimmableObjectPool - where T : class - { - protected TrimmableObjectPoolBase(Func factory, Action? recycler, Action? disposer, int capacity, bool countTrackingEnabled = true) - : base(factory, recycler, disposer, capacity) - { - _countTrackingEnabled = countTrackingEnabled; - } + protected TrimmableObjectPoolBase(Func factory, Action? recycler, Action? disposer, int capacity, bool countTrackingEnabled = true) + : base(factory, recycler, disposer, capacity) => _countTrackingEnabled = countTrackingEnabled; - int _count; - readonly bool _countTrackingEnabled; // When true this enables tracking the number of entries entering and exiting the pool instead of calling '.Count'. + int _count; + readonly bool _countTrackingEnabled; // When true this enables tracking the number of entries entering and exiting the pool instead of calling '.Count'. - protected int CountInternal => _countTrackingEnabled ? _count : Count; + protected int CountInternal => _countTrackingEnabled ? _count : Count; - protected override bool CanReceive => CountInternal < MaxSize; + protected override bool CanReceive => CountInternal < MaxSize; - /// - /// Signal for when an item was taken (actually removed) from the pool. - /// - public event ObjectPoolResizeEvent? Released; - protected void OnReleased(int newSize) - { - Debug.Assert(newSize > -2, $"newSize: {newSize}, _count: {_count}"); // Should never get out of control. It may go negative temporarily but should be 100% accounted for. - Released?.Invoke(newSize); - } + /// + /// Signal for when an item was taken (actually removed) from the pool. + /// + public event ObjectPoolResizeEvent? Released; + protected void OnReleased(int newSize) + { + Debug.Assert(newSize > -2, $"newSize: {newSize}, _count: {_count}"); // Should never get out of control. It may go negative temporarily but should be 100% accounted for. + Released?.Invoke(newSize); + } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - protected override void OnReleased() - { - OnReleased(_countTrackingEnabled ? Interlocked.Decrement(ref _count) : Count); - } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + protected override void OnReleased() => OnReleased(_countTrackingEnabled ? Interlocked.Decrement(ref _count) : Count); - /// - /// Signal for when an item was given (actually accepted) to the pool. - /// - public event ObjectPoolResizeEvent? Received; + /// + /// Signal for when an item was given (actually accepted) to the pool. + /// + public event ObjectPoolResizeEvent? Received; - [MethodImpl(MethodImplOptions.AggressiveInlining)] - protected void OnReceived(int newSize) - { - Received?.Invoke(newSize); - } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + protected void OnReceived(int newSize) => Received?.Invoke(newSize); - [MethodImpl(MethodImplOptions.AggressiveInlining)] - protected override void OnReceived() - { - OnReceived(_countTrackingEnabled ? Interlocked.Increment(ref _count) : Count); - } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + protected override void OnReceived() => OnReceived(_countTrackingEnabled ? Interlocked.Increment(ref _count) : Count); + + /// + public virtual void TrimTo(int targetSize) + { + if (targetSize < 0) return; // Possible upstream math hiccup or default -1. Silently dismiss. + var count = CountInternal; - /// - public virtual void TrimTo(int targetSize) + if (count <= targetSize) return; + + var i = 0; // Prevent an possiblility of indefinite loop. + for ( + var attempts = count - targetSize; + i < attempts; + i++) { - if (targetSize < 0) return; // Possible upstream math hiccup or default -1. Silently dismiss. - var count = CountInternal; + var e = TryRelease(); + if (e is null) break; - if (count > targetSize) + if (_countTrackingEnabled) { - var i = 0; // Prevent an possiblility of indefinite loop. - for ( - var attempts = count - targetSize; - i < attempts; - i++) - { - var e = TryRelease(); - if (e is null) break; - - if (_countTrackingEnabled) - { - Interlocked.Decrement(ref _count); - //var c = Interlocked.Decrement(ref _count); - //Debug.Assert(c >= 0); - } - - OnDiscarded?.Invoke(e); - } - - if (i != 0) OnReleased(CountInternal); + Interlocked.Decrement(ref _count); + //var c = Interlocked.Decrement(ref _count); + //Debug.Assert(c >= 0); } + + OnDiscarded?.Invoke(e); } - //protected ReferenceContainer Pocket2; // Default struct constructs itself. + if (i != 0) OnReleased(CountInternal); + } - //protected override bool SaveToPocket(T item) - // => Pocket.TrySave(item) || Pocket2.TrySave(item); + //protected ReferenceContainer Pocket2; // Default struct constructs itself. - //protected override T TakeFromPocket() - // => Pocket.TryRetrieve() ?? Pocket2.TryRetrieve(); + //protected override bool SaveToPocket(T item) + // => Pocket.TrySave(item) || Pocket2.TrySave(item); - //protected override int PocketCount => - // base.PocketCount + (Pocket2.Value is null ? 0 : 1); - } + //protected override T TakeFromPocket() + // => Pocket.TryRetrieve() ?? Pocket2.TryRetrieve(); + + //protected override int PocketCount => + // base.PocketCount + (Pocket2.Value is null ? 0 : 1); } From b8306ea1cec9f58ffae4b611e7df3dd27704edcc Mon Sep 17 00:00:00 2001 From: electricessence <5899455+electricessence@users.noreply.github.com> Date: Tue, 28 Sep 2021 20:31:27 -0700 Subject: [PATCH 68/83] Implemented obselete messaging for disposal. --- source/SharedPools.cs | 3 ++- tests/ObjectPoolSmokeTests.cs | 5 +++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/source/SharedPools.cs b/source/SharedPools.cs index 0fff6b1..b4daa4e 100644 --- a/source/SharedPools.cs +++ b/source/SharedPools.cs @@ -14,9 +14,10 @@ public SharedPool( int capacity) : base(factory, recycler, disposer, capacity) { } + [Obsolete("Shared pools do not support disposal.")] public new void Dispose() { - throw new NotImplementedException("Shared pools cannot be disposed."); + throw new NotSupportedException("Shared pools cannot be disposed."); } } diff --git a/tests/ObjectPoolSmokeTests.cs b/tests/ObjectPoolSmokeTests.cs index d8b1054..e919b4a 100644 --- a/tests/ObjectPoolSmokeTests.cs +++ b/tests/ObjectPoolSmokeTests.cs @@ -1,4 +1,5 @@ using Microsoft.VisualStudio.TestTools.UnitTesting; +using System; namespace Open.Disposable { @@ -27,6 +28,10 @@ public void ListPool_RecycleTest() pool.Give(list); Assert.AreEqual(list, pool.Take()); Assert.AreEqual(0, list.Count); + +#pragma warning disable CS0618 // Type or member is obsolete + Assert.ThrowsException(() => pool.Dispose()); +#pragma warning restore CS0618 // Type or member is obsolete } } From 6055d21f061fdab86b1545049a45cbf0a30cb2b1 Mon Sep 17 00:00:00 2001 From: electricessence <5899455+electricessence@users.noreply.github.com> Date: Tue, 28 Sep 2021 20:37:47 -0700 Subject: [PATCH 69/83] Cleanup. --- benchmarking/Benchmark.cs | 5 +---- benchmarking/DefaultObjectPool.cs | 13 ++++--------- source/Array/InterlockedArrayObjectPool.cs | 2 +- source/Array/OptimisticArrayObjectPool.cs | 2 +- source/Collection/ConcurrentQueueObjectPool.cs | 2 +- source/Collection/ConcurrentQueueObjectPoolSlim.cs | 2 -- source/Collection/ConcurrentStackObjectPool.cs | 2 +- source/ObjectPoolBase.cs | 2 +- source/ObjectPoolCompatibilityExtensions.cs | 2 +- source/SharedPools.cs | 4 +--- 10 files changed, 12 insertions(+), 24 deletions(-) diff --git a/benchmarking/Benchmark.cs b/benchmarking/Benchmark.cs index dc08b4e..7e46b23 100644 --- a/benchmarking/Benchmark.cs +++ b/benchmarking/Benchmark.cs @@ -86,10 +86,7 @@ protected override IEnumerable TestOnceInternal() //}); } - public static TimedResult[] Results(uint size, uint repeat, Func> poolFactory) - { - return (new Benchmark(size, repeat, poolFactory)).Result; - } + public static TimedResult[] Results(uint size, uint repeat, Func> poolFactory) => (new Benchmark(size, repeat, poolFactory)).Result; } } diff --git a/benchmarking/DefaultObjectPool.cs b/benchmarking/DefaultObjectPool.cs index 1d41237..463249d 100644 --- a/benchmarking/DefaultObjectPool.cs +++ b/benchmarking/DefaultObjectPool.cs @@ -14,7 +14,7 @@ public class DefaultObjectPool : Microsoft.Extensions.ObjectPool.DefaultObjec class Policy : IPooledObjectPolicy { private readonly Func factory; -// private readonly Action? recycler; + // private readonly Action? recycler; public Policy(Func factory)//, Action? recycler) { @@ -27,18 +27,13 @@ public Policy(Func factory)//, Action? recycler) } public T Create() => factory(); - public bool Return(T obj) - { + public bool Return(T obj) => // recycler?.Invoke(obj); - return true; - } + true; } public DefaultObjectPool(Func factory, int capacity = 64) - : base(new Policy(factory), capacity) - { - _factory = factory; - } + : base(new Policy(factory), capacity) => _factory = factory; public int Capacity { get; } diff --git a/source/Array/InterlockedArrayObjectPool.cs b/source/Array/InterlockedArrayObjectPool.cs index 20367a0..48959b8 100644 --- a/source/Array/InterlockedArrayObjectPool.cs +++ b/source/Array/InterlockedArrayObjectPool.cs @@ -48,7 +48,7 @@ protected override bool Receive(T item) for (var i = 0; i < len; i++) { - if (!Store(elements!, item, i)) continue; + if (!Store(elements!, item, i)) continue; var m = MaxStored; if (i >= m) Interlocked.CompareExchange(ref MaxStored, m + MaxStoredIncrement, m); return true; diff --git a/source/Array/OptimisticArrayObjectPool.cs b/source/Array/OptimisticArrayObjectPool.cs index eb25e0a..bc439b9 100644 --- a/source/Array/OptimisticArrayObjectPool.cs +++ b/source/Array/OptimisticArrayObjectPool.cs @@ -22,7 +22,7 @@ public OptimisticArrayObjectPool( public OptimisticArrayObjectPool( Func factory, int capacity = DEFAULT_CAPACITY) - : this(factory, null, capacity) { } + : this(factory, null, capacity) { } // As suggested by Roslyn's implementation, don't worry about interlocking here. It's okay if a few get loose. diff --git a/source/Collection/ConcurrentQueueObjectPool.cs b/source/Collection/ConcurrentQueueObjectPool.cs index ccbcdd0..8116167 100644 --- a/source/Collection/ConcurrentQueueObjectPool.cs +++ b/source/Collection/ConcurrentQueueObjectPool.cs @@ -19,7 +19,7 @@ public ConcurrentQueueObjectPool( public ConcurrentQueueObjectPool( Func factory, int capacity = DEFAULT_CAPACITY) - : this(factory, null, null, capacity) { } + : this(factory, null, null, capacity) { } /* * NOTE: ConcurrentQueue is very fast and will perform quite well without using the 'Pocket' feature. diff --git a/source/Collection/ConcurrentQueueObjectPoolSlim.cs b/source/Collection/ConcurrentQueueObjectPoolSlim.cs index 7ddc6a6..30b1171 100644 --- a/source/Collection/ConcurrentQueueObjectPoolSlim.cs +++ b/source/Collection/ConcurrentQueueObjectPoolSlim.cs @@ -1,6 +1,4 @@ using System; -using System.Collections.Concurrent; -using System.Runtime.CompilerServices; namespace Open.Disposable; diff --git a/source/Collection/ConcurrentStackObjectPool.cs b/source/Collection/ConcurrentStackObjectPool.cs index 356b857..22bb5cf 100644 --- a/source/Collection/ConcurrentStackObjectPool.cs +++ b/source/Collection/ConcurrentStackObjectPool.cs @@ -13,7 +13,7 @@ public ConcurrentStackObjectPool( Action? recycler, Action? disposer, int capacity = DEFAULT_CAPACITY) - : base(new ConcurrentStack(), factory, recycler, disposer, capacity) { } + : base(new ConcurrentStack(), factory, recycler, disposer, capacity) { } public ConcurrentStackObjectPool(Func factory, int capacity = DEFAULT_CAPACITY) : this(factory, null, null, capacity) { } diff --git a/source/ObjectPoolBase.cs b/source/ObjectPoolBase.cs index e647357..d561e68 100644 --- a/source/ObjectPoolBase.cs +++ b/source/ObjectPoolBase.cs @@ -91,7 +91,7 @@ public void Give(T item) /// #if NETSTANDARD2_1_OR_GREATER - public bool TryTake([System.Diagnostics.CodeAnalysis.NotNullWhen(true)] out T? item) + public bool TryTake([System.Diagnostics.CodeAnalysis.NotNullWhen(true)] out T? item) #else public bool TryTake(out T? item) #endif diff --git a/source/ObjectPoolCompatibilityExtensions.cs b/source/ObjectPoolCompatibilityExtensions.cs index 4263ce7..89a146c 100644 --- a/source/ObjectPoolCompatibilityExtensions.cs +++ b/source/ObjectPoolCompatibilityExtensions.cs @@ -8,7 +8,7 @@ public static class ObjectPoolCompatibilityExtensions { /// public static T Get(this IObjectPool pool) - where T : class => pool.Take(); + where T : class => pool.Take(); /// public static void Return(this IObjectPool pool, T item) diff --git a/source/SharedPools.cs b/source/SharedPools.cs index b4daa4e..260c113 100644 --- a/source/SharedPools.cs +++ b/source/SharedPools.cs @@ -16,9 +16,7 @@ public SharedPool( [Obsolete("Shared pools do not support disposal.")] public new void Dispose() - { - throw new NotSupportedException("Shared pools cannot be disposed."); - } + => throw new NotSupportedException("Shared pools cannot be disposed."); } public static class ListPool From dd268e0163c98a389fbc9cd155f27e9be0010cb0 Mon Sep 17 00:00:00 2001 From: electricessence <5899455+electricessence@users.noreply.github.com> Date: Tue, 28 Sep 2021 22:27:30 -0700 Subject: [PATCH 70/83] Shared pool tweaks. --- source/Open.Disposable.ObjectPools.csproj | 2 +- source/SharedPools.cs | 18 +++++++++++++++++- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/source/Open.Disposable.ObjectPools.csproj b/source/Open.Disposable.ObjectPools.csproj index e816676..dd19d20 100644 --- a/source/Open.Disposable.ObjectPools.csproj +++ b/source/Open.Disposable.ObjectPools.csproj @@ -17,7 +17,7 @@ Part of the "Open" set of libraries. https://github.com/Open-NET-Libraries/Open.Disposable.ObjectPools/ git objectpool;idisposable;thread safe - 2.8.0 + 2.8.1 MIT true diff --git a/source/SharedPools.cs b/source/SharedPools.cs index 260c113..97af566 100644 --- a/source/SharedPools.cs +++ b/source/SharedPools.cs @@ -56,7 +56,7 @@ public static SharedPool> Create(int capacity = Constants.DEFAULT_CAP => new(() => new(), h => h.Clear(), null, capacity); } -public static class StringBuilderPool +public static class StringBuilderPool { /// /// A shared object pool for use with StringBuilders. @@ -71,6 +71,22 @@ public static class StringBuilderPool /// public static SharedPool Create(int capacity = Constants.DEFAULT_CAPACITY) => new(() => new(), sb => sb.Clear(), null, capacity); + + /// + /// Provides a StringBuilder to be used for processing and finalizes by calling .ToString() and returning the value. + /// + /// If either the pool or action are null. + public static string RentToString(this IObjectPool pool, Action action) + { + if (pool is null) throw new ArgumentNullException(nameof(pool)); + if (action is null) throw new ArgumentNullException(nameof(action)); + + var sb = pool.Take(); + action(sb); + var result = sb.ToString(); + pool.Give(sb); + return result; + } } public static class DictionaryPool From 6094482a37b43b5c40988c1e948634e00c98f253 Mon Sep 17 00:00:00 2001 From: electricessence <5899455+electricessence@users.noreply.github.com> Date: Wed, 29 Sep 2021 10:45:04 -0700 Subject: [PATCH 71/83] Added shortcuts for renting. --- source/Open.Disposable.ObjectPools.csproj | 2 +- source/SharedPools.cs | 32 +++++++++++++++++++++++ 2 files changed, 33 insertions(+), 1 deletion(-) diff --git a/source/Open.Disposable.ObjectPools.csproj b/source/Open.Disposable.ObjectPools.csproj index dd19d20..185c372 100644 --- a/source/Open.Disposable.ObjectPools.csproj +++ b/source/Open.Disposable.ObjectPools.csproj @@ -17,7 +17,7 @@ Part of the "Open" set of libraries. https://github.com/Open-NET-Libraries/Open.Disposable.ObjectPools/ git objectpool;idisposable;thread safe - 2.8.1 + 2.8.2 MIT true diff --git a/source/SharedPools.cs b/source/SharedPools.cs index 97af566..6a970b1 100644 --- a/source/SharedPools.cs +++ b/source/SharedPools.cs @@ -37,6 +37,14 @@ public static class ListPool h.Clear(); if (h.Capacity > 16) h.Capacity = 16; }, null, capacity); + + /// + /// Provides a disposable RecycleHelper that contains an item from the pool.
+ /// Will be returned to the pool once .Dispose() is called. + ///
+ /// The generic type of the collection. + /// A RecycleHelper containing an item from the pool. + public static RecycleHelper> Rent() => Shared.Rent(); } public static class HashSetPool @@ -54,6 +62,9 @@ public static class HashSetPool /// public static SharedPool> Create(int capacity = Constants.DEFAULT_CAPACITY) => new(() => new(), h => h.Clear(), null, capacity); + + /// + public static RecycleHelper> Rent() => Shared.Rent(); } public static class StringBuilderPool @@ -87,6 +98,18 @@ public static string RentToString(this IObjectPool pool, ActionIf the action is null. + /// + public static string RentToString(Action action) + => Shared.RentToString(action); + + /// + /// Provides a disposable RecycleHelper that contains a StringBuilder from the pool.
+ /// Will be returned to the pool once .Dispose() is called. + ///
+ /// A RecycleHelper containing a StringBuilder from the pool. + public static RecycleHelper Rent() => Shared.Rent(); } public static class DictionaryPool @@ -105,4 +128,13 @@ public static class DictionaryPool public static SharedPool> Create(int capacity = Constants.DEFAULT_CAPACITY) => new(() => new Dictionary(), h => h.Clear(), null, capacity); + /// + /// Provides a disposable RecycleHelper that contains an item from the pool.
+ /// Will be returned to the pool once .Dispose() is called. + ///
+ /// The generic type of the dictionary keys. + /// The generic type of the dictionary values. + /// A RecycleHelper containing a item from the pool. + public static RecycleHelper> Rent() => Shared.Rent(); + } From dc303758c99a31263a465b70d5c27cf611bc34ff Mon Sep 17 00:00:00 2001 From: electricessence <5899455+electricessence@users.noreply.github.com> Date: Wed, 23 Feb 2022 15:59:29 -0800 Subject: [PATCH 72/83] Cosmetic changes. --- source/Array/InterlockedArrayObjectPool.cs | 2 -- source/Collection/CollectionWrapperObjectPool.cs | 1 - source/Collection/ConcurrentQueueObjectPool.cs | 3 --- source/Collection/ConcurrentQueueObjectPoolSlim.cs | 1 - source/Collection/ConcurrentQueueObjectPoolSlimBase.cs | 1 - source/Collection/ConcurrentStackObjectPool.cs | 3 --- source/Collection/QueueObjectPool.cs | 3 --- source/Collection/StackObjectPool.cs | 6 ++---- source/IObjectPool.cs | 1 - source/ObjectPoolAutoTrimmer.cs | 5 +++-- source/ObjectPoolBase.cs | 6 ++++-- source/RecycleHelper.cs | 1 - source/ReferenceContainer.cs | 2 +- source/SharedPools.cs | 3 +-- source/TrimmableCollectionObjectPoolBase.cs | 2 -- 15 files changed, 11 insertions(+), 29 deletions(-) diff --git a/source/Array/InterlockedArrayObjectPool.cs b/source/Array/InterlockedArrayObjectPool.cs index 48959b8..4c27347 100644 --- a/source/Array/InterlockedArrayObjectPool.cs +++ b/source/Array/InterlockedArrayObjectPool.cs @@ -79,7 +79,6 @@ protected override void OnDispose() Pool = null!; MaxStored = 0; } - } public static class InterlockedArrayObjectPool @@ -101,5 +100,4 @@ public static InterlockedArrayObjectPool CreateAutoDisposal(Func factor public static InterlockedArrayObjectPool CreateAutoDisposal(int capacity = Constants.DEFAULT_CAPACITY) where T : class, IDisposable, new() => CreateAutoDisposal(() => new T(), capacity); - } diff --git a/source/Collection/CollectionWrapperObjectPool.cs b/source/Collection/CollectionWrapperObjectPool.cs index efd7b98..8b2ebbb 100644 --- a/source/Collection/CollectionWrapperObjectPool.cs +++ b/source/Collection/CollectionWrapperObjectPool.cs @@ -55,7 +55,6 @@ protected override bool Receive(T item) return item; } - } public class CollectionWrapperObjectPool diff --git a/source/Collection/ConcurrentQueueObjectPool.cs b/source/Collection/ConcurrentQueueObjectPool.cs index 8116167..a21436d 100644 --- a/source/Collection/ConcurrentQueueObjectPool.cs +++ b/source/Collection/ConcurrentQueueObjectPool.cs @@ -8,7 +8,6 @@ public class ConcurrentQueueObjectPool : TrimmableCollectionObjectPoolBase> where T : class { - public ConcurrentQueueObjectPool( Func factory, Action? recycler, @@ -43,7 +42,6 @@ protected override bool Receive(T item) p.TryDequeue(out var item); return item; } - } public static class ConcurrentQueueObjectPool { @@ -64,5 +62,4 @@ public static ConcurrentQueueObjectPool CreateAutoDisposal(Func factory public static ConcurrentQueueObjectPool CreateAutoDisposal(int capacity = Constants.DEFAULT_CAPACITY) where T : class, IDisposable, new() => CreateAutoDisposal(() => new T(), capacity); - } diff --git a/source/Collection/ConcurrentQueueObjectPoolSlim.cs b/source/Collection/ConcurrentQueueObjectPoolSlim.cs index 30b1171..8ddbffe 100644 --- a/source/Collection/ConcurrentQueueObjectPoolSlim.cs +++ b/source/Collection/ConcurrentQueueObjectPoolSlim.cs @@ -38,5 +38,4 @@ public static ConcurrentQueueObjectPoolSlim CreateAutoDisposal(Func fac public static ConcurrentQueueObjectPoolSlim CreateAutoDisposal(int capacity = Constants.DEFAULT_CAPACITY) where T : class, IDisposable, new() => CreateAutoDisposal(() => new T(), capacity); - } diff --git a/source/Collection/ConcurrentQueueObjectPoolSlimBase.cs b/source/Collection/ConcurrentQueueObjectPoolSlimBase.cs index 6bbd402..24483fa 100644 --- a/source/Collection/ConcurrentQueueObjectPoolSlimBase.cs +++ b/source/Collection/ConcurrentQueueObjectPoolSlimBase.cs @@ -46,5 +46,4 @@ protected override void OnDispose() base.OnDispose(); Pool = null!; } - } diff --git a/source/Collection/ConcurrentStackObjectPool.cs b/source/Collection/ConcurrentStackObjectPool.cs index 22bb5cf..bc01239 100644 --- a/source/Collection/ConcurrentStackObjectPool.cs +++ b/source/Collection/ConcurrentStackObjectPool.cs @@ -7,7 +7,6 @@ public sealed class ConcurrentStackObjectPool : TrimmableCollectionObjectPoolBase> where T : class { - public ConcurrentStackObjectPool( Func factory, Action? recycler, @@ -33,7 +32,6 @@ protected override bool Receive(T item) p.TryPop(out var item); return item; } - } public static class ConcurrentStackObjectPool @@ -55,5 +53,4 @@ public static ConcurrentStackObjectPool CreateAutoDisposal(Func factory public static ConcurrentStackObjectPool CreateAutoDisposal(int capacity = Constants.DEFAULT_CAPACITY) where T : class, IDisposable, new() => CreateAutoDisposal(() => new T(), capacity); - } diff --git a/source/Collection/QueueObjectPool.cs b/source/Collection/QueueObjectPool.cs index 784f2cc..86de515 100644 --- a/source/Collection/QueueObjectPool.cs +++ b/source/Collection/QueueObjectPool.cs @@ -46,12 +46,10 @@ protected override bool Receive(T item) if (p.Count != 0) return p.Dequeue(); } - } return null; } - } public static class QueueObjectPool @@ -73,5 +71,4 @@ public static QueueObjectPool CreateAutoDisposal(Func factory, int capa public static QueueObjectPool CreateAutoDisposal(int capacity = Constants.DEFAULT_CAPACITY) where T : class, IDisposable, new() => CreateAutoDisposal(() => new T(), capacity); - } diff --git a/source/Collection/StackObjectPool.cs b/source/Collection/StackObjectPool.cs index aa07249..9fbf1dd 100644 --- a/source/Collection/StackObjectPool.cs +++ b/source/Collection/StackObjectPool.cs @@ -7,7 +7,6 @@ public sealed class StackObjectPool : TrimmableCollectionObjectPoolBase> where T : class { - public StackObjectPool( Func factory, Action? recycler, @@ -15,7 +14,8 @@ public StackObjectPool( int capacity = DEFAULT_CAPACITY) : base( new Stack(Math.Min(DEFAULT_CAPACITY, capacity)) /* Very very slight speed improvment when capacity is set. */, - factory, recycler, disposer, capacity, false) { } + factory, recycler, disposer, capacity, false) + { } public StackObjectPool( Func factory, @@ -52,7 +52,6 @@ protected override bool Receive(T item) } } - public static class StackObjectPool { public static StackObjectPool Create(Func factory, int capacity = Constants.DEFAULT_CAPACITY) @@ -72,5 +71,4 @@ public static StackObjectPool CreateAutoDisposal(Func factory, int capa public static StackObjectPool CreateAutoDisposal(int capacity = Constants.DEFAULT_CAPACITY) where T : class, IDisposable, new() => CreateAutoDisposal(() => new T(), capacity); - } diff --git a/source/IObjectPool.cs b/source/IObjectPool.cs index e14f461..02fdebf 100644 --- a/source/IObjectPool.cs +++ b/source/IObjectPool.cs @@ -43,7 +43,6 @@ public interface IObjectPool /// The item to return if available. Will be null if none avaialable. T? TryTake(); - /// /// If the pool has an item currently avaialable, removes it from the pool and returns it. /// If none is available, it generates one. diff --git a/source/ObjectPoolAutoTrimmer.cs b/source/ObjectPoolAutoTrimmer.cs index 866f45c..b966f9e 100644 --- a/source/ObjectPoolAutoTrimmer.cs +++ b/source/ObjectPoolAutoTrimmer.cs @@ -5,7 +5,8 @@ namespace Open.Disposable; [SuppressMessage("Style", "IDE0044:Add readonly modifier", Justification = "Micro-optimization for retrieving this value as read-only is slightly slower")] -public class ObjectPoolAutoTrimmer +[SuppressMessage("Roslynator", "RCS1169:Make field read-only.")] +class ObjectPoolAutoTrimmer : DisposableBase { ITrimmableObjectPool _pool; @@ -30,8 +31,8 @@ public class ObjectPoolAutoTrimmer /// /// Constructs an auto-trimming ObjectPool helper. /// - /// The governable object pool to maintain. /// The target size to limit to after a half second timeout. Allowing the pool to still grow to the max size until the trim occurs. + /// The governable object pool to maintain. /// The amount of time to wait/defer trimming. public ObjectPoolAutoTrimmer( ushort trimmedSize, diff --git a/source/ObjectPoolBase.cs b/source/ObjectPoolBase.cs index d561e68..7a78c7a 100644 --- a/source/ObjectPoolBase.cs +++ b/source/ObjectPoolBase.cs @@ -38,7 +38,6 @@ protected ObjectPoolBase( protected readonly Action? Recycler; // Before entering the pool. protected readonly Action? OnDiscarded; // When not able to be used. - // Read-only because if Take() is called after disposal, this still facilitates returing an object. // Allow the GC to do the final cleanup after dispose. protected readonly Func Factory; @@ -69,7 +68,6 @@ protected bool PrepareToReceive(T item) protected virtual void OnReceived() { - } /// @@ -79,9 +77,13 @@ public void Give(T item) if (PrepareToReceive(item) && (SaveToPocket(item) || Receive(item))) + { OnReceived(); + } else + { OnDiscarded?.Invoke(item); + } } #endregion diff --git a/source/RecycleHelper.cs b/source/RecycleHelper.cs index 9085250..4ed132f 100644 --- a/source/RecycleHelper.cs +++ b/source/RecycleHelper.cs @@ -19,7 +19,6 @@ private RecycleHelper(IObjectPool pool, T item) public RecycleHelper(IObjectPool pool) : this(pool ?? throw new ArgumentNullException(nameof(pool)), pool.Take()) { - } private T? _item; diff --git a/source/ReferenceContainer.cs b/source/ReferenceContainer.cs index b0e9c9f..e977bee 100644 --- a/source/ReferenceContainer.cs +++ b/source/ReferenceContainer.cs @@ -48,7 +48,7 @@ public bool SetIfNull(T value) /// true if the value was set; otherwise false. [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool TrySave(T value) - => _value is null && null == Interlocked.CompareExchange(ref _value, value, null); + => _value is null && Interlocked.CompareExchange(ref _value, value, null) == null; /// /// Tries to atomically retrieve the value. diff --git a/source/SharedPools.cs b/source/SharedPools.cs index 6a970b1..4f5c557 100644 --- a/source/SharedPools.cs +++ b/source/SharedPools.cs @@ -44,7 +44,7 @@ public static class ListPool /// /// The generic type of the collection. /// A RecycleHelper containing an item from the pool. - public static RecycleHelper> Rent() => Shared.Rent(); + public static RecycleHelper> Rent() => Shared.Rent(); } public static class HashSetPool @@ -136,5 +136,4 @@ public static SharedPool> Create(int capacity = Constan /// The generic type of the dictionary values. /// A RecycleHelper containing a item from the pool. public static RecycleHelper> Rent() => Shared.Rent(); - } diff --git a/source/TrimmableCollectionObjectPoolBase.cs b/source/TrimmableCollectionObjectPoolBase.cs index e4ae5f7..73b1626 100644 --- a/source/TrimmableCollectionObjectPoolBase.cs +++ b/source/TrimmableCollectionObjectPoolBase.cs @@ -14,7 +14,6 @@ public abstract class TrimmableCollectionObjectPoolBase where T : class where TCollection : class, ICollection { - protected TrimmableCollectionObjectPoolBase( TCollection pool, Func factory, @@ -42,7 +41,6 @@ public abstract class TrimmableGenericCollectionObjectPoolBase where T : class where TCollection : class, ICollection { - protected TrimmableGenericCollectionObjectPoolBase( TCollection pool, Func factory, From f566316f4e21d8bec8e0ab7e8b8818b0d5428a21 Mon Sep 17 00:00:00 2001 From: electricessence <5899455+electricessence@users.noreply.github.com> Date: Thu, 24 Feb 2022 15:59:28 -0800 Subject: [PATCH 73/83] Update TrimmableObjectPoolBase.cs --- source/TrimmableObjectPoolBase.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/source/TrimmableObjectPoolBase.cs b/source/TrimmableObjectPoolBase.cs index ce29307..911ff45 100644 --- a/source/TrimmableObjectPoolBase.cs +++ b/source/TrimmableObjectPoolBase.cs @@ -20,7 +20,7 @@ protected TrimmableObjectPoolBase(Func factory, Action? recycler, Action CountInternal < MaxSize; /// - /// Signal for when an item was taken (actually removed) from the pool. + /// Signal for when an item was taken (actually removed) from the pool. /// public event ObjectPoolResizeEvent? Released; protected void OnReleased(int newSize) @@ -33,7 +33,7 @@ protected void OnReleased(int newSize) protected override void OnReleased() => OnReleased(_countTrackingEnabled ? Interlocked.Decrement(ref _count) : Count); /// - /// Signal for when an item was given (actually accepted) to the pool. + /// Signal for when an item was given (actually accepted) to the pool. /// public event ObjectPoolResizeEvent? Received; From 58835c3a49a17b58c9dfec51cd79e9d7e95cb218 Mon Sep 17 00:00:00 2001 From: electricessence <5899455+electricessence@users.noreply.github.com> Date: Thu, 24 Feb 2022 16:36:47 -0800 Subject: [PATCH 74/83] No functional changes. --- source/Open.Disposable.ObjectPools.csproj | 2 +- tests/ObjectPoolSmokeTests.cs | 50 +++++++++---------- .../Open.Disposable.ObjectPools.Tests.csproj | 8 +-- 3 files changed, 29 insertions(+), 31 deletions(-) diff --git a/source/Open.Disposable.ObjectPools.csproj b/source/Open.Disposable.ObjectPools.csproj index 185c372..e8b5d48 100644 --- a/source/Open.Disposable.ObjectPools.csproj +++ b/source/Open.Disposable.ObjectPools.csproj @@ -43,7 +43,7 @@ Part of the "Open" set of libraries. - + \ No newline at end of file diff --git a/tests/ObjectPoolSmokeTests.cs b/tests/ObjectPoolSmokeTests.cs index e919b4a..d9099c2 100644 --- a/tests/ObjectPoolSmokeTests.cs +++ b/tests/ObjectPoolSmokeTests.cs @@ -1,38 +1,36 @@ using Microsoft.VisualStudio.TestTools.UnitTesting; using System; -namespace Open.Disposable +namespace Open.Disposable; + +[TestClass] +public class ObjectPoolSmokeTests { - [TestClass] - public class ObjectPoolSmokeTests + class IdContainer { - class IdContainer - { - public int ID; - } + public int ID; + } - [TestMethod] - public void OptimisticArrayObjectPool_FactoryTest() - { - var i = 0; - var pool = OptimisticArrayObjectPool.Create(() => new IdContainer { ID = ++i }); - Assert.AreEqual(1, pool.Take().ID); - } + [TestMethod] + public void OptimisticArrayObjectPool_FactoryTest() + { + var i = 0; + var pool = OptimisticArrayObjectPool.Create(() => new IdContainer { ID = ++i }); + Assert.AreEqual(1, pool.Take().ID); + } - [TestMethod] - public void ListPool_RecycleTest() - { - var pool = ListPool.Shared; - var list = pool.Take(); - list.Add(1); - pool.Give(list); - Assert.AreEqual(list, pool.Take()); - Assert.AreEqual(0, list.Count); + [TestMethod] + public void ListPool_RecycleTest() + { + var pool = ListPool.Shared; + var list = pool.Take(); + list.Add(1); + pool.Give(list); + Assert.AreEqual(list, pool.Take()); + Assert.AreEqual(0, list.Count); #pragma warning disable CS0618 // Type or member is obsolete - Assert.ThrowsException(() => pool.Dispose()); + Assert.ThrowsException(() => pool.Dispose()); #pragma warning restore CS0618 // Type or member is obsolete - } - } } diff --git a/tests/Open.Disposable.ObjectPools.Tests.csproj b/tests/Open.Disposable.ObjectPools.Tests.csproj index 51814b6..c48565f 100644 --- a/tests/Open.Disposable.ObjectPools.Tests.csproj +++ b/tests/Open.Disposable.ObjectPools.Tests.csproj @@ -1,7 +1,7 @@  - net5.0 + net6.0 Open.Disposable @@ -14,9 +14,9 @@ - - - + + + From ea0afe96a0d78f7f997ea4d53302da096b57a492 Mon Sep 17 00:00:00 2001 From: electricessence <5899455+electricessence@users.noreply.github.com> Date: Tue, 19 Apr 2022 18:03:47 -0700 Subject: [PATCH 75/83] Eliminate additional allocations from delegates. --- .editorconfig | 6 + benchmarking/Benchmark.cs | 124 +++++++++--------- benchmarking/ConsoleReport.cs | 19 ++- benchmarking/DefaultObjectPool.cs | 74 +++++------ ...Disposable.ObjectPools.Benchmarking.csproj | 2 +- benchmarking/Program.cs | 9 +- source/CountTrackedObjectPoolBase.cs | 1 - source/Open.Disposable.ObjectPools.csproj | 14 +- source/RecycleHelper.cs | 2 + source/Recycler.cs | 6 +- 10 files changed, 136 insertions(+), 121 deletions(-) diff --git a/.editorconfig b/.editorconfig index 880dfa3..78e2b33 100644 --- a/.editorconfig +++ b/.editorconfig @@ -217,3 +217,9 @@ dotnet_naming_style.begins_with_i.required_prefix = I dotnet_naming_style.begins_with_i.required_suffix = dotnet_naming_style.begins_with_i.word_separator = dotnet_naming_style.begins_with_i.capitalization = pascal_case + +dotnet_diagnostic.HAA0303.severity = silent +dotnet_diagnostic.HAA0401.severity = silent +dotnet_diagnostic.HAA0501.severity = silent +dotnet_diagnostic.HAA0502.severity = silent +dotnet_diagnostic.HAA0601.severity = silent \ No newline at end of file diff --git a/benchmarking/Benchmark.cs b/benchmarking/Benchmark.cs index 7e46b23..4d83393 100644 --- a/benchmarking/Benchmark.cs +++ b/benchmarking/Benchmark.cs @@ -7,86 +7,84 @@ using System.Diagnostics; #endif -namespace Open.Disposable.ObjectPools +namespace Open.Disposable.ObjectPools; + +public class Benchmark : BenchmarkBase>> + where T : class { - public class Benchmark : BenchmarkBase>> - where T : class + public Benchmark(uint size, uint repeat, Func> poolFactory) : base(size, repeat, poolFactory) { - public Benchmark(uint size, uint repeat, Func> poolFactory) : base(size, repeat, poolFactory) - { - // Because some pools do comparison checks on values, we have have unique instances/values. - _items = new T[(int)size]; - var pool = Param(); - Parallel.For(0, TestSize, i => _items[i] = pool.Take()); - } + // Because some pools do comparison checks on values, we have have unique instances/values. + _items = new T[(int)size]; + var pool = Param(); + Parallel.For(0, TestSize, i => _items[i] = pool.Take()); + } - readonly T[] _items; + readonly T[] _items; - protected override IEnumerable TestOnceInternal() - { - using var pool = Param(); - if (pool is null) throw new NullReferenceException(); - //yield return TimedResult.Measure("Take From Empty (In Parallel)", () => - //{ - // Parallel.For(0, TestSize, i => _items[i] = pool.Take()); - //}); + protected override IEnumerable TestOnceInternal() + { + using var pool = Param(); + if (pool is null) throw new NullReferenceException(); + //yield return TimedResult.Measure("Take From Empty (In Parallel)", () => + //{ + // Parallel.For(0, TestSize, i => _items[i] = pool.Take()); + //}); - yield return TimedResult.Measure("Give To (In Parallel)", () => - { - // ReSharper disable once AccessToDisposedClosure - Parallel.For(0, TestSize, i => pool.Give(_items[i])); + yield return TimedResult.Measure("Give To (In Parallel)", () => + { + // ReSharper disable once AccessToDisposedClosure + Parallel.For(0, TestSize, i => pool.Give(_items[i])); #if DEBUG - if (pool is DefaultObjectPool) return; - var count = pool.Count; - Debug.Assert(pool is OptimisticArrayObjectPool || count == TestSize, $"Expected {TestSize}, acutal count: {count}"); + if (pool is DefaultObjectPool) return; + var count = pool.Count; + Debug.Assert(pool is OptimisticArrayObjectPool || count == TestSize, $"Expected {TestSize}, acutal count: {count}"); #endif - }); + }); - yield return TimedResult.Measure("Mixed 90%-Take/10%-Give (In Parallel)", () => + yield return TimedResult.Measure("Mixed 90%-Take/10%-Give (In Parallel)", () => + { + Parallel.For(0, TestSize, i => { - Parallel.For(0, TestSize, i => - { - if (i % 10 == 0) - pool.Give(_items[i]); - else - _items[i] = pool.Take(); - }); + if (i % 10 == 0) + pool.Give(_items[i]); + else + _items[i] = pool.Take(); }); + }); - yield return TimedResult.Measure("Mixed 50%-Take/50%-Give (In Parallel)", () => + yield return TimedResult.Measure("Mixed 50%-Take/50%-Give (In Parallel)", () => + { + Parallel.For(0, TestSize, i => { - Parallel.For(0, TestSize, i => - { - if (i % 2 == 0) - _items[i] = pool.Take(); - else - pool.Give(_items[i]); - }); + if (i % 2 == 0) + _items[i] = pool.Take(); + else + pool.Give(_items[i]); }); + }); - yield return TimedResult.Measure("Mixed 10%-Take/90%-Give (In Parallel)", () => + yield return TimedResult.Measure("Mixed 10%-Take/90%-Give (In Parallel)", () => + { + Parallel.For(0, TestSize, i => { - Parallel.For(0, TestSize, i => - { - if (i % 10 == 0) - _items[i] = pool.Take(); - else - pool.Give(_items[i]); - }); + if (i % 10 == 0) + _items[i] = pool.Take(); + else + pool.Give(_items[i]); }); + }); - //if (pool is DefaultObjectPool) yield break; - //yield return TimedResult.Measure("Empty Pool (.TryTake())", () => - //{ - - // while (pool.TryTake() is not null) - // { - // // remaining++; - // } - //}); - } - - public static TimedResult[] Results(uint size, uint repeat, Func> poolFactory) => (new Benchmark(size, repeat, poolFactory)).Result; + //if (pool is DefaultObjectPool) yield break; + //yield return TimedResult.Measure("Empty Pool (.TryTake())", () => + //{ + // while (pool.TryTake() is not null) + // { + // // remaining++; + // } + //}); } + + public static TimedResult[] Results(uint size, uint repeat, Func> poolFactory) => (new Benchmark(size, repeat, poolFactory)).Result; } diff --git a/benchmarking/ConsoleReport.cs b/benchmarking/ConsoleReport.cs index d8037a6..8083759 100644 --- a/benchmarking/ConsoleReport.cs +++ b/benchmarking/ConsoleReport.cs @@ -2,18 +2,17 @@ using System.IO; using System.Text; -namespace Open.Disposable.ObjectPools +namespace Open.Disposable.ObjectPools; + +public class ConsoleReport : Diagnostics.BenchmarkConsoleReport>> + where T : class { - public class ConsoleReport : Diagnostics.BenchmarkConsoleReport>> - where T : class + const int ITERATIONS = 100000; + public ConsoleReport(TextWriter output = null) : base(ITERATIONS, output, Benchmark.Results) { - const int ITERATIONS = 100000; - public ConsoleReport(TextWriter output = null) : base(ITERATIONS, output, Benchmark.Results) - { - } + } - public ConsoleReport(StringBuilder output) : this(new StringWriter(output)) - { - } + public ConsoleReport(StringBuilder output) : this(new StringWriter(output)) + { } } diff --git a/benchmarking/DefaultObjectPool.cs b/benchmarking/DefaultObjectPool.cs index 463249d..1da26f9 100644 --- a/benchmarking/DefaultObjectPool.cs +++ b/benchmarking/DefaultObjectPool.cs @@ -4,57 +4,57 @@ #nullable enable -namespace Open.Disposable.ObjectPools +namespace Open.Disposable.ObjectPools; + +public class DefaultObjectPool + : Microsoft.Extensions.ObjectPool.DefaultObjectPool, IObjectPool + where T : class { - public class DefaultObjectPool : Microsoft.Extensions.ObjectPool.DefaultObjectPool, IObjectPool - where T : class + private readonly Func _factory; + + class Policy : IPooledObjectPolicy { - private readonly Func _factory; + private readonly Func factory; + // private readonly Action? recycler; - class Policy : IPooledObjectPolicy + public Policy(Func factory)//, Action? recycler) { - private readonly Func factory; - // private readonly Action? recycler; - - public Policy(Func factory)//, Action? recycler) - { - if (factory is null) - throw new ArgumentNullException(nameof(factory)); - + if (factory is null) + throw new ArgumentNullException(nameof(factory)); - this.factory = factory; - //this.recycler = recycler; - } - public T Create() => factory(); - - public bool Return(T obj) => - // recycler?.Invoke(obj); - true; + this.factory = factory; + //this.recycler = recycler; } + public T Create() => factory(); - public DefaultObjectPool(Func factory, int capacity = 64) - : base(new Policy(factory), capacity) => _factory = factory; + public bool Return(T obj) => + // recycler?.Invoke(obj); + true; + } - public int Capacity { get; } + public DefaultObjectPool(Func factory, int capacity = 64) + : base(new Policy(factory), capacity) => _factory = factory; - public int Count => throw new NotImplementedException(); + public int Capacity { get; } - public void Dispose() - { - } + public int Count => throw new NotImplementedException(); - public T Generate() => _factory(); + [SuppressMessage("Usage", "CA1816:Dispose methods should call SuppressFinalize")] + public void Dispose() + { + } - public void Give(T item) => Return(item); + public T Generate() => _factory(); - public T Take() => Get(); + public void Give(T item) => Return(item); - public bool TryTake([NotNullWhen(true)] out T item) - { - item = Get(); - return true; - } + public T Take() => Get(); - public T? TryTake() => Get(); + public bool TryTake([NotNullWhen(true)] out T item) + { + item = Get(); + return true; } + + public T? TryTake() => Get(); } diff --git a/benchmarking/Open.Disposable.ObjectPools.Benchmarking.csproj b/benchmarking/Open.Disposable.ObjectPools.Benchmarking.csproj index 871c99b..86d60aa 100644 --- a/benchmarking/Open.Disposable.ObjectPools.Benchmarking.csproj +++ b/benchmarking/Open.Disposable.ObjectPools.Benchmarking.csproj @@ -2,7 +2,7 @@ Exe - netcoreapp3.1 + net6.0 Open.Disposable 1.0.0 diff --git a/benchmarking/Program.cs b/benchmarking/Program.cs index 7c15d5f..6e8f256 100644 --- a/benchmarking/Program.cs +++ b/benchmarking/Program.cs @@ -1,11 +1,11 @@ -using Open.Disposable; -using Open.Disposable.ObjectPools; -using Open.Text.CSV; +using Open.Text.CSV; using System; using System.IO; using System.Text; -class Program +namespace Open.Disposable.ObjectPools.Benchmarks; + +static class Program { static void Main() { @@ -70,5 +70,4 @@ static void Main() Console.WriteLine("(press any key when finished)"); Console.ReadKey(); } - } diff --git a/source/CountTrackedObjectPoolBase.cs b/source/CountTrackedObjectPoolBase.cs index 1e83b36..2b6f7f7 100644 --- a/source/CountTrackedObjectPoolBase.cs +++ b/source/CountTrackedObjectPoolBase.cs @@ -15,7 +15,6 @@ protected CountTrackedObjectPoolBase( int capacity = DEFAULT_CAPACITY) : base(factory, recycler, disposer, capacity) { } - int _count; /// public override int Count => _count; diff --git a/source/Open.Disposable.ObjectPools.csproj b/source/Open.Disposable.ObjectPools.csproj index e8b5d48..0aed77a 100644 --- a/source/Open.Disposable.ObjectPools.csproj +++ b/source/Open.Disposable.ObjectPools.csproj @@ -17,7 +17,7 @@ Part of the "Open" set of libraries. https://github.com/Open-NET-Libraries/Open.Disposable.ObjectPools/ git objectpool;idisposable;thread safe - 2.8.2 + 2.9.0 MIT true @@ -41,8 +41,16 @@ Part of the "Open" set of libraries. - - + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + diff --git a/source/RecycleHelper.cs b/source/RecycleHelper.cs index 4ed132f..fddb433 100644 --- a/source/RecycleHelper.cs +++ b/source/RecycleHelper.cs @@ -31,4 +31,6 @@ public void Dispose() if (i is null) return; _pool.Give(i); } + + public static implicit operator T(RecycleHelper helper) => helper.Item; } diff --git a/source/Recycler.cs b/source/Recycler.cs index 1f776b3..43813f5 100644 --- a/source/Recycler.cs +++ b/source/Recycler.cs @@ -66,7 +66,11 @@ public static Recycler CreateRecycler( Action recycleFunction) where T : class => new(pool, recycleFunction, limit); - public static void Recycle(IRecyclable r) => (r ?? throw new ArgumentNullException(nameof(r))).Recycle(); + private static Action? _recycleDelegate; + // A predefined delegate instead of a method to avoid additional allocations. + public static Action Recycle + => _recycleDelegate ??= (IRecyclable r) + => (r ?? throw new ArgumentNullException(nameof(r))).Recycle(); public static Recycler CreateRecycler( this IObjectPool pool, From 4f3ff8707c053994288205ae5a509741b91c3e7b Mon Sep 17 00:00:00 2001 From: electricessence <5899455+electricessence@users.noreply.github.com> Date: Thu, 14 Nov 2024 09:10:04 -0800 Subject: [PATCH 76/83] Update benchmarks and upgrade dependencies Updated benchmark results for various object pool implementations, showing significant performance improvements. Upgraded several packages across multiple project files: - `Open.Disposable.ObjectPools.Benchmarking.csproj`: - `Microsoft.Extensions.ObjectPool` from 5.0.10 to 9.0.0 - `Open.Text.CSV` from 3.1.0 to 3.4.0 - Removed `OldTest.cs` from compilation - `Open.Disposable.ObjectPools.csproj`: - `Microsoft.SourceLink.GitHub` from 1.1.1 to 8.0.0 - `System.Threading.Channels` from 6.0.0 to 9.0.0 - `Open.Disposable.ObjectPools.Tests.csproj`: - `Microsoft.NET.Test.Sdk` from 17.1.0 to 17.11.1 - `MSTest.TestAdapter` from 2.2.8 to 3.6.3 - `MSTest.TestFramework` from 2.2.8 to 3.6.3 --- benchmarking/BenchmarkResult.csv | 50 ++-- benchmarking/BenchmarkResult.txt | 250 +++++++++--------- ...Disposable.ObjectPools.Benchmarking.csproj | 4 +- source/Open.Disposable.ObjectPools.csproj | 6 +- source/SharedPools.cs | 17 +- .../Open.Disposable.ObjectPools.Tests.csproj | 6 +- 6 files changed, 165 insertions(+), 168 deletions(-) diff --git a/benchmarking/BenchmarkResult.csv b/benchmarking/BenchmarkResult.csv index d40e6c3..7e154d6 100644 --- a/benchmarking/BenchmarkResult.csv +++ b/benchmarking/BenchmarkResult.csv @@ -1,29 +1,29 @@ Batch,Pool Type," To (In Parallel)",d 90%-Take/10%-Give (In Parallel),d 50%-Take/50%-Give (In Parallel),d 10%-Take/90%-Give (In Parallel),L, -Repeat 2400000 for size 4,Microsoft.Extensions.ObjectPool.DefaultObjectPool,00:00:08.7536436,00:00:08.8078884,00:00:08.7786113,00:00:08.7696869,00:00:35.1098302, -Repeat 2400000 for size 4,ConcurrentQueueObjectPoolSlim,00:00:10.9719702,00:00:11.2012940,00:00:10.9157990,00:00:10.9839675,00:00:44.0730307, -Repeat 2400000 for size 4,ConcurrentQueueObjectPool,00:00:11.9357472,00:00:12.0596338,00:00:11.8062979,00:00:11.9269534,00:00:47.7286323, -Repeat 2400000 for size 4,OptimisticArrayObjectPool,00:00:11.3448787,00:00:11.9320120,00:00:11.6168340,00:00:11.5151251,00:00:46.4088498, -Repeat 2400000 for size 4,InterlockedArrayObjectPool,00:00:11.4076831,00:00:12.0350556,00:00:11.5814744,00:00:11.4369017,00:00:46.4611148, -Repeat 960000 for size 10,Microsoft.Extensions.ObjectPool.DefaultObjectPool,00:00:03.1998733,00:00:02.8702785,00:00:02.6837168,00:00:02.9629105,00:00:11.7167791, -Repeat 960000 for size 10,ConcurrentQueueObjectPoolSlim,00:00:02.9492963,00:00:03.2428289,00:00:03.0176898,00:00:03.0319249,00:00:12.2417399, -Repeat 960000 for size 10,ConcurrentQueueObjectPool,00:00:02.8828210,00:00:03.2011413,00:00:02.8822661,00:00:02.9505638,00:00:11.9167922, -Repeat 960000 for size 10,OptimisticArrayObjectPool,00:00:03.0700174,00:00:03.5310316,00:00:02.9952085,00:00:03.0371614,00:00:12.6334189, -Repeat 960000 for size 10,InterlockedArrayObjectPool,00:00:03.2483005,00:00:03.3170740,00:00:02.9369241,00:00:03.2100109,00:00:12.7123095, -Repeat 288000 for size 50,Microsoft.Extensions.ObjectPool.DefaultObjectPool,00:00:04.6794305,00:00:01.7429937,00:00:01.3513779,00:00:03.6357603,00:00:11.4095624, -Repeat 288000 for size 50,ConcurrentQueueObjectPoolSlim,00:00:01.6163124,00:00:01.7762298,00:00:01.3300174,00:00:01.6749817,00:00:06.3975413, -Repeat 288000 for size 50,ConcurrentQueueObjectPool,00:00:01.7832340,00:00:01.9323201,00:00:01.4793899,00:00:01.8423448,00:00:07.0372888, -Repeat 288000 for size 50,OptimisticArrayObjectPool,00:00:01.8823951,00:00:01.9428592,00:00:01.4112225,00:00:01.7877122,00:00:07.0241890, -Repeat 288000 for size 50,InterlockedArrayObjectPool,00:00:02.2154790,00:00:02.0187594,00:00:01.4717288,00:00:02.0234332,00:00:07.7294004, -Repeat 192000 for size 100,Microsoft.Extensions.ObjectPool.DefaultObjectPool,00:00:08.6239380,00:00:01.7883047,00:00:01.2339147,00:00:06.6098240,00:00:18.2559814, -Repeat 192000 for size 100,ConcurrentQueueObjectPoolSlim,00:00:02.6089351,00:00:02.6653084,00:00:01.4554945,00:00:02.4419762,00:00:09.1717142, -Repeat 192000 for size 100,ConcurrentQueueObjectPool,00:00:02.8683847,00:00:02.8497468,00:00:01.7801622,00:00:02.7221468,00:00:10.2204405, -Repeat 192000 for size 100,OptimisticArrayObjectPool,00:00:02.2792049,00:00:02.4557422,00:00:01.4629279,00:00:01.9573853,00:00:08.1552603, -Repeat 192000 for size 100,InterlockedArrayObjectPool,00:00:03.1284602,00:00:02.5353265,00:00:01.5067879,00:00:02.6633642,00:00:09.8339388, -Repeat 153600 for size 250,Microsoft.Extensions.ObjectPool.DefaultObjectPool,00:00:27.6249504,00:00:03.3819394,00:00:02.3777699,00:00:20.7230291,00:00:54.1076888, -Repeat 153600 for size 250,ConcurrentQueueObjectPoolSlim,00:00:06.4197135,00:00:05.5706983,00:00:02.9047885,00:00:05.5148018,00:00:20.4100021, -Repeat 153600 for size 250,ConcurrentQueueObjectPool,00:00:06.6929502,00:00:05.8257810,00:00:03.6463958,00:00:05.7592677,00:00:21.9243947, -Repeat 153600 for size 250,OptimisticArrayObjectPool,00:00:04.5394559,00:00:04.9284349,00:00:02.4790010,00:00:03.4449412,00:00:15.3918330, -Repeat 153600 for size 250,InterlockedArrayObjectPool,00:00:07.2010676,00:00:05.3582282,00:00:02.8324961,00:00:05.4306754,00:00:20.8224673, +Repeat 2400000 for size 4,Microsoft.Extensions.ObjectPool.DefaultObjectPool,00:00:04.6806830,00:00:04.6583573,00:00:04.5553516,00:00:04.5907984,00:00:18.4851903, +Repeat 2400000 for size 4,ConcurrentQueueObjectPoolSlim,00:00:04.6434921,00:00:04.6168234,00:00:04.5416934,00:00:04.5484522,00:00:18.3504611, +Repeat 2400000 for size 4,ConcurrentQueueObjectPool,00:00:04.6675084,00:00:04.7225567,00:00:04.6075077,00:00:04.6475008,00:00:18.6450736, +Repeat 2400000 for size 4,OptimisticArrayObjectPool,00:00:04.4477417,00:00:04.5492105,00:00:04.4136180,00:00:04.4211642,00:00:17.8317344, +Repeat 2400000 for size 4,InterlockedArrayObjectPool,00:00:04.6062887,00:00:04.6160379,00:00:04.5198815,00:00:04.5427923,00:00:18.2850004, +Repeat 960000 for size 10,Microsoft.Extensions.ObjectPool.DefaultObjectPool,00:00:02.9357977,00:00:02.8557461,00:00:02.7329589,00:00:02.8748652,00:00:11.3993679, +Repeat 960000 for size 10,ConcurrentQueueObjectPoolSlim,00:00:02.9089273,00:00:03.0068183,00:00:02.9061875,00:00:02.9211530,00:00:11.7430861, +Repeat 960000 for size 10,ConcurrentQueueObjectPool,00:00:02.9752786,00:00:03.0213148,00:00:02.9525180,00:00:02.9666653,00:00:11.9157767, +Repeat 960000 for size 10,OptimisticArrayObjectPool,00:00:02.9220788,00:00:03.0495182,00:00:02.8634061,00:00:02.9117262,00:00:11.7467293, +Repeat 960000 for size 10,InterlockedArrayObjectPool,00:00:03.0736469,00:00:03.0757118,00:00:02.9158325,00:00:03.0132024,00:00:12.0783936, +Repeat 288000 for size 50,Microsoft.Extensions.ObjectPool.DefaultObjectPool,00:00:04.6116279,00:00:01.4271008,00:00:01.1995458,00:00:03.8620274,00:00:11.1003019, +Repeat 288000 for size 50,ConcurrentQueueObjectPoolSlim,00:00:01.6053882,00:00:01.5539986,00:00:01.3297930,00:00:01.4265693,00:00:05.9157491, +Repeat 288000 for size 50,ConcurrentQueueObjectPool,00:00:01.6341590,00:00:01.5788351,00:00:01.3905222,00:00:01.4808920,00:00:06.0844083, +Repeat 288000 for size 50,OptimisticArrayObjectPool,00:00:01.5190463,00:00:01.5950048,00:00:01.2525564,00:00:01.3887756,00:00:05.7553831, +Repeat 288000 for size 50,InterlockedArrayObjectPool,00:00:01.9498678,00:00:01.6175048,00:00:01.2955638,00:00:01.7124882,00:00:06.5754246, +Repeat 192000 for size 100,Microsoft.Extensions.ObjectPool.DefaultObjectPool,00:00:07.5978322,00:00:01.6466424,00:00:01.0579968,00:00:05.9963234,00:00:16.2987948, +Repeat 192000 for size 100,ConcurrentQueueObjectPoolSlim,00:00:02.1198723,00:00:01.7900611,00:00:01.3785010,00:00:01.6728172,00:00:06.9612516, +Repeat 192000 for size 100,ConcurrentQueueObjectPool,00:00:02.1535484,00:00:01.8115602,00:00:01.4565032,00:00:01.7248405,00:00:07.1464523, +Repeat 192000 for size 100,OptimisticArrayObjectPool,00:00:01.6787071,00:00:01.8529905,00:00:01.1923887,00:00:01.4014375,00:00:06.1255238, +Repeat 192000 for size 100,InterlockedArrayObjectPool,00:00:02.6162570,00:00:01.9290369,00:00:01.2375542,00:00:02.1077313,00:00:07.8905794, +Repeat 153600 for size 250,Microsoft.Extensions.ObjectPool.DefaultObjectPool,00:00:24.8126209,00:00:03.1502726,00:00:01.6389862,00:00:17.9663555,00:00:47.5682352, +Repeat 153600 for size 250,ConcurrentQueueObjectPoolSlim,00:00:04.3281379,00:00:03.5048937,00:00:02.2074143,00:00:03.3092668,00:00:13.3497127, +Repeat 153600 for size 250,ConcurrentQueueObjectPool,00:00:04.3244745,00:00:03.4839443,00:00:02.3115293,00:00:03.3110660,00:00:13.4310141, +Repeat 153600 for size 250,OptimisticArrayObjectPool,00:00:03.3123930,00:00:03.7821922,00:00:01.9659170,00:00:02.3799803,00:00:11.4404825, +Repeat 153600 for size 250,InterlockedArrayObjectPool,00:00:06.8300609,00:00:03.9634994,00:00:01.9064904,00:00:05.0161669,00:00:17.7162176, 0:02.0767858,00:00:00.6488593,00:00:09.9478764, Repeat 12800 for size 2000,ConcurrentStackObjectPool,00:00:00.6708805,00:00:02.5628201,00:00:04.7978301,00:00:04.7563902,00:00:03.4723738,00:00:00.6475781,00:00:16.9078728, Repeat 12800 for size 2000,OptimisticArrayObjectPool,00:00:00.7184635,00:00:07.2485577,00:00:07.4304114,00:00:01.1244839,00:00:04.7572699,00:00:15.8850338,00:00:37.1642202, diff --git a/benchmarking/BenchmarkResult.txt b/benchmarking/BenchmarkResult.txt index 1d2080b..011184b 100644 --- a/benchmarking/BenchmarkResult.txt +++ b/benchmarking/BenchmarkResult.txt @@ -2,194 +2,194 @@ Repeat 2400000 for size 4 ------------------------------------ Microsoft.Extensions.ObjectPool.DefaultObjectPool....... -00:00:08.7536436 Give To (In Parallel) -00:00:08.8078884 Mixed 90%-Take/10%-Give (In Parallel) -00:00:08.7786113 Mixed 50%-Take/50%-Give (In Parallel) -00:00:08.7696869 Mixed 10%-Take/90%-Give (In Parallel) -00:00:35.1098302 TOTAL +00:00:04.6806830 Give To (In Parallel) +00:00:04.6583573 Mixed 90%-Take/10%-Give (In Parallel) +00:00:04.5553516 Mixed 50%-Take/50%-Give (In Parallel) +00:00:04.5907984 Mixed 10%-Take/90%-Give (In Parallel) +00:00:18.4851903 TOTAL ConcurrentQueueObjectPoolSlim........................... -00:00:10.9719702 Give To (In Parallel) -00:00:11.2012940 Mixed 90%-Take/10%-Give (In Parallel) -00:00:10.9157990 Mixed 50%-Take/50%-Give (In Parallel) -00:00:10.9839675 Mixed 10%-Take/90%-Give (In Parallel) -00:00:44.0730307 TOTAL +00:00:04.6434921 Give To (In Parallel) +00:00:04.6168234 Mixed 90%-Take/10%-Give (In Parallel) +00:00:04.5416934 Mixed 50%-Take/50%-Give (In Parallel) +00:00:04.5484522 Mixed 10%-Take/90%-Give (In Parallel) +00:00:18.3504611 TOTAL ConcurrentQueueObjectPool............................... -00:00:11.9357472 Give To (In Parallel) -00:00:12.0596338 Mixed 90%-Take/10%-Give (In Parallel) -00:00:11.8062979 Mixed 50%-Take/50%-Give (In Parallel) -00:00:11.9269534 Mixed 10%-Take/90%-Give (In Parallel) -00:00:47.7286323 TOTAL +00:00:04.6675084 Give To (In Parallel) +00:00:04.7225567 Mixed 90%-Take/10%-Give (In Parallel) +00:00:04.6075077 Mixed 50%-Take/50%-Give (In Parallel) +00:00:04.6475008 Mixed 10%-Take/90%-Give (In Parallel) +00:00:18.6450736 TOTAL OptimisticArrayObjectPool............................... -00:00:11.3448787 Give To (In Parallel) -00:00:11.9320120 Mixed 90%-Take/10%-Give (In Parallel) -00:00:11.6168340 Mixed 50%-Take/50%-Give (In Parallel) -00:00:11.5151251 Mixed 10%-Take/90%-Give (In Parallel) -00:00:46.4088498 TOTAL +00:00:04.4477417 Give To (In Parallel) +00:00:04.5492105 Mixed 90%-Take/10%-Give (In Parallel) +00:00:04.4136180 Mixed 50%-Take/50%-Give (In Parallel) +00:00:04.4211642 Mixed 10%-Take/90%-Give (In Parallel) +00:00:17.8317344 TOTAL InterlockedArrayObjectPool.............................. -00:00:11.4076831 Give To (In Parallel) -00:00:12.0350556 Mixed 90%-Take/10%-Give (In Parallel) -00:00:11.5814744 Mixed 50%-Take/50%-Give (In Parallel) -00:00:11.4369017 Mixed 10%-Take/90%-Give (In Parallel) -00:00:46.4611148 TOTAL +00:00:04.6062887 Give To (In Parallel) +00:00:04.6160379 Mixed 90%-Take/10%-Give (In Parallel) +00:00:04.5198815 Mixed 50%-Take/50%-Give (In Parallel) +00:00:04.5427923 Mixed 10%-Take/90%-Give (In Parallel) +00:00:18.2850004 TOTAL Repeat 960000 for size 10 ------------------------------------ Microsoft.Extensions.ObjectPool.DefaultObjectPool....... -00:00:03.1998733 Give To (In Parallel) -00:00:02.8702785 Mixed 90%-Take/10%-Give (In Parallel) -00:00:02.6837168 Mixed 50%-Take/50%-Give (In Parallel) -00:00:02.9629105 Mixed 10%-Take/90%-Give (In Parallel) -00:00:11.7167791 TOTAL +00:00:02.9357977 Give To (In Parallel) +00:00:02.8557461 Mixed 90%-Take/10%-Give (In Parallel) +00:00:02.7329589 Mixed 50%-Take/50%-Give (In Parallel) +00:00:02.8748652 Mixed 10%-Take/90%-Give (In Parallel) +00:00:11.3993679 TOTAL ConcurrentQueueObjectPoolSlim........................... -00:00:02.9492963 Give To (In Parallel) -00:00:03.2428289 Mixed 90%-Take/10%-Give (In Parallel) -00:00:03.0176898 Mixed 50%-Take/50%-Give (In Parallel) -00:00:03.0319249 Mixed 10%-Take/90%-Give (In Parallel) -00:00:12.2417399 TOTAL +00:00:02.9089273 Give To (In Parallel) +00:00:03.0068183 Mixed 90%-Take/10%-Give (In Parallel) +00:00:02.9061875 Mixed 50%-Take/50%-Give (In Parallel) +00:00:02.9211530 Mixed 10%-Take/90%-Give (In Parallel) +00:00:11.7430861 TOTAL ConcurrentQueueObjectPool............................... -00:00:02.8828210 Give To (In Parallel) -00:00:03.2011413 Mixed 90%-Take/10%-Give (In Parallel) -00:00:02.8822661 Mixed 50%-Take/50%-Give (In Parallel) -00:00:02.9505638 Mixed 10%-Take/90%-Give (In Parallel) -00:00:11.9167922 TOTAL +00:00:02.9752786 Give To (In Parallel) +00:00:03.0213148 Mixed 90%-Take/10%-Give (In Parallel) +00:00:02.9525180 Mixed 50%-Take/50%-Give (In Parallel) +00:00:02.9666653 Mixed 10%-Take/90%-Give (In Parallel) +00:00:11.9157767 TOTAL OptimisticArrayObjectPool............................... -00:00:03.0700174 Give To (In Parallel) -00:00:03.5310316 Mixed 90%-Take/10%-Give (In Parallel) -00:00:02.9952085 Mixed 50%-Take/50%-Give (In Parallel) -00:00:03.0371614 Mixed 10%-Take/90%-Give (In Parallel) -00:00:12.6334189 TOTAL +00:00:02.9220788 Give To (In Parallel) +00:00:03.0495182 Mixed 90%-Take/10%-Give (In Parallel) +00:00:02.8634061 Mixed 50%-Take/50%-Give (In Parallel) +00:00:02.9117262 Mixed 10%-Take/90%-Give (In Parallel) +00:00:11.7467293 TOTAL InterlockedArrayObjectPool.............................. -00:00:03.2483005 Give To (In Parallel) -00:00:03.3170740 Mixed 90%-Take/10%-Give (In Parallel) -00:00:02.9369241 Mixed 50%-Take/50%-Give (In Parallel) -00:00:03.2100109 Mixed 10%-Take/90%-Give (In Parallel) -00:00:12.7123095 TOTAL +00:00:03.0736469 Give To (In Parallel) +00:00:03.0757118 Mixed 90%-Take/10%-Give (In Parallel) +00:00:02.9158325 Mixed 50%-Take/50%-Give (In Parallel) +00:00:03.0132024 Mixed 10%-Take/90%-Give (In Parallel) +00:00:12.0783936 TOTAL Repeat 288000 for size 50 ------------------------------------ Microsoft.Extensions.ObjectPool.DefaultObjectPool....... -00:00:04.6794305 Give To (In Parallel) -00:00:01.7429937 Mixed 90%-Take/10%-Give (In Parallel) -00:00:01.3513779 Mixed 50%-Take/50%-Give (In Parallel) -00:00:03.6357603 Mixed 10%-Take/90%-Give (In Parallel) -00:00:11.4095624 TOTAL +00:00:04.6116279 Give To (In Parallel) +00:00:01.4271008 Mixed 90%-Take/10%-Give (In Parallel) +00:00:01.1995458 Mixed 50%-Take/50%-Give (In Parallel) +00:00:03.8620274 Mixed 10%-Take/90%-Give (In Parallel) +00:00:11.1003019 TOTAL ConcurrentQueueObjectPoolSlim........................... -00:00:01.6163124 Give To (In Parallel) -00:00:01.7762298 Mixed 90%-Take/10%-Give (In Parallel) -00:00:01.3300174 Mixed 50%-Take/50%-Give (In Parallel) -00:00:01.6749817 Mixed 10%-Take/90%-Give (In Parallel) -00:00:06.3975413 TOTAL +00:00:01.6053882 Give To (In Parallel) +00:00:01.5539986 Mixed 90%-Take/10%-Give (In Parallel) +00:00:01.3297930 Mixed 50%-Take/50%-Give (In Parallel) +00:00:01.4265693 Mixed 10%-Take/90%-Give (In Parallel) +00:00:05.9157491 TOTAL ConcurrentQueueObjectPool............................... -00:00:01.7832340 Give To (In Parallel) -00:00:01.9323201 Mixed 90%-Take/10%-Give (In Parallel) -00:00:01.4793899 Mixed 50%-Take/50%-Give (In Parallel) -00:00:01.8423448 Mixed 10%-Take/90%-Give (In Parallel) -00:00:07.0372888 TOTAL +00:00:01.6341590 Give To (In Parallel) +00:00:01.5788351 Mixed 90%-Take/10%-Give (In Parallel) +00:00:01.3905222 Mixed 50%-Take/50%-Give (In Parallel) +00:00:01.4808920 Mixed 10%-Take/90%-Give (In Parallel) +00:00:06.0844083 TOTAL OptimisticArrayObjectPool............................... -00:00:01.8823951 Give To (In Parallel) -00:00:01.9428592 Mixed 90%-Take/10%-Give (In Parallel) -00:00:01.4112225 Mixed 50%-Take/50%-Give (In Parallel) -00:00:01.7877122 Mixed 10%-Take/90%-Give (In Parallel) -00:00:07.0241890 TOTAL +00:00:01.5190463 Give To (In Parallel) +00:00:01.5950048 Mixed 90%-Take/10%-Give (In Parallel) +00:00:01.2525564 Mixed 50%-Take/50%-Give (In Parallel) +00:00:01.3887756 Mixed 10%-Take/90%-Give (In Parallel) +00:00:05.7553831 TOTAL InterlockedArrayObjectPool.............................. -00:00:02.2154790 Give To (In Parallel) -00:00:02.0187594 Mixed 90%-Take/10%-Give (In Parallel) -00:00:01.4717288 Mixed 50%-Take/50%-Give (In Parallel) -00:00:02.0234332 Mixed 10%-Take/90%-Give (In Parallel) -00:00:07.7294004 TOTAL +00:00:01.9498678 Give To (In Parallel) +00:00:01.6175048 Mixed 90%-Take/10%-Give (In Parallel) +00:00:01.2955638 Mixed 50%-Take/50%-Give (In Parallel) +00:00:01.7124882 Mixed 10%-Take/90%-Give (In Parallel) +00:00:06.5754246 TOTAL Repeat 192000 for size 100 ------------------------------------ Microsoft.Extensions.ObjectPool.DefaultObjectPool....... -00:00:08.6239380 Give To (In Parallel) -00:00:01.7883047 Mixed 90%-Take/10%-Give (In Parallel) -00:00:01.2339147 Mixed 50%-Take/50%-Give (In Parallel) -00:00:06.6098240 Mixed 10%-Take/90%-Give (In Parallel) -00:00:18.2559814 TOTAL +00:00:07.5978322 Give To (In Parallel) +00:00:01.6466424 Mixed 90%-Take/10%-Give (In Parallel) +00:00:01.0579968 Mixed 50%-Take/50%-Give (In Parallel) +00:00:05.9963234 Mixed 10%-Take/90%-Give (In Parallel) +00:00:16.2987948 TOTAL ConcurrentQueueObjectPoolSlim........................... -00:00:02.6089351 Give To (In Parallel) -00:00:02.6653084 Mixed 90%-Take/10%-Give (In Parallel) -00:00:01.4554945 Mixed 50%-Take/50%-Give (In Parallel) -00:00:02.4419762 Mixed 10%-Take/90%-Give (In Parallel) -00:00:09.1717142 TOTAL +00:00:02.1198723 Give To (In Parallel) +00:00:01.7900611 Mixed 90%-Take/10%-Give (In Parallel) +00:00:01.3785010 Mixed 50%-Take/50%-Give (In Parallel) +00:00:01.6728172 Mixed 10%-Take/90%-Give (In Parallel) +00:00:06.9612516 TOTAL ConcurrentQueueObjectPool............................... -00:00:02.8683847 Give To (In Parallel) -00:00:02.8497468 Mixed 90%-Take/10%-Give (In Parallel) -00:00:01.7801622 Mixed 50%-Take/50%-Give (In Parallel) -00:00:02.7221468 Mixed 10%-Take/90%-Give (In Parallel) -00:00:10.2204405 TOTAL +00:00:02.1535484 Give To (In Parallel) +00:00:01.8115602 Mixed 90%-Take/10%-Give (In Parallel) +00:00:01.4565032 Mixed 50%-Take/50%-Give (In Parallel) +00:00:01.7248405 Mixed 10%-Take/90%-Give (In Parallel) +00:00:07.1464523 TOTAL OptimisticArrayObjectPool............................... -00:00:02.2792049 Give To (In Parallel) -00:00:02.4557422 Mixed 90%-Take/10%-Give (In Parallel) -00:00:01.4629279 Mixed 50%-Take/50%-Give (In Parallel) -00:00:01.9573853 Mixed 10%-Take/90%-Give (In Parallel) -00:00:08.1552603 TOTAL +00:00:01.6787071 Give To (In Parallel) +00:00:01.8529905 Mixed 90%-Take/10%-Give (In Parallel) +00:00:01.1923887 Mixed 50%-Take/50%-Give (In Parallel) +00:00:01.4014375 Mixed 10%-Take/90%-Give (In Parallel) +00:00:06.1255238 TOTAL InterlockedArrayObjectPool.............................. -00:00:03.1284602 Give To (In Parallel) -00:00:02.5353265 Mixed 90%-Take/10%-Give (In Parallel) -00:00:01.5067879 Mixed 50%-Take/50%-Give (In Parallel) -00:00:02.6633642 Mixed 10%-Take/90%-Give (In Parallel) -00:00:09.8339388 TOTAL +00:00:02.6162570 Give To (In Parallel) +00:00:01.9290369 Mixed 90%-Take/10%-Give (In Parallel) +00:00:01.2375542 Mixed 50%-Take/50%-Give (In Parallel) +00:00:02.1077313 Mixed 10%-Take/90%-Give (In Parallel) +00:00:07.8905794 TOTAL Repeat 153600 for size 250 ------------------------------------ Microsoft.Extensions.ObjectPool.DefaultObjectPool....... -00:00:27.6249504 Give To (In Parallel) -00:00:03.3819394 Mixed 90%-Take/10%-Give (In Parallel) -00:00:02.3777699 Mixed 50%-Take/50%-Give (In Parallel) -00:00:20.7230291 Mixed 10%-Take/90%-Give (In Parallel) -00:00:54.1076888 TOTAL +00:00:24.8126209 Give To (In Parallel) +00:00:03.1502726 Mixed 90%-Take/10%-Give (In Parallel) +00:00:01.6389862 Mixed 50%-Take/50%-Give (In Parallel) +00:00:17.9663555 Mixed 10%-Take/90%-Give (In Parallel) +00:00:47.5682352 TOTAL ConcurrentQueueObjectPoolSlim........................... -00:00:06.4197135 Give To (In Parallel) -00:00:05.5706983 Mixed 90%-Take/10%-Give (In Parallel) -00:00:02.9047885 Mixed 50%-Take/50%-Give (In Parallel) -00:00:05.5148018 Mixed 10%-Take/90%-Give (In Parallel) -00:00:20.4100021 TOTAL +00:00:04.3281379 Give To (In Parallel) +00:00:03.5048937 Mixed 90%-Take/10%-Give (In Parallel) +00:00:02.2074143 Mixed 50%-Take/50%-Give (In Parallel) +00:00:03.3092668 Mixed 10%-Take/90%-Give (In Parallel) +00:00:13.3497127 TOTAL ConcurrentQueueObjectPool............................... -00:00:06.6929502 Give To (In Parallel) -00:00:05.8257810 Mixed 90%-Take/10%-Give (In Parallel) -00:00:03.6463958 Mixed 50%-Take/50%-Give (In Parallel) -00:00:05.7592677 Mixed 10%-Take/90%-Give (In Parallel) -00:00:21.9243947 TOTAL +00:00:04.3244745 Give To (In Parallel) +00:00:03.4839443 Mixed 90%-Take/10%-Give (In Parallel) +00:00:02.3115293 Mixed 50%-Take/50%-Give (In Parallel) +00:00:03.3110660 Mixed 10%-Take/90%-Give (In Parallel) +00:00:13.4310141 TOTAL OptimisticArrayObjectPool............................... -00:00:04.5394559 Give To (In Parallel) -00:00:04.9284349 Mixed 90%-Take/10%-Give (In Parallel) -00:00:02.4790010 Mixed 50%-Take/50%-Give (In Parallel) -00:00:03.4449412 Mixed 10%-Take/90%-Give (In Parallel) -00:00:15.3918330 TOTAL +00:00:03.3123930 Give To (In Parallel) +00:00:03.7821922 Mixed 90%-Take/10%-Give (In Parallel) +00:00:01.9659170 Mixed 50%-Take/50%-Give (In Parallel) +00:00:02.3799803 Mixed 10%-Take/90%-Give (In Parallel) +00:00:11.4404825 TOTAL InterlockedArrayObjectPool.............................. -00:00:07.2010676 Give To (In Parallel) -00:00:05.3582282 Mixed 90%-Take/10%-Give (In Parallel) -00:00:02.8324961 Mixed 50%-Take/50%-Give (In Parallel) -00:00:05.4306754 Mixed 10%-Take/90%-Give (In Parallel) -00:00:20.8224673 TOTAL +00:00:06.8300609 Give To (In Parallel) +00:00:03.9634994 Mixed 90%-Take/10%-Give (In Parallel) +00:00:01.9064904 Mixed 50%-Take/50%-Give (In Parallel) +00:00:05.0161669 Mixed 10%-Take/90%-Give (In Parallel) +00:00:17.7162176 TOTAL diff --git a/benchmarking/Open.Disposable.ObjectPools.Benchmarking.csproj b/benchmarking/Open.Disposable.ObjectPools.Benchmarking.csproj index 86d60aa..4da731d 100644 --- a/benchmarking/Open.Disposable.ObjectPools.Benchmarking.csproj +++ b/benchmarking/Open.Disposable.ObjectPools.Benchmarking.csproj @@ -20,9 +20,9 @@ - + - + diff --git a/source/Open.Disposable.ObjectPools.csproj b/source/Open.Disposable.ObjectPools.csproj index 0aed77a..3ee6cef 100644 --- a/source/Open.Disposable.ObjectPools.csproj +++ b/source/Open.Disposable.ObjectPools.csproj @@ -45,13 +45,13 @@ Part of the "Open" set of libraries. all runtime; build; native; contentfiles; analyzers; buildtransitive - + all runtime; build; native; contentfiles; analyzers; buildtransitive - + - + \ No newline at end of file diff --git a/source/SharedPools.cs b/source/SharedPools.cs index 4f5c557..47b4f00 100644 --- a/source/SharedPools.cs +++ b/source/SharedPools.cs @@ -4,16 +4,14 @@ namespace Open.Disposable; -public class SharedPool : ConcurrentQueueObjectPoolSlimBase +public class SharedPool( + Func factory, + Action? recycler, + Action? disposer, + int capacity) + : ConcurrentQueueObjectPoolSlimBase(factory, recycler, disposer, capacity) where T : class { - public SharedPool( - Func factory, - Action? recycler, - Action? disposer, - int capacity) - : base(factory, recycler, disposer, capacity) { } - [Obsolete("Shared pools do not support disposal.")] public new void Dispose() => throw new NotSupportedException("Shared pools cannot be disposed."); @@ -32,7 +30,7 @@ public static class ListPool /// Creates an object pool for use with lists. /// The list is cleared after being returned. /// - public static SharedPool> Create(int capacity = Constants.DEFAULT_CAPACITY) => new(() => new(), h => + public static SharedPool> Create(int capacity = Constants.DEFAULT_CAPACITY) => new(() => [], h => { h.Clear(); if (h.Capacity > 16) h.Capacity = 16; @@ -42,7 +40,6 @@ public static class ListPool /// Provides a disposable RecycleHelper that contains an item from the pool.
/// Will be returned to the pool once .Dispose() is called. /// - /// The generic type of the collection. /// A RecycleHelper containing an item from the pool. public static RecycleHelper> Rent() => Shared.Rent(); } diff --git a/tests/Open.Disposable.ObjectPools.Tests.csproj b/tests/Open.Disposable.ObjectPools.Tests.csproj index c48565f..14bc40c 100644 --- a/tests/Open.Disposable.ObjectPools.Tests.csproj +++ b/tests/Open.Disposable.ObjectPools.Tests.csproj @@ -14,9 +14,9 @@ - - - + + + From 830ff475c3362f7aef881d52d2be5659df68c97e Mon Sep 17 00:00:00 2001 From: electricessence <5899455+electricessence@users.noreply.github.com> Date: Thu, 14 Nov 2024 11:09:42 -0800 Subject: [PATCH 77/83] Refactor constructors and update target frameworks Refactored multiple classes to use primary constructors, simplifying initialization and reducing boilerplate code. Updated target frameworks to `net9.0` across several projects. Added nullable reference type annotations to improve null safety. Made several properties readonly for better immutability. Introduced a new file for global suppression of specific code analysis warnings. --- benchmarking/Benchmark.cs | 3 +- benchmarking/ConsoleReport.cs | 9 +++-- benchmarking/DefaultObjectPool.cs | 26 ++++---------- benchmarking/GlobalSuppressions.cs | 8 +++++ ...Disposable.ObjectPools.Benchmarking.csproj | 3 +- source/ObjectPoolAutoTrimmer.cs | 2 +- source/Open.Disposable.ObjectPools.csproj | 36 +++++++++++-------- source/RecycleHelper.cs | 3 +- source/RecyclerBase.cs | 2 +- source/ReferenceContainer.cs | 4 +-- source/SharedPools.cs | 7 ++-- .../Open.Disposable.ObjectPools.Tests.csproj | 2 +- 12 files changed, 53 insertions(+), 52 deletions(-) create mode 100644 benchmarking/GlobalSuppressions.cs diff --git a/benchmarking/Benchmark.cs b/benchmarking/Benchmark.cs index 4d83393..654dfcb 100644 --- a/benchmarking/Benchmark.cs +++ b/benchmarking/Benchmark.cs @@ -24,8 +24,7 @@ public Benchmark(uint size, uint repeat, Func> poolFactory) : bas protected override IEnumerable TestOnceInternal() { - using var pool = Param(); - if (pool is null) throw new NullReferenceException(); + using var pool = Param() ?? throw new NullReferenceException(); //yield return TimedResult.Measure("Take From Empty (In Parallel)", () => //{ // Parallel.For(0, TestSize, i => _items[i] = pool.Take()); diff --git a/benchmarking/ConsoleReport.cs b/benchmarking/ConsoleReport.cs index 8083759..c92de46 100644 --- a/benchmarking/ConsoleReport.cs +++ b/benchmarking/ConsoleReport.cs @@ -4,15 +4,14 @@ namespace Open.Disposable.ObjectPools; -public class ConsoleReport : Diagnostics.BenchmarkConsoleReport>> +public class ConsoleReport(TextWriter output = null) + : Diagnostics.BenchmarkConsoleReport>>(ITERATIONS, output, Benchmark.Results) where T : class { const int ITERATIONS = 100000; - public ConsoleReport(TextWriter output = null) : base(ITERATIONS, output, Benchmark.Results) - { - } - public ConsoleReport(StringBuilder output) : this(new StringWriter(output)) + public ConsoleReport(StringBuilder output) + : this(new StringWriter(output)) { } } diff --git a/benchmarking/DefaultObjectPool.cs b/benchmarking/DefaultObjectPool.cs index 1da26f9..775f42c 100644 --- a/benchmarking/DefaultObjectPool.cs +++ b/benchmarking/DefaultObjectPool.cs @@ -6,35 +6,21 @@ namespace Open.Disposable.ObjectPools; -public class DefaultObjectPool - : Microsoft.Extensions.ObjectPool.DefaultObjectPool, IObjectPool +public class DefaultObjectPool(Func factory, int capacity = 64) + : Microsoft.Extensions.ObjectPool.DefaultObjectPool(new Policy(factory), capacity), IObjectPool where T : class { - private readonly Func _factory; - - class Policy : IPooledObjectPolicy + class Policy(Func factory) : IPooledObjectPolicy { - private readonly Func factory; - // private readonly Action? recycler; - - public Policy(Func factory)//, Action? recycler) - { - if (factory is null) - throw new ArgumentNullException(nameof(factory)); + private readonly Func _factory = factory ?? throw new ArgumentNullException(nameof(factory)); - this.factory = factory; - //this.recycler = recycler; - } - public T Create() => factory(); + public T Create() => _factory(); public bool Return(T obj) => // recycler?.Invoke(obj); true; } - public DefaultObjectPool(Func factory, int capacity = 64) - : base(new Policy(factory), capacity) => _factory = factory; - public int Capacity { get; } public int Count => throw new NotImplementedException(); @@ -44,7 +30,7 @@ public void Dispose() { } - public T Generate() => _factory(); + public T Generate() => factory(); public void Give(T item) => Return(item); diff --git a/benchmarking/GlobalSuppressions.cs b/benchmarking/GlobalSuppressions.cs new file mode 100644 index 0000000..f90df4c --- /dev/null +++ b/benchmarking/GlobalSuppressions.cs @@ -0,0 +1,8 @@ +// This file is used by Code Analysis to maintain SuppressMessage +// attributes that are applied to this project. +// Project-level suppressions either have no target or are given +// a specific target and scoped to a namespace, type, member, etc. + +using System.Diagnostics.CodeAnalysis; + +[assembly: SuppressMessage("CodeQuality", "IDE0079:Remove unnecessary suppression", Justification = "", Scope = "member", Target = "~M:Open.Disposable.ObjectPools.DefaultObjectPool`1.Dispose")] diff --git a/benchmarking/Open.Disposable.ObjectPools.Benchmarking.csproj b/benchmarking/Open.Disposable.ObjectPools.Benchmarking.csproj index 4da731d..95c63dd 100644 --- a/benchmarking/Open.Disposable.ObjectPools.Benchmarking.csproj +++ b/benchmarking/Open.Disposable.ObjectPools.Benchmarking.csproj @@ -2,9 +2,10 @@ Exe - net6.0 + net9.0 Open.Disposable 1.0.0 + IDE0130; diff --git a/source/ObjectPoolAutoTrimmer.cs b/source/ObjectPoolAutoTrimmer.cs index b966f9e..3506075 100644 --- a/source/ObjectPoolAutoTrimmer.cs +++ b/source/ObjectPoolAutoTrimmer.cs @@ -68,7 +68,7 @@ protected virtual void Target_TakenFrom(int newSize) _trimmer?.Cancel(); } - void Pool_BeforeDispose(object sender, EventArgs e) => Dispose(); + void Pool_BeforeDispose(object? sender, EventArgs e) => Dispose(); protected virtual void TrimInternal() { diff --git a/source/Open.Disposable.ObjectPools.csproj b/source/Open.Disposable.ObjectPools.csproj index 3ee6cef..dad623f 100644 --- a/source/Open.Disposable.ObjectPools.csproj +++ b/source/Open.Disposable.ObjectPools.csproj @@ -1,38 +1,45 @@  - netstandard2.0;netstandard2.1 + netstandard2.0;netstandard2.1;net9.0 latest enable true Open.Disposable True electricessence - A set of variations on ObjectPool implementations with differing underlying collections. + + A set of variations on ObjectPool implementations with differing underlying collections. -Part of the "Open" set of libraries. - + Part of the "Open" set of libraries. + © electricessence (Oren F.) All rights reserved. https://github.com/Open-NET-Libraries/Open.Disposable.ObjectPools/ https://github.com/Open-NET-Libraries/Open.Disposable.ObjectPools/ git objectpool;idisposable;thread safe - 2.9.0 + 3.0.0 MIT true true snupkg logo.png + README.md + IDE0290;CA1510;IDE0130; - + True - + \ + + + True + @@ -41,17 +48,18 @@ Part of the "Open" set of libraries. - - all - runtime; build; native; contentfiles; analyzers; buildtransitive + + all + runtime; build; native; contentfiles; analyzers; buildtransitive - - all - runtime; build; native; contentfiles; analyzers; buildtransitive + + all + runtime; build; native; contentfiles; analyzers; buildtransitive - + + \ No newline at end of file diff --git a/source/RecycleHelper.cs b/source/RecycleHelper.cs index fddb433..98882cc 100644 --- a/source/RecycleHelper.cs +++ b/source/RecycleHelper.cs @@ -22,7 +22,8 @@ public RecycleHelper(IObjectPool pool) } private T? _item; - public T Item => _item ?? throw new ObjectDisposedException(GetType().ToString()); + public readonly T Item + => _item ?? throw new ObjectDisposedException(GetType().ToString()); public void Dispose() { diff --git a/source/RecyclerBase.cs b/source/RecyclerBase.cs index 26024d4..c46a24b 100644 --- a/source/RecyclerBase.cs +++ b/source/RecyclerBase.cs @@ -28,7 +28,7 @@ protected RecyclerBase( // Could possibly dispose before this line somewhere... But that's just nasty. :P } - void Pool_BeforeDispose(object sender, EventArgs e) => Dispose(); + void Pool_BeforeDispose(object? sender, EventArgs e) => Dispose(); public abstract bool Recycle(T item); diff --git a/source/ReferenceContainer.cs b/source/ReferenceContainer.cs index e977bee..f2b47fa 100644 --- a/source/ReferenceContainer.cs +++ b/source/ReferenceContainer.cs @@ -17,7 +17,7 @@ public interface IReferenceContainer public struct ReferenceContainer : IReferenceContainer where T : class { - public int Capacity => 1; + public readonly int Capacity => 1; T? _value; @@ -26,7 +26,7 @@ public struct ReferenceContainer : IReferenceContainer /// public T? Value { - get => _value; + readonly get => _value; set => _value = value; } diff --git a/source/SharedPools.cs b/source/SharedPools.cs index 47b4f00..2dfd19c 100644 --- a/source/SharedPools.cs +++ b/source/SharedPools.cs @@ -58,7 +58,7 @@ public static class HashSetPool /// The hash-set is cleared after being returned. /// public static SharedPool> Create(int capacity = Constants.DEFAULT_CAPACITY) - => new(() => new(), h => h.Clear(), null, capacity); + => new(() => [], h => h.Clear(), null, capacity); /// public static RecycleHelper> Rent() => Shared.Rent(); @@ -110,6 +110,7 @@ public static string RentToString(Action action) } public static class DictionaryPool + where TKey : notnull { /// /// A shared object pool for use with dictionaries. @@ -123,14 +124,12 @@ public static class DictionaryPool /// The dictionary is cleared after being returned. /// public static SharedPool> Create(int capacity = Constants.DEFAULT_CAPACITY) - => new(() => new Dictionary(), h => h.Clear(), null, capacity); + => new(() => [], h => h.Clear(), null, capacity); /// /// Provides a disposable RecycleHelper that contains an item from the pool.
/// Will be returned to the pool once .Dispose() is called. ///
- /// The generic type of the dictionary keys. - /// The generic type of the dictionary values. /// A RecycleHelper containing a item from the pool. public static RecycleHelper> Rent() => Shared.Rent(); } diff --git a/tests/Open.Disposable.ObjectPools.Tests.csproj b/tests/Open.Disposable.ObjectPools.Tests.csproj index 14bc40c..73cc3c8 100644 --- a/tests/Open.Disposable.ObjectPools.Tests.csproj +++ b/tests/Open.Disposable.ObjectPools.Tests.csproj @@ -1,7 +1,7 @@  - net6.0 + net9.0 Open.Disposable From 85c497f39ec4b82082474ce057c8ccfd37ff22e6 Mon Sep 17 00:00:00 2001 From: electricessence <5899455+electricessence@users.noreply.github.com> Date: Thu, 14 Nov 2024 11:19:11 -0800 Subject: [PATCH 78/83] Refactor locking mechanism to use common SyncRoot Modified locking in CollectionWrapperObjectPool, QueueObjectPool, StackObjectPool, and TrimmableObjectPoolBase to use a common synchronization object (SyncRoot) instead of locking directly on the pool object (p). This change improves thread safety by ensuring consistent locking, reducing the risk of deadlocks and other synchronization issues. Introduced conditional compilation in TrimmableObjectPoolBase for .NET 9.0 or greater to use a Lock type for SyncRoot. --- source/Collection/CollectionWrapperObjectPool.cs | 4 ++-- source/Collection/QueueObjectPool.cs | 4 ++-- source/Collection/StackObjectPool.cs | 4 ++-- source/Open.Disposable.ObjectPools.csproj | 4 ++-- source/TrimmableObjectPoolBase.cs | 6 ++++++ 5 files changed, 14 insertions(+), 8 deletions(-) diff --git a/source/Collection/CollectionWrapperObjectPool.cs b/source/Collection/CollectionWrapperObjectPool.cs index 8b2ebbb..6ce6815 100644 --- a/source/Collection/CollectionWrapperObjectPool.cs +++ b/source/Collection/CollectionWrapperObjectPool.cs @@ -29,7 +29,7 @@ protected override bool Receive(T item) { var p = Pool; if (p is null) return false; - lock (p) + lock (SyncRoot) { // It's possible that the count could exceed MaxSize here, but the risk is negligble as a few over the limit won't hurt. // The lock operation should be quick enough to not pile up too many items. @@ -50,7 +50,7 @@ protected override bool Receive(T item) * This implementation is in place for reference more than practice. Sub classes should override. */ bool wasRemoved; - lock (p!) wasRemoved = p.Remove(item); + lock (SyncRoot) wasRemoved = p!.Remove(item); if (!wasRemoved) goto retry; return item; diff --git a/source/Collection/QueueObjectPool.cs b/source/Collection/QueueObjectPool.cs index 86de515..ce0756e 100644 --- a/source/Collection/QueueObjectPool.cs +++ b/source/Collection/QueueObjectPool.cs @@ -29,7 +29,7 @@ protected override bool Receive(T item) { // It's possible that the count could exceed MaxSize here, but the risk is negligble as a few over the limit won't hurt. // The lock operation should be quick enough to not pile up too many items. - lock (p) p.Enqueue(item); + lock (SyncRoot) p.Enqueue(item); return true; } @@ -41,7 +41,7 @@ protected override bool Receive(T item) var p = Pool; if (p is not null && p.Count != 0) { - lock (p) + lock (SyncRoot) { if (p.Count != 0) return p.Dequeue(); diff --git a/source/Collection/StackObjectPool.cs b/source/Collection/StackObjectPool.cs index 9fbf1dd..1cdfd96 100644 --- a/source/Collection/StackObjectPool.cs +++ b/source/Collection/StackObjectPool.cs @@ -29,7 +29,7 @@ protected override bool Receive(T item) { // It's possible that the count could exceed MaxSize here, but the risk is negligble as a few over the limit won't hurt. // The lock operation should be quick enough to not pile up too many items. - lock (p) p.Push(item); + lock (SyncRoot) p.Push(item); return true; } @@ -42,7 +42,7 @@ protected override bool Receive(T item) if (p is null || p.Count == 0) return null; - lock (p) + lock (SyncRoot) { if (p.Count != 0) return p.Pop(); diff --git a/source/Open.Disposable.ObjectPools.csproj b/source/Open.Disposable.ObjectPools.csproj index dad623f..0a8124d 100644 --- a/source/Open.Disposable.ObjectPools.csproj +++ b/source/Open.Disposable.ObjectPools.csproj @@ -58,8 +58,8 @@ - - + + \ No newline at end of file diff --git a/source/TrimmableObjectPoolBase.cs b/source/TrimmableObjectPoolBase.cs index 911ff45..f37ee39 100644 --- a/source/TrimmableObjectPoolBase.cs +++ b/source/TrimmableObjectPoolBase.cs @@ -12,6 +12,12 @@ public abstract class TrimmableObjectPoolBase : ObjectPoolBase, ITrimmable protected TrimmableObjectPoolBase(Func factory, Action? recycler, Action? disposer, int capacity, bool countTrackingEnabled = true) : base(factory, recycler, disposer, capacity) => _countTrackingEnabled = countTrackingEnabled; +#if NET9_0_OR_GREATER + protected readonly Lock SyncRoot = new(); +#else + protected readonly object SyncRoot = new(); +#endif + int _count; readonly bool _countTrackingEnabled; // When true this enables tracking the number of entries entering and exiting the pool instead of calling '.Count'. From ba2d5f78a5d809b4dfae79e15e749635b91ae976 Mon Sep 17 00:00:00 2001 From: electricessence <5899455+electricessence@users.noreply.github.com> Date: Thu, 14 Nov 2024 12:11:34 -0800 Subject: [PATCH 79/83] Improve object pool performance and update benchmarks Updated benchmark results for various object pool implementations, showing improved performance metrics. Changed `Pool` field type in `InterlockedArrayObjectPool` to `Memory>` for better memory management. Updated `Store` method in `OptimisticArrayObjectPool` to use `ReadOnlySpan>`. Added `System.Memory` package reference in `Open.Disposable.ObjectPools.csproj`. Changed `SharedPool` to inherit from `OptimisticArrayObjectPool`. Simplified exception assertions in `ObjectPoolSmokeTests`. --- benchmarking/BenchmarkResult.csv | 50 ++--- benchmarking/BenchmarkResult.txt | 250 ++++++++++----------- source/Array/InterlockedArrayObjectPool.cs | 32 ++- source/Array/OptimisticArrayObjectPool.cs | 3 +- source/Open.Disposable.ObjectPools.csproj | 3 +- source/SharedPools.cs | 13 +- tests/ObjectPoolSmokeTests.cs | 2 +- 7 files changed, 180 insertions(+), 173 deletions(-) diff --git a/benchmarking/BenchmarkResult.csv b/benchmarking/BenchmarkResult.csv index 7e154d6..3747579 100644 --- a/benchmarking/BenchmarkResult.csv +++ b/benchmarking/BenchmarkResult.csv @@ -1,29 +1,29 @@ Batch,Pool Type," To (In Parallel)",d 90%-Take/10%-Give (In Parallel),d 50%-Take/50%-Give (In Parallel),d 10%-Take/90%-Give (In Parallel),L, -Repeat 2400000 for size 4,Microsoft.Extensions.ObjectPool.DefaultObjectPool,00:00:04.6806830,00:00:04.6583573,00:00:04.5553516,00:00:04.5907984,00:00:18.4851903, -Repeat 2400000 for size 4,ConcurrentQueueObjectPoolSlim,00:00:04.6434921,00:00:04.6168234,00:00:04.5416934,00:00:04.5484522,00:00:18.3504611, -Repeat 2400000 for size 4,ConcurrentQueueObjectPool,00:00:04.6675084,00:00:04.7225567,00:00:04.6075077,00:00:04.6475008,00:00:18.6450736, -Repeat 2400000 for size 4,OptimisticArrayObjectPool,00:00:04.4477417,00:00:04.5492105,00:00:04.4136180,00:00:04.4211642,00:00:17.8317344, -Repeat 2400000 for size 4,InterlockedArrayObjectPool,00:00:04.6062887,00:00:04.6160379,00:00:04.5198815,00:00:04.5427923,00:00:18.2850004, -Repeat 960000 for size 10,Microsoft.Extensions.ObjectPool.DefaultObjectPool,00:00:02.9357977,00:00:02.8557461,00:00:02.7329589,00:00:02.8748652,00:00:11.3993679, -Repeat 960000 for size 10,ConcurrentQueueObjectPoolSlim,00:00:02.9089273,00:00:03.0068183,00:00:02.9061875,00:00:02.9211530,00:00:11.7430861, -Repeat 960000 for size 10,ConcurrentQueueObjectPool,00:00:02.9752786,00:00:03.0213148,00:00:02.9525180,00:00:02.9666653,00:00:11.9157767, -Repeat 960000 for size 10,OptimisticArrayObjectPool,00:00:02.9220788,00:00:03.0495182,00:00:02.8634061,00:00:02.9117262,00:00:11.7467293, -Repeat 960000 for size 10,InterlockedArrayObjectPool,00:00:03.0736469,00:00:03.0757118,00:00:02.9158325,00:00:03.0132024,00:00:12.0783936, -Repeat 288000 for size 50,Microsoft.Extensions.ObjectPool.DefaultObjectPool,00:00:04.6116279,00:00:01.4271008,00:00:01.1995458,00:00:03.8620274,00:00:11.1003019, -Repeat 288000 for size 50,ConcurrentQueueObjectPoolSlim,00:00:01.6053882,00:00:01.5539986,00:00:01.3297930,00:00:01.4265693,00:00:05.9157491, -Repeat 288000 for size 50,ConcurrentQueueObjectPool,00:00:01.6341590,00:00:01.5788351,00:00:01.3905222,00:00:01.4808920,00:00:06.0844083, -Repeat 288000 for size 50,OptimisticArrayObjectPool,00:00:01.5190463,00:00:01.5950048,00:00:01.2525564,00:00:01.3887756,00:00:05.7553831, -Repeat 288000 for size 50,InterlockedArrayObjectPool,00:00:01.9498678,00:00:01.6175048,00:00:01.2955638,00:00:01.7124882,00:00:06.5754246, -Repeat 192000 for size 100,Microsoft.Extensions.ObjectPool.DefaultObjectPool,00:00:07.5978322,00:00:01.6466424,00:00:01.0579968,00:00:05.9963234,00:00:16.2987948, -Repeat 192000 for size 100,ConcurrentQueueObjectPoolSlim,00:00:02.1198723,00:00:01.7900611,00:00:01.3785010,00:00:01.6728172,00:00:06.9612516, -Repeat 192000 for size 100,ConcurrentQueueObjectPool,00:00:02.1535484,00:00:01.8115602,00:00:01.4565032,00:00:01.7248405,00:00:07.1464523, -Repeat 192000 for size 100,OptimisticArrayObjectPool,00:00:01.6787071,00:00:01.8529905,00:00:01.1923887,00:00:01.4014375,00:00:06.1255238, -Repeat 192000 for size 100,InterlockedArrayObjectPool,00:00:02.6162570,00:00:01.9290369,00:00:01.2375542,00:00:02.1077313,00:00:07.8905794, -Repeat 153600 for size 250,Microsoft.Extensions.ObjectPool.DefaultObjectPool,00:00:24.8126209,00:00:03.1502726,00:00:01.6389862,00:00:17.9663555,00:00:47.5682352, -Repeat 153600 for size 250,ConcurrentQueueObjectPoolSlim,00:00:04.3281379,00:00:03.5048937,00:00:02.2074143,00:00:03.3092668,00:00:13.3497127, -Repeat 153600 for size 250,ConcurrentQueueObjectPool,00:00:04.3244745,00:00:03.4839443,00:00:02.3115293,00:00:03.3110660,00:00:13.4310141, -Repeat 153600 for size 250,OptimisticArrayObjectPool,00:00:03.3123930,00:00:03.7821922,00:00:01.9659170,00:00:02.3799803,00:00:11.4404825, -Repeat 153600 for size 250,InterlockedArrayObjectPool,00:00:06.8300609,00:00:03.9634994,00:00:01.9064904,00:00:05.0161669,00:00:17.7162176, +Repeat 2400000 for size 4,Microsoft.Extensions.ObjectPool.DefaultObjectPool,00:00:03.8980333,00:00:03.9366455,00:00:03.9072529,00:00:03.8850033,00:00:15.6269350, +Repeat 2400000 for size 4,ConcurrentQueueObjectPoolSlim,00:00:03.8494058,00:00:03.9230416,00:00:03.8901577,00:00:03.8867848,00:00:15.5493899, +Repeat 2400000 for size 4,ConcurrentQueueObjectPool,00:00:03.9883122,00:00:04.0076765,00:00:03.9682549,00:00:03.9925014,00:00:15.9567450, +Repeat 2400000 for size 4,OptimisticArrayObjectPool,00:00:03.9568135,00:00:04.2170893,00:00:04.0558332,00:00:03.9863018,00:00:16.2160378, +Repeat 2400000 for size 4,InterlockedArrayObjectPool,00:00:03.9797846,00:00:04.2078410,00:00:04.0431276,00:00:03.9695552,00:00:16.2003084, +Repeat 960000 for size 10,Microsoft.Extensions.ObjectPool.DefaultObjectPool,00:00:02.4087556,00:00:02.5079212,00:00:02.4920962,00:00:02.4665527,00:00:09.8753257, +Repeat 960000 for size 10,ConcurrentQueueObjectPoolSlim,00:00:02.4513135,00:00:02.6173013,00:00:02.5037361,00:00:02.5337465,00:00:10.1060974, +Repeat 960000 for size 10,ConcurrentQueueObjectPool,00:00:02.5503897,00:00:02.6621958,00:00:02.5548883,00:00:02.5976682,00:00:10.3651420, +Repeat 960000 for size 10,OptimisticArrayObjectPool,00:00:02.4579233,00:00:02.7069571,00:00:02.4949206,00:00:02.4763234,00:00:10.1361244, +Repeat 960000 for size 10,InterlockedArrayObjectPool,00:00:02.5692140,00:00:02.7507886,00:00:02.5367601,00:00:02.5870651,00:00:10.4438278, +Repeat 288000 for size 50,Microsoft.Extensions.ObjectPool.DefaultObjectPool,00:00:01.3692996,00:00:01.3587399,00:00:01.0988709,00:00:01.2363235,00:00:05.0632339, +Repeat 288000 for size 50,ConcurrentQueueObjectPoolSlim,00:00:01.4405049,00:00:01.4442130,00:00:01.2083265,00:00:01.2814624,00:00:05.3745068, +Repeat 288000 for size 50,ConcurrentQueueObjectPool,00:00:01.5758473,00:00:01.5021273,00:00:01.3193568,00:00:01.3970673,00:00:05.7943987, +Repeat 288000 for size 50,OptimisticArrayObjectPool,00:00:01.0780899,00:00:01.2999000,00:00:01.1287828,00:00:01.1037828,00:00:04.6105555, +Repeat 288000 for size 50,InterlockedArrayObjectPool,00:00:01.1225109,00:00:01.2954306,00:00:01.1756768,00:00:01.1486593,00:00:04.7422776, +Repeat 192000 for size 100,Microsoft.Extensions.ObjectPool.DefaultObjectPool,00:00:01.8633666,00:00:01.6135337,00:00:01.0777082,00:00:01.4542875,00:00:06.0088960, +Repeat 192000 for size 100,ConcurrentQueueObjectPoolSlim,00:00:01.9024162,00:00:01.6790522,00:00:01.2862753,00:00:01.5210385,00:00:06.3887822, +Repeat 192000 for size 100,ConcurrentQueueObjectPool,00:00:02.0542650,00:00:01.7484990,00:00:01.4418580,00:00:01.6489656,00:00:06.8935876, +Repeat 192000 for size 100,OptimisticArrayObjectPool,00:00:00.8471340,00:00:01.0865546,00:00:00.9761332,00:00:00.8651384,00:00:03.7749602, +Repeat 192000 for size 100,InterlockedArrayObjectPool,00:00:00.9431637,00:00:01.1474065,00:00:01.0727406,00:00:00.9714928,00:00:04.1348036, +Repeat 153600 for size 250,Microsoft.Extensions.ObjectPool.DefaultObjectPool,00:00:04.0471422,00:00:03.2658468,00:00:01.8253285,00:00:03.0743397,00:00:12.2126572, +Repeat 153600 for size 250,ConcurrentQueueObjectPoolSlim,00:00:04.0982231,00:00:03.2845860,00:00:02.1882361,00:00:03.0712649,00:00:12.6423101, +Repeat 153600 for size 250,ConcurrentQueueObjectPool,00:00:04.1283099,00:00:03.2866676,00:00:02.3430258,00:00:03.1057312,00:00:12.8637345, +Repeat 153600 for size 250,OptimisticArrayObjectPool,00:00:00.8952769,00:00:01.2805030,00:00:01.2960246,00:00:01.0256400,00:00:04.4974445, +Repeat 153600 for size 250,InterlockedArrayObjectPool,00:00:01.0063385,00:00:01.3870075,00:00:01.4698480,00:00:01.1912251,00:00:05.0544191, 0:02.0767858,00:00:00.6488593,00:00:09.9478764, Repeat 12800 for size 2000,ConcurrentStackObjectPool,00:00:00.6708805,00:00:02.5628201,00:00:04.7978301,00:00:04.7563902,00:00:03.4723738,00:00:00.6475781,00:00:16.9078728, Repeat 12800 for size 2000,OptimisticArrayObjectPool,00:00:00.7184635,00:00:07.2485577,00:00:07.4304114,00:00:01.1244839,00:00:04.7572699,00:00:15.8850338,00:00:37.1642202, diff --git a/benchmarking/BenchmarkResult.txt b/benchmarking/BenchmarkResult.txt index 011184b..b0427c4 100644 --- a/benchmarking/BenchmarkResult.txt +++ b/benchmarking/BenchmarkResult.txt @@ -2,194 +2,194 @@ Repeat 2400000 for size 4 ------------------------------------ Microsoft.Extensions.ObjectPool.DefaultObjectPool....... -00:00:04.6806830 Give To (In Parallel) -00:00:04.6583573 Mixed 90%-Take/10%-Give (In Parallel) -00:00:04.5553516 Mixed 50%-Take/50%-Give (In Parallel) -00:00:04.5907984 Mixed 10%-Take/90%-Give (In Parallel) -00:00:18.4851903 TOTAL +00:00:03.8980333 Give To (In Parallel) +00:00:03.9366455 Mixed 90%-Take/10%-Give (In Parallel) +00:00:03.9072529 Mixed 50%-Take/50%-Give (In Parallel) +00:00:03.8850033 Mixed 10%-Take/90%-Give (In Parallel) +00:00:15.6269350 TOTAL ConcurrentQueueObjectPoolSlim........................... -00:00:04.6434921 Give To (In Parallel) -00:00:04.6168234 Mixed 90%-Take/10%-Give (In Parallel) -00:00:04.5416934 Mixed 50%-Take/50%-Give (In Parallel) -00:00:04.5484522 Mixed 10%-Take/90%-Give (In Parallel) -00:00:18.3504611 TOTAL +00:00:03.8494058 Give To (In Parallel) +00:00:03.9230416 Mixed 90%-Take/10%-Give (In Parallel) +00:00:03.8901577 Mixed 50%-Take/50%-Give (In Parallel) +00:00:03.8867848 Mixed 10%-Take/90%-Give (In Parallel) +00:00:15.5493899 TOTAL ConcurrentQueueObjectPool............................... -00:00:04.6675084 Give To (In Parallel) -00:00:04.7225567 Mixed 90%-Take/10%-Give (In Parallel) -00:00:04.6075077 Mixed 50%-Take/50%-Give (In Parallel) -00:00:04.6475008 Mixed 10%-Take/90%-Give (In Parallel) -00:00:18.6450736 TOTAL +00:00:03.9883122 Give To (In Parallel) +00:00:04.0076765 Mixed 90%-Take/10%-Give (In Parallel) +00:00:03.9682549 Mixed 50%-Take/50%-Give (In Parallel) +00:00:03.9925014 Mixed 10%-Take/90%-Give (In Parallel) +00:00:15.9567450 TOTAL OptimisticArrayObjectPool............................... -00:00:04.4477417 Give To (In Parallel) -00:00:04.5492105 Mixed 90%-Take/10%-Give (In Parallel) -00:00:04.4136180 Mixed 50%-Take/50%-Give (In Parallel) -00:00:04.4211642 Mixed 10%-Take/90%-Give (In Parallel) -00:00:17.8317344 TOTAL +00:00:03.9568135 Give To (In Parallel) +00:00:04.2170893 Mixed 90%-Take/10%-Give (In Parallel) +00:00:04.0558332 Mixed 50%-Take/50%-Give (In Parallel) +00:00:03.9863018 Mixed 10%-Take/90%-Give (In Parallel) +00:00:16.2160378 TOTAL InterlockedArrayObjectPool.............................. -00:00:04.6062887 Give To (In Parallel) -00:00:04.6160379 Mixed 90%-Take/10%-Give (In Parallel) -00:00:04.5198815 Mixed 50%-Take/50%-Give (In Parallel) -00:00:04.5427923 Mixed 10%-Take/90%-Give (In Parallel) -00:00:18.2850004 TOTAL +00:00:03.9797846 Give To (In Parallel) +00:00:04.2078410 Mixed 90%-Take/10%-Give (In Parallel) +00:00:04.0431276 Mixed 50%-Take/50%-Give (In Parallel) +00:00:03.9695552 Mixed 10%-Take/90%-Give (In Parallel) +00:00:16.2003084 TOTAL Repeat 960000 for size 10 ------------------------------------ Microsoft.Extensions.ObjectPool.DefaultObjectPool....... -00:00:02.9357977 Give To (In Parallel) -00:00:02.8557461 Mixed 90%-Take/10%-Give (In Parallel) -00:00:02.7329589 Mixed 50%-Take/50%-Give (In Parallel) -00:00:02.8748652 Mixed 10%-Take/90%-Give (In Parallel) -00:00:11.3993679 TOTAL +00:00:02.4087556 Give To (In Parallel) +00:00:02.5079212 Mixed 90%-Take/10%-Give (In Parallel) +00:00:02.4920962 Mixed 50%-Take/50%-Give (In Parallel) +00:00:02.4665527 Mixed 10%-Take/90%-Give (In Parallel) +00:00:09.8753257 TOTAL ConcurrentQueueObjectPoolSlim........................... -00:00:02.9089273 Give To (In Parallel) -00:00:03.0068183 Mixed 90%-Take/10%-Give (In Parallel) -00:00:02.9061875 Mixed 50%-Take/50%-Give (In Parallel) -00:00:02.9211530 Mixed 10%-Take/90%-Give (In Parallel) -00:00:11.7430861 TOTAL +00:00:02.4513135 Give To (In Parallel) +00:00:02.6173013 Mixed 90%-Take/10%-Give (In Parallel) +00:00:02.5037361 Mixed 50%-Take/50%-Give (In Parallel) +00:00:02.5337465 Mixed 10%-Take/90%-Give (In Parallel) +00:00:10.1060974 TOTAL ConcurrentQueueObjectPool............................... -00:00:02.9752786 Give To (In Parallel) -00:00:03.0213148 Mixed 90%-Take/10%-Give (In Parallel) -00:00:02.9525180 Mixed 50%-Take/50%-Give (In Parallel) -00:00:02.9666653 Mixed 10%-Take/90%-Give (In Parallel) -00:00:11.9157767 TOTAL +00:00:02.5503897 Give To (In Parallel) +00:00:02.6621958 Mixed 90%-Take/10%-Give (In Parallel) +00:00:02.5548883 Mixed 50%-Take/50%-Give (In Parallel) +00:00:02.5976682 Mixed 10%-Take/90%-Give (In Parallel) +00:00:10.3651420 TOTAL OptimisticArrayObjectPool............................... -00:00:02.9220788 Give To (In Parallel) -00:00:03.0495182 Mixed 90%-Take/10%-Give (In Parallel) -00:00:02.8634061 Mixed 50%-Take/50%-Give (In Parallel) -00:00:02.9117262 Mixed 10%-Take/90%-Give (In Parallel) -00:00:11.7467293 TOTAL +00:00:02.4579233 Give To (In Parallel) +00:00:02.7069571 Mixed 90%-Take/10%-Give (In Parallel) +00:00:02.4949206 Mixed 50%-Take/50%-Give (In Parallel) +00:00:02.4763234 Mixed 10%-Take/90%-Give (In Parallel) +00:00:10.1361244 TOTAL InterlockedArrayObjectPool.............................. -00:00:03.0736469 Give To (In Parallel) -00:00:03.0757118 Mixed 90%-Take/10%-Give (In Parallel) -00:00:02.9158325 Mixed 50%-Take/50%-Give (In Parallel) -00:00:03.0132024 Mixed 10%-Take/90%-Give (In Parallel) -00:00:12.0783936 TOTAL +00:00:02.5692140 Give To (In Parallel) +00:00:02.7507886 Mixed 90%-Take/10%-Give (In Parallel) +00:00:02.5367601 Mixed 50%-Take/50%-Give (In Parallel) +00:00:02.5870651 Mixed 10%-Take/90%-Give (In Parallel) +00:00:10.4438278 TOTAL Repeat 288000 for size 50 ------------------------------------ Microsoft.Extensions.ObjectPool.DefaultObjectPool....... -00:00:04.6116279 Give To (In Parallel) -00:00:01.4271008 Mixed 90%-Take/10%-Give (In Parallel) -00:00:01.1995458 Mixed 50%-Take/50%-Give (In Parallel) -00:00:03.8620274 Mixed 10%-Take/90%-Give (In Parallel) -00:00:11.1003019 TOTAL +00:00:01.3692996 Give To (In Parallel) +00:00:01.3587399 Mixed 90%-Take/10%-Give (In Parallel) +00:00:01.0988709 Mixed 50%-Take/50%-Give (In Parallel) +00:00:01.2363235 Mixed 10%-Take/90%-Give (In Parallel) +00:00:05.0632339 TOTAL ConcurrentQueueObjectPoolSlim........................... -00:00:01.6053882 Give To (In Parallel) -00:00:01.5539986 Mixed 90%-Take/10%-Give (In Parallel) -00:00:01.3297930 Mixed 50%-Take/50%-Give (In Parallel) -00:00:01.4265693 Mixed 10%-Take/90%-Give (In Parallel) -00:00:05.9157491 TOTAL +00:00:01.4405049 Give To (In Parallel) +00:00:01.4442130 Mixed 90%-Take/10%-Give (In Parallel) +00:00:01.2083265 Mixed 50%-Take/50%-Give (In Parallel) +00:00:01.2814624 Mixed 10%-Take/90%-Give (In Parallel) +00:00:05.3745068 TOTAL ConcurrentQueueObjectPool............................... -00:00:01.6341590 Give To (In Parallel) -00:00:01.5788351 Mixed 90%-Take/10%-Give (In Parallel) -00:00:01.3905222 Mixed 50%-Take/50%-Give (In Parallel) -00:00:01.4808920 Mixed 10%-Take/90%-Give (In Parallel) -00:00:06.0844083 TOTAL +00:00:01.5758473 Give To (In Parallel) +00:00:01.5021273 Mixed 90%-Take/10%-Give (In Parallel) +00:00:01.3193568 Mixed 50%-Take/50%-Give (In Parallel) +00:00:01.3970673 Mixed 10%-Take/90%-Give (In Parallel) +00:00:05.7943987 TOTAL OptimisticArrayObjectPool............................... -00:00:01.5190463 Give To (In Parallel) -00:00:01.5950048 Mixed 90%-Take/10%-Give (In Parallel) -00:00:01.2525564 Mixed 50%-Take/50%-Give (In Parallel) -00:00:01.3887756 Mixed 10%-Take/90%-Give (In Parallel) -00:00:05.7553831 TOTAL +00:00:01.0780899 Give To (In Parallel) +00:00:01.2999000 Mixed 90%-Take/10%-Give (In Parallel) +00:00:01.1287828 Mixed 50%-Take/50%-Give (In Parallel) +00:00:01.1037828 Mixed 10%-Take/90%-Give (In Parallel) +00:00:04.6105555 TOTAL InterlockedArrayObjectPool.............................. -00:00:01.9498678 Give To (In Parallel) -00:00:01.6175048 Mixed 90%-Take/10%-Give (In Parallel) -00:00:01.2955638 Mixed 50%-Take/50%-Give (In Parallel) -00:00:01.7124882 Mixed 10%-Take/90%-Give (In Parallel) -00:00:06.5754246 TOTAL +00:00:01.1225109 Give To (In Parallel) +00:00:01.2954306 Mixed 90%-Take/10%-Give (In Parallel) +00:00:01.1756768 Mixed 50%-Take/50%-Give (In Parallel) +00:00:01.1486593 Mixed 10%-Take/90%-Give (In Parallel) +00:00:04.7422776 TOTAL Repeat 192000 for size 100 ------------------------------------ Microsoft.Extensions.ObjectPool.DefaultObjectPool....... -00:00:07.5978322 Give To (In Parallel) -00:00:01.6466424 Mixed 90%-Take/10%-Give (In Parallel) -00:00:01.0579968 Mixed 50%-Take/50%-Give (In Parallel) -00:00:05.9963234 Mixed 10%-Take/90%-Give (In Parallel) -00:00:16.2987948 TOTAL +00:00:01.8633666 Give To (In Parallel) +00:00:01.6135337 Mixed 90%-Take/10%-Give (In Parallel) +00:00:01.0777082 Mixed 50%-Take/50%-Give (In Parallel) +00:00:01.4542875 Mixed 10%-Take/90%-Give (In Parallel) +00:00:06.0088960 TOTAL ConcurrentQueueObjectPoolSlim........................... -00:00:02.1198723 Give To (In Parallel) -00:00:01.7900611 Mixed 90%-Take/10%-Give (In Parallel) -00:00:01.3785010 Mixed 50%-Take/50%-Give (In Parallel) -00:00:01.6728172 Mixed 10%-Take/90%-Give (In Parallel) -00:00:06.9612516 TOTAL +00:00:01.9024162 Give To (In Parallel) +00:00:01.6790522 Mixed 90%-Take/10%-Give (In Parallel) +00:00:01.2862753 Mixed 50%-Take/50%-Give (In Parallel) +00:00:01.5210385 Mixed 10%-Take/90%-Give (In Parallel) +00:00:06.3887822 TOTAL ConcurrentQueueObjectPool............................... -00:00:02.1535484 Give To (In Parallel) -00:00:01.8115602 Mixed 90%-Take/10%-Give (In Parallel) -00:00:01.4565032 Mixed 50%-Take/50%-Give (In Parallel) -00:00:01.7248405 Mixed 10%-Take/90%-Give (In Parallel) -00:00:07.1464523 TOTAL +00:00:02.0542650 Give To (In Parallel) +00:00:01.7484990 Mixed 90%-Take/10%-Give (In Parallel) +00:00:01.4418580 Mixed 50%-Take/50%-Give (In Parallel) +00:00:01.6489656 Mixed 10%-Take/90%-Give (In Parallel) +00:00:06.8935876 TOTAL OptimisticArrayObjectPool............................... -00:00:01.6787071 Give To (In Parallel) -00:00:01.8529905 Mixed 90%-Take/10%-Give (In Parallel) -00:00:01.1923887 Mixed 50%-Take/50%-Give (In Parallel) -00:00:01.4014375 Mixed 10%-Take/90%-Give (In Parallel) -00:00:06.1255238 TOTAL +00:00:00.8471340 Give To (In Parallel) +00:00:01.0865546 Mixed 90%-Take/10%-Give (In Parallel) +00:00:00.9761332 Mixed 50%-Take/50%-Give (In Parallel) +00:00:00.8651384 Mixed 10%-Take/90%-Give (In Parallel) +00:00:03.7749602 TOTAL InterlockedArrayObjectPool.............................. -00:00:02.6162570 Give To (In Parallel) -00:00:01.9290369 Mixed 90%-Take/10%-Give (In Parallel) -00:00:01.2375542 Mixed 50%-Take/50%-Give (In Parallel) -00:00:02.1077313 Mixed 10%-Take/90%-Give (In Parallel) -00:00:07.8905794 TOTAL +00:00:00.9431637 Give To (In Parallel) +00:00:01.1474065 Mixed 90%-Take/10%-Give (In Parallel) +00:00:01.0727406 Mixed 50%-Take/50%-Give (In Parallel) +00:00:00.9714928 Mixed 10%-Take/90%-Give (In Parallel) +00:00:04.1348036 TOTAL Repeat 153600 for size 250 ------------------------------------ Microsoft.Extensions.ObjectPool.DefaultObjectPool....... -00:00:24.8126209 Give To (In Parallel) -00:00:03.1502726 Mixed 90%-Take/10%-Give (In Parallel) -00:00:01.6389862 Mixed 50%-Take/50%-Give (In Parallel) -00:00:17.9663555 Mixed 10%-Take/90%-Give (In Parallel) -00:00:47.5682352 TOTAL +00:00:04.0471422 Give To (In Parallel) +00:00:03.2658468 Mixed 90%-Take/10%-Give (In Parallel) +00:00:01.8253285 Mixed 50%-Take/50%-Give (In Parallel) +00:00:03.0743397 Mixed 10%-Take/90%-Give (In Parallel) +00:00:12.2126572 TOTAL ConcurrentQueueObjectPoolSlim........................... -00:00:04.3281379 Give To (In Parallel) -00:00:03.5048937 Mixed 90%-Take/10%-Give (In Parallel) -00:00:02.2074143 Mixed 50%-Take/50%-Give (In Parallel) -00:00:03.3092668 Mixed 10%-Take/90%-Give (In Parallel) -00:00:13.3497127 TOTAL +00:00:04.0982231 Give To (In Parallel) +00:00:03.2845860 Mixed 90%-Take/10%-Give (In Parallel) +00:00:02.1882361 Mixed 50%-Take/50%-Give (In Parallel) +00:00:03.0712649 Mixed 10%-Take/90%-Give (In Parallel) +00:00:12.6423101 TOTAL ConcurrentQueueObjectPool............................... -00:00:04.3244745 Give To (In Parallel) -00:00:03.4839443 Mixed 90%-Take/10%-Give (In Parallel) -00:00:02.3115293 Mixed 50%-Take/50%-Give (In Parallel) -00:00:03.3110660 Mixed 10%-Take/90%-Give (In Parallel) -00:00:13.4310141 TOTAL +00:00:04.1283099 Give To (In Parallel) +00:00:03.2866676 Mixed 90%-Take/10%-Give (In Parallel) +00:00:02.3430258 Mixed 50%-Take/50%-Give (In Parallel) +00:00:03.1057312 Mixed 10%-Take/90%-Give (In Parallel) +00:00:12.8637345 TOTAL OptimisticArrayObjectPool............................... -00:00:03.3123930 Give To (In Parallel) -00:00:03.7821922 Mixed 90%-Take/10%-Give (In Parallel) -00:00:01.9659170 Mixed 50%-Take/50%-Give (In Parallel) -00:00:02.3799803 Mixed 10%-Take/90%-Give (In Parallel) -00:00:11.4404825 TOTAL +00:00:00.8952769 Give To (In Parallel) +00:00:01.2805030 Mixed 90%-Take/10%-Give (In Parallel) +00:00:01.2960246 Mixed 50%-Take/50%-Give (In Parallel) +00:00:01.0256400 Mixed 10%-Take/90%-Give (In Parallel) +00:00:04.4974445 TOTAL InterlockedArrayObjectPool.............................. -00:00:06.8300609 Give To (In Parallel) -00:00:03.9634994 Mixed 90%-Take/10%-Give (In Parallel) -00:00:01.9064904 Mixed 50%-Take/50%-Give (In Parallel) -00:00:05.0161669 Mixed 10%-Take/90%-Give (In Parallel) -00:00:17.7162176 TOTAL +00:00:01.0063385 Give To (In Parallel) +00:00:01.3870075 Mixed 90%-Take/10%-Give (In Parallel) +00:00:01.4698480 Mixed 50%-Take/50%-Give (In Parallel) +00:00:01.1912251 Mixed 10%-Take/90%-Give (In Parallel) +00:00:05.0544191 TOTAL diff --git a/source/Array/InterlockedArrayObjectPool.cs b/source/Array/InterlockedArrayObjectPool.cs index 4c27347..909a324 100644 --- a/source/Array/InterlockedArrayObjectPool.cs +++ b/source/Array/InterlockedArrayObjectPool.cs @@ -1,7 +1,6 @@ /* Based on Roslyn's ObjectPool */ using System; -using System.Linq; using System.Runtime.CompilerServices; using System.Threading; @@ -10,7 +9,6 @@ namespace Open.Disposable; /// /// An extremely fast ObjectPool when the capacity is in the low 100s. /// -/// public class InterlockedArrayObjectPool : ObjectPoolBase where T : class @@ -28,27 +26,38 @@ public InterlockedArrayObjectPool( int capacity = DEFAULT_CAPACITY) : this(factory, null, null, capacity) { } - protected ReferenceContainer[] Pool; + protected Memory> Pool; // Sets a limit on what has been stored yet to prevent over searching the array unnecessarily.. protected int MaxStored; protected const int MaxStoredIncrement = 5; // Instead of every one. public override int Count - => Pool.Count(e => e.Value is not null) + PocketCount; + { + get + { + var p = Pool.Span; + int count = PocketCount; + foreach(var e in p) + if (e.Value is not null) count++; + + return count; + } + } [MethodImpl(MethodImplOptions.AggressiveInlining)] - protected virtual bool Store(ReferenceContainer[] p, T item, int index) + protected virtual bool Store(ReadOnlySpan> p, T item, int index) => p[index].TrySave(item); protected override bool Receive(T item) { var elements = Pool; - var len = elements?.Length ?? 0; + var len = elements.Length; + var span = elements.Span; for (var i = 0; i < len; i++) { - if (!Store(elements!, item, i)) continue; + if (!Store(span, item, i)) continue; var m = MaxStored; if (i >= m) Interlocked.CompareExchange(ref MaxStored, m + MaxStoredIncrement, m); return true; @@ -60,11 +69,10 @@ protected override bool Receive(T item) protected override T? TryRelease() { // We missed getting the first item or it wasn't there. - var elements = Pool; - if (elements is null) return null; - + var elements = Pool.Span; var len = elements.Length; - for (var i = 0; i < MaxStored && i < len; i++) + + for (var i = 0; i < len && i < MaxStored; i++) { var item = elements[i].TryRetrieve(); if (item is not null) return item; @@ -76,7 +84,7 @@ protected override bool Receive(T item) protected override void OnDispose() { base.OnDispose(); - Pool = null!; + Pool = Array.Empty>(); MaxStored = 0; } } diff --git a/source/Array/OptimisticArrayObjectPool.cs b/source/Array/OptimisticArrayObjectPool.cs index bc439b9..bc1c121 100644 --- a/source/Array/OptimisticArrayObjectPool.cs +++ b/source/Array/OptimisticArrayObjectPool.cs @@ -8,7 +8,6 @@ namespace Open.Disposable; /// /// An extremely fast ObjectPool when the capacity is in the low 100s. /// -/// public class OptimisticArrayObjectPool : InterlockedArrayObjectPool where T : class @@ -33,7 +32,7 @@ protected override bool SaveToPocket(T item) // As suggested by Roslyn's implementation, don't worry about interlocking here. It's okay if a few get loose. [MethodImpl(MethodImplOptions.AggressiveInlining)] - protected override bool Store(ReferenceContainer[] p, T item, int index) + protected override bool Store(ReadOnlySpan> p, T item, int index) => p[index].SetIfNull(item); } diff --git a/source/Open.Disposable.ObjectPools.csproj b/source/Open.Disposable.ObjectPools.csproj index 0a8124d..459358d 100644 --- a/source/Open.Disposable.ObjectPools.csproj +++ b/source/Open.Disposable.ObjectPools.csproj @@ -26,7 +26,7 @@ snupkg logo.png README.md - IDE0290;CA1510;IDE0130; + IDE0290;CA1510;IDE0130;IDE0301; @@ -56,6 +56,7 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive + diff --git a/source/SharedPools.cs b/source/SharedPools.cs index 2dfd19c..1c98825 100644 --- a/source/SharedPools.cs +++ b/source/SharedPools.cs @@ -4,12 +4,11 @@ namespace Open.Disposable; -public class SharedPool( +public sealed class SharedPool( Func factory, Action? recycler, - Action? disposer, int capacity) - : ConcurrentQueueObjectPoolSlimBase(factory, recycler, disposer, capacity) + : OptimisticArrayObjectPool(factory, recycler, capacity) where T : class { [Obsolete("Shared pools do not support disposal.")] @@ -34,7 +33,7 @@ public static class ListPool { h.Clear(); if (h.Capacity > 16) h.Capacity = 16; - }, null, capacity); + }, capacity); /// /// Provides a disposable RecycleHelper that contains an item from the pool.
@@ -58,7 +57,7 @@ public static class HashSetPool /// The hash-set is cleared after being returned. ///
public static SharedPool> Create(int capacity = Constants.DEFAULT_CAPACITY) - => new(() => [], h => h.Clear(), null, capacity); + => new(() => [], h => h.Clear(), capacity); /// public static RecycleHelper> Rent() => Shared.Rent(); @@ -78,7 +77,7 @@ public static class StringBuilderPool /// The StringBuilder is cleared after being returned. /// public static SharedPool Create(int capacity = Constants.DEFAULT_CAPACITY) - => new(() => new(), sb => sb.Clear(), null, capacity); + => new(() => new(), sb => sb.Clear(), capacity); /// /// Provides a StringBuilder to be used for processing and finalizes by calling .ToString() and returning the value. @@ -124,7 +123,7 @@ public static class DictionaryPool /// The dictionary is cleared after being returned. /// public static SharedPool> Create(int capacity = Constants.DEFAULT_CAPACITY) - => new(() => [], h => h.Clear(), null, capacity); + => new(() => [], h => h.Clear(), capacity); /// /// Provides a disposable RecycleHelper that contains an item from the pool.
diff --git a/tests/ObjectPoolSmokeTests.cs b/tests/ObjectPoolSmokeTests.cs index d9099c2..2c940c4 100644 --- a/tests/ObjectPoolSmokeTests.cs +++ b/tests/ObjectPoolSmokeTests.cs @@ -30,7 +30,7 @@ public void ListPool_RecycleTest() Assert.AreEqual(0, list.Count); #pragma warning disable CS0618 // Type or member is obsolete - Assert.ThrowsException(() => pool.Dispose()); + Assert.ThrowsException(pool.Dispose); #pragma warning restore CS0618 // Type or member is obsolete } } From 695866c89ac68dd7f038df965c17e0b39a5c8af4 Mon Sep 17 00:00:00 2001 From: electricessence <5899455+electricessence@users.noreply.github.com> Date: Fri, 15 Nov 2024 07:37:35 -0800 Subject: [PATCH 80/83] Ensure ConfigureAwait(false). --- .editorconfig | 41 ++++++++++++++++------- source/Open.Disposable.ObjectPools.csproj | 4 +-- 2 files changed, 30 insertions(+), 15 deletions(-) diff --git a/.editorconfig b/.editorconfig index 78e2b33..8cb6fd1 100644 --- a/.editorconfig +++ b/.editorconfig @@ -80,14 +80,14 @@ csharp_style_var_for_built_in_types = true csharp_style_var_when_type_is_apparent = true # Expression-bodied members -csharp_style_expression_bodied_accessors = true -csharp_style_expression_bodied_constructors = when_on_single_line -csharp_style_expression_bodied_indexers = true -csharp_style_expression_bodied_lambdas = true -csharp_style_expression_bodied_local_functions = true -csharp_style_expression_bodied_methods = true -csharp_style_expression_bodied_operators = when_on_single_line -csharp_style_expression_bodied_properties = true +csharp_style_expression_bodied_accessors = true:silent +csharp_style_expression_bodied_constructors = when_on_single_line:silent +csharp_style_expression_bodied_indexers = true:silent +csharp_style_expression_bodied_lambdas = true:silent +csharp_style_expression_bodied_local_functions = true:silent +csharp_style_expression_bodied_methods = true:silent +csharp_style_expression_bodied_operators = when_on_single_line:silent +csharp_style_expression_bodied_properties = true:silent # Pattern matching preferences csharp_style_pattern_matching_over_as_with_null_check = true @@ -104,9 +104,9 @@ csharp_prefer_static_local_function = true csharp_preferred_modifier_order = public,private,protected,internal,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,volatile,async # Code-block preferences -csharp_prefer_braces = when_multiline -csharp_prefer_simple_using_statement = true -csharp_style_namespace_declarations = file_scoped +csharp_prefer_braces = when_multiline:silent +csharp_prefer_simple_using_statement = true:suggestion +csharp_style_namespace_declarations = file_scoped:silent # Expression-level preferences csharp_prefer_simple_default_expression = true @@ -122,7 +122,7 @@ csharp_style_unused_value_assignment_preference = discard_variable csharp_style_unused_value_expression_statement_preference = discard_variable # 'using' directive preferences -csharp_using_directive_placement = outside_namespace +csharp_using_directive_placement = outside_namespace:silent # New line preferences csharp_style_allow_blank_line_after_colon_in_constructor_initializer_experimental = true @@ -222,4 +222,19 @@ dotnet_diagnostic.HAA0303.severity = silent dotnet_diagnostic.HAA0401.severity = silent dotnet_diagnostic.HAA0501.severity = silent dotnet_diagnostic.HAA0502.severity = silent -dotnet_diagnostic.HAA0601.severity = silent \ No newline at end of file +dotnet_diagnostic.HAA0601.severity = silent +csharp_style_prefer_method_group_conversion = true:silent +csharp_style_prefer_top_level_statements = true:silent +csharp_style_prefer_primary_constructors = true:suggestion +csharp_prefer_system_threading_lock = true:suggestion +[*.{cs,vb}] +dotnet_style_operator_placement_when_wrapping = beginning_of_line +tab_width = 4 +indent_size = 4 +end_of_line = crlf +dotnet_style_coalesce_expression = true:suggestion +dotnet_style_null_propagation = true:suggestion +dotnet_style_prefer_is_null_check_over_reference_equality_method = true:suggestion +dotnet_style_prefer_auto_properties = true:silent +dotnet_style_object_initializer = true:suggestion +dotnet_diagnostic.CA2007.severity = error \ No newline at end of file diff --git a/source/Open.Disposable.ObjectPools.csproj b/source/Open.Disposable.ObjectPools.csproj index 459358d..abac7ee 100644 --- a/source/Open.Disposable.ObjectPools.csproj +++ b/source/Open.Disposable.ObjectPools.csproj @@ -56,8 +56,8 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive - - + + From b87cb7cd562e6385e48dc7dd3026aa67bfb2434d Mon Sep 17 00:00:00 2001 From: electricessence <5899455+electricessence@users.noreply.github.com> Date: Fri, 15 Nov 2024 07:37:35 -0800 Subject: [PATCH 81/83] Ensure ConfigureAwait(false). --- .editorconfig | 41 ++++++++++++++++------- source/Open.Disposable.ObjectPools.csproj | 6 ++-- 2 files changed, 31 insertions(+), 16 deletions(-) diff --git a/.editorconfig b/.editorconfig index 78e2b33..8cb6fd1 100644 --- a/.editorconfig +++ b/.editorconfig @@ -80,14 +80,14 @@ csharp_style_var_for_built_in_types = true csharp_style_var_when_type_is_apparent = true # Expression-bodied members -csharp_style_expression_bodied_accessors = true -csharp_style_expression_bodied_constructors = when_on_single_line -csharp_style_expression_bodied_indexers = true -csharp_style_expression_bodied_lambdas = true -csharp_style_expression_bodied_local_functions = true -csharp_style_expression_bodied_methods = true -csharp_style_expression_bodied_operators = when_on_single_line -csharp_style_expression_bodied_properties = true +csharp_style_expression_bodied_accessors = true:silent +csharp_style_expression_bodied_constructors = when_on_single_line:silent +csharp_style_expression_bodied_indexers = true:silent +csharp_style_expression_bodied_lambdas = true:silent +csharp_style_expression_bodied_local_functions = true:silent +csharp_style_expression_bodied_methods = true:silent +csharp_style_expression_bodied_operators = when_on_single_line:silent +csharp_style_expression_bodied_properties = true:silent # Pattern matching preferences csharp_style_pattern_matching_over_as_with_null_check = true @@ -104,9 +104,9 @@ csharp_prefer_static_local_function = true csharp_preferred_modifier_order = public,private,protected,internal,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,volatile,async # Code-block preferences -csharp_prefer_braces = when_multiline -csharp_prefer_simple_using_statement = true -csharp_style_namespace_declarations = file_scoped +csharp_prefer_braces = when_multiline:silent +csharp_prefer_simple_using_statement = true:suggestion +csharp_style_namespace_declarations = file_scoped:silent # Expression-level preferences csharp_prefer_simple_default_expression = true @@ -122,7 +122,7 @@ csharp_style_unused_value_assignment_preference = discard_variable csharp_style_unused_value_expression_statement_preference = discard_variable # 'using' directive preferences -csharp_using_directive_placement = outside_namespace +csharp_using_directive_placement = outside_namespace:silent # New line preferences csharp_style_allow_blank_line_after_colon_in_constructor_initializer_experimental = true @@ -222,4 +222,19 @@ dotnet_diagnostic.HAA0303.severity = silent dotnet_diagnostic.HAA0401.severity = silent dotnet_diagnostic.HAA0501.severity = silent dotnet_diagnostic.HAA0502.severity = silent -dotnet_diagnostic.HAA0601.severity = silent \ No newline at end of file +dotnet_diagnostic.HAA0601.severity = silent +csharp_style_prefer_method_group_conversion = true:silent +csharp_style_prefer_top_level_statements = true:silent +csharp_style_prefer_primary_constructors = true:suggestion +csharp_prefer_system_threading_lock = true:suggestion +[*.{cs,vb}] +dotnet_style_operator_placement_when_wrapping = beginning_of_line +tab_width = 4 +indent_size = 4 +end_of_line = crlf +dotnet_style_coalesce_expression = true:suggestion +dotnet_style_null_propagation = true:suggestion +dotnet_style_prefer_is_null_check_over_reference_equality_method = true:suggestion +dotnet_style_prefer_auto_properties = true:silent +dotnet_style_object_initializer = true:suggestion +dotnet_diagnostic.CA2007.severity = error \ No newline at end of file diff --git a/source/Open.Disposable.ObjectPools.csproj b/source/Open.Disposable.ObjectPools.csproj index 459358d..317d587 100644 --- a/source/Open.Disposable.ObjectPools.csproj +++ b/source/Open.Disposable.ObjectPools.csproj @@ -18,7 +18,7 @@ https://github.com/Open-NET-Libraries/Open.Disposable.ObjectPools/ git objectpool;idisposable;thread safe - 3.0.0 + 3.0.1 MIT true @@ -56,8 +56,8 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive - - + + From 6d5b02040c31d3de62cf668ba676009c2f6b359f Mon Sep 17 00:00:00 2001 From: electricessence <5899455+electricessence@users.noreply.github.com> Date: Sun, 17 Nov 2024 10:45:05 -0800 Subject: [PATCH 82/83] Remove IDisposable from IObjectPool and update version - Removed IDisposable inheritance from IObjectPool interface. - Updated project version in Open.Disposable.ObjectPools.csproj to 3.0.2. - Added OnDispose method to SharedPool class to throw NotSupportedException, indicating shared pools cannot be disposed. --- benchmarking/DefaultObjectPool.cs | 4 ++-- source/IObjectPool.cs | 1 - source/Open.Disposable.ObjectPools.csproj | 2 +- source/SharedPools.cs | 3 +++ 4 files changed, 6 insertions(+), 4 deletions(-) diff --git a/benchmarking/DefaultObjectPool.cs b/benchmarking/DefaultObjectPool.cs index 775f42c..966b26c 100644 --- a/benchmarking/DefaultObjectPool.cs +++ b/benchmarking/DefaultObjectPool.cs @@ -16,9 +16,9 @@ class Policy(Func factory) : IPooledObjectPolicy public T Create() => _factory(); - public bool Return(T obj) => + public bool Return(T obj) // recycler?.Invoke(obj); - true; + => true; } public int Capacity { get; } diff --git a/source/IObjectPool.cs b/source/IObjectPool.cs index 02fdebf..e4993fb 100644 --- a/source/IObjectPool.cs +++ b/source/IObjectPool.cs @@ -6,7 +6,6 @@ namespace Open.Disposable; public interface IObjectPool - : IDisposable where T : class { /// diff --git a/source/Open.Disposable.ObjectPools.csproj b/source/Open.Disposable.ObjectPools.csproj index 317d587..6e14fe1 100644 --- a/source/Open.Disposable.ObjectPools.csproj +++ b/source/Open.Disposable.ObjectPools.csproj @@ -18,7 +18,7 @@ https://github.com/Open-NET-Libraries/Open.Disposable.ObjectPools/ git objectpool;idisposable;thread safe - 3.0.1 + 3.0.2 MIT true diff --git a/source/SharedPools.cs b/source/SharedPools.cs index 1c98825..810c253 100644 --- a/source/SharedPools.cs +++ b/source/SharedPools.cs @@ -14,6 +14,9 @@ public sealed class SharedPool( [Obsolete("Shared pools do not support disposal.")] public new void Dispose() => throw new NotSupportedException("Shared pools cannot be disposed."); + + protected override void OnDispose() + => throw new NotSupportedException("Shared pools cannot be disposed."); } public static class ListPool From 8f23478b5df78a9fdfdf1cc1cd624da0abac70b3 Mon Sep 17 00:00:00 2001 From: electricessence <5899455+electricessence@users.noreply.github.com> Date: Sun, 17 Nov 2024 10:46:51 -0800 Subject: [PATCH 83/83] Refactor disposal of pool variable Replaced direct `using` statement for `pool` with a `using` statement for a variable named `_` that casts `pool` to `IDisposable`. This ensures `pool` is disposed of if it implements `IDisposable` without enforcing the `using` pattern directly on `pool`. --- benchmarking/Benchmark.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/benchmarking/Benchmark.cs b/benchmarking/Benchmark.cs index 654dfcb..98e0379 100644 --- a/benchmarking/Benchmark.cs +++ b/benchmarking/Benchmark.cs @@ -24,7 +24,8 @@ public Benchmark(uint size, uint repeat, Func> poolFactory) : bas protected override IEnumerable TestOnceInternal() { - using var pool = Param() ?? throw new NullReferenceException(); + var pool = Param() ?? throw new NullReferenceException(); + using var _ = pool as IDisposable; //yield return TimedResult.Measure("Take From Empty (In Parallel)", () => //{ // Parallel.For(0, TestSize, i => _items[i] = pool.Take());