Esenlikler
D'de neleri anımsıyorum diye bakmak için Dotnet kütüphanesinde var olan çöp toplayacıdaki baskıyı azaltmak için kullanılar array pool'u D'de de gerçekleştireyim istedim.
Sanırım çalışır işe yarabilir durumda. En azından öyle umuyorum.
Ancak // private static immutable ArrayPool s_shared = new ConfigurableArrayPool();
yorum satırını sildiğimdeki hatanın mantığını anlayamadım.
module array_pool;
template MyTemplate(T)
{
public abstract class ArrayPool
{
// @property
// public static auto sharedPool()
// {
// return s_shared;
// }
@property
public static auto create()
{
return new ConfigurableArrayPool();
}
@property
public static auto create(size_t maxArrayLength, size_t maxArraysPerBucket)
{
return new ConfigurableArrayPool(maxArrayLength, maxArraysPerBucket);
}
/*
Error: pointer cast from `array_pool.MyTemplate!(MyClass).ConfigurableArrayPool` to `void*` is not supported at compile time
source\array_pool.d(70,39): called from here: `this.toHash()`
source\array_pool.d(57,29): called from here: `this.id()`
source\array_pool.d(41,17): called from here: `this.this(1048576LU, 50LU)`
*/
// private static immutable ArrayPool s_shared = new ConfigurableArrayPool();
public abstract T[] rent(size_t minimumLength);
public abstract void returnToPool(T[] array, bool clearArray = false);
}
final public class ConfigurableArrayPool : ArrayPool
{
private enum size_t defaultMaxArrayLength = 1024 * 1024;
private enum size_t defaultMaxNumberOfArraysPerBucket = 50;
private /*immutable*/ Bucket[] buckets;
public this()
{
this(defaultMaxArrayLength, defaultMaxNumberOfArraysPerBucket);
}
public this(size_t maxArrayLength, size_t maxArraysPerBucket)
{
enum size_t minumumArrayLength = 0x10;
enum size_t maximumArrayLength = 0x40000000;
if (maxArrayLength > maximumArrayLength)
{
maxArrayLength = maximumArrayLength;
}
else if (maxArrayLength < minumumArrayLength)
{
maxArrayLength = minumumArrayLength;
}
size_t poolId = id;
size_t maxBuckets = Utilities.selectBucketIndex(maxArrayLength);
auto buckets = new Bucket[maxBuckets + 1];
for (size_t i = 0; i < buckets.length; i++)
{
buckets[i] = new Bucket(Utilities.getMaxSizeForBucket(i), maxArraysPerBucket, poolId);
}
this.buckets = buckets;
}
@property
size_t id()
{
return cast(size_t) toHash();
}
public override T[] rent(size_t minimumLength)
{
if (minimumLength == 0)
{
return new T[0];
}
T[] buffer;
size_t index = Utilities.selectBucketIndex(cast(size_t) minimumLength);
if (index < buckets.length)
{
enum maxBucketsToTry = 2;
size_t i = index;
do
{
buffer = this.buckets[i].rent();
if (buffer != null)
{
return buffer;
}
}
while (++i < this.buckets.length && i != index + maxBucketsToTry);
buffer = new T[this.buckets[index].bufferLength];
}
else
{
buffer = new T[minimumLength];
}
return buffer;
}
public override void returnToPool(T[] array, bool clearArray = false)
{
if (array.length == 0)
{
return;
}
size_t bucketsize_t = Utilities.selectBucketIndex(cast(size_t) array.length);
bool haveBucket = bucketsize_t < this.buckets.length;
if (haveBucket)
{
if (clearArray)
{
for (size_t i = 0; i < array.length; i++)
{
array[i] = T.init;
}
}
this.buckets[bucketsize_t].returnToPool(array);
}
}
final class Bucket
{
private immutable size_t bufferLength;
private /*immutable*/ T[][] buffers;
private immutable size_t poolId;
import core.sync.mutex;
Mutex mutex;
private size_t index;
this(size_t bufferLength, size_t numberOfBuffers, size_t poolId)
{
mutex = new Mutex();
this.buffers = new T[][numberOfBuffers];
this.bufferLength = bufferLength;
this.poolId = poolId;
}
@property
size_t id()
{
return cast(size_t) toHash();
}
T[] rent()
{
T[] buffer = null;
bool lockTaken = false;
bool allocateBuffer = false;
try
{
lockTaken = mutex.tryLock_nothrow();
if (index < buffers[].length)
{
buffer = buffers[][index];
buffers[][index++] = null;
allocateBuffer = buffer == null;
}
}
finally
{
if (lockTaken)
{
mutex.tryLock_nothrow();
}
}
if (allocateBuffer)
{
buffer = new T[bufferLength];
}
return buffer;
}
void returnToPool(T[] array)
{
if (array.length != bufferLength)
{
throw new Exception("Buffer not From Pool");
}
bool returned;
bool lockTaken = false;
try
{
lockTaken = mutex.tryLock_nothrow();
returned = index != 0;
if (returned)
{
buffers[][--index] = array;
}
}
finally
{
if (lockTaken)
{
mutex.unlock_nothrow();
}
}
}
}
static class Utilities
{
static size_t selectBucketIndex(size_t bufferSize)
{
import std.math.exponential;
return cast(size_t) log2(cast(size_t) bufferSize - 1 | 15) - 3;
}
static size_t getMaxSizeForBucket(size_t binIndex)
{
size_t maxSize = 16 << binIndex;
return maxSize;
}
}
}
}
örnek kullanımı:
import std.stdio;
import array_pool;
public class MyClass
{
public int A;
public int B;
}
import std.random;
int main(string[] args)
{
auto _buckets = MyTemplate!(MyClass).ArrayPool.create;
foreach (key; 1 .. 100)
{
auto rnd = Random(unpredictableSeed);
auto items = _buckets.rent(100);
for (int i = 0; i < items.length; i++)
{
items[i] = new MyClass();
items[i].A = cast(int) i;
items[i].B = uniform(0, 100, rnd);
rnd.popFront;
}
foreach (item; items)
{
writeln(item.A);
writeln(item.B);
}
_buckets.returnToPool(items, true);
}
return 0;
}