[9102] | 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 |
|
---|