// BenchmarkDialog.cpp #include "StdAfx.h" #include "Common/IntToString.h" #include "Common/StringToInt.h" #include "Common/MyException.h" #include "Windows/Thread.h" #include "Windows/Error.h" #include "Windows/System.h" #include "../FileManager/HelpUtils.h" // #include "BenchmarkDialogRes.h" #include "BenchmarkDialog.h" using namespace NWindows; // const int kNumBenchDictionaryBitsStart = 21; static LPCWSTR kHelpTopic = L"fm/benchmark.htm"; static const UINT_PTR kTimerID = 4; static const UINT kTimerElapse = 1000; #ifdef LANG #include "../FileManager/LangUtils.h" #endif using namespace NWindows; #ifdef LANG static CIDLangPair kIDLangPairs[] = { { IDC_BENCHMARK_DICTIONARY, 0x02000D0C }, { IDC_BENCHMARK_MEMORY, 0x03080001 }, { IDC_BENCHMARK_NUM_THREADS, 0x02000D12 }, { IDC_BENCHMARK_SPEED_LABEL, 0x03080004 }, { IDC_BENCHMARK_RATING_LABEL, 0x03080005 }, { IDC_BENCHMARK_COMPRESSING, 0x03080002 }, { IDC_BENCHMARK_DECOMPRESSING, 0x03080003 }, { IDC_BENCHMARK_CURRENT, 0x03080007 }, { IDC_BENCHMARK_RESULTING, 0x03080008 }, { IDC_BENCHMARK_CURRENT2, 0x03080007 }, { IDC_BENCHMARK_RESULTING2, 0x03080008 }, { IDC_BENCHMARK_TOTAL_RATING, 0x03080006 }, { IDC_BENCHMARK_ELAPSED, 0x02000C01 }, { IDC_BENCHMARK_SIZE, 0x02000C03 }, { IDC_BENCHMARK_PASSES, 0x03080009 }, // { IDC_BENCHMARK_ERRORS, 0x0308000A }, { IDC_BENCHMARK_USAGE_LABEL, 0x0308000B }, { IDC_BENCHMARK_RPU_LABEL, 0x0308000C }, { IDC_BENCHMARK_COMBO_NUM_THREADS, 0x02000D12}, { IDC_BUTTON_STOP, 0x02000714 }, { IDC_BUTTON_RESTART, 0x02000715 }, { IDHELP, 0x02000720 }, { IDCANCEL, 0x02000710 } }; #endif static void MyMessageBoxError(HWND hwnd, LPCWSTR message) { MessageBoxW(hwnd, message, L"7-Zip", MB_ICONERROR); } const LPCTSTR kProcessingString = TEXT("..."); const LPCTSTR kMB = TEXT(" MB"); const LPCTSTR kMIPS = TEXT(" MIPS"); const LPCTSTR kKBs = TEXT(" KB/s"); static const int kMinDicLogSize = 21; static const UInt32 kMinDicSize = (1 << kMinDicLogSize); static const UInt32 kMaxDicSize = #ifdef _WIN64 (1 << 30); #else (1 << 27); #endif bool CBenchmarkDialog::OnInit() { #ifdef LANG LangSetWindowText(HWND(*this), 0x03080000); LangSetDlgItemsText(HWND(*this), kIDLangPairs, sizeof(kIDLangPairs) / sizeof(kIDLangPairs[0])); #endif _syncInfo.Init(); m_Dictionary.Attach(GetItem(IDC_BENCHMARK_COMBO_DICTIONARY)); int cur = 0; // _syncInfo.DictionarySize = (1 << kNumBenchDictionaryBitsStart); UInt32 numCPUs = NSystem::GetNumberOfProcessors(); if (numCPUs < 1) numCPUs = 1; numCPUs = MyMin(numCPUs, (UInt32)(1 << 8)); cur = 0; bool setDefaultThreads = (_syncInfo.NumThreads == (UInt32)(-1)); if (setDefaultThreads) { _syncInfo.NumThreads = numCPUs; if (_syncInfo.NumThreads > 1) _syncInfo.NumThreads &= ~1; } UInt64 ramSize = NSystem::GetRamSize(); bool setDefaultDictionary = (_syncInfo.DictionarySize == (UInt32)(-1)); if (setDefaultDictionary) { int dicSizeLog; for (dicSizeLog = 25; dicSizeLog >= kBenchMinDicLogSize; dicSizeLog--) if (GetBenchMemoryUsage(_syncInfo.NumThreads, ((UInt32)1 << dicSizeLog)) + (8 << 20) <= ramSize) break; _syncInfo.DictionarySize = (1 << dicSizeLog); } if (_syncInfo.DictionarySize < kMinDicSize) _syncInfo.DictionarySize = kMinDicSize; if (_syncInfo.DictionarySize > kMaxDicSize) _syncInfo.DictionarySize = kMaxDicSize; for (int i = kMinDicLogSize; i <= 30; i++) for (int j = 0; j < 2; j++) { UInt32 dictionary = (1 << i) + (j << (i - 1)); if (dictionary > kMaxDicSize) continue; TCHAR s[40]; ConvertUInt64ToString((dictionary >> 20), s); lstrcat(s, kMB); int index = (int)m_Dictionary.AddString(s); m_Dictionary.SetItemData(index, dictionary); if (dictionary <= _syncInfo.DictionarySize) cur = index; } m_Dictionary.SetCurSel(cur); m_NumThreads.Attach(GetItem(IDC_BENCHMARK_COMBO_NUM_THREADS)); for (UInt32 num = 1; ;) { if (num > numCPUs * 2) break; TCHAR s[40]; ConvertUInt64ToString(num, s); int index = (int)m_NumThreads.AddString(s); m_NumThreads.SetItemData(index, num); if (num <= numCPUs && setDefaultThreads) { _syncInfo.NumThreads = num; cur = index; } if (num > 1) num++; num++; } m_NumThreads.SetCurSel(cur); OnChangeSettings(); _syncInfo._startEvent.Set(); _timer = SetTimer(kTimerID, kTimerElapse); return CModalDialog::OnInit(); } UInt32 CBenchmarkDialog::GetNumberOfThreads() { return (UInt32)m_NumThreads.GetItemData(m_NumThreads.GetCurSel()); } UInt32 CBenchmarkDialog::OnChangeDictionary() { UInt32 dictionary = (UInt32)m_Dictionary.GetItemData(m_Dictionary.GetCurSel()); UInt64 memUsage = GetBenchMemoryUsage(GetNumberOfThreads(), dictionary); memUsage = (memUsage + (1 << 20) - 1) >> 20; TCHAR s[40]; ConvertUInt64ToString(memUsage, s); lstrcat(s, kMB); SetItemText(IDC_BENCHMARK_MEMORY_VALUE, s); return dictionary; } static const UInt32 g_IDs[] = { IDC_BENCHMARK_COMPRESSING_USAGE, IDC_BENCHMARK_COMPRESSING_USAGE2, IDC_BENCHMARK_COMPRESSING_SPEED, IDC_BENCHMARK_COMPRESSING_SPEED2, IDC_BENCHMARK_COMPRESSING_RATING, IDC_BENCHMARK_COMPRESSING_RATING2, IDC_BENCHMARK_COMPRESSING_RPU, IDC_BENCHMARK_COMPRESSING_RPU2, IDC_BENCHMARK_DECOMPRESSING_SPEED, IDC_BENCHMARK_DECOMPRESSING_SPEED2, IDC_BENCHMARK_DECOMPRESSING_RATING, IDC_BENCHMARK_DECOMPRESSING_RATING2, IDC_BENCHMARK_DECOMPRESSING_USAGE, IDC_BENCHMARK_DECOMPRESSING_USAGE2, IDC_BENCHMARK_DECOMPRESSING_RPU, IDC_BENCHMARK_DECOMPRESSING_RPU2, IDC_BENCHMARK_TOTAL_USAGE_VALUE, IDC_BENCHMARK_TOTAL_RATING_VALUE, IDC_BENCHMARK_TOTAL_RPU_VALUE }; void CBenchmarkDialog::OnChangeSettings() { EnableItem(IDC_BUTTON_STOP, true); UInt32 dictionary = OnChangeDictionary(); TCHAR s[40] = { TEXT('/'), TEXT(' '), 0 }; ConvertUInt64ToString(NSystem::GetNumberOfProcessors(), s + 2); SetItemText(IDC_BENCHMARK_HARDWARE_THREADS, s); for (int i = 0; i < sizeof(g_IDs) / sizeof(g_IDs[0]); i++) SetItemText(g_IDs[i], kProcessingString); _startTime = GetTickCount(); PrintTime(); NWindows::NSynchronization::CCriticalSectionLock lock(_syncInfo.CS); _syncInfo.Init(); _syncInfo.DictionarySize = dictionary; _syncInfo.Changed = true; _syncInfo.NumThreads = GetNumberOfThreads(); } void CBenchmarkDialog::OnRestartButton() { OnChangeSettings(); } void CBenchmarkDialog::OnStopButton() { EnableItem(IDC_BUTTON_STOP, false); _syncInfo.Pause(); } void CBenchmarkDialog::OnHelp() { ShowHelpWindow(NULL, kHelpTopic); } void CBenchmarkDialog::OnCancel() { _syncInfo.Stop(); KillTimer(_timer); CModalDialog::OnCancel(); } static void GetTimeString(UInt64 timeValue, TCHAR *s) { wsprintf(s, TEXT("%02d:%02d:%02d"), UInt32(timeValue / 3600), UInt32((timeValue / 60) % 60), UInt32(timeValue % 60)); } void CBenchmarkDialog::PrintTime() { UInt32 curTime = ::GetTickCount(); UInt32 elapsedTime = (curTime - _startTime); UInt32 elapsedSec = elapsedTime / 1000; if (elapsedSec != 0 && _syncInfo.WasPaused()) return; TCHAR s[40]; GetTimeString(elapsedSec, s); SetItemText(IDC_BENCHMARK_ELAPSED_VALUE, s); } void CBenchmarkDialog::PrintRating(UInt64 rating, UINT controlID) { TCHAR s[40]; ConvertUInt64ToString(rating / 1000000, s); lstrcat(s, kMIPS); SetItemText(controlID, s); } void CBenchmarkDialog::PrintUsage(UInt64 usage, UINT controlID) { TCHAR s[40]; ConvertUInt64ToString((usage + 5000) / 10000, s); lstrcat(s, TEXT("%")); SetItemText(controlID, s); } void CBenchmarkDialog::PrintResults( UInt32 dictionarySize, const CBenchInfo2 &info, UINT usageID, UINT speedID, UINT rpuID, UINT ratingID, bool decompressMode) { if (info.GlobalTime == 0) return; UInt64 size = info.UnpackSize; TCHAR s[40]; { UInt64 speed = size * info.GlobalFreq / info.GlobalTime; ConvertUInt64ToString(speed / 1024, s); lstrcat(s, kKBs); SetItemText(speedID, s); } UInt64 rating; if (decompressMode) rating = GetDecompressRating(info.GlobalTime, info.GlobalFreq, size, info.PackSize, 1); else rating = GetCompressRating(dictionarySize, info.GlobalTime, info.GlobalFreq, size * info.NumIterations); PrintRating(rating, ratingID); PrintRating(GetRatingPerUsage(info, rating), rpuID); PrintUsage(GetUsage(info), usageID); } bool CBenchmarkDialog::OnTimer(WPARAM /* timerID */, LPARAM /* callback */) { PrintTime(); NWindows::NSynchronization::CCriticalSectionLock lock(_syncInfo.CS); TCHAR s[40]; ConvertUInt64ToString((_syncInfo.ProcessedSize >> 20), s); lstrcat(s, kMB); SetItemText(IDC_BENCHMARK_SIZE_VALUE, s); ConvertUInt64ToString(_syncInfo.NumPasses, s); SetItemText(IDC_BENCHMARK_PASSES_VALUE, s); /* ConvertUInt64ToString(_syncInfo.NumErrors, s); SetItemText(IDC_BENCHMARK_ERRORS_VALUE, s); */ { UInt32 dicSizeTemp = (UInt32)MyMax(_syncInfo.ProcessedSize, UInt64(1) << 20); dicSizeTemp = MyMin(dicSizeTemp, _syncInfo.DictionarySize), PrintResults(dicSizeTemp, _syncInfo.CompressingInfoTemp, IDC_BENCHMARK_COMPRESSING_USAGE, IDC_BENCHMARK_COMPRESSING_SPEED, IDC_BENCHMARK_COMPRESSING_RPU, IDC_BENCHMARK_COMPRESSING_RATING); } { PrintResults( _syncInfo.DictionarySize, _syncInfo.CompressingInfo, IDC_BENCHMARK_COMPRESSING_USAGE2, IDC_BENCHMARK_COMPRESSING_SPEED2, IDC_BENCHMARK_COMPRESSING_RPU2, IDC_BENCHMARK_COMPRESSING_RATING2); } { PrintResults( _syncInfo.DictionarySize, _syncInfo.DecompressingInfoTemp, IDC_BENCHMARK_DECOMPRESSING_USAGE, IDC_BENCHMARK_DECOMPRESSING_SPEED, IDC_BENCHMARK_DECOMPRESSING_RPU, IDC_BENCHMARK_DECOMPRESSING_RATING, true); } { PrintResults( _syncInfo.DictionarySize, _syncInfo.DecompressingInfo, IDC_BENCHMARK_DECOMPRESSING_USAGE2, IDC_BENCHMARK_DECOMPRESSING_SPEED2, IDC_BENCHMARK_DECOMPRESSING_RPU2, IDC_BENCHMARK_DECOMPRESSING_RATING2, true); if (_syncInfo.DecompressingInfo.GlobalTime > 0 && _syncInfo.CompressingInfo.GlobalTime > 0) { UInt64 comprRating = GetCompressRating(_syncInfo.DictionarySize, _syncInfo.CompressingInfo.GlobalTime, _syncInfo.CompressingInfo.GlobalFreq, _syncInfo.CompressingInfo.UnpackSize); UInt64 decomprRating = GetDecompressRating(_syncInfo.DecompressingInfo.GlobalTime, _syncInfo.DecompressingInfo.GlobalFreq, _syncInfo.DecompressingInfo.UnpackSize, _syncInfo.DecompressingInfo.PackSize, 1); PrintRating((comprRating + decomprRating) / 2, IDC_BENCHMARK_TOTAL_RATING_VALUE); PrintRating(( GetRatingPerUsage(_syncInfo.CompressingInfo, comprRating) + GetRatingPerUsage(_syncInfo.DecompressingInfo, decomprRating)) / 2, IDC_BENCHMARK_TOTAL_RPU_VALUE); PrintUsage((GetUsage(_syncInfo.CompressingInfo) + GetUsage(_syncInfo.DecompressingInfo)) / 2, IDC_BENCHMARK_TOTAL_USAGE_VALUE); } } return true; } bool CBenchmarkDialog::OnCommand(int code, int itemID, LPARAM lParam) { if (code == CBN_SELCHANGE && (itemID == IDC_BENCHMARK_COMBO_DICTIONARY || itemID == IDC_BENCHMARK_COMBO_NUM_THREADS)) { OnChangeSettings(); return true; } return CModalDialog::OnCommand(code, itemID, lParam); } bool CBenchmarkDialog::OnButtonClicked(int buttonID, HWND buttonHWND) { switch(buttonID) { case IDC_BUTTON_RESTART: OnRestartButton(); return true; case IDC_BUTTON_STOP: OnStopButton(); return true; } return CModalDialog::OnButtonClicked(buttonID, buttonHWND); } struct CThreadBenchmark { CProgressSyncInfo *SyncInfo; UInt64 _startTime; #ifdef EXTERNAL_LZMA CCodecs *codecs; #endif // UInt32 dictionarySize; // UInt32 numThreads; HRESULT Process(); HRESULT Result; static THREAD_FUNC_DECL MyThreadFunction(void *param) { ((CThreadBenchmark *)param)->Result = ((CThreadBenchmark *)param)->Process(); return 0; } }; struct CBenchCallback: public IBenchCallback { UInt32 dictionarySize; CProgressSyncInfo *SyncInfo; HRESULT SetEncodeResult(const CBenchInfo &info, bool final); HRESULT SetDecodeResult(const CBenchInfo &info, bool final); }; HRESULT CBenchCallback::SetEncodeResult(const CBenchInfo &info, bool final) { NSynchronization::CCriticalSectionLock lock(SyncInfo->CS); if (SyncInfo->Changed || SyncInfo->Paused || SyncInfo->Stopped) return E_ABORT; SyncInfo->ProcessedSize = info.UnpackSize; if (final && SyncInfo->CompressingInfo.GlobalTime == 0) { (CBenchInfo&)SyncInfo->CompressingInfo = info; if (SyncInfo->CompressingInfo.GlobalTime == 0) SyncInfo->CompressingInfo.GlobalTime = 1; } else (CBenchInfo&)SyncInfo->CompressingInfoTemp = info; return S_OK; } HRESULT CBenchCallback::SetDecodeResult(const CBenchInfo &info, bool final) { NSynchronization::CCriticalSectionLock lock(SyncInfo->CS); if (SyncInfo->Changed || SyncInfo->Paused || SyncInfo->Stopped) return E_ABORT; CBenchInfo info2 = info; if (info2.NumIterations == 0) info2.NumIterations = 1; info2.UnpackSize *= info2.NumIterations; info2.PackSize *= info2.NumIterations; info2.NumIterations = 1; if (final && SyncInfo->DecompressingInfo.GlobalTime == 0) { (CBenchInfo&)SyncInfo->DecompressingInfo = info2; if (SyncInfo->DecompressingInfo.GlobalTime == 0) SyncInfo->DecompressingInfo.GlobalTime = 1; } else (CBenchInfo&)SyncInfo->DecompressingInfoTemp = info2; return S_OK; } HRESULT CThreadBenchmark::Process() { try { SyncInfo->WaitCreating(); for (;;) { if (SyncInfo->WasStopped()) return 0; if (SyncInfo->WasPaused()) { Sleep(200); continue; } UInt32 dictionarySize; UInt32 numThreads; { NSynchronization::CCriticalSectionLock lock(SyncInfo->CS); if (SyncInfo->Stopped || SyncInfo->Paused) continue; if (SyncInfo->Changed) SyncInfo->Init(); dictionarySize = SyncInfo->DictionarySize; numThreads = SyncInfo->NumThreads; } CBenchCallback callback; callback.dictionarySize = dictionarySize; callback.SyncInfo = SyncInfo; HRESULT result; try { result = LzmaBench( #ifdef EXTERNAL_LZMA codecs, #endif numThreads, dictionarySize, &callback); } catch(...) { result = E_FAIL; } if (result != S_OK) { if (result != E_ABORT) { // SyncInfo->NumErrors++; { NSynchronization::CCriticalSectionLock lock(SyncInfo->CS); SyncInfo->Pause(); } CSysString message; if (result == S_FALSE) message = TEXT("Decoding error"); else message = NError::MyFormatMessage(result); MessageBox(0, message, TEXT("7-Zip"), MB_ICONERROR); } } else { NSynchronization::CCriticalSectionLock lock(SyncInfo->CS); SyncInfo->NumPasses++; } } // return S_OK; } catch(CSystemException &e) { MessageBox(0, NError::MyFormatMessage(e.ErrorCode), TEXT("7-Zip"), MB_ICONERROR); return E_FAIL; } catch(...) { MyMessageBoxError(0, L"Some error"); return E_FAIL; } } HRESULT Benchmark( #ifdef EXTERNAL_LZMA CCodecs *codecs, #endif UInt32 numThreads, UInt32 dictionarySize) { CThreadBenchmark benchmarker; #ifdef EXTERNAL_LZMA benchmarker.codecs = codecs; #endif CBenchmarkDialog benchmarkDialog; benchmarkDialog._syncInfo.DictionarySize = dictionarySize; benchmarkDialog._syncInfo.NumThreads = numThreads; benchmarker.SyncInfo = &benchmarkDialog._syncInfo; NWindows::CThread thread; RINOK(thread.Create(CThreadBenchmark::MyThreadFunction, &benchmarker)); benchmarkDialog.Create(0); return thread.Wait(); }