Figure 1 ShowTime
//////////////////
// ShowTime displays diagnostics that tell how long an operation took.
// To use, instantiate a ShowTime object at the top of the code block
// you want to clock. If you create a PerfLog, ShowTime will use it.
//
// // in some block of code:
// {
//
ShowTime("time for operation X is");
// // do
operation X
// }
//
class ShowTime {
protected:
LPCTSTR msg;
// display message
clock_t start;
// start time
clock_t end;
// end time
// convert clock ticks to milliseconds
static long msec(clock_t clocktime)
{
return (long)(clocktime *
CLOCKS_PER_SEC / 1000);
}
public:
// ctor: save message, record start time.
ShowTime(LPCTSTR m)
{
msg = m;
start = msec(clock());
}
// dtor: record end time and log results.
~ShowTime()
{
end = msec(clock());
TCHAR buf[512];
_snprintf(buf, 512, _T(" %s: %d msec\n"),
msg, end-start);
PerfLog::Write(buf);
}
};
¡¡
////////////////////////////////////////////////////////////////
// MSDN Magazine ¡ª June 2004
// If this code works, it was written by Paul DiLascia.
// If not, I don't know who wrote it.
// Compiles with Visual Studio .NET 2003 on Windows XP. Tab size=3.
//
#include "StdAfx.h"
#include "PerfTest.h"
#include "Doc.h"
#include "ShowTime.h"
•••
const BIGNUMALLOC = 20000; // allocate this many objects
PerfLog mylog(_T("PerfTest.log")); // open log file
// Typical fixed-length record object.
class CMyRecord : public CObject {
protected:
CMyRecord* next; // next record in list
TCHAR name[64];
int num;
BOOL flag;
friend class CMyDoc;
public:
CMyRecord() { }
CMyRecord(int i) { InitRec(i); }
void InitRec(int i) {
_snprintf(name, 64, _T("rec %d"),i);
num = i;
flag = TRUE;
}
virtual void Serialize(CArchive& ar);
DECLARE_SERIAL(CMyRecord)
};
IMPLEMENT_SERIAL(CMyRecord, CObject, 1)
// Serialize one record: write/read string and other data.
void CMyRecord::Serialize(CArchive& ar) {
if (ar.IsStoring()) {
ar.WriteString(name);
ar.WriteString(_T("\n")); // don't forget!
ar << num << flag;
} else {
ar.ReadString(name,countof(name));
ar >> num >> flag;
}
}
// Serialize document. This fn serializes each record list array using a
// different technique to illustrate the performance of each.
void CMyDoc::Serialize(CArchive& ar) {
CString msg;
msg.Format(_T("CMyDoc::Serialize: %s\n"),
ar.IsStoring() ? _T("Write") : _T("Read"));
PerfLog::Write(msg);
CFile& file = *ar.GetFile();
{
// Method 1: Serialize record list the vanilla MFC way (CObList)
ShowFileUsed fs(file);
ShowTime st(_T("M1: Serializing CObList"));
m_recObList.Serialize(ar);
}
{
// Method 2: Serialize individual records (no CObList/list cell)
ShowFileUsed fs(file, _T("Total size"));
ShowTime st(_T("M2: Serializing individual records"));
if (ar.IsStoring()) {
ar << m_nRecList;
} else {
ar >> m_nRecList; // read number of records
AllocateRecordsIndividual(m_nRecList); // allocate them
// (link too)
}
for (CMyRecord* prec=m_pRecList; prec; prec=prec->next) {
prec->Serialize(ar);
}
}
{
// Method 3: Read/write all records as single memory block
ShowFileUsed fs(file, _T("Total size"));
ShowTime st(_T("M3: Serializing bulk array"));
if (ar.IsStoring()) {
ar << m_nArray;
ar.Write(m_pArray,m_nArray*sizeof(CMyRecord));
} else {
ar >> m_nArray;
AllocateRecordsBulk(m_nArray);
ar.Read(m_pArray,m_nArray*sizeof(CMyRecord));
LinkRecords(m_pArray, m_nArray);
}
TRACE(_T("len=%d\n"),m_nArray);
}
}
// Create new document: allocate three large record arrays using the three
// different methods described above.
BOOL CMyDoc::OnNewDocument()
{
PerfLog::Write(_T("CMyDoc::OnNewDocument\n"));
VERIFY(CDocument::OnNewDocument());
{
ShowTime st(_T("M1: Allocating for CObList"));
AllocateRecordsObList(BIGNUMALLOC);
}
{
ShowTime st(_T("M2: Allocating individual records"));
AllocateRecordsIndividual(BIGNUMALLOC);
}
{
ShowTime st(_T("M3: Allocating bulk array"));
AllocateRecordsBulk(BIGNUMALLOC);
LinkRecords(m_pArray, m_nArray);
}
return TRUE;
}
// Allocate records using Method 1: MFC CObjList
void CMyDoc::AllocateRecordsObList(int nrec) {
for (int i=0; i<nrec; i++) {
m_recObList.AddTail(new CMyRecord(i));
}
}
// Allocate individual records using Method 2: Manual linked list.
// Also links the records.
void CMyDoc::AllocateRecordsIndividual(int nrec) {
CMyRecord* prev = NULL;
for (int i=0; i<BIGNUMALLOC; i++) {
CMyRecord* rec = new CMyRecord(i);
ASSERT(rec);
if (prev) {
prev->next = rec;
} else {
m_pRecList = rec;
}
prev = rec;
}
prev->next = NULL;
m_nRecList = nrec;
}
// Allocate records using Method 3: allocate array as one memory block.
void CMyDoc::AllocateRecordsBulk(int nrec) {
m_pArray = new CMyRecord[nrec];
ASSERT(m_pArray);
for (int i=0; i<nrec; i++) {
m_pArray[i].InitRec(i);
}
m_nArray = nrec;
}
// Link bulk-allocated records (make each record point to the next)
void CMyDoc::LinkRecords(CMyRecord* array, int nrec) {
if (array && nrec>1) {
for (int i=0; i<nrec-1; i++) {
array[i].next = &array[i+1];
}
array[nrec-1].next = NULL;
}
}
// Start PerfTest program. This is an MFC SDI app, so MFC creates a new // doc. Program allocates 20,000 records using 3 different methods. CMyDoc::OnNewDocument M1: Allocating for CObList: 130 msec M2: Allocating individual records: 90 msec M3: Allocating bulk array: 70 msec // Save the file (write test). CMyDoc::Serialize: Write M1: Serializing CObList: 200 msec file size = 577457 bytes M2: Serializing individual records: 60 msec Total size = 536514 bytes M3: Serializing bulk array: 61 msec Total size = 2879488 bytes // Open the same file (read test). MFC called DeleteContents first. CMyDoc::DeleteContents M1: Delete CObList: 80 msec M2: Delete individual records: 51 msec M3: Delete bulk array: 10 msec CMyDoc::Serialize: Read M1: Serializing CObList: 240 msec file size = 581553 bytes M2: Serializing individual records: 170 msec Total size = 536514 bytes M3: Serializing bulk array: 90 msec Total size = 2877516 bytes // Quit the app. This calls DeleteContents again. CMyDoc::DeleteContents M1: Delete CObList: 100 msec M2: Delete individual records: 50 msec M3: Delete bulk array: 10 msec