1 | ///
|
---|
2 | /// This file is part of ILNumerics Community Edition.
|
---|
3 | ///
|
---|
4 | /// ILNumerics Community Edition - high performance computing for applications.
|
---|
5 | /// Copyright (C) 2006 - 2012 Haymo Kutschbach, http://ilnumerics.net
|
---|
6 | ///
|
---|
7 | /// ILNumerics Community Edition is free software: you can redistribute it and/or modify
|
---|
8 | /// it under the terms of the GNU General Public License version 3 as published by
|
---|
9 | /// the Free Software Foundation.
|
---|
10 | ///
|
---|
11 | /// ILNumerics Community Edition is distributed in the hope that it will be useful,
|
---|
12 | /// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
---|
13 | /// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
---|
14 | /// GNU General Public License for more details.
|
---|
15 | ///
|
---|
16 | /// You should have received a copy of the GNU General Public License
|
---|
17 | /// along with ILNumerics Community Edition. See the file License.txt in the root
|
---|
18 | /// of your distribution package. If not, see <http://www.gnu.org/licenses/>.
|
---|
19 | ///
|
---|
20 | /// In addition this software uses the following components and/or licenses:
|
---|
21 | ///
|
---|
22 | /// =================================================================================
|
---|
23 | /// The Open Toolkit Library License
|
---|
24 | ///
|
---|
25 | /// Copyright (c) 2006 - 2009 the Open Toolkit library.
|
---|
26 | ///
|
---|
27 | /// Permission is hereby granted, free of charge, to any person obtaining a copy
|
---|
28 | /// of this software and associated documentation files (the "Software"), to deal
|
---|
29 | /// in the Software without restriction, including without limitation the rights to
|
---|
30 | /// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
---|
31 | /// the Software, and to permit persons to whom the Software is furnished to do
|
---|
32 | /// so, subject to the following conditions:
|
---|
33 | ///
|
---|
34 | /// The above copyright notice and this permission notice shall be included in all
|
---|
35 | /// copies or substantial portions of the Software.
|
---|
36 | ///
|
---|
37 | /// =================================================================================
|
---|
38 | ///
|
---|
39 |
|
---|
40 | using System;
|
---|
41 | using System.Collections;
|
---|
42 | using System.Collections.Generic;
|
---|
43 | using System.Text;
|
---|
44 | using ILNumerics.Exceptions;
|
---|
45 | using ILNumerics.Data;
|
---|
46 |
|
---|
47 | namespace ILNumerics.Misc {
|
---|
48 | /// <summary>
|
---|
49 | /// Memory pool serving as temporary storage for System.Array objects
|
---|
50 | /// </summary>
|
---|
51 | /// <remarks>The pool reduces the pressure on the systems memory caused by larger objects.
|
---|
52 | /// <para>Arrays created in ILNumerics will first try to reclaim their memory from this pool. If that
|
---|
53 | /// fails, the memory is allocated from the managed heap only.</para>
|
---|
54 | /// <para>Disposed array objects register their underlying System.Array in the pool for
|
---|
55 | /// later reusing. The process is triggered by the ILNumerics memory management automatically.</para></remarks>
|
---|
56 | internal partial class ILMemoryPoolInternal<T> : IILMemoryPool {
|
---|
57 |
|
---|
58 | internal enum MemoryPoolShrinkStrategies {
|
---|
59 | Popularity,
|
---|
60 | Smallest
|
---|
61 | }
|
---|
62 |
|
---|
63 | #region attributes
|
---|
64 | private Dictionary<long,Stack<T[]>> m_pool;
|
---|
65 | private Dictionary<long,int> m_requests;
|
---|
66 | private ILAVLTree m_lengths;
|
---|
67 | private Dictionary<long, string> m_profiler;
|
---|
68 | private long m_maxBytes;
|
---|
69 | private long m_curBytes;
|
---|
70 | private long m_minArrLength;
|
---|
71 | private long m_reclaimedBytesCount;
|
---|
72 | private long m_reclaimedObjectsCount;
|
---|
73 | private int m_catchedOOMs;
|
---|
74 | private bool[] m_rateHistVals = new bool[100];
|
---|
75 | private int m_rateHistPos = 0;
|
---|
76 | private int m_shrinksCount = 0;
|
---|
77 | private int m_curObjectsCount = 0;
|
---|
78 | private double m_autoIncreaseNewRequests = 1.1;
|
---|
79 | private double m_maxRequestedLengthIncrease = 1.2;
|
---|
80 | private double m_maxBytesIncreaseRate = 1.02;
|
---|
81 | private double m_OOMdecreaseMaxBytesRate = 0.8;
|
---|
82 | private Comparer<KeyValuePair<long, int>> m_comparer = new ILRequestsValueComparer();
|
---|
83 | private static readonly int s_singleElementSize;
|
---|
84 | private static readonly double s_shrinkPercent = .5;
|
---|
85 | private static readonly double s_shrinkRequestLenghtIncreaseRate;
|
---|
86 | private static readonly string s_elementTypeName;
|
---|
87 | internal static ILMemoryPoolInternal<T> Pool;
|
---|
88 | internal static ILPerformanceCounter<T> s_performanceCounters = new ILPerformanceCounter<T>();
|
---|
89 | #if VERBOSE
|
---|
90 | internal static string s_logfilename = String.Format("ILMemoryPool_VERBOSE_Log_{0}.log",typeof(T).Name);
|
---|
91 | internal static StringBuilder s_logBuilder = new StringBuilder("ILMemoryPoolInternal allocation history from " + DateTime.Now.ToShortDateString() + " " + DateTime.Now.ToShortTimeString() + Environment.NewLine);
|
---|
92 | #endif
|
---|
93 | #endregion
|
---|
94 |
|
---|
95 | #region properties
|
---|
96 | /// <summary>
|
---|
97 | /// factor used, to allow returned arrays to exceed the requested array length
|
---|
98 | /// </summary>
|
---|
99 | public double MaxRequestedLengthIncrease {
|
---|
100 | get { return m_maxRequestedLengthIncrease; }
|
---|
101 | set {
|
---|
102 | m_maxRequestedLengthIncrease = value;
|
---|
103 | if (Settings.s_measurePerformanceAtRuntime)
|
---|
104 | s_performanceCounters.PCAcceptLengthIncreaseSet((int)Math.Log10(m_maxRequestedLengthIncrease));
|
---|
105 | }
|
---|
106 | }
|
---|
107 | /// <summary>
|
---|
108 | /// minimum length of array objects for recognition in the pool (default: 80k)
|
---|
109 | /// </summary>
|
---|
110 | public long MinArrayLength {
|
---|
111 | get { return m_minArrLength; }
|
---|
112 | set { m_minArrLength = value; }
|
---|
113 | }
|
---|
114 | /// <summary>
|
---|
115 | /// maximum size of the pool configured in bytes
|
---|
116 | /// </summary>
|
---|
117 | public long MaxBytes {
|
---|
118 | get { return m_maxBytes; }
|
---|
119 | set {
|
---|
120 | m_maxBytes = value;
|
---|
121 | if (Settings.s_measurePerformanceAtRuntime)
|
---|
122 | s_performanceCounters.PCmaximumPoolSizeSet(m_maxBytes);
|
---|
123 | }
|
---|
124 | }
|
---|
125 | /// <summary>
|
---|
126 | /// percentage of allocation requests which could be successfully be completed
|
---|
127 | /// </summary>
|
---|
128 | public double SuccessRate {
|
---|
129 | get {
|
---|
130 | int succ = 0;
|
---|
131 | foreach (bool b in m_rateHistVals) {
|
---|
132 | if (b) succ++;
|
---|
133 | }
|
---|
134 | return succ / (double)m_rateHistVals.Length * 100d;
|
---|
135 | }
|
---|
136 | }
|
---|
137 | /// <summary>
|
---|
138 | /// Number of reclaimed bytes since the pool exists
|
---|
139 | /// </summary>
|
---|
140 | /// <remarks>The counter will be reset by calls to <see cref="ILNumerics.Misc.ILMemoryPool.Reset(int, int)"/></remarks>
|
---|
141 | public long ReclaimedBytesCount {
|
---|
142 | get { return m_reclaimedBytesCount; }
|
---|
143 | }
|
---|
144 | /// <summary>
|
---|
145 | /// Number of reclaimed objects since the pool exists
|
---|
146 | /// </summary>
|
---|
147 | /// <remarks>The counter will be reset by calls to <see cref="ILNumerics.Misc.ILMemoryPool.Reset(int, int)"/></remarks>
|
---|
148 | public long ReclaimedObjectsCount {
|
---|
149 | get { return m_reclaimedObjectsCount; }
|
---|
150 | }
|
---|
151 | #endregion
|
---|
152 |
|
---|
153 | #region constructors
|
---|
154 | private ILMemoryPoolInternal()
|
---|
155 | : this (getDefaultMinArrayLength(),200) {} // (int)(Environment.WorkingSet / 1024.0 / 1024 / 10)) { }
|
---|
156 |
|
---|
157 | private ILMemoryPoolInternal(int MinArrayLength, int PoolSizeMB) {
|
---|
158 | m_maxBytes = PoolSizeMB * 1024 * 1024;
|
---|
159 | m_minArrLength = MinArrayLength;
|
---|
160 | m_requests = new Dictionary<long,int>();
|
---|
161 | m_pool = new Dictionary<long,Stack<T[]>>();
|
---|
162 | m_lengths = new ILAVLTree();
|
---|
163 | m_profiler = new Dictionary<long,string>();
|
---|
164 | MaxRequestedLengthIncrease = 1.2;
|
---|
165 | ILMemoryPool.Pools.Add(typeof(T),this);
|
---|
166 | }
|
---|
167 |
|
---|
168 | static ILMemoryPoolInternal() {
|
---|
169 | if (typeof(T).IsValueType) {
|
---|
170 | T tmp = default(T);
|
---|
171 | s_singleElementSize = Math.Max(System.Runtime.InteropServices.Marshal.SizeOf(tmp), 1);
|
---|
172 | } else {
|
---|
173 | s_singleElementSize = 4;
|
---|
174 | }
|
---|
175 | Pool = new ILMemoryPoolInternal<T>();
|
---|
176 | s_elementTypeName = typeof(T).Name;
|
---|
177 |
|
---|
178 | #if DEBUG
|
---|
179 | //System.Diagnostics.StackTrace.METHODS_TO_SKIP
|
---|
180 | #endif
|
---|
181 |
|
---|
182 | #if VERBOSE
|
---|
183 | // manage log file
|
---|
184 | System.IO.File.Delete(s_logfilename);
|
---|
185 | #endif
|
---|
186 | }
|
---|
187 |
|
---|
188 | #endregion
|
---|
189 |
|
---|
190 | #region public API
|
---|
191 | /// <summary>
|
---|
192 | /// Reset & reconfigure the pool
|
---|
193 | /// </summary>
|
---|
194 | /// <param name="MinArrayLength">Minimum length for array object to be stored inside the pool</param>
|
---|
195 | /// <param name="PoolSizeMB">Overall size the memory pool consumes at most</param>
|
---|
196 | /// <remarks>Reset will dispose all objects currently hold in the pool!</remarks>
|
---|
197 | public void Reset ( long MinArrayLength, int PoolSizeMB ) {
|
---|
198 | lock (this) {
|
---|
199 | if (PoolSizeMB < 10)
|
---|
200 | PoolSizeMB = 10;
|
---|
201 | m_maxBytes = PoolSizeMB * 1024 * 1024;
|
---|
202 | m_minArrLength = MinArrayLength;
|
---|
203 | DisposeContent();
|
---|
204 | m_reclaimedBytesCount = 0;
|
---|
205 | m_reclaimedObjectsCount = 0;
|
---|
206 | }
|
---|
207 | }
|
---|
208 | /// <summary>
|
---|
209 | /// Dispose all objects currently hold in the pool
|
---|
210 | /// </summary>
|
---|
211 | /// <remarks>The pool get cleared and continues working with the same parameters after the call has finished. </remarks>
|
---|
212 | public void DisposeContent() {
|
---|
213 | lock (this) {
|
---|
214 | //m_requests.Clear(); // lets keep the statistics
|
---|
215 | m_pool.Clear();
|
---|
216 | m_lengths.Clear();
|
---|
217 | m_requests.Clear();
|
---|
218 | m_curBytes = 0;
|
---|
219 | m_curObjectsCount = 0;
|
---|
220 |
|
---|
221 | if (Settings.s_measurePerformanceAtRuntime) {
|
---|
222 | s_performanceCounters.PCoverallSizeSet(0);
|
---|
223 | }
|
---|
224 | }
|
---|
225 | }
|
---|
226 | /// <summary>
|
---|
227 | /// Return or register an array object of value type in the pool that is not used anymore, i.e. free it
|
---|
228 | /// </summary>
|
---|
229 | /// <typeparam name="T">arbitrary element type</typeparam>
|
---|
230 | /// <param name="arr">array</param>
|
---|
231 | /// <remarks><para>In order to be stored in the pool, the array must meet the minimum array length and must fit into the global pool size.
|
---|
232 | /// Null objects or empty arrays or array not suitable for the pool will be silently ignored.</para>
|
---|
233 | /// <para>If the new array is too large to fit into the remaining pool space, the oldest objects in the pool will be released until the object can get registered.</para></remarks>
|
---|
234 | public void Free(T[] arr) {
|
---|
235 | #if VERBOSE
|
---|
236 | //System.Diagnostics.Debug.WriteLine("ILMemoryPoolInternal registering array: {0}[] {1}",typeof(T).Name, arr.Length);
|
---|
237 | Log(arr.Length,"REG");
|
---|
238 | #endif
|
---|
239 | if (arr == null || arr.Length == 0 || arr.Length < m_minArrLength)
|
---|
240 | return;
|
---|
241 | #if DEBUG
|
---|
242 | //System.Diagnostics.StackTrace st = new System.Diagnostics.StackTrace();
|
---|
243 | //DebuggerTraceHelper.Output.Append(String.Format("\r\n{0}[{1}]-{2}\r\n{3}\r\n======================================"
|
---|
244 | // ,arr.ToString(), arr.Length, arr.GetHashCode() ,st.ToString()));
|
---|
245 | //DebuggerTraceHelper.Output.Append(String.Format("============================="));
|
---|
246 | #endif
|
---|
247 | if (!String.IsNullOrEmpty(Settings.s_memoryPoolProfileFileName)) {
|
---|
248 | lock (m_profiler) {
|
---|
249 | int key = arr.GetHashCode();
|
---|
250 | if (m_profiler.ContainsKey(key))
|
---|
251 | m_profiler.Remove(key);
|
---|
252 | }
|
---|
253 | }
|
---|
254 | // determine size of new array
|
---|
255 | long newSize = arr.Length * s_singleElementSize + 8; // up to .NET 4.0
|
---|
256 | if (newSize >= m_maxBytes) {
|
---|
257 | m_maxBytes = newSize+1;
|
---|
258 | return;
|
---|
259 | }
|
---|
260 | lock (this) {
|
---|
261 | if (m_curBytes + newSize > m_maxBytes)
|
---|
262 | Shrink(newSize);
|
---|
263 | // add the array to the pool
|
---|
264 | Stack<T[]> inPool;
|
---|
265 | if (m_pool.TryGetValue(arr.Length, out inPool)) {
|
---|
266 | // append to pool
|
---|
267 | System.Diagnostics.Debug.Assert(!inPool.Contains(arr));
|
---|
268 | inPool.Push(arr);
|
---|
269 | } else {
|
---|
270 | // add new length to pool
|
---|
271 | inPool = new Stack<T[]>();
|
---|
272 | inPool.Push(arr);
|
---|
273 | m_pool.Add(arr.Length,inPool);
|
---|
274 | }
|
---|
275 | m_lengths.Add(arr.Length);
|
---|
276 | m_curObjectsCount++;
|
---|
277 | m_curBytes += newSize;
|
---|
278 | if (Settings.s_measurePerformanceAtRuntime) {
|
---|
279 | s_performanceCounters.PCoverallSizeSet(m_curBytes);
|
---|
280 | s_performanceCounters.PCcurObjectsCountSet(m_curObjectsCount);
|
---|
281 | }
|
---|
282 | }
|
---|
283 | }
|
---|
284 | /// <summary>
|
---|
285 | /// Request a System.Array instance, may not be cleared
|
---|
286 | /// </summary>
|
---|
287 | /// <typeparam name="T">value type</typeparam>
|
---|
288 | /// <param name="length"><b>minimum</b> length of array</param>
|
---|
289 | /// <returns>System.Array - either from the pool or a newly created array</returns>
|
---|
290 | /// <remarks><para>If a suitable System.Array was found in the pool, this object is returned.
|
---|
291 | /// Otherwise a new array is created.</para>
|
---|
292 | /// <para>There is no way of determining, if the array was reclaimed from pool or newly created!
|
---|
293 | /// If you must be sure, the element values are set to default(T), call the overloaded version
|
---|
294 | /// <see cref="ILNumerics.Misc.ILMemoryPool.New<T>(int, bool, out bool)"/> instead!</para>
|
---|
295 | /// <para>If a new array could not get created due to an OutOfMemoryException, a garbage collection
|
---|
296 | /// is triggered and the array is again requested from the pool. If this again fails, another attempt
|
---|
297 | /// to create the array is done. Exceptions may thrown from this last attempt are not catched and
|
---|
298 | /// therefore must be handled by the calling function.</para></remarks>
|
---|
299 | public T[] New(long length) {
|
---|
300 | #if VERBOSE
|
---|
301 | //System.Diagnostics.Debug.WriteLine(String.Format("Pool<{0}>: requesting length: {1}", typeof(T).Name, length));
|
---|
302 | Log((int)length,"NEW");
|
---|
303 | #endif
|
---|
304 | T[] ret = null;
|
---|
305 | lock (this) {
|
---|
306 | // try to find a suitable object in the pool first
|
---|
307 | if (length >= m_minArrLength && (ret = GetFromPool(length)) != null) {
|
---|
308 | int oldRequestCount = 0;
|
---|
309 | length = ret.Length;
|
---|
310 | m_requests.TryGetValue(length, out oldRequestCount);
|
---|
311 | m_requests[length] = ++oldRequestCount;
|
---|
312 | return ret;
|
---|
313 | }
|
---|
314 | }
|
---|
315 | // must create a new one
|
---|
316 | try {
|
---|
317 | length = (long)(m_autoIncreaseNewRequests * length);
|
---|
318 | ret = new T[length];
|
---|
319 | } catch (OutOfMemoryException) {
|
---|
320 | m_catchedOOMs++;
|
---|
321 | m_maxBytes = (long) (m_maxBytes * m_OOMdecreaseMaxBytesRate);
|
---|
322 | if (Settings.s_measurePerformanceAtRuntime) {
|
---|
323 | s_performanceCounters.PCOOMcatchedIncrement();
|
---|
324 | }
|
---|
325 | DisposeContent();
|
---|
326 | }
|
---|
327 | if (ret == null) {
|
---|
328 | ret = new T[length];
|
---|
329 | }
|
---|
330 | lock (this) {
|
---|
331 | int oldRequestCount = 0;
|
---|
332 | m_requests.TryGetValue(length, out oldRequestCount);
|
---|
333 | m_requests[length] = ++oldRequestCount;
|
---|
334 | }
|
---|
335 | return ret;
|
---|
336 | }
|
---|
337 | /// <summary>
|
---|
338 | /// Request a System.Array instance and optionally clear the array
|
---|
339 | /// </summary>
|
---|
340 | /// <typeparam name="T">value type</typeparam>
|
---|
341 | /// <param name="length">length of array</param>
|
---|
342 | /// <param name="clear">if true, the elements of the array returned are set to default(T).</param>
|
---|
343 | /// <param name="iscleared">out paramater determining if the array returned has been cleared</param>
|
---|
344 | /// <returns>System.Array - either from the pool or a newly created array</returns>
|
---|
345 | /// <remarks><para>If a suitable System.Array was found in the pool, this object is returned. Otherwise a new array is created.</para>
|
---|
346 | /// <para>If the <paramref name="clear">clear </paramref> parameter was set to false, the
|
---|
347 | /// <paramref name="iscleared">iscleared</paramref> parameter can be used to determine, if the object
|
---|
348 | /// was returnd from the pool and may need extra clearing.</para>
|
---|
349 | /// <para>If a new array could not get created due to an OutOfMemoryException, a garbage
|
---|
350 | /// collection is triggered and the array is again requested from the pool. If this again failes,
|
---|
351 | /// another attempt to create the array is done. Exceptions eventually thrown from this last
|
---|
352 | /// attempt are not catched and given back to the callee.</para></remarks>
|
---|
353 | public T[] New(long length, bool clear, out bool iscleared) {
|
---|
354 | #if VERBOSE
|
---|
355 | //System.Diagnostics.Debug.Write(String.Format("Pool<{0}>: requesting length: {1}", typeof(T).Name, length));
|
---|
356 | Log(length,"NEW");
|
---|
357 | #endif
|
---|
358 | T[] ret = null;
|
---|
359 | lock (this) {
|
---|
360 | // try to find a suitable object in the pool first
|
---|
361 | if (length >= m_minArrLength && (ret = GetFromPool(length)) != null) {
|
---|
362 | int oldRequestCount = 0;
|
---|
363 | length = ret.Length;
|
---|
364 | m_requests.TryGetValue(length, out oldRequestCount);
|
---|
365 | m_requests[length] = ++oldRequestCount;
|
---|
366 | if (clear) {
|
---|
367 | for (int i = 0; i < length; i++) {
|
---|
368 | ret[i] = default(T);
|
---|
369 | }
|
---|
370 | iscleared = true;
|
---|
371 | } else {
|
---|
372 | iscleared = false;
|
---|
373 | }
|
---|
374 | return ret;
|
---|
375 | }
|
---|
376 | }
|
---|
377 | // must create a new one
|
---|
378 | try {
|
---|
379 | length = (int)(m_autoIncreaseNewRequests * length);
|
---|
380 | ret = new T[length];
|
---|
381 | } catch (OutOfMemoryException) {
|
---|
382 | DisposeContent();
|
---|
383 | m_maxBytes = (long)(m_maxBytes * m_OOMdecreaseMaxBytesRate);
|
---|
384 | m_catchedOOMs++;
|
---|
385 | if (Settings.s_measurePerformanceAtRuntime) {
|
---|
386 | s_performanceCounters.PCOOMcatchedIncrement();
|
---|
387 | }
|
---|
388 | }
|
---|
389 | if (ret == null) {
|
---|
390 | ret = new T[length];
|
---|
391 | }
|
---|
392 | iscleared = true;
|
---|
393 | lock (this) {
|
---|
394 | int oldRequestCount = 0;
|
---|
395 | length = ret.Length;
|
---|
396 | m_requests.TryGetValue(length, out oldRequestCount);
|
---|
397 | m_requests[length] = ++oldRequestCount;
|
---|
398 | }
|
---|
399 | return ret;
|
---|
400 | }
|
---|
401 | /// <summary>
|
---|
402 | /// Give infos about pool state, optionally reset counters
|
---|
403 | /// </summary>
|
---|
404 | /// <param name="shortVersion">true: abbreviate infos to: current MB in Pool, reclaimed MB for livetime, reclaimed objects for livetime. False: give full info (see below)</param>
|
---|
405 | /// <param name="reset">true: reset internal counter for reclaimed objects/ - bytes</param>
|
---|
406 | /// <returns>infos about current pool state</returns>
|
---|
407 | public string Info(bool shortVersion, bool reset) {
|
---|
408 | if (shortVersion) {
|
---|
409 | string s = String.Format("CurMB: {0}| CurObj: {1}| ReclMB: {2}| ReclObj: {3}| Rate: {4:G6}",m_curBytes/1024/1024,countObj(),m_reclaimedBytesCount/1024/1024,m_reclaimedObjectsCount,SuccessRate);
|
---|
410 | if (reset) {
|
---|
411 | m_reclaimedBytesCount = 0;
|
---|
412 | m_reclaimedObjectsCount = 0;
|
---|
413 | }
|
---|
414 | return s;
|
---|
415 | } else {
|
---|
416 | StringBuilder sb = new StringBuilder();
|
---|
417 | sb.Append("ILMemoryPool - ");
|
---|
418 | sb.Append(DateTime.Now.ToLongTimeString() + Environment.NewLine);
|
---|
419 | string tmp;
|
---|
420 | lock (this) {
|
---|
421 | sb.Append(String.Format("CurMB: {0}| CurObj: {1}| ReclMB: {2}| ReclObj: {3}| Rate: {4:G6}",m_curBytes/1024/1024,countObj(),m_reclaimedBytesCount/1024/1024,m_reclaimedObjectsCount,SuccessRate) + Environment.NewLine);
|
---|
422 | sb.Append(String.Format("OOM: {0}| Shrink: {1:}| LFact: {2:G2}| MaxMB: {3}\r\n",m_catchedOOMs, m_shrinksCount, m_maxRequestedLengthIncrease, m_maxBytes/1024/1024));
|
---|
423 | foreach (KeyValuePair<long,Stack<T[]>> item in m_pool) {
|
---|
424 | sb.Append(item.Key + ": ");
|
---|
425 | foreach(Array a in item.Value) {
|
---|
426 | tmp = a.GetType().GetElementType().Name;
|
---|
427 | if (char.IsNumber(tmp[tmp.Length-1])) {
|
---|
428 | tmp = new String(new char[]{tmp[0], tmp[tmp.Length-1]});
|
---|
429 | } else {
|
---|
430 | tmp = tmp.Substring(0,2);
|
---|
431 | }
|
---|
432 | sb.Append(tmp);
|
---|
433 | sb.Append(" ");
|
---|
434 | }
|
---|
435 | sb.Append(Environment.NewLine);
|
---|
436 | }
|
---|
437 | if (reset) {
|
---|
438 | m_reclaimedBytesCount = 0;
|
---|
439 | m_reclaimedObjectsCount = 0;
|
---|
440 | }
|
---|
441 | }
|
---|
442 | return sb.ToString();
|
---|
443 | }
|
---|
444 | }
|
---|
445 | /// <summary>
|
---|
446 | /// Give extended infos about pool state
|
---|
447 | /// </summary>
|
---|
448 | /// <returns>Full info about current and reclaimed pool objects</returns>
|
---|
449 | /// <remarks>For short version infos use the overloaded version <see cref="ILNumerics.Misc.ILMemoryPool.Info(bool)"/></remarks>
|
---|
450 | public string Info() {
|
---|
451 | return Info(false, false);
|
---|
452 | }
|
---|
453 | /// <summary>
|
---|
454 | /// Give infos about pool state, optionally reset counters
|
---|
455 | /// </summary>
|
---|
456 | /// <param name="shortVersion">true: abbreviate infos to: current MB in Pool, reclaimed MB for livetime, reclaimed objects for livetime. False: give full info (see below)</param>
|
---|
457 | /// <returns>infos about current pool state</returns>
|
---|
458 | public string Info(bool shortVersion) {
|
---|
459 | return Info(shortVersion, false);
|
---|
460 | }
|
---|
461 | #endregion
|
---|
462 |
|
---|
463 | #region private helper
|
---|
464 | #if VERBOSE
|
---|
465 | private void Log( int length, string prefix ) {
|
---|
466 | lock (this) {
|
---|
467 | s_logBuilder.Append(
|
---|
468 | String.Format("{0},\t{1},\t{2},\t{3}" + Environment.NewLine
|
---|
469 | , prefix
|
---|
470 | , System.DateTime.Now.ToShortDateString()
|
---|
471 | , System.DateTime.Now.ToLongTimeString() + "." + System.DateTime.Now.Millisecond.ToString()
|
---|
472 | , length));
|
---|
473 | if (s_logBuilder.Length > 10000) {
|
---|
474 | System.IO.File.AppendAllText(s_logfilename, s_logBuilder.ToString());
|
---|
475 | s_logBuilder.Clear();
|
---|
476 | }
|
---|
477 | }
|
---|
478 | }
|
---|
479 | #endif
|
---|
480 | private T[] GetFromPool(long length) {
|
---|
481 | Stack<T[]> inPool;
|
---|
482 | T[] ret = null;
|
---|
483 | lock (this) {
|
---|
484 | if (m_pool.TryGetValue(length, out inPool)) {
|
---|
485 | ret = inPool.Pop();
|
---|
486 | long newSize = (ret.Length * s_singleElementSize + 8);
|
---|
487 | m_curBytes -= newSize;
|
---|
488 | m_curObjectsCount--;
|
---|
489 | if (Settings.s_measurePerformanceAtRuntime) {
|
---|
490 | s_performanceCounters.PCcurObjectsCountSet(m_curObjectsCount);
|
---|
491 | s_performanceCounters.PCoverallSizeSet(m_curBytes);
|
---|
492 | s_performanceCounters.PCsuccessRateIncrement();
|
---|
493 | s_performanceCounters.PCsuccessRateBaseIncrement();
|
---|
494 | s_performanceCounters.PCreclaimedObjectsCountIncrement();
|
---|
495 | }
|
---|
496 | if (inPool.Count == 0) {
|
---|
497 | m_pool.Remove(length);
|
---|
498 | m_lengths.Remove(length);
|
---|
499 | m_requests.Remove(length);
|
---|
500 | }
|
---|
501 | m_reclaimedObjectsCount += 1;
|
---|
502 | m_reclaimedBytesCount += newSize;
|
---|
503 | m_rateHistVals[m_rateHistPos++] = true;
|
---|
504 | if (m_rateHistPos >= m_rateHistVals.Length)
|
---|
505 | m_rateHistPos = 0;
|
---|
506 | } else {
|
---|
507 | // try to get next larger array
|
---|
508 | long largerLength = m_lengths.Next(length);
|
---|
509 | if (largerLength >= 0 && largerLength <= length * m_maxRequestedLengthIncrease) {
|
---|
510 | return GetFromPool(largerLength);
|
---|
511 | }
|
---|
512 | }
|
---|
513 | }
|
---|
514 | if (ret == null) {
|
---|
515 | m_rateHistVals[m_rateHistPos++] = false;
|
---|
516 | if (Settings.s_measurePerformanceAtRuntime) {
|
---|
517 | s_performanceCounters.PCsuccessRateBaseIncrement();
|
---|
518 | }
|
---|
519 | if (m_rateHistPos >= m_rateHistVals.Length)
|
---|
520 | m_rateHistPos = 0;
|
---|
521 | } else if (!String.IsNullOrEmpty(Settings.s_memoryPoolProfileFileName)) {
|
---|
522 | lock (m_profiler) {
|
---|
523 | int key = ret.GetHashCode();
|
---|
524 | m_profiler[key] = s_elementTypeName + "[" + length + "] - " + new System.Diagnostics.StackTrace(4).ToString();
|
---|
525 | if (m_profiler.Count > 500) {
|
---|
526 | //throw new Exception("The profiler status indicates the existence of a memory leak! Check the stack traces for common functions requesting an array which is never returnd to the pool!");
|
---|
527 | System.IO.File.WriteAllText(Settings.s_memoryPoolProfileFileName,
|
---|
528 | String.Join(Environment.NewLine, m_profiler.Values));
|
---|
529 | m_profiler.Clear();
|
---|
530 | }
|
---|
531 | }
|
---|
532 | }
|
---|
533 | return ret;
|
---|
534 | }
|
---|
535 | private int countObj() {
|
---|
536 | int ret = 0;
|
---|
537 | lock (this)
|
---|
538 | foreach (KeyValuePair<long,Stack<T[]>> item in m_pool) {
|
---|
539 | foreach(Array a in item.Value) {
|
---|
540 | ret ++;
|
---|
541 | }
|
---|
542 | }
|
---|
543 | return ret;
|
---|
544 | }
|
---|
545 | private static int getDefaultMinArrayLength() {
|
---|
546 | T[] tmp = new T[0];
|
---|
547 | if (tmp is double[]) {
|
---|
548 | return 500;
|
---|
549 | } else {
|
---|
550 | return 70 * 1024 / s_singleElementSize;
|
---|
551 | }
|
---|
552 | }
|
---|
553 | /// <summary>
|
---|
554 | /// shrink the pool's content to SHRINK_PERCENT of the maximum pool size or at the size, suitable to store requestLen
|
---|
555 | /// </summary>
|
---|
556 | /// <param name="requestLen">minimum length to make available (bytes)</param>
|
---|
557 | private void Shrink(long requestLen) {
|
---|
558 | try {
|
---|
559 | long targetSize = Math.Min(m_maxBytes - requestLen, (long)(s_shrinkPercent * m_maxBytes));
|
---|
560 | if (m_curBytes <= targetSize)
|
---|
561 | return;
|
---|
562 | if (Settings.s_measurePerformanceAtRuntime) {
|
---|
563 | s_performanceCounters.PCshrinksCountIncrement();
|
---|
564 | } m_shrinksCount++;
|
---|
565 | // recalculate new parameters
|
---|
566 | m_maxBytes = (long)(m_maxBytesIncreaseRate * m_maxBytes);
|
---|
567 | if (m_pool.Count <= 1) {
|
---|
568 | MaxRequestedLengthIncrease /= 1.02;
|
---|
569 | } else {
|
---|
570 | MaxRequestedLengthIncrease *= 1.02;
|
---|
571 | }
|
---|
572 |
|
---|
573 | // first clear all arrays in pool, which were not requested
|
---|
574 | List<int> keys4Remove = new List<int>();
|
---|
575 | foreach (int key in m_pool.Keys) {
|
---|
576 | if (!m_requests.ContainsKey(key)) {
|
---|
577 | keys4Remove.Add(key);
|
---|
578 | }
|
---|
579 | }
|
---|
580 | foreach (int key in keys4Remove) {
|
---|
581 | int size = key * s_singleElementSize + 8;
|
---|
582 | Stack<T[]> arrays = m_pool[key];
|
---|
583 | while (arrays.Count > 0) {
|
---|
584 | arrays.Pop();
|
---|
585 | m_curBytes -= size;
|
---|
586 | m_curObjectsCount--;
|
---|
587 | //if (m_curBytes < targetSize) return;
|
---|
588 | }
|
---|
589 | if (arrays.Count == 0) {
|
---|
590 | m_requests.Remove(key);
|
---|
591 | m_pool.Remove(key);
|
---|
592 | m_lengths.Remove(key);
|
---|
593 | }
|
---|
594 | }
|
---|
595 | if (m_curBytes < targetSize) {
|
---|
596 | if (Settings.s_measurePerformanceAtRuntime) {
|
---|
597 | s_performanceCounters.PCoverallSizeSet(m_curBytes);
|
---|
598 | s_performanceCounters.PCcurObjectsCountSet(m_curObjectsCount);
|
---|
599 | }
|
---|
600 | return;
|
---|
601 | }
|
---|
602 | // sort requests statistics by its values
|
---|
603 |
|
---|
604 | List<KeyValuePair<long, int>> sorted = new List<KeyValuePair<long, int>>(m_requests);
|
---|
605 | sorted.Sort(m_comparer);
|
---|
606 | foreach (KeyValuePair<long, int> item in sorted) {
|
---|
607 | long curKey = item.Key;
|
---|
608 | Stack<T[]> inPool;
|
---|
609 | if (!m_pool.TryGetValue(curKey, out inPool)) {
|
---|
610 | continue;
|
---|
611 | }
|
---|
612 | while (inPool.Count > 0 && m_curBytes > targetSize) {
|
---|
613 | inPool.Pop();
|
---|
614 | m_curBytes -= ((long)curKey * s_singleElementSize + 8);
|
---|
615 | m_curObjectsCount--;
|
---|
616 | }
|
---|
617 | if (inPool.Count == 0) {
|
---|
618 | m_lengths.Remove(curKey);
|
---|
619 | m_pool.Remove(curKey);
|
---|
620 | m_requests.Remove(curKey);
|
---|
621 | }
|
---|
622 | if (m_curBytes <= targetSize) {
|
---|
623 | if (Settings.s_measurePerformanceAtRuntime) {
|
---|
624 | s_performanceCounters.PCoverallSizeSet(m_curBytes);
|
---|
625 | s_performanceCounters.PCcurObjectsCountSet(m_curObjectsCount);
|
---|
626 | }
|
---|
627 | return;
|
---|
628 | }
|
---|
629 | }
|
---|
630 | if (Settings.s_measurePerformanceAtRuntime) {
|
---|
631 | s_performanceCounters.PCoverallSizeSet(m_curBytes);
|
---|
632 | s_performanceCounters.PCcurObjectsCountSet(m_curObjectsCount);
|
---|
633 | }
|
---|
634 | } finally {
|
---|
635 | m_requests.Clear();
|
---|
636 | }
|
---|
637 | }
|
---|
638 | #endregion
|
---|
639 |
|
---|
640 | }
|
---|
641 | }
|
---|
642 |
|
---|