Skip to content

Commit 2ebf73f

Browse files
committed
Implemented returning buffer to the pool.
1 parent 5c6179a commit 2ebf73f

File tree

2 files changed

+73
-35
lines changed

2 files changed

+73
-35
lines changed

Diff for: FlashCap.Core/BufferPool.cs

+40-15
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ public sealed class DefaultBufferPool :
3232
// Tried and tested, but simple strategies were the fastest.
3333
// Probably because each buffer table and lookup fragments on the CPU cache.
3434

35-
private const int BufferHolderLength = 13;
35+
private const int bufferHolderLength = 13;
3636

3737
[DebuggerStepThrough]
3838
private sealed class BufferHolder
@@ -42,66 +42,91 @@ private sealed class BufferHolder
4242
public BufferHolder(int maxReservedBufferElements) =>
4343
this.buffers = new byte[maxReservedBufferElements][];
4444

45-
public byte[] Rent(int size, bool exactSize)
45+
public byte[]? Rent(int size, bool exactSize)
4646
{
4747
for (var index = 0; index < this.buffers.Length; index++)
4848
{
4949
var buffer = this.buffers[index];
50-
if (buffer != null && (exactSize ? (buffer.Length == size) : (buffer.Length <= size)))
50+
if (buffer != null && (exactSize ? (buffer.Length == size) : (buffer.Length >= size)))
5151
{
5252
if (Interlocked.CompareExchange(ref this.buffers[index], null, buffer) == buffer)
5353
{
54-
return buffer!;
54+
return buffer;
5555
}
5656
}
5757
}
5858

59-
return new byte[size];
59+
return null;
6060
}
6161

62-
public void Return(byte[] buffer)
62+
public bool Return(byte[] buffer)
6363
{
6464
for (var index = 0; index < this.buffers.Length; index++)
6565
{
6666
if (this.buffers[index] == null)
6767
{
6868
if (Interlocked.CompareExchange(ref this.buffers[index], buffer, null) == null)
6969
{
70-
break;
70+
return true;
7171
}
7272
}
7373
}
7474

7575
// It was better to simply discard a buffer instance than the cost of extending the table.
76+
return false;
7677
}
7778
}
7879

7980
private readonly BufferHolder[] bufferHolders;
81+
private int saved;
8082

81-
public DefaultBufferPool() : this(32)
83+
public DefaultBufferPool() : this(16)
8284
{
8385
}
8486

8587
public DefaultBufferPool(int maxReservedBufferElements) =>
86-
this.bufferHolders = Enumerable.Range(0, BufferHolderLength).
88+
this.bufferHolders = Enumerable.Range(0, bufferHolderLength).
8789
Select(_ => new BufferHolder(maxReservedBufferElements)).
8890
ToArray();
8991

9092
public override byte[] Rent(int size, bool exactSize)
9193
{
92-
// NOTE: Size is determined on a "less than" basis,
94+
// NOTE: Size is determined on a "less than" basis when not exact size,
9395
// which may result in placement in different buckets and missed opportunities for reuse.
94-
// This implementation is ignored it.
96+
// This implementation is ignored penalties.
97+
int saved;
9598
var bufferHolder = this.bufferHolders[size % this.bufferHolders.Length];
96-
return bufferHolder.Rent(size, exactSize);
99+
if (bufferHolder.Rent(size, exactSize) is { } b)
100+
{
101+
saved = Interlocked.Decrement(ref this.saved);
102+
}
103+
else
104+
{
105+
b = new byte[size];
106+
saved = this.saved;
107+
}
108+
109+
#if DEBUG
110+
Debug.WriteLine($"DefaultBufferPool: Rend: Size={b.Length}/{size}, ExactSize={exactSize}, Saved={saved}");
111+
#endif
112+
return b;
97113
}
98114

99115
public override void Return(byte[] buffer)
100116
{
101-
if (Interlocked.Exchange(ref buffer, null!) is { } b)
117+
int saved;
118+
var bufferHolder = this.bufferHolders[buffer.Length % this.bufferHolders.Length];
119+
if (bufferHolder.Return(buffer))
120+
{
121+
saved = Interlocked.Increment(ref this.saved);
122+
}
123+
else
102124
{
103-
var bufferHolder = this.bufferHolders[b.Length % this.bufferHolders.Length];
104-
bufferHolder.Return(b);
125+
saved = this.saved;
105126
}
127+
128+
#if DEBUG
129+
Debug.WriteLine($"DefaultBufferPool: Returned: Size={buffer.Length}, Saved={saved}");
130+
#endif
106131
}
107132
}

Diff for: FlashCap.Core/PixelBuffer.cs

+33-20
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ public sealed class PixelBuffer
1919

