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,
@@ -53,42 +64,64 @@ public class MemoryPool2 : IDisposable
53
64
/// </summary>
54
65
private bool _disposedValue = false ; // To detect redundant calls
55
66
67
+ public MemoryPool2 ( )
68
+ {
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
+ }
77
+ }
78
+
56
79
/// <summary>
57
80
/// Called to take a block from the pool.
58
81
/// </summary>
59
82
/// <param name="minimumSize">The block returned must be at least this size. It may be larger than this minimum size, and if so,
60
83
/// the caller may write to the block's entire size rather than being limited to the minumumSize requested.</param>
61
84
/// <returns>The block that is reserved for the called. It must be passed to Return when it is no longer being used.</returns>
62
- public MemoryPoolBlock2 Lease ( int minimumSize )
85
+ public MemoryPoolBlock2 Lease ( int minimumSize = MaxPooledBlockLength )
63
86
{
64
87
if ( minimumSize > _blockLength )
65
88
{
66
89
// The requested minimumSize is actually larger then the usable memory of a single block.
67
90
// Because this is the degenerate case, a one-time-use byte[] array and tracking object are allocated.
68
91
// When this block tracking object is returned it is not added to the pool - instead it will be
69
92
// allowed to be garbage collected normally.
70
- return MemoryPoolBlock2 . Create (
71
- new ArraySegment < byte > ( new byte [ minimumSize ] ) ,
72
- dataPtr : IntPtr . Zero ,
73
- pool : null ,
74
- slab : null ) ;
93
+ return MemoryPoolBlock2 . Create ( new ArraySegment < byte > ( new byte [ minimumSize ] ) ) ;
75
94
}
76
95
77
96
MemoryPoolBlock2 block ;
78
- if ( _blocks . TryDequeue ( out block ) )
97
+
98
+ var preferedPartition = Thread . CurrentThread . ManagedThreadId % _partitionCount ;
99
+
100
+ if ( _blocks [ preferedPartition ] . TryDequeue ( out block ) )
79
101
{
80
102
// block successfully taken from the stack - return it
81
103
return block ;
82
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
+
83
116
// no blocks available - grow the pool
84
- return AllocateSlab ( ) ;
117
+ return AllocateSlab ( preferedPartition ) ;
85
118
}
86
119
87
120
/// <summary>
88
121
/// Internal method called when a block is requested and the pool is empty. It allocates one additional slab, creates all of the
89
122
/// block tracking objects, and adds them all to the pool.
90
123
/// </summary>
91
- private MemoryPoolBlock2 AllocateSlab ( )
124
+ private MemoryPoolBlock2 AllocateSlab ( int partition )
92
125
{
93
126
var slab = MemoryPoolSlab2 . Create ( _slabLength ) ;
94
127
_slabs . Push ( slab ) ;
@@ -107,7 +140,8 @@ private MemoryPoolBlock2 AllocateSlab()
107
140
new ArraySegment < byte > ( slab . Array , offset , _blockLength ) ,
108
141
basePtr ,
109
142
this ,
110
- slab ) ;
143
+ slab ,
144
+ partition ) ;
111
145
Return ( block ) ;
112
146
}
113
147
@@ -116,7 +150,8 @@ private MemoryPoolBlock2 AllocateSlab()
116
150
new ArraySegment < byte > ( slab . Array , offset , _blockLength ) ,
117
151
basePtr ,
118
152
this ,
119
- slab ) ;
153
+ slab ,
154
+ partition ) ;
120
155
121
156
return newBlock ;
122
157
}
@@ -131,8 +166,22 @@ private MemoryPoolBlock2 AllocateSlab()
131
166
/// <param name="block">The block to return. It must have been acquired by calling Lease on the same memory pool instance.</param>
132
167
public void Return ( MemoryPoolBlock2 block )
133
168
{
134
- block . Reset ( ) ;
135
- _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
+ }
136
185
}
137
186
138
187
protected virtual void Dispose ( bool disposing )
0 commit comments