1 /**
2 Module  contains multithreated allcoators. Few of them with similar interface.
3  */
4 module mutils.container_shared.shared_allocator;
5 
6 import std.stdio;
7 import std.conv:emplace;
8 import std.experimental.allocator;
9 import std.experimental.allocator.mallocator;
10 
11 import mutils.job_manager.shared_utils;
12 import mutils.job_manager.utils;
13 import mutils.container.vector;
14 
15 
16 class MyMallocator{
17 	shared Mallocator allocator;
18 	this(){
19 		allocator=Mallocator.instance;
20 	}
21 	auto make(T,Args...)(auto ref Args args){
22 		return allocator.make!T(args);
23 	}
24 	void dispose(T)(ref T* obj){
25 		allocator.dispose(obj);
26 		//obj=T.init;
27 	}
28 }
29 class MyGcAllcoator{
30 	auto make(T,Args...)(auto ref Args args){
31 		auto var=new T(args);
32 		import core.memory;
33 		GC.addRoot(var);
34 		return var;
35 	}
36 	void dispose(T)(ref T* obj){
37 		import core.memory;
38 		GC.removeRoot(obj);
39 		//obj=T.init;
40 	}
41 }
42 
43 import core.atomic;
44 import std.random:uniform;
45 
46 
47 
48 class BucketAllocator(uint bucketSize){
49 	static assert(bucketSize>=8);
50 	enum shared Bucket* invalidValue=cast(shared Bucket*)858567;
51 	
52 	static struct Bucket{
53 		union{
54 			void[bucketSize] data;
55 			Bucket* next;
56 		}
57 	}
58 	enum bucketsNum=128;
59 	
60 	
61 	static struct BucketsArray{
62 	@nogc:
63 		Bucket[bucketsNum] buckets;
64 		shared Bucket* empty;
65 		void initialize() shared {
66 			shared Bucket* last;
67 			foreach(i,ref bucket;buckets){
68 				bucket.next=last;
69 				last=&bucket;
70 			}
71 			empty=cast(shared Bucket*)last;
72 		}
73 		uint freeSlots()shared {
74 			uint i;
75 			shared Bucket* slot=empty;
76 			while(slot !is null){
77 				i++;
78 				slot=slot.next;
79 			}
80 			return i;
81 		}
82 		uint usedSlots()shared {
83 			return bucketsNum-freeSlots;
84 		}
85 	}
86 	alias BucketArraysType=Vector!(shared BucketsArray*);
87 	
88 	//shared BucketsArray*[] bucketArrays;
89 	BucketArraysType bucketArrays;
90 	
91 	
92 	this(){
93 		//bucketArrays=Mallocator.instance.make!(BucketArraysType);
94 		bucketArrays.extend(128);
95 	}
96 	~this(){
97 	}
98 	void[] oldData;
99 	void extend(){
100 		//shared BucketsArray* arr=new shared BucketsArray;
101 		shared BucketsArray* arr=cast(shared BucketsArray*)Mallocator.instance.make!(BucketsArray);
102 		(*arr).initialize();
103 		if(!bucketArrays.canAddWithoutRealloc){
104 			if(oldData !is null){
105 				bucketArrays.freeData(oldData);//free on next alloc, noone should use the old array
106 			}
107 			oldData=bucketArrays.manualExtend();
108 		}
109 		bucketArrays~=arr;
110 	}
111 	T* make(T,Args...)(auto ref Args args){
112 		void[] memory=allocate();
113 		//TODO some checks: aligment, size, itp??
114 		return memory.emplace!(T)( args );
115 	}
116 	void[] allocate(){
117 	FF:foreach(i,bucketsArray;bucketArrays){
118 			if(bucketsArray.empty is null)continue;
119 			
120 			shared Bucket* emptyBucket;
121 			do{
122 			BACK:
123 				emptyBucket=atomicLoad(bucketsArray.empty);
124 				if(emptyBucket is null){
125 					continue FF;
126 				}
127 				if(emptyBucket==invalidValue){
128 					goto BACK;
129 				}
130 			}while(!cas(&bucketsArray.empty,emptyBucket,invalidValue));
131 			atomicStore(bucketsArray.empty,emptyBucket.next);
132 			return cast(void[])emptyBucket.data;
133 		}
134 		
135 		//assert(0);
136 		synchronized(this){
137 			extend();
138 			auto bucketsArray=bucketArrays[$-1];
139 			shared Bucket* empty=bucketsArray.empty;
140 			bucketsArray.empty=(*bucketsArray.empty).next;
141 			return 	cast(void[])empty.data;		
142 		}
143 		
144 	}
145 	void dispose(T)(T* obj){
146 		deallocate(cast(void[])obj[0..1]);
147 	}
148 	void deallocate(void[] data){
149 		foreach(bucketsArray;bucketArrays){
150 			auto ptr=bucketsArray.buckets.ptr;
151 			auto dptr=data.ptr;
152 			if(dptr>=ptr+bucketsNum || dptr<ptr){
153 				continue;
154 			}
155 			shared Bucket* bucket=cast(shared Bucket*)data.ptr;
156 			shared Bucket* emptyBucket;
157 			
158 			do{
159 			BACK:
160 				emptyBucket=atomicLoad(bucketsArray.empty);
161 				if(emptyBucket==invalidValue){
162 					goto BACK;
163 				}
164 				bucket.next=emptyBucket;
165 			}while(!cas(&bucketsArray.empty,emptyBucket,bucket));
166 			return;
167 		}
168 		writelnng(data.ptr);
169 		assert(0);
170 	}
171 	
172 	uint usedSlots(){
173 		uint sum;
174 		foreach(bucketsArray;bucketArrays)sum+=bucketsArray.usedSlots;
175 		return sum;
176 		
177 	}
178 }
179 
180 
181 
182 void ttt(){
183 	BucketAllocator!(64) allocator=Mallocator.instance.make!(BucketAllocator!64);
184 	scope(exit)Mallocator.instance.dispose(allocator);
185 	foreach(k;0..123){
186 		void[][] memories;
187 		assert(allocator.bucketArrays[0].freeSlots==allocator.bucketsNum);
188 		foreach(i;0..allocator.bucketsNum){
189 			memories~=allocator.allocate();
190 		}
191 		assert(allocator.bucketArrays[0].freeSlots==0);
192 		foreach(i;0..allocator.bucketsNum){
193 			memories~=allocator.allocate();
194 			assert(allocator.bucketArrays.length==2);
195 		}
196 		foreach(i,m;memories){
197 			allocator.deallocate(m);
198 		}
199 	}
200 	
201 }
202 import mutils.benchmark;
203 void testAL(){
204 	BucketAllocator!(64) allocator=Mallocator.instance.make!(BucketAllocator!(64));
205 	scope(exit)Mallocator.instance.dispose(allocator);
206 	shared ulong sum;
207 	void test(){
208 		foreach(k;0..1000){
209 			int*[] memories;
210 			uint rand=uniform(130,140);
211 			memories=Mallocator.instance.makeArray!(int*)(rand);
212 			scope(exit)Mallocator.instance.dispose(memories);
213 			foreach(i;0..rand){
214 				memories[i]=allocator.make!int();
215 			}
216 			foreach(m;memories){
217 				allocator.dispose(m);
218 			}
219 			atomicOp!"+="(sum,memories.length);
220 		}
221 	}
222 	void testAdd(){
223 		foreach(i;0..128){
224 			allocator.allocate();
225 		}
226 	}
227 	foreach(i;0..10000){
228 		sum=0;
229 		StopWatch sw;
230 		sw.start();
231 		testMultithreaded(&test,16);
232 		sw.stop();  	
233 		writefln( "Benchmark: %s %s[ms], %s[it/ms]",sum,sw.msecs,sum/sw.msecs);
234 		
235 		assert(allocator.usedSlots==0);
236 	}
237 	
238 }
239 unittest{
240 	//testAL();
241 }