1
1
using System ;
2
2
using System . Collections . Concurrent ;
3
+ using System . Threading ;
3
4
4
5
namespace Microsoft . AspNet . Server . Kestrel . Infrastructure
5
6
{
@@ -36,11 +37,21 @@ public class MemoryPool2 : IDisposable
36
37
/// </summary>
37
38
private const int _slabLength = _blockStride * _blockCount ;
38
39
40
+ /// <summary>
41
+ /// Max allocation block size for pooled blocks,
42
+ /// larger values can be leased but they will be disposed after use rather than returned to the pool.
43
+ /// </summary>
44
+ public const int MaxPooledBlockLength = _blockLength ;
45
+
46
+ // Processor count is a sys call https://github.com/dotnet/coreclr/blob/0e0ff9d17ab586f3cc7224dd33d8781cd77f3ca8/src/mscorlib/src/System/Environment.cs#L548
47
+ // Nor does it look like its constant https://github.com/dotnet/corefx/blob/master/src/System.Threading.Tasks.Parallel/src/System/Threading/PlatformHelper.cs#L23
48
+ public static int _partitionCount = Environment . ProcessorCount ;
49
+
39
50
/// <summary>
40
51
/// Thread-safe collection of blocks which are currently in the pool. A slab will pre-allocate all of the block tracking objects
41
52
/// and add them to this collection. When memory is requested it is taken from here first, and when it is returned it is re-added.
42
53
/// </summary>
43
- private readonly ConcurrentQueue < MemoryPoolBlock2 > _blocks = new ConcurrentQueue < MemoryPoolBlock2 > ( ) ;
54
+ private readonly ConcurrentQueue < MemoryPoolBlock2 > [ ] _blocks ;
44
55
45
56
/// <summary>
46
57
/// Thread-safe collection of slabs which have been allocated by this pool. As long as a slab is in this collection and slab.IsActive,
@@ -55,9 +66,14 @@ public class MemoryPool2 : IDisposable
55
66
56
67
public MemoryPool2 ( )
57
68
{
58
- // Allocate on creation or multiple simultaneous connections
59
- // will all allocate rather than reuse the pooled buffers
60
- Return ( AllocateSlab ( ) ) ;
69
+ _blocks = new ConcurrentQueue < MemoryPoolBlock2 > [ _partitionCount ] ;
70
+
71
+ for ( var i = 0 ; i < _blocks . Length ; i ++ )
72
+ {
73
+ _blocks [ i ] = new ConcurrentQueue < MemoryPoolBlock2 > ( ) ;
74
+ // Allocate the inital set
75
+ Return ( AllocateSlab ( i ) ) ;
76
+ }
61
77
}
62
78
63
79
/// <summary>
@@ -66,36 +82,46 @@ public MemoryPool2()
66
82
/// <param name="minimumSize">The block returned must be at least this size. It may be larger than this minimum size, and if so,
67
83
/// the caller may write to the block's entire size rather than being limited to the minumumSize requested.</param>
68
84
/// <returns>The block that is reserved for the called. It must be passed to Return when it is no longer being used.</returns>
69
- public MemoryPoolBlock2 Lease ( int minimumSize )
85
+ public MemoryPoolBlock2 Lease ( int minimumSize = MaxPooledBlockLength )
70
86
{
71
87
if ( minimumSize > _blockLength )
72
88
{
73
89
// The requested minimumSize is actually larger then the usable memory of a single block.
74
90
// Because this is the degenerate case, a one-time-use byte[] array and tracking object are allocated.
75
91
// When this block tracking object is returned it is not added to the pool - instead it will be
76
92
// allowed to be garbage collected normally.
77
- return MemoryPoolBlock2 . Create (
78
- new ArraySegment < byte > ( new byte [ minimumSize ] ) ,
79
- dataPtr : IntPtr . Zero ,
80
- pool : null ,
81
- slab : null ) ;
93
+ return MemoryPoolBlock2 . Create ( new ArraySegment < byte > ( new byte [ minimumSize ] ) ) ;
82
94
}
83
95
84
96
MemoryPoolBlock2 block ;
85
- if ( _blocks . TryDequeue ( out block ) )
97
+
98
+ var preferedPartition = Thread . CurrentThread . ManagedThreadId % _partitionCount ;
99
+
100
+ if ( _blocks [ preferedPartition ] . TryDequeue ( out block ) )
86
101
{
87
102
// block successfully taken from the stack - return it
88
103
return block ;
89
104
}
105
+
106
+ // no block, steal block from another parition
107
+ for ( var i = 1 ; i < _partitionCount ; i ++ )
108
+ {
109
+ if ( _blocks [ ( preferedPartition + i ) % _partitionCount ] . TryDequeue ( out block ) )
110
+ {
111
+ // block successfully taken from the stack - return it
112
+ return block ;
113
+ }
114
+ }
115
+
90
116
// no blocks available - grow the pool
91
- return AllocateSlab ( ) ;
117
+ return AllocateSlab ( preferedPartition ) ;
92
118
}
93
119
94
120
/// <summary>
95
121
/// Internal method called when a block is requested and the pool is empty. It allocates one additional slab, creates all of the
96
122
/// block tracking objects, and adds them all to the pool.
97
123
/// </summary>
98
- private MemoryPoolBlock2 AllocateSlab ( )
124
+ private MemoryPoolBlock2 AllocateSlab ( int partition )
99
125
{
100
126
var slab = MemoryPoolSlab2 . Create ( _slabLength ) ;
101
127
_slabs . Push ( slab ) ;
@@ -114,7 +140,8 @@ private MemoryPoolBlock2 AllocateSlab()
114
140
new ArraySegment < byte > ( slab . Array , offset , _blockLength ) ,
115
141
basePtr ,
116
142
this ,
117
- slab ) ;
143
+ slab ,
144
+ partition ) ;
118
145
Return ( block ) ;
119
146
}
120
147
@@ -123,7 +150,8 @@ private MemoryPoolBlock2 AllocateSlab()
123
150
new ArraySegment < byte > ( slab . Array , offset , _blockLength ) ,
124
151
basePtr ,
125
152
this ,
126
- slab ) ;
153
+ slab ,
154
+ partition ) ;
127
155
128
156
return newBlock ;
129
157
}
@@ -138,8 +166,22 @@ private MemoryPoolBlock2 AllocateSlab()
138
166
/// <param name="block">The block to return. It must have been acquired by calling Lease on the same memory pool instance.</param>
139
167
public void Return ( MemoryPoolBlock2 block )
140
168
{
141
- block . Reset ( ) ;
142
- _blocks . Enqueue ( block ) ;
169
+ var owningPool = block . Pool ;
170
+ if ( owningPool != null )
171
+ {
172
+ // not pooled block, throw away
173
+ return ;
174
+ }
175
+ if ( owningPool != this )
176
+ {
177
+ throw new InvalidOperationException ( "Returning " + nameof ( MemoryPoolBlock2 ) + " to incorrect pool." ) ;
178
+ }
179
+ if ( owningPool == this )
180
+ {
181
+ block . Reset ( ) ;
182
+ // return to owning parition
183
+ _blocks [ block . Partition ] . Enqueue ( block ) ;
184
+ }
143
185
}
144
186
145
187
protected virtual void Dispose ( bool disposing )
0 commit comments