2020
private byte[]? imageContainer;
2121
private int imageContainerSize;
22-
private byte[]? transcodedImageContainer = null;
22+
private byte[]? transcodedImageContainer;
2323
private bool isValidTranscodedImage;
2424
private long timestampMicroseconds;
2525
private TranscodeFormats transcodeFormat;
@@ -48,18 +48,25 @@ internal unsafe void CopyIn(
4848

4949
lock (this)
5050
{
51-
if (this.imageContainer == null ||
52-
this.imageContainer.Length < totalSize)
51+
var imageContainer = this.imageContainer;
52+
53+
if (imageContainer == null ||
54+
imageContainer.Length < totalSize)
5355
{
54-
Debug.WriteLine($"Allocated: CurrentSize={this.imageContainer?.Length ?? 0}, Size={totalSize}");
56+
if (imageContainer != null)
57+
{
58+
this.bufferPool.Return(imageContainer);
59+
}
60+
imageContainer = this.bufferPool.Rent(totalSize, false);
61+
this.imageContainer = imageContainer;
5562

56-
this.imageContainer = this.bufferPool.Rent(totalSize, false);
63+
Debug.WriteLine($"Allocated: Size={totalSize}");
5764
}
5865

5966
this.imageContainerSize = totalSize;
6067
this.isValidTranscodedImage = false;
6168

62-
fixed (byte* pImageContainer = this.imageContainer!)
69+
fixed (byte* pImageContainer = imageContainer)
6370
{
6471
if (pBih->biCompression == NativeMethods.Compression.MJPG ||
6572
pBih->biCompression == NativeMethods.Compression.BI_JPEG ||
@@ -117,24 +124,28 @@ internal unsafe ArraySegment<byte> InternalExtractImage(BufferStrategies strateg
117124
{
118125
lock (this)
119126
{
120-
if (this.imageContainer == null)
127+
var imageContainer = this.imageContainer;
128+
if (imageContainer == null)
121129
{
122130
throw new InvalidOperationException("Extracted before capture.");
123131
}
124132

125133
if (this.transcodeFormat != TranscodeFormats.DoNotTranscode)
126134
{
127-
if (this.isValidTranscodedImage && this.transcodedImageContainer != null)
135+
var transcodedImageContainer = this.transcodedImageContainer;
136+
if (this.isValidTranscodedImage && transcodedImageContainer != null)
128137
{
129138
if (strategy == BufferStrategies.ForceReuse)
130139
{
131-
return new ArraySegment<byte>(this.transcodedImageContainer);
140+
return new ArraySegment<byte>(transcodedImageContainer);
132141
}
133142
else
134143
{
135-
var copied1 = this.bufferPool.Rent(this.transcodedImageContainer.Length, true);
136-
Array.Copy(this.transcodedImageContainer, copied1, copied1.Length);
137-
return new ArraySegment<byte>(copied1);
144+
var copiedImageContainer = new byte[transcodedImageContainer.Length];
145+
Array.Copy(transcodedImageContainer,
146+
copiedImageContainer, copiedImageContainer.Length);
147+
148+
return new ArraySegment<byte>(copiedImageContainer);
138149
}
139150
}
140151

@@ -154,6 +165,10 @@ internal unsafe ArraySegment<byte> InternalExtractImage(BufferStrategies strateg
154165
if (this.transcodedImageContainer == null ||
155166
this.transcodedImageContainer.Length != totalSize)
156167
{
168+
if (this.transcodedImageContainer != null)
169+
{
170+
this.bufferPool.Return(this.transcodedImageContainer);
171+
}
157172
this.transcodedImageContainer = this.bufferPool.Rent(totalSize, true);
158173
}
159174

@@ -200,9 +215,7 @@ internal unsafe ArraySegment<byte> InternalExtractImage(BufferStrategies strateg
200215
}
201216
else
202217
{
203-
var copied1 = this.transcodedImageContainer;
204-
this.transcodedImageContainer = null;
205-
return new ArraySegment<byte>(copied1);
218+
return new ArraySegment<byte>(this.transcodedImageContainer);
206219
}
207220
}
208221
}
@@ -211,19 +224,19 @@ internal unsafe ArraySegment<byte> InternalExtractImage(BufferStrategies strateg
211224
switch (strategy)
212225
{
213226
case BufferStrategies.ForceReuse:
214-
return new ArraySegment<byte>(this.imageContainer, 0, this.imageContainerSize);
227+
return new ArraySegment<byte>(imageContainer, 0, this.imageContainerSize);
215228
case BufferStrategies.CopyWhenDifferentSizeOrReuse:
216-
if (this.imageContainer.Length == this.imageContainerSize)
229+
if (imageContainer.Length == this.imageContainerSize)
217230
{
218-
return new ArraySegment<byte>(this.imageContainer);
231+
return new ArraySegment<byte>(imageContainer);
219232
}
220233
break;
221234
}
222235

223236
var copied = this.bufferPool.Rent(this.imageContainerSize, true);
224-
Array.Copy(this.imageContainer, copied, copied.Length);
237+
Array.Copy(imageContainer, copied, copied.Length);
225238

226-
Debug.WriteLine($"Copied: CurrentSize={this.imageContainer.Length}, Size={this.imageContainerSize}");
239+
Debug.WriteLine($"Copied: CurrentSize={imageContainer.Length}, Size={this.imageContainerSize}");
227240

228241
return new ArraySegment<byte>(copied);
229242
}

0 commit comments

Comments
 (0)