14 8月 2013

[Win32] 在C/C++中偵測精準的程式時間(Performance Time)

在Windows底下,要偵測精準的Perfromance Time,可以透過QueryPerformanceFrequency() & QueryPerformanceCounter() 來取得。

由於取得的時間存在LARGE_INTEGER的structure,要取出來計算所花費的時間需要多點程式碼才行。這邊寫了一個簡單的Wrapper Class,讓這個AutoQueryPerformance的instance life-time結束後,自行dump出所花費的時間。

AutoQueryPerformance

#include "windows.h"

// Auto dump the life-time of AutoQueryPerformance
// Ex:
// {
//     AutoQueryPerformance autoQuery("Test", true);
//     ...
// } // it will dump message when @autoQuery destroy
class AutoQueryPerformance
{
public:
    AutoQueryPerformance(const char* info, INT loop_count=1, BOOL autoDump=TRUE)
        : m_count_time(0), m_loop_count(loop_count), m_auto(autoDump)
    {
        m_info = info;
        ::ZeroMemory(&m_cpu_freq, sizeof(LARGE_INTEGER));
        ::QueryPerformanceFrequency(&m_cpu_freq);

        if( autoDump )
            begin();
    }
    ~AutoQueryPerformance()
    {
        pause();
        if( autoDump )
            printf("[AutoQueryPerformance][%s] spend time=%.4f", m_info.c_str(), getTime());
    }
    inline void begin()
    {
        ::ZeroMemory(&m_start_clock, sizeof(LARGE_INTEGER));;
        ::QueryPerformanceCounter(&m_start_clock);
    }
    inline void pause()
    {
        ::ZeroMemory(&m_cur_clock, sizeof(LARGE_INTEGER));
        ::QueryPerformanceCounter(&m_cur_clock);
        m_count_time += m_cur_clock.QuadPart-m_start_clock.QuadPart;
    }
    inline void stop()
    {
        pause();
        m_count_time = 0;
    }
    inline float getTime()
    {
        return (m_count_time/(float)m_cpu_freq.QuadPart) / m_loop_count;
    }
private:
    LARGE_INTEGER m_cpu_freq;
    LARGE_INTEGER m_start_clock;
    LARGE_INTEGER m_cur_clock;
    LONGLONG      m_count_time;
    INT           m_loop_count;//for for-loop to calculate correct average time
    std::string   m_info;
    BOOL          m_auto;
};
#define AUTO_QUERY_PERFORMANCE(x) AutoQueryPerformance __at_qp = AutoQueryPerformance(x);


使用範例

void TestingTotalTime(int loop_count)
{
    AutoQueryPerformance ap("TestingTotalTime");
    int sum = 0;
    for(int i=0 ; i<loop_count; i++)
       sum += i;
}

void TestingSingleTime(int loop_count)
{
    AutoQueryPerformance ap("TestingSingleTime", loop_count);
    for(int i=0 ; i<loop_count; i++)
       //HeavyFunction call here;
}

Reference

12 8月 2013

[Win32/COM] Use COM DLL without registered

依照Windows COM元件的使用方法,應該要將COM DLL註冊後,即可在程式中,以CoCreateInstance()帶入CLSID、IID就可以將該DLL自動load起來,創建該class的instance了。

但是在開發時,實際上總是沒這麼美好,註冊DLL其實是很麻煩的,新舊相容性,開發中的版本等等的問題,而走manifest其實可以避開註冊這條路,但是今天我需要寫一個COM module讓別人使用,而這個COM module在AP裡是走manifest的,所以不會被註冊,連帶的module會用到其他的Library也不走註冊這條路,而因為一些其他的原因,也不能將此Library寫入manifest裡,只能尋求其他途徑了。

由於COM的DLL都會link到一些基本的COM Library,所以是有一些export的API可以繞過註冊來create COM instance的,直接參考以下的Sample Code吧。


// {599D2F5F-BA8D-4009-941C-A5393EBD5C58}
DEFINE_GUID(CLSID_MyCOM, 
0x599d2f5f, 0xba8d, 0x4009, 0x94, 0x1c, 0xa5, 0x39, 0x3e, 0xbd, 0x5c, 0x58);

// {4C452CB2-469C-4236-AE5C-F48A28EC0870}
DEFINE_GUID(IID_IMyCOM, 
0x4c452cb2, 0x469c, 0x4236, 0xae, 0x5c, 0xf4, 0x8a, 0x28, 0xec, 0x8, 0x70);

HRESULT CreateInstance(IMyCOM** pMyCom)
{
    HMODULE hDll = LoadLibrary("MY_COM.dll");//Need refine by your self
    if(hDll)
    {
        //////////////////////////////////////////////////////////////////////////
        // Initial COM instance without register
        //   Try to load COM DLL directly, and call DllGetClassObject to get IID_IClassFactory
        //   And Create COM instance by IID_IClassFactory
        // -----------------------------------------------------------------------
        //   HRESULT hr = CoCreateInstance(CLSID_MyCOM, 
        //                                 NULL, 
        //                                 CLSCTX_INPROC_SERVER, 
        //                                 IID_IMyCOM, 
        //                                 (PVOID*)&pMyCom);
        //////////////////////////////////////////////////////////////////////////
        CComPtr<IClassFactory> spFactory;

        // Get address of DllGetClassObject exported method
        PDLLGETCLSOBJ pDllGetCLSObj = (PDLLGETCLSOBJ)GetProcAddress(hDll, "DllGetClassObject");

        // Call DllGetClassObject and get the proper IClassFactory interface
        HRESULT hr = pDllGetCLSObj(CLSID_MyCOM, IID_IClassFactory, reinterpret_cast<PVOID*>(&spFactory));
        if( FAILED(hr) )
        {
            printf(__FUNCTION__" [Err] Can't get spFactory hr=%X", hr);
            return E_FAIL;
        }

        // Create an instance of IMyCOM
        hr = spFactory->CreateInstance(NULL, IID_IMyCOM, (PVOID*)pMyCom);
        if( FAILED(hr) )
        {
            printf(__FUNCTION__" [Err] Init IMyCOM Fail! hr=%X", hr);
            return E_FAIL;
        }
    }
    else
    {
        return E_FAIL;
    }
    return S_OK;
}


Reference:

[Python] 使用objgraph找出memory leak(circular reference)

在Python的世界裡,所有的東西都是Object,而控制Object的life cycle靠的就是reference的機制,一個簡單的assignement (=等號),或是function call就會讓Object的reference count自動加1。
而當Object的reference count減至0的時候,Python的GC會找時間把這些Object清除掉。

對Python而言,memory leak產生的可能性有兩種
  • Circular reference
  • Referece在global的變數或是module上