ASStoredProcedures /ASSP/FileSystemCache.cs

Language C# Lines 347
MD5 Hash 0d1ccd3b409f2517219d0339fc56ad46 Estimated Cost $6,700 (why?)
Repository https://ASStoredProcedures.svn.codeplex.com/svn View Raw File
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
/*============================================================================
  File:    FileSystemCache.cs

  Summary: Exposes the Windows System File Cache usage and allows it to be
           cleared.

  Date:    February 23, 2009

  ----------------------------------------------------------------------------
  This file is part of the Analysis Services Stored Procedure Project.
  http://www.codeplex.com/Wiki/View.aspx?ProjectName=ASStoredProcedures
  
  THIS CODE AND INFORMATION ARE PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
  KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
  IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
  PARTICULAR PURPOSE.
============================================================================*/
using System;
using System.Runtime.InteropServices;
using Microsoft.AnalysisServices.AdomdServer;
using System.Security.Principal;

namespace ASStoredProcs
{
    public class FileSystemCache
    {
        #region ClearFileSystemCache
        public static void ClearFileSystemCache()
        {
            ClearFileSystemCache(true); //clear the standby cache by default since this must be done to really test cold cache performance
        }

        public static void ClearFileSystemCache(bool ClearStandbyCache)
        {
            SetIncreasePrivilege(SE_INCREASE_QUOTA_NAME);

            //clear the active file system cache
            int iReturn;
            if (!Is64BitMode())
            {
                SYSTEM_CACHE_INFORMATION info = new SYSTEM_CACHE_INFORMATION();
                info.MinimumWorkingSet = uint.MaxValue; //means to clear the active file system cache
                info.MaximumWorkingSet = uint.MaxValue; //means to clear the active file system cache
                int iSize = Marshal.SizeOf(info);

                GCHandle gch = GCHandle.Alloc(info, GCHandleType.Pinned);
                iReturn = NtSetSystemInformation(SYSTEMCACHEINFORMATION, gch.AddrOfPinnedObject(), iSize);
                gch.Free();
            }
            else
            {
                SYSTEM_CACHE_INFORMATION_64_BIT info = new SYSTEM_CACHE_INFORMATION_64_BIT();
                info.MinimumWorkingSet = -1; //means to clear the active file system cache
                info.MaximumWorkingSet = -1; //means to clear the active file system cache
                int iSize = Marshal.SizeOf(info);

                GCHandle gch = GCHandle.Alloc(info, GCHandleType.Pinned);
                iReturn = NtSetSystemInformation(SYSTEMCACHEINFORMATION, gch.AddrOfPinnedObject(), iSize);
                gch.Free();
            }

            if (iReturn != 0)
            {
                throw new Exception("NtSetSystemInformation(SYSTEMCACHEINFORMATION) error: ", new System.ComponentModel.Win32Exception(Marshal.GetLastWin32Error()));
            }

            if (ClearStandbyCache)
            {
                try
                {
                    SetIncreasePrivilege(SE_PROFILE_SINGLE_PROCESS_NAME);


                    int iSize = Marshal.SizeOf(ClearStandbyPageList);
                    GCHandle gch = GCHandle.Alloc(ClearStandbyPageList, GCHandleType.Pinned);
                    iReturn = NtSetSystemInformation(SYSTEMMEMORYLISTINFORMATION, gch.AddrOfPinnedObject(), iSize);
                    gch.Free();

                    if (iReturn != 0)
                    {
                        throw new Exception("NtSetSystemInformation(SYSTEMMEMORYLISTINFORMATION) error: ", new System.ComponentModel.Win32Exception(Marshal.GetLastWin32Error()));
                    }
                }
                catch (Exception ex)
                {
                    Context.TraceEvent(1, 101, "Problem clearing standby cache with API call: " + ex.Message);

                    //this is a fallback if the API call doesn't work on an older OS
                    ClearStandbyFileSystemCacheByConsumingAvailableMemory();
                }
            }
        }
        #endregion

        #region ClearAllCaches
        /// <summary>
        /// Clear both the Analysis Services cache and the active+standby system file cache in one sproc call
        /// </summary>
        public static void ClearAllCaches()
        {
            XmlaDiscover xmla = new XmlaDiscover();
            xmla.ClearCache();

            ClearFileSystemCache(true);
        }
        #endregion

        #region ClearStandbyFileSystemCacheByConsumingAvailableMemory
        private static void ClearStandbyFileSystemCacheByConsumingAvailableMemory()
        {
            //consume all available memory then free it, which will wipe out the standby cache
            Context.TraceEvent(1, 1, "Clearing standby cache by consuming all available memory");

            //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
            SYSTEM_INFO sysinfo = new SYSTEM_INFO();
            GetSystemInfo(ref sysinfo);

            Context.TraceEvent(1, 2, "Page size on this server is " + sysinfo.dwPageSize + " bytes");

            System.Diagnostics.PerformanceCounter pcAvailableBytes = null;
            long lngAvailableBytes = 0;

            pcAvailableBytes = new System.Diagnostics.PerformanceCounter("Memory", "Available Bytes", true);
            lngAvailableBytes = (long)pcAvailableBytes.NextValue();
            Context.TraceEvent(1, 3, "Available Bytes after clearing active cache: " + lngAvailableBytes);

            long lngRemainingBytes = lngAvailableBytes - (1024 * 1024); //take up all available memory minus 1MB
            System.Collections.Generic.List<IntPtr> listPtrMem = new System.Collections.Generic.List<IntPtr>();
            try
            {
                Context.TraceEvent(1, 4, "Preparing to consume " + lngRemainingBytes + " bytes of memory");

                while (lngRemainingBytes > 0)
                {
                    //figure out the next allocation size
                    int iAllocLen = (int)Math.Min((long)(sysinfo.dwPageSize * 1024), lngRemainingBytes);
                    lngRemainingBytes -= iAllocLen;

                    //allocate this memory
                    listPtrMem.Add(Marshal.AllocHGlobal(iAllocLen));

                    //write one byte per page which is the minimum necessary to make sure this page gets committed to this process' working set
                    for (int j = 0; j < iAllocLen; j += (int)sysinfo.dwPageSize)
                    {
                        Marshal.WriteByte(listPtrMem[listPtrMem.Count - 1], j, (byte)1);
                    }
                }

                lngAvailableBytes = (long)pcAvailableBytes.NextValue();
                Context.TraceEvent(1, 5, "Available Bytes after consuming memory: " + lngAvailableBytes);

            }
            catch (OutOfMemoryException ex)
            {
                Context.TraceEvent(1, 5, "Received OutOfMemoryException: " + ex.Message);
                Context.TraceEvent(1, 10, "Was able to consume desired memory except for the following number of bytes: " + lngRemainingBytes);
            }
            finally
            {
                // dont forget to free up the memory. 
                foreach (IntPtr ptrMem in listPtrMem)
                {
                    if (ptrMem != IntPtr.Zero)
                        Marshal.FreeHGlobal(ptrMem);
                }
            }

            lngAvailableBytes = (long)pcAvailableBytes.NextValue();
            Context.TraceEvent(1, 6, "Available Bytes after freeing consumed memory: " + lngAvailableBytes);
        }
        #endregion

        #region GetFileSystemCacheBytes
        public static System.Data.DataTable GetFileSystemCacheBytes()
        {
            System.Data.DataTable tableReturn = new System.Data.DataTable();
            tableReturn.Columns.Add("Cache Bytes", typeof(long));
            tableReturn.Columns.Add("Cache GB", typeof(decimal));
            tableReturn.Columns.Add("Cache Bytes Peak", typeof(long));
            tableReturn.Columns.Add("Cache GB Peak", typeof(decimal));
            tableReturn.Columns.Add("Standby Cache Bytes", typeof(long));
            tableReturn.Columns.Add("Standby Cache GB", typeof(decimal));

            System.Diagnostics.PerformanceCounter pcCacheBytes = new System.Diagnostics.PerformanceCounter("Memory", "Cache Bytes", true);
            System.Diagnostics.PerformanceCounter pcCacheBytesPeak = new System.Diagnostics.PerformanceCounter("Memory", "Cache Bytes Peak", true);

            float fltCacheBytes = pcCacheBytes.NextValue();
            float fltCacheBytesPeak = pcCacheBytesPeak.NextValue();

            float? fltStandbyCacheBytes = null;

            try
            {
                System.Diagnostics.PerformanceCounter pcStandbyCacheBytes = new System.Diagnostics.PerformanceCounter("Memory", "Standby Cache Normal Priority Bytes", true);
                fltStandbyCacheBytes = pcStandbyCacheBytes.NextValue();
                pcStandbyCacheBytes.Close();
            }
            catch { }

            pcCacheBytes.Close();
            pcCacheBytesPeak.Close();

            object[] row = new object[] { 
                (long)fltCacheBytes, 
                decimal.Round((decimal)(fltCacheBytes / 1024 / 1024 / 1024), 2), 
                (long)fltCacheBytesPeak, 
                decimal.Round((decimal)(fltCacheBytesPeak / 1024 / 1024 / 1024), 2), 
                (fltStandbyCacheBytes==null?null:fltStandbyCacheBytes), 
                (fltStandbyCacheBytes==null?(decimal?)null:(decimal?)decimal.Round((decimal)(fltStandbyCacheBytes / 1024 / 1024 / 1024), 2))
            };

            tableReturn.Rows.Add(row);
            return tableReturn;
        }
        #endregion

        #region Windows APIs
        public static bool Is64BitMode()
        {
            return System.Runtime.InteropServices.Marshal.SizeOf(typeof(IntPtr)) == 8;
        }

        [DllImport("NTDLL.dll", SetLastError = true)]
        internal static extern int NtSetSystemInformation(int SystemInformationClass, IntPtr SystemInfo, int SystemInfoLength);

        //SystemInformationClass values
        private static int SYSTEMCACHEINFORMATION = 0x15;
        private static int SYSTEMMEMORYLISTINFORMATION = 80;

        //SystemInfo values
        private static int ClearStandbyPageList = 4;

#pragma warning disable 649 //disable the "is never assigned to" warning
        private struct SYSTEM_CACHE_INFORMATION
        {
            public uint CurrentSize;
            public uint PeakSize;
            public uint PageFaultCount;
            public uint MinimumWorkingSet;
            public uint MaximumWorkingSet;
            public uint Unused1;
            public uint Unused2;
            public uint Unused3;
            public uint Unused4;
        }

        private struct SYSTEM_CACHE_INFORMATION_64_BIT
        {
            public long CurrentSize;
            public long PeakSize;
            public long PageFaultCount;
            public long MinimumWorkingSet;
            public long MaximumWorkingSet;
            public long Unused1;
            public long Unused2;
            public long Unused3;
            public long Unused4;
        }
#pragma warning restore 649 //reenable the warning

        [DllImport("kernel32.dll", SetLastError = true)]
        internal static extern bool CloseHandle(IntPtr hObject);

        [DllImport("advapi32.dll", ExactSpelling = true, SetLastError = true)]
        internal static extern bool AdjustTokenPrivileges(IntPtr htok, bool disall, ref TokPriv1Luid newst, int len, IntPtr prev, IntPtr relen);

        [DllImport("kernel32.dll", ExactSpelling = true)]
        internal static extern IntPtr GetCurrentProcess();

        [DllImport("advapi32.dll", ExactSpelling = true, SetLastError = true)]
        internal static extern bool OpenProcessToken(IntPtr h, int acc, ref IntPtr phtok);

        [DllImport("advapi32.dll", SetLastError = true)]
        internal static extern bool LookupPrivilegeValue(string host, string name, ref long pluid);

        [StructLayout(LayoutKind.Sequential, Pack = 1)]
        internal struct TokPriv1Luid
        {
            public int Count;
            public long Luid;
            public int Attr;
        }

        private const int SE_PRIVILEGE_ENABLED = 0x00000002;
        private const int TOKEN_QUERY = 0x00000008;
        private const int TOKEN_ADJUST_PRIVILEGES = 0x00000020;
        private const string SE_INCREASE_QUOTA_NAME = "SeIncreaseQuotaPrivilege";
        private const string SE_PROFILE_SINGLE_PROCESS_NAME = "SeProfileSingleProcessPrivilege";


        private static bool SetIncreasePrivilege(string privilegeName)
        {
            try
            {
                using (WindowsIdentity currentIdentity = WindowsIdentity.GetCurrent(TokenAccessLevels.AdjustPrivileges | TokenAccessLevels.Query))
                {
                    bool retVal;
                    TokPriv1Luid tp;
                    tp.Count = 1;
                    tp.Luid = 0;
                    tp.Attr = SE_PRIVILEGE_ENABLED;
                    retVal = LookupPrivilegeValue(null, privilegeName, ref tp.Luid);
                    if (!retVal) throw new Exception("Error in LookupPrivilegeValue: ", new System.ComponentModel.Win32Exception(Marshal.GetLastWin32Error()));
                    retVal = AdjustTokenPrivileges(currentIdentity.Token, false, ref tp, 0, IntPtr.Zero, IntPtr.Zero);
                    if (!retVal) throw new Exception("Error in AdjustTokenPrivileges: ", new System.ComponentModel.Win32Exception(Marshal.GetLastWin32Error()));
                    return retVal;
                }
            }
            catch (Exception ex)
            {
                throw new Exception("Error in SetIncreasePrivilege(" + privilegeName + "):", ex);
            }
        }

        [DllImport("kernel32.dll")]
        private static extern void GetSystemInfo([MarshalAs(UnmanagedType.Struct)] ref SYSTEM_INFO lpSystemInfo);

        [StructLayout(LayoutKind.Sequential)]
        private struct SYSTEM_INFO
        {
            internal _PROCESSOR_INFO_UNION uProcessorInfo;
            public uint dwPageSize;
            public IntPtr lpMinimumApplicationAddress;
            public IntPtr lpMaximumApplicationAddress;
            public IntPtr dwActiveProcessorMask;
            public uint dwNumberOfProcessors;
            public uint dwProcessorType;
            public uint dwAllocationGranularity;
            public ushort dwProcessorLevel;
            public ushort dwProcessorRevision;
        }

        [StructLayout(LayoutKind.Explicit)]
        private struct _PROCESSOR_INFO_UNION
        {
            [FieldOffset(0)]
            internal uint dwOemId;
            [FieldOffset(0)]
            internal ushort wProcessorArchitecture;
            [FieldOffset(2)]
            internal ushort wReserved;
        }

        #endregion
    }
}
Back to Top