PageRenderTime 55ms CodeModel.GetById 25ms RepoModel.GetById 0ms app.codeStats 0ms

/ASSP/FileSystemCache.cs

#
C# | 346 lines | 264 code | 52 blank | 30 comment | 13 complexity | 0d1ccd3b409f2517219d0339fc56ad46 MD5 | raw file
  1. /*============================================================================
  2. File: FileSystemCache.cs
  3. Summary: Exposes the Windows System File Cache usage and allows it to be
  4. cleared.
  5. Date: February 23, 2009
  6. ----------------------------------------------------------------------------
  7. This file is part of the Analysis Services Stored Procedure Project.
  8. http://www.codeplex.com/Wiki/View.aspx?ProjectName=ASStoredProcedures
  9. THIS CODE AND INFORMATION ARE PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
  10. KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
  11. IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
  12. PARTICULAR PURPOSE.
  13. ============================================================================*/
  14. using System;
  15. using System.Runtime.InteropServices;
  16. using Microsoft.AnalysisServices.AdomdServer;
  17. using System.Security.Principal;
  18. namespace ASStoredProcs
  19. {
  20. public class FileSystemCache
  21. {
  22. #region ClearFileSystemCache
  23. public static void ClearFileSystemCache()
  24. {
  25. ClearFileSystemCache(true); //clear the standby cache by default since this must be done to really test cold cache performance
  26. }
  27. public static void ClearFileSystemCache(bool ClearStandbyCache)
  28. {
  29. SetIncreasePrivilege(SE_INCREASE_QUOTA_NAME);
  30. //clear the active file system cache
  31. int iReturn;
  32. if (!Is64BitMode())
  33. {
  34. SYSTEM_CACHE_INFORMATION info = new SYSTEM_CACHE_INFORMATION();
  35. info.MinimumWorkingSet = uint.MaxValue; //means to clear the active file system cache
  36. info.MaximumWorkingSet = uint.MaxValue; //means to clear the active file system cache
  37. int iSize = Marshal.SizeOf(info);
  38. GCHandle gch = GCHandle.Alloc(info, GCHandleType.Pinned);
  39. iReturn = NtSetSystemInformation(SYSTEMCACHEINFORMATION, gch.AddrOfPinnedObject(), iSize);
  40. gch.Free();
  41. }
  42. else
  43. {
  44. SYSTEM_CACHE_INFORMATION_64_BIT info = new SYSTEM_CACHE_INFORMATION_64_BIT();
  45. info.MinimumWorkingSet = -1; //means to clear the active file system cache
  46. info.MaximumWorkingSet = -1; //means to clear the active file system cache
  47. int iSize = Marshal.SizeOf(info);
  48. GCHandle gch = GCHandle.Alloc(info, GCHandleType.Pinned);
  49. iReturn = NtSetSystemInformation(SYSTEMCACHEINFORMATION, gch.AddrOfPinnedObject(), iSize);
  50. gch.Free();
  51. }
  52. if (iReturn != 0)
  53. {
  54. throw new Exception("NtSetSystemInformation(SYSTEMCACHEINFORMATION) error: ", new System.ComponentModel.Win32Exception(Marshal.GetLastWin32Error()));
  55. }
  56. if (ClearStandbyCache)
  57. {
  58. try
  59. {
  60. SetIncreasePrivilege(SE_PROFILE_SINGLE_PROCESS_NAME);
  61. int iSize = Marshal.SizeOf(ClearStandbyPageList);
  62. GCHandle gch = GCHandle.Alloc(ClearStandbyPageList, GCHandleType.Pinned);
  63. iReturn = NtSetSystemInformation(SYSTEMMEMORYLISTINFORMATION, gch.AddrOfPinnedObject(), iSize);
  64. gch.Free();
  65. if (iReturn != 0)
  66. {
  67. throw new Exception("NtSetSystemInformation(SYSTEMMEMORYLISTINFORMATION) error: ", new System.ComponentModel.Win32Exception(Marshal.GetLastWin32Error()));
  68. }
  69. }
  70. catch (Exception ex)
  71. {
  72. Context.TraceEvent(1, 101, "Problem clearing standby cache with API call: " + ex.Message);
  73. //this is a fallback if the API call doesn't work on an older OS
  74. ClearStandbyFileSystemCacheByConsumingAvailableMemory();
  75. }
  76. }
  77. }
  78. #endregion
  79. #region ClearAllCaches
  80. /// <summary>
  81. /// Clear both the Analysis Services cache and the active+standby system file cache in one sproc call
  82. /// </summary>
  83. public static void ClearAllCaches()
  84. {
  85. XmlaDiscover xmla = new XmlaDiscover();
  86. xmla.ClearCache();
  87. ClearFileSystemCache(true);
  88. }
  89. #endregion
  90. #region ClearStandbyFileSystemCacheByConsumingAvailableMemory
  91. private static void ClearStandbyFileSystemCacheByConsumingAvailableMemory()
  92. {
  93. //consume all available memory then free it, which will wipe out the standby cache
  94. Context.TraceEvent(1, 1, "Clearing standby cache by consuming all available memory");
  95. //get the page size. will need to write at least one byte per page to make sure that page is committed to this process' working set: http://blogs.msdn.com/b/ntdebugging/archive/2007/11/27/too-much-cache.aspx?CommentPosted=true&PageIndex=2#comments
  96. SYSTEM_INFO sysinfo = new SYSTEM_INFO();
  97. GetSystemInfo(ref sysinfo);
  98. Context.TraceEvent(1, 2, "Page size on this server is " + sysinfo.dwPageSize + " bytes");
  99. System.Diagnostics.PerformanceCounter pcAvailableBytes = null;
  100. long lngAvailableBytes = 0;
  101. pcAvailableBytes = new System.Diagnostics.PerformanceCounter("Memory", "Available Bytes", true);
  102. lngAvailableBytes = (long)pcAvailableBytes.NextValue();
  103. Context.TraceEvent(1, 3, "Available Bytes after clearing active cache: " + lngAvailableBytes);
  104. long lngRemainingBytes = lngAvailableBytes - (1024 * 1024); //take up all available memory minus 1MB
  105. System.Collections.Generic.List<IntPtr> listPtrMem = new System.Collections.Generic.List<IntPtr>();
  106. try
  107. {
  108. Context.TraceEvent(1, 4, "Preparing to consume " + lngRemainingBytes + " bytes of memory");
  109. while (lngRemainingBytes > 0)
  110. {
  111. //figure out the next allocation size
  112. int iAllocLen = (int)Math.Min((long)(sysinfo.dwPageSize * 1024), lngRemainingBytes);
  113. lngRemainingBytes -= iAllocLen;
  114. //allocate this memory
  115. listPtrMem.Add(Marshal.AllocHGlobal(iAllocLen));
  116. //write one byte per page which is the minimum necessary to make sure this page gets committed to this process' working set
  117. for (int j = 0; j < iAllocLen; j += (int)sysinfo.dwPageSize)
  118. {
  119. Marshal.WriteByte(listPtrMem[listPtrMem.Count - 1], j, (byte)1);
  120. }
  121. }
  122. lngAvailableBytes = (long)pcAvailableBytes.NextValue();
  123. Context.TraceEvent(1, 5, "Available Bytes after consuming memory: " + lngAvailableBytes);
  124. }
  125. catch (OutOfMemoryException ex)
  126. {
  127. Context.TraceEvent(1, 5, "Received OutOfMemoryException: " + ex.Message);
  128. Context.TraceEvent(1, 10, "Was able to consume desired memory except for the following number of bytes: " + lngRemainingBytes);
  129. }
  130. finally
  131. {
  132. // dont forget to free up the memory.
  133. foreach (IntPtr ptrMem in listPtrMem)
  134. {
  135. if (ptrMem != IntPtr.Zero)
  136. Marshal.FreeHGlobal(ptrMem);
  137. }
  138. }
  139. lngAvailableBytes = (long)pcAvailableBytes.NextValue();
  140. Context.TraceEvent(1, 6, "Available Bytes after freeing consumed memory: " + lngAvailableBytes);
  141. }
  142. #endregion
  143. #region GetFileSystemCacheBytes
  144. public static System.Data.DataTable GetFileSystemCacheBytes()
  145. {
  146. System.Data.DataTable tableReturn = new System.Data.DataTable();
  147. tableReturn.Columns.Add("Cache Bytes", typeof(long));
  148. tableReturn.Columns.Add("Cache GB", typeof(decimal));
  149. tableReturn.Columns.Add("Cache Bytes Peak", typeof(long));
  150. tableReturn.Columns.Add("Cache GB Peak", typeof(decimal));
  151. tableReturn.Columns.Add("Standby Cache Bytes", typeof(long));
  152. tableReturn.Columns.Add("Standby Cache GB", typeof(decimal));
  153. System.Diagnostics.PerformanceCounter pcCacheBytes = new System.Diagnostics.PerformanceCounter("Memory", "Cache Bytes", true);
  154. System.Diagnostics.PerformanceCounter pcCacheBytesPeak = new System.Diagnostics.PerformanceCounter("Memory", "Cache Bytes Peak", true);
  155. float fltCacheBytes = pcCacheBytes.NextValue();
  156. float fltCacheBytesPeak = pcCacheBytesPeak.NextValue();
  157. float? fltStandbyCacheBytes = null;
  158. try
  159. {
  160. System.Diagnostics.PerformanceCounter pcStandbyCacheBytes = new System.Diagnostics.PerformanceCounter("Memory", "Standby Cache Normal Priority Bytes", true);
  161. fltStandbyCacheBytes = pcStandbyCacheBytes.NextValue();
  162. pcStandbyCacheBytes.Close();
  163. }
  164. catch { }
  165. pcCacheBytes.Close();
  166. pcCacheBytesPeak.Close();
  167. object[] row = new object[] {
  168. (long)fltCacheBytes,
  169. decimal.Round((decimal)(fltCacheBytes / 1024 / 1024 / 1024), 2),
  170. (long)fltCacheBytesPeak,
  171. decimal.Round((decimal)(fltCacheBytesPeak / 1024 / 1024 / 1024), 2),
  172. (fltStandbyCacheBytes==null?null:fltStandbyCacheBytes),
  173. (fltStandbyCacheBytes==null?(decimal?)null:(decimal?)decimal.Round((decimal)(fltStandbyCacheBytes / 1024 / 1024 / 1024), 2))
  174. };
  175. tableReturn.Rows.Add(row);
  176. return tableReturn;
  177. }
  178. #endregion
  179. #region Windows APIs
  180. public static bool Is64BitMode()
  181. {
  182. return System.Runtime.InteropServices.Marshal.SizeOf(typeof(IntPtr)) == 8;
  183. }
  184. [DllImport("NTDLL.dll", SetLastError = true)]
  185. internal static extern int NtSetSystemInformation(int SystemInformationClass, IntPtr SystemInfo, int SystemInfoLength);
  186. //SystemInformationClass values
  187. private static int SYSTEMCACHEINFORMATION = 0x15;
  188. private static int SYSTEMMEMORYLISTINFORMATION = 80;
  189. //SystemInfo values
  190. private static int ClearStandbyPageList = 4;
  191. #pragma warning disable 649 //disable the "is never assigned to" warning
  192. private struct SYSTEM_CACHE_INFORMATION
  193. {
  194. public uint CurrentSize;
  195. public uint PeakSize;
  196. public uint PageFaultCount;
  197. public uint MinimumWorkingSet;
  198. public uint MaximumWorkingSet;
  199. public uint Unused1;
  200. public uint Unused2;
  201. public uint Unused3;
  202. public uint Unused4;
  203. }
  204. private struct SYSTEM_CACHE_INFORMATION_64_BIT
  205. {
  206. public long CurrentSize;
  207. public long PeakSize;
  208. public long PageFaultCount;
  209. public long MinimumWorkingSet;
  210. public long MaximumWorkingSet;
  211. public long Unused1;
  212. public long Unused2;
  213. public long Unused3;
  214. public long Unused4;
  215. }
  216. #pragma warning restore 649 //reenable the warning
  217. [DllImport("kernel32.dll", SetLastError = true)]
  218. internal static extern bool CloseHandle(IntPtr hObject);
  219. [DllImport("advapi32.dll", ExactSpelling = true, SetLastError = true)]
  220. internal static extern bool AdjustTokenPrivileges(IntPtr htok, bool disall, ref TokPriv1Luid newst, int len, IntPtr prev, IntPtr relen);
  221. [DllImport("kernel32.dll", ExactSpelling = true)]
  222. internal static extern IntPtr GetCurrentProcess();
  223. [DllImport("advapi32.dll", ExactSpelling = true, SetLastError = true)]
  224. internal static extern bool OpenProcessToken(IntPtr h, int acc, ref IntPtr phtok);
  225. [DllImport("advapi32.dll", SetLastError = true)]
  226. internal static extern bool LookupPrivilegeValue(string host, string name, ref long pluid);
  227. [StructLayout(LayoutKind.Sequential, Pack = 1)]
  228. internal struct TokPriv1Luid
  229. {
  230. public int Count;
  231. public long Luid;
  232. public int Attr;
  233. }
  234. private const int SE_PRIVILEGE_ENABLED = 0x00000002;
  235. private const int TOKEN_QUERY = 0x00000008;
  236. private const int TOKEN_ADJUST_PRIVILEGES = 0x00000020;
  237. private const string SE_INCREASE_QUOTA_NAME = "SeIncreaseQuotaPrivilege";
  238. private const string SE_PROFILE_SINGLE_PROCESS_NAME = "SeProfileSingleProcessPrivilege";
  239. private static bool SetIncreasePrivilege(string privilegeName)
  240. {
  241. try
  242. {
  243. using (WindowsIdentity currentIdentity = WindowsIdentity.GetCurrent(TokenAccessLevels.AdjustPrivileges | TokenAccessLevels.Query))
  244. {
  245. bool retVal;
  246. TokPriv1Luid tp;
  247. tp.Count = 1;
  248. tp.Luid = 0;
  249. tp.Attr = SE_PRIVILEGE_ENABLED;
  250. retVal = LookupPrivilegeValue(null, privilegeName, ref tp.Luid);
  251. if (!retVal) throw new Exception("Error in LookupPrivilegeValue: ", new System.ComponentModel.Win32Exception(Marshal.GetLastWin32Error()));
  252. retVal = AdjustTokenPrivileges(currentIdentity.Token, false, ref tp, 0, IntPtr.Zero, IntPtr.Zero);
  253. if (!retVal) throw new Exception("Error in AdjustTokenPrivileges: ", new System.ComponentModel.Win32Exception(Marshal.GetLastWin32Error()));
  254. return retVal;
  255. }
  256. }
  257. catch (Exception ex)
  258. {
  259. throw new Exception("Error in SetIncreasePrivilege(" + privilegeName + "):", ex);
  260. }
  261. }
  262. [DllImport("kernel32.dll")]
  263. private static extern void GetSystemInfo([MarshalAs(UnmanagedType.Struct)] ref SYSTEM_INFO lpSystemInfo);
  264. [StructLayout(LayoutKind.Sequential)]
  265. private struct SYSTEM_INFO
  266. {
  267. internal _PROCESSOR_INFO_UNION uProcessorInfo;
  268. public uint dwPageSize;
  269. public IntPtr lpMinimumApplicationAddress;
  270. public IntPtr lpMaximumApplicationAddress;
  271. public IntPtr dwActiveProcessorMask;
  272. public uint dwNumberOfProcessors;
  273. public uint dwProcessorType;
  274. public uint dwAllocationGranularity;
  275. public ushort dwProcessorLevel;
  276. public ushort dwProcessorRevision;
  277. }
  278. [StructLayout(LayoutKind.Explicit)]
  279. private struct _PROCESSOR_INFO_UNION
  280. {
  281. [FieldOffset(0)]
  282. internal uint dwOemId;
  283. [FieldOffset(0)]
  284. internal ushort wProcessorArchitecture;
  285. [FieldOffset(2)]
  286. internal ushort wReserved;
  287. }
  288. #endregion
  289. }
  290. }