xref: /aosp_15_r20/external/lzma/CPP/7zip/UI/GUI/BenchmarkDialog.cpp (revision f6dc9357d832569d4d1f5d24eacdb3935a1ae8e6)
1 // BenchmarkDialog.cpp
2 
3 #include "StdAfx.h"
4 
5 #include "../../../../C/CpuArch.h"
6 
7 #include "../../../Common/Defs.h"
8 #include "../../../Common/IntToString.h"
9 #include "../../../Common/MyException.h"
10 #include "../../../Common/StringConvert.h"
11 #include "../../../Common/StringToInt.h"
12 
13 #include "../../../Windows/Synchronization.h"
14 #include "../../../Windows/System.h"
15 #include "../../../Windows/Thread.h"
16 #include "../../../Windows/SystemInfo.h"
17 
18 #include "../../../Windows/Control/ComboBox.h"
19 #include "../../../Windows/Control/Edit.h"
20 
21 #include "../../Common/MethodProps.h"
22 
23 #include "../FileManager/DialogSize.h"
24 #include "../FileManager/HelpUtils.h"
25 #include "../FileManager/LangUtils.h"
26 #include "../FileManager/resourceGui.h"
27 
28 #include "../../MyVersion.h"
29 
30 #include "../Common/Bench.h"
31 
32 #include "BenchmarkDialogRes.h"
33 #include "BenchmarkDialog.h"
34 
35 using namespace NWindows;
36 
37 #define kHelpTopic "fm/benchmark.htm"
38 
39 static const UINT_PTR kTimerID = 4;
40 static const UINT kTimerElapse = 1000; // 1000
41 
42 // use PRINT_ITER_TIME to show time of each iteration in log box
43 // #define PRINT_ITER_TIME
44 
45 static const unsigned kRatingVector_NumBundlesMax = 20;
46 
47 enum MyBenchMessages
48 {
49   k_Message_Finished = WM_APP + 1
50 };
51 
52 enum My_Message_WPARAM
53 {
54   k_Msg_WPARM_Thread_Finished = 0,
55   k_Msg_WPARM_Iter_Finished,
56   k_Msg_WPARM_Enc1_Finished
57 };
58 
59 
60 struct CBenchPassResult
61 {
62   CTotalBenchRes Enc;
63   CTotalBenchRes Dec;
64 #ifdef PRINT_ITER_TIME
65   DWORD Ticks;
66 #endif
67   // CBenchInfo EncInfo; // for debug
68   // CBenchPassResult() {};
69 };
70 
71 
72 struct CTotalBenchRes2: public CTotalBenchRes
73 {
74   UInt64 UnpackSize;
75 
InitCTotalBenchRes276   void Init()
77   {
78     CTotalBenchRes::Init();
79     UnpackSize = 0;
80   }
81 
SetFrom_BenchInfoCTotalBenchRes282   void SetFrom_BenchInfo(const CBenchInfo &info)
83   {
84     NumIterations2 = 1;
85     Generate_From_BenchInfo(info);
86     UnpackSize = info.Get_UnpackSize_Full();
87   }
88 
Update_With_Res2CTotalBenchRes289   void Update_With_Res2(const CTotalBenchRes2 &r)
90   {
91     Update_With_Res(r);
92     UnpackSize += r.UnpackSize;
93   }
94 };
95 
96 
97 struct CSyncData
98 {
99   UInt32 NumPasses_Finished;
100 #ifdef PRINT_ITER_TIME
101   DWORD TotalTicks;
102 #endif
103   int RatingVector_DeletedIndex;
104   // UInt64 RatingVector_NumDeleted;
105 
106   bool BenchWasFinished; // all passes were finished
107   bool NeedPrint_Freq;
108   bool NeedPrint_RatingVector;
109   bool NeedPrint_Enc_1;
110   bool NeedPrint_Enc;
111   bool NeedPrint_Dec_1;
112   bool NeedPrint_Dec;
113   bool NeedPrint_Tot; // intermediate Total was updated after current pass
114 
115   // UInt64 NumEncProgress; // for debug
116   // UInt64 NumDecProgress; // for debug
117   // CBenchInfo EncInfo; // for debug
118 
119   CTotalBenchRes2 Enc_BenchRes_1;
120   CTotalBenchRes2 Enc_BenchRes;
121 
122   CTotalBenchRes2 Dec_BenchRes_1;
123   CTotalBenchRes2 Dec_BenchRes;
124 
125   void Init();
126 };
127 
128 
Init()129 void CSyncData::Init()
130 {
131   NumPasses_Finished = 0;
132 
133   // NumEncProgress = 0;
134   // NumDecProgress = 0;
135 
136   Enc_BenchRes.Init();
137   Enc_BenchRes_1.Init();
138   Dec_BenchRes.Init();
139   Dec_BenchRes_1.Init();
140 
141   #ifdef PRINT_ITER_TIME
142   TotalTicks = 0;
143   #endif
144 
145   RatingVector_DeletedIndex = -1;
146   // RatingVector_NumDeleted = 0;
147 
148   BenchWasFinished =
149     NeedPrint_Freq =
150     NeedPrint_RatingVector =
151     NeedPrint_Enc_1 =
152     NeedPrint_Enc   =
153     NeedPrint_Dec_1 =
154     NeedPrint_Dec   =
155     NeedPrint_Tot   = false;
156 }
157 
158 
159 struct CBenchProgressSync
160 {
161   bool Exit; // GUI asks BenchThread to Exit, and BenchThread reads that variable
162   bool TextWasChanged;
163 
164   UInt32 NumThreads;
165   UInt64 DictSize;
166   UInt32 NumPasses_Limit;
167   int Level;
168 
169   AString Text;
170 
171   /* BenchFinish_Task_HRESULT    - for result from benchmark code
172      BenchFinish_Thread_HRESULT  - for Exceptions and service errors
173              these arreos must be shown even if user escapes benchmark */
174   HRESULT BenchFinish_Task_HRESULT;
175   HRESULT BenchFinish_Thread_HRESULT;
176 
177   UInt32 NumFreqThreadsPrev;
178   UString FreqString_Sync;
179   UString FreqString_GUI;
180 
181   // must be written by benchmark thread, read by GUI thread */
182   CRecordVector<CBenchPassResult> RatingVector;
183   CSyncData sd;
184 
185   NWindows::NSynchronization::CCriticalSection CS;
186 
CBenchProgressSyncCBenchProgressSync187   CBenchProgressSync()
188   {
189     NumPasses_Limit = 1;
190   }
191 
192   void Init();
193 
SendExitCBenchProgressSync194   void SendExit()
195   {
196     NWindows::NSynchronization::CCriticalSectionLock lock(CS);
197     Exit = true;
198   }
199 };
200 
201 
Init()202 void CBenchProgressSync::Init()
203 {
204   Exit = false;
205 
206   BenchFinish_Task_HRESULT = S_OK;
207   BenchFinish_Thread_HRESULT = S_OK;
208 
209   sd.Init();
210   RatingVector.Clear();
211 
212   NumFreqThreadsPrev = 0;
213   FreqString_Sync.Empty();
214   FreqString_GUI.Empty();
215 
216   Text.Empty();
217   TextWasChanged = true;
218 }
219 
220 
221 
222 struct CMyFont
223 {
224   HFONT _font;
CMyFontCMyFont225   CMyFont(): _font(NULL) {}
~CMyFontCMyFont226   ~CMyFont()
227   {
228     if (_font)
229       DeleteObject(_font);
230   }
CreateCMyFont231   void Create(const LOGFONT *lplf)
232   {
233     _font = CreateFontIndirect(lplf);
234   }
235 };
236 
237 
238 class CBenchmarkDialog;
239 
240 struct CThreadBenchmark
241 {
242   CBenchmarkDialog *BenchmarkDialog;
243   DECL_EXTERNAL_CODECS_LOC_VARS_DECL
244   // HRESULT Result;
245 
246   HRESULT Process();
MyThreadFunctionCThreadBenchmark247   static THREAD_FUNC_DECL MyThreadFunction(void *param)
248   {
249     /* ((CThreadBenchmark *)param)->Result = */
250     ((CThreadBenchmark *)param)->Process();
251     return 0;
252   }
253 };
254 
255 
256 class CBenchmarkDialog:
257   public NWindows::NControl::CModalDialog
258 {
259   bool _finishTime_WasSet;
260 
261   bool WasStopped_in_GUI;
262   bool ExitWasAsked_in_GUI;
263   bool NeedRestart;
264 
265   bool RamSize_Defined;
266 
267 public:
268   bool TotalMode;
269 
270 private:
271 
272   NWindows::NControl::CComboBox m_Dictionary;
273   NWindows::NControl::CComboBox m_NumThreads;
274   NWindows::NControl::CComboBox m_NumPasses;
275   NWindows::NControl::CEdit _consoleEdit;
276   UINT_PTR _timer;
277 
278   UInt32 _startTime;
279   UInt32 _finishTime;
280 
281   CMyFont _font;
282 
283   size_t RamSize;
284   size_t RamSize_Limit;
285 
286   UInt32 NumPasses_Finished_Prev;
287 
288   UString ElapsedSec_Prev;
289 
InitSyncNew()290   void InitSyncNew()
291   {
292     NumPasses_Finished_Prev = (UInt32)(Int32)-1;
293     ElapsedSec_Prev.Empty();
294     Sync.Init();
295   }
296 
297   virtual bool OnInit() Z7_override;
298   virtual bool OnDestroy() Z7_override;
299   virtual bool OnSize(WPARAM /* wParam */, int xSize, int ySize) Z7_override;
300   virtual bool OnMessage(UINT message, WPARAM wParam, LPARAM lParam) Z7_override;
301   virtual bool OnCommand(unsigned code, unsigned itemID, LPARAM lParam) Z7_override;
302   virtual void OnHelp() Z7_override;
303   virtual void OnCancel() Z7_override;
304   virtual bool OnTimer(WPARAM timerID, LPARAM callback) Z7_override;
305   virtual bool OnButtonClicked(unsigned buttonID, HWND buttonHWND) Z7_override;
306 
307   void Disable_Stop_Button();
308   void OnStopButton();
309   void RestartBenchmark();
310   void StartBenchmark();
311 
312   void UpdateGui();
313 
314   void PrintTime();
315   void PrintRating(UInt64 rating, UINT controlID);
316   void PrintUsage(UInt64 usage, UINT controlID);
317   void PrintBenchRes(const CTotalBenchRes2 &info, const UINT ids[]);
318 
319   UInt32 GetNumberOfThreads();
320   size_t OnChangeDictionary();
321 
322   void SetItemText_Number(unsigned itemID, UInt64 val, LPCTSTR post = NULL);
323   void Print_MemUsage(UString &s, UInt64 memUsage) const;
IsMemoryUsageOK(UInt64 memUsage) const324   bool IsMemoryUsageOK(UInt64 memUsage) const
325     { return memUsage + (1 << 20) <= RamSize_Limit; }
326 
327   void MyKillTimer();
328 
SendExit_Status(const wchar_t * message)329   void SendExit_Status(const wchar_t *message)
330   {
331     SetItemText(IDT_BENCH_ERROR_MESSAGE, message);
332     Sync.SendExit();
333   }
334 
335 public:
336   CBenchProgressSync Sync;
337 
338   CObjectVector<CProperty> Props;
339 
340   CSysString Bench2Text;
341 
342   NWindows::CThread _thread;
343   CThreadBenchmark _threadBenchmark;
344 
CBenchmarkDialog()345   CBenchmarkDialog():
346       WasStopped_in_GUI(false),
347       ExitWasAsked_in_GUI(false),
348       NeedRestart(false),
349       TotalMode(false),
350       _timer(0)
351       {}
352 
353   ~CBenchmarkDialog() Z7_DESTRUCTOR_override;
354 
PostMsg_Finish(WPARAM wparam)355   bool PostMsg_Finish(WPARAM wparam)
356   {
357     if ((HWND)*this)
358       return PostMsg(k_Message_Finished, wparam);
359     // the (HWND)*this is NULL only for some internal code failure
360     return true;
361   }
362 
Create(HWND wndParent=NULL)363   INT_PTR Create(HWND wndParent = NULL)
364   {
365     BIG_DIALOG_SIZE(332, 228);
366     return CModalDialog::Create(TotalMode ? IDD_BENCH_TOTAL : SIZED_DIALOG(IDD_BENCH), wndParent);
367   }
MessageBoxError(LPCWSTR message)368   void MessageBoxError(LPCWSTR message)
369   {
370     MessageBoxW(*this, message, L"7-Zip", MB_ICONERROR);
371   }
MessageBoxError_Status(LPCWSTR message)372   void MessageBoxError_Status(LPCWSTR message)
373   {
374     UString s ("ERROR: ");
375     s += message;
376     MessageBoxError(s);
377     SetItemText(IDT_BENCH_ERROR_MESSAGE, s);
378   }
379 };
380 
381 
382 
383 
384 
385 
386 
387 
388 
389 UString HResultToMessage(HRESULT errorCode);
390 
391 #ifdef Z7_LANG
392 static const UInt32 kLangIDs[] =
393 {
394   IDT_BENCH_DICTIONARY,
395   IDT_BENCH_MEMORY,
396   IDT_BENCH_NUM_THREADS,
397   IDT_BENCH_SIZE,
398   IDT_BENCH_RATING_LABEL,
399   IDT_BENCH_USAGE_LABEL,
400   IDT_BENCH_RPU_LABEL,
401   IDG_BENCH_COMPRESSING,
402   IDG_BENCH_DECOMPRESSING,
403   IDG_BENCH_TOTAL_RATING,
404   IDT_BENCH_CURRENT,
405   IDT_BENCH_RESULTING,
406   IDT_BENCH_ELAPSED,
407   IDT_BENCH_PASSES,
408   IDB_STOP,
409   IDB_RESTART
410 };
411 
412 static const UInt32 kLangIDs_RemoveColon[] =
413 {
414   IDT_BENCH_SPEED
415 };
416 
417 #endif
418 
419 static LPCTSTR const kProcessingString = TEXT("...");
420 static LPCTSTR const kGB = TEXT(" GB");
421 static LPCTSTR const kMB = TEXT(" MB");
422 static LPCTSTR const kKB = TEXT(" KB");
423 // static LPCTSTR const kMIPS = TEXT(" MIPS");
424 static LPCTSTR const kKBs = TEXT(" KB/s");
425 
426 static const unsigned kMinDicLogSize = 18;
427 
428 static const UInt32 kMinDicSize = (UInt32)1 << kMinDicLogSize;
429 static const size_t kMaxDicSize = (size_t)1 << (22 + sizeof(size_t) / 4 * 5);
430 // static const size_t kMaxDicSize = (size_t)1 << 16;
431     /*
432     #ifdef MY_CPU_64BIT
433       (UInt32)(Int32)-1; // we can use it, if we want 4 GB buffer
434       // (UInt32)15 << 28;
435     #else
436       (UInt32)1 << 27;
437     #endif
438     */
439 
440 
ComboBox_Add_UInt32(NWindows::NControl::CComboBox & cb,UInt32 v)441 static int ComboBox_Add_UInt32(NWindows::NControl::CComboBox &cb, UInt32 v)
442 {
443   TCHAR s[16];
444   ConvertUInt32ToString(v, s);
445   const int index = (int)cb.AddString(s);
446   cb.SetItemData(index, (LPARAM)v);
447   return index;
448 }
449 
450 
OnInit()451 bool CBenchmarkDialog::OnInit()
452 {
453   #ifdef Z7_LANG
454   LangSetWindowText(*this, IDD_BENCH);
455   LangSetDlgItems(*this, kLangIDs, Z7_ARRAY_SIZE(kLangIDs));
456   LangSetDlgItems_RemoveColon(*this, kLangIDs_RemoveColon, Z7_ARRAY_SIZE(kLangIDs_RemoveColon));
457   LangSetDlgItemText(*this, IDT_BENCH_CURRENT2, IDT_BENCH_CURRENT);
458   LangSetDlgItemText(*this, IDT_BENCH_RESULTING2, IDT_BENCH_RESULTING);
459   #endif
460 
461   InitSyncNew();
462 
463   if (TotalMode)
464   {
465     _consoleEdit.Attach(GetItem(IDE_BENCH2_EDIT));
466     LOGFONT f;
467     memset(&f, 0, sizeof(f));
468     f.lfHeight = 14;
469     f.lfWidth = 0;
470     f.lfWeight = FW_DONTCARE;
471     f.lfCharSet = DEFAULT_CHARSET;
472     f.lfOutPrecision = OUT_DEFAULT_PRECIS;
473     f.lfClipPrecision = CLIP_DEFAULT_PRECIS;
474     f.lfQuality = DEFAULT_QUALITY;
475 
476     f.lfPitchAndFamily = FIXED_PITCH;
477     // MyStringCopy(f.lfFaceName, TEXT(""));
478     // f.lfFaceName[0] = 0;
479     _font.Create(&f);
480     if (_font._font)
481       _consoleEdit.SendMsg(WM_SETFONT, (WPARAM)_font._font, TRUE);
482   }
483 
484   UInt32 numCPUs = 1;
485 
486   {
487     AString s ("/ ");
488 
489     NSystem::CProcessAffinity threadsInfo;
490     threadsInfo.InitST();
491 
492     #ifndef Z7_ST
493     if (threadsInfo.Get() && threadsInfo.processAffinityMask != 0)
494       numCPUs = threadsInfo.GetNumProcessThreads();
495     else
496       numCPUs = NSystem::GetNumberOfProcessors();
497     #endif
498 
499     s.Add_UInt32(numCPUs);
500     s += GetProcessThreadsInfo(threadsInfo);
501     SetItemTextA(IDT_BENCH_HARDWARE_THREADS, s);
502 
503     {
504       AString s2;
505       GetSysInfo(s, s2);
506       SetItemTextA(IDT_BENCH_SYS1, s);
507       if (s != s2 && !s2.IsEmpty())
508         SetItemTextA(IDT_BENCH_SYS2, s2);
509     }
510     {
511       AString registers;
512       GetCpuName_MultiLine(s, registers);
513       SetItemTextA(IDT_BENCH_CPU, s);
514     }
515     {
516       GetOsInfoText(s);
517       s += " : ";
518       AddCpuFeatures(s);
519       SetItemTextA(IDT_BENCH_CPU_FEATURE, s);
520     }
521 
522     s = "7-Zip " MY_VERSION_CPU;
523     SetItemTextA(IDT_BENCH_VER, s);
524   }
525 
526 
527   // ----- Num Threads ----------
528 
529   if (numCPUs < 1)
530     numCPUs = 1;
531   numCPUs = MyMin(numCPUs, (UInt32)(1 << 6)); // it's WIN32 limit
532 
533   UInt32 numThreads = Sync.NumThreads;
534 
535   if (numThreads == (UInt32)(Int32)-1)
536     numThreads = numCPUs;
537   if (numThreads > 1)
538     numThreads &= ~(UInt32)1;
539   const UInt32 kNumThreadsMax = (1 << 12);
540   if (numThreads > kNumThreadsMax)
541     numThreads = kNumThreadsMax;
542 
543   m_NumThreads.Attach(GetItem(IDC_BENCH_NUM_THREADS));
544   const UInt32 numTheads_Combo = numCPUs * 2;
545   UInt32 v = 1;
546   int cur = 0;
547   for (; v <= numTheads_Combo;)
548   {
549     int index = ComboBox_Add_UInt32(m_NumThreads, v);
550     const UInt32 vNext = v + (v < 2 ? 1 : 2);
551     if (v <= numThreads)
552     if (numThreads < vNext || vNext > numTheads_Combo)
553     {
554       if (v != numThreads)
555         index = ComboBox_Add_UInt32(m_NumThreads, numThreads);
556       cur = index;
557     }
558     v = vNext;
559   }
560   m_NumThreads.SetCurSel(cur);
561   Sync.NumThreads = GetNumberOfThreads();
562 
563 
564   // ----- Dictionary ----------
565 
566   m_Dictionary.Attach(GetItem(IDC_BENCH_DICTIONARY));
567 
568   RamSize = (UInt64)(sizeof(size_t)) << 29;
569   RamSize_Defined = NSystem::GetRamSize(RamSize);
570 
571 
572   #ifdef UNDER_CE
573   const UInt32 kNormalizedCeSize = (16 << 20);
574   if (RamSize > kNormalizedCeSize && RamSize < (33 << 20))
575     RamSize = kNormalizedCeSize;
576   #endif
577   RamSize_Limit = RamSize / 16 * 15;
578 
579   if (Sync.DictSize == (UInt64)(Int64)-1)
580   {
581     unsigned dicSizeLog = 25;
582     #ifdef UNDER_CE
583     dicSizeLog = 20;
584     #endif
585     if (RamSize_Defined)
586     for (; dicSizeLog > kBenchMinDicLogSize; dicSizeLog--)
587       if (IsMemoryUsageOK(GetBenchMemoryUsage(
588           Sync.NumThreads, Sync.Level, (UInt64)1 << dicSizeLog, TotalMode)))
589         break;
590     Sync.DictSize = (UInt64)1 << dicSizeLog;
591   }
592 
593   if (Sync.DictSize < kMinDicSize) Sync.DictSize = kMinDicSize;
594   if (Sync.DictSize > kMaxDicSize) Sync.DictSize = kMaxDicSize;
595 
596   cur = 0;
597   for (unsigned i = (kMinDicLogSize - 1) * 2; i <= (32 - 1) * 2; i++)
598    {
599       const size_t dict = (size_t)(2 + (i & 1)) << (i / 2);
600       // if (i == (32 - 1) * 2) dict = kMaxDicSize;
601       TCHAR s[32];
602       const TCHAR *post;
603       UInt32 d;
604            if (dict >= ((UInt32)1 << 31)) { d = (UInt32)(dict >> 30); post = kGB; }
605       else if (dict >= ((UInt32)1 << 21)) { d = (UInt32)(dict >> 20); post = kMB; }
606       else                                { d = (UInt32)(dict >> 10); post = kKB; }
607       ConvertUInt32ToString(d, s);
608       lstrcat(s, post);
609       const int index = (int)m_Dictionary.AddString(s);
610       m_Dictionary.SetItemData(index, (LPARAM)dict);
611       if (dict <= Sync.DictSize)
612         cur = index;
613       if (dict >= kMaxDicSize)
614         break;
615     }
616   m_Dictionary.SetCurSel(cur);
617 
618 
619   // ----- Num Passes ----------
620 
621   m_NumPasses.Attach(GetItem(IDC_BENCH_NUM_PASSES));
622   cur = 0;
623   v = 1;
624   for (;;)
625   {
626     int index = ComboBox_Add_UInt32(m_NumPasses, v);
627     const bool isLast = (v >= 10000000);
628     UInt32 vNext = v * 10;
629          if (v < 2) vNext = 2;
630     else if (v < 5) vNext = 5;
631     else if (v < 10) vNext = 10;
632 
633     if (v <= Sync.NumPasses_Limit)
634     if (isLast || Sync.NumPasses_Limit < vNext)
635     {
636       if (v != Sync.NumPasses_Limit)
637         index = ComboBox_Add_UInt32(m_NumPasses, Sync.NumPasses_Limit);
638       cur = index;
639     }
640     v = vNext;
641     if (isLast)
642       break;
643   }
644   m_NumPasses.SetCurSel(cur);
645 
646   if (TotalMode)
647     NormalizeSize(true);
648   else
649     NormalizePosition();
650 
651   RestartBenchmark();
652 
653   return CModalDialog::OnInit();
654 }
655 
656 
OnSize(WPARAM,int xSize,int ySize)657 bool CBenchmarkDialog::OnSize(WPARAM /* wParam */, int xSize, int ySize)
658 {
659   int mx, my;
660   GetMargins(8, mx, my);
661 
662   if (!TotalMode)
663   {
664     RECT rect;
665     GetClientRectOfItem(IDT_BENCH_LOG, rect);
666     int x = xSize - rect.left - mx;
667     int y = ySize - rect.top - my;
668     if (x < 0) x = 0;
669     if (y < 0) y = 0;
670     MoveItem(IDT_BENCH_LOG, rect.left, rect.top, x, y, true);
671     return false;
672   }
673 
674   int bx1, bx2, by;
675 
676   GetItemSizes(IDCANCEL, bx1, by);
677   GetItemSizes(IDHELP, bx2, by);
678 
679   {
680     int y = ySize - my - by;
681     int x = xSize - mx - bx1;
682 
683     InvalidateRect(NULL);
684 
685     MoveItem(IDCANCEL, x, y, bx1, by);
686     MoveItem(IDHELP, x - mx - bx2, y, bx2, by);
687   }
688 
689   if (_consoleEdit)
690   {
691     int yPos = ySize - my - by;
692     RECT rect;
693     GetClientRectOfItem(IDE_BENCH2_EDIT, rect);
694     int y = rect.top;
695     int ySize2 = yPos - my - y;
696     const int kMinYSize = 20;
697     int xx = xSize - mx * 2;
698     if (ySize2 < kMinYSize)
699     {
700       ySize2 = kMinYSize;
701     }
702     _consoleEdit.Move(mx, y, xx, ySize2);
703   }
704   return false;
705 }
706 
707 
GetNumberOfThreads()708 UInt32 CBenchmarkDialog::GetNumberOfThreads()
709 {
710   return (UInt32)m_NumThreads.GetItemData_of_CurSel();
711 }
712 
713 
714 #define UINT_TO_STR_3(s, val) { \
715   s[0] = (wchar_t)('0' + (val) / 100); \
716   s[1] = (wchar_t)('0' + (val) % 100 / 10); \
717   s[2] = (wchar_t)('0' + (val) % 10); \
718   s += 3; s[0] = 0; }
719 
NumberToDot3(UInt64 val,WCHAR * s)720 static WCHAR *NumberToDot3(UInt64 val, WCHAR *s)
721 {
722   s = ConvertUInt64ToString(val / 1000, s);
723   const UInt32 rem = (UInt32)(val % 1000);
724   *s++ = '.';
725   UINT_TO_STR_3(s, rem)
726   return s;
727 }
728 
SetItemText_Number(unsigned itemID,UInt64 val,LPCTSTR post)729 void CBenchmarkDialog::SetItemText_Number(unsigned itemID, UInt64 val, LPCTSTR post)
730 {
731   TCHAR s[64];
732   ConvertUInt64ToString(val, s);
733   if (post)
734     lstrcat(s, post);
735   SetItemText(itemID, s);
736 }
737 
AddSize_MB(UString & s,UInt64 size)738 static void AddSize_MB(UString &s, UInt64 size)
739 {
740   s.Add_UInt64((size + (1 << 20) - 1) >> 20);
741   s += kMB;
742 }
743 
Print_MemUsage(UString & s,UInt64 memUsage) const744 void CBenchmarkDialog::Print_MemUsage(UString &s, UInt64 memUsage) const
745 {
746   AddSize_MB(s, memUsage);
747   if (RamSize_Defined)
748   {
749     s += " / ";
750     AddSize_MB(s, RamSize);
751   }
752 }
753 
OnChangeDictionary()754 size_t CBenchmarkDialog::OnChangeDictionary()
755 {
756   const size_t dict = (size_t)m_Dictionary.GetItemData_of_CurSel();
757   const UInt64 memUsage = GetBenchMemoryUsage(GetNumberOfThreads(),
758       Sync.Level,
759       dict,
760       false); // totalBench mode
761 
762   UString s;
763   Print_MemUsage(s, memUsage);
764 
765   #ifdef Z7_LARGE_PAGES
766   {
767     AString s2;
768     Add_LargePages_String(s2);
769     if (!s2.IsEmpty())
770     {
771       s.Add_Space();
772       s += s2;
773     }
774   }
775   #endif
776 
777   SetItemText(IDT_BENCH_MEMORY_VAL, s);
778 
779   return dict;
780 }
781 
782 
783 static const UInt32 g_IDs[] =
784 {
785   IDT_BENCH_COMPRESS_SIZE1,
786   IDT_BENCH_COMPRESS_SIZE2,
787   IDT_BENCH_COMPRESS_USAGE1,
788   IDT_BENCH_COMPRESS_USAGE2,
789   IDT_BENCH_COMPRESS_SPEED1,
790   IDT_BENCH_COMPRESS_SPEED2,
791   IDT_BENCH_COMPRESS_RATING1,
792   IDT_BENCH_COMPRESS_RATING2,
793   IDT_BENCH_COMPRESS_RPU1,
794   IDT_BENCH_COMPRESS_RPU2,
795 
796   IDT_BENCH_DECOMPR_SIZE1,
797   IDT_BENCH_DECOMPR_SIZE2,
798   IDT_BENCH_DECOMPR_SPEED1,
799   IDT_BENCH_DECOMPR_SPEED2,
800   IDT_BENCH_DECOMPR_RATING1,
801   IDT_BENCH_DECOMPR_RATING2,
802   IDT_BENCH_DECOMPR_USAGE1,
803   IDT_BENCH_DECOMPR_USAGE2,
804   IDT_BENCH_DECOMPR_RPU1,
805   IDT_BENCH_DECOMPR_RPU2,
806 
807   IDT_BENCH_TOTAL_USAGE_VAL,
808   IDT_BENCH_TOTAL_RATING_VAL,
809   IDT_BENCH_TOTAL_RPU_VAL
810 };
811 
812 
813 static const unsigned k_Ids_Enc_1[] = {
814   IDT_BENCH_COMPRESS_USAGE1,
815   IDT_BENCH_COMPRESS_SPEED1,
816   IDT_BENCH_COMPRESS_RPU1,
817   IDT_BENCH_COMPRESS_RATING1,
818   IDT_BENCH_COMPRESS_SIZE1 };
819 
820 static const unsigned k_Ids_Enc[] = {
821   IDT_BENCH_COMPRESS_USAGE2,
822   IDT_BENCH_COMPRESS_SPEED2,
823   IDT_BENCH_COMPRESS_RPU2,
824   IDT_BENCH_COMPRESS_RATING2,
825   IDT_BENCH_COMPRESS_SIZE2 };
826 
827 static const unsigned k_Ids_Dec_1[] = {
828   IDT_BENCH_DECOMPR_USAGE1,
829   IDT_BENCH_DECOMPR_SPEED1,
830   IDT_BENCH_DECOMPR_RPU1,
831   IDT_BENCH_DECOMPR_RATING1,
832   IDT_BENCH_DECOMPR_SIZE1 };
833 
834 static const unsigned k_Ids_Dec[] = {
835   IDT_BENCH_DECOMPR_USAGE2,
836   IDT_BENCH_DECOMPR_SPEED2,
837   IDT_BENCH_DECOMPR_RPU2,
838   IDT_BENCH_DECOMPR_RATING2,
839   IDT_BENCH_DECOMPR_SIZE2 };
840 
841 static const unsigned k_Ids_Tot[] = {
842   IDT_BENCH_TOTAL_USAGE_VAL,
843   0,
844   IDT_BENCH_TOTAL_RPU_VAL,
845   IDT_BENCH_TOTAL_RATING_VAL,
846   0 };
847 
848 
MyKillTimer()849 void CBenchmarkDialog::MyKillTimer()
850 {
851   if (_timer != 0)
852   {
853     KillTimer(kTimerID);
854     _timer = 0;
855   }
856 }
857 
858 
OnDestroy()859 bool CBenchmarkDialog::OnDestroy()
860 {
861   /* actually timer was removed before.
862      also the timer must be removed by Windows, when window  will be removed. */
863   MyKillTimer(); // it's optional code
864   return false; // we return (false) to perform default dialog operation
865 }
866 
867 void SetErrorMessage_MemUsage(UString &s, UInt64 reqSize, UInt64 ramSize, UInt64 ramLimit, const UString &usageString);
868 
StartBenchmark()869 void CBenchmarkDialog::StartBenchmark()
870 {
871   NeedRestart = false;
872   WasStopped_in_GUI = false;
873 
874   SetItemText_Empty(IDT_BENCH_ERROR_MESSAGE);
875 
876   MyKillTimer(); // optional code. timer was killed before
877 
878   const size_t dict = OnChangeDictionary();
879   const UInt32 numThreads = GetNumberOfThreads();
880   const UInt32 numPasses = (UInt32)m_NumPasses.GetItemData_of_CurSel();
881 
882   for (unsigned i = 0; i < Z7_ARRAY_SIZE(g_IDs); i++)
883     SetItemText(g_IDs[i], kProcessingString);
884 
885   SetItemText_Empty(IDT_BENCH_LOG);
886   SetItemText_Empty(IDT_BENCH_ELAPSED_VAL);
887   SetItemText_Empty(IDT_BENCH_ERROR_MESSAGE);
888 
889   const UInt64 memUsage = GetBenchMemoryUsage(numThreads, Sync.Level, dict,
890       false); // totalBench
891   if (!IsMemoryUsageOK(memUsage))
892   {
893     UString s2;
894     LangString_OnlyFromLangFile(IDS_MEM_REQUIRED_MEM_SIZE, s2);
895     if (s2.IsEmpty())
896     {
897       s2 = LangString(IDT_BENCH_MEMORY);
898       if (s2.IsEmpty())
899         GetItemText(IDT_BENCH_MEMORY, s2);
900       s2.RemoveChar(L':');
901     }
902     UString s;
903     SetErrorMessage_MemUsage(s, memUsage, RamSize, RamSize_Limit, s2);
904     MessageBoxError_Status(s);
905     return;
906   }
907 
908   EnableItem(IDB_STOP, true);
909 
910   _startTime = GetTickCount();
911   _finishTime = _startTime;
912   _finishTime_WasSet = false;
913 
914   {
915     NWindows::NSynchronization::CCriticalSectionLock lock(Sync.CS);
916     InitSyncNew();
917     Sync.DictSize = dict;
918     Sync.NumThreads = numThreads;
919     Sync.NumPasses_Limit = numPasses;
920   }
921 
922   PrintTime();
923 
924   _timer = SetTimer(kTimerID, kTimerElapse);
925   if (_thread.Create(CThreadBenchmark::MyThreadFunction, &_threadBenchmark) != 0)
926   {
927     MyKillTimer();
928     MessageBoxError_Status(L"Can't create thread");
929   }
930   return;
931 }
932 
933 
RestartBenchmark()934 void CBenchmarkDialog::RestartBenchmark()
935 {
936   if (ExitWasAsked_in_GUI)
937     return;
938 
939   if (_thread.IsCreated())
940   {
941     NeedRestart = true;
942     SendExit_Status(L"Stop for restart ...");
943   }
944   else
945     StartBenchmark();
946 }
947 
948 
Disable_Stop_Button()949 void CBenchmarkDialog::Disable_Stop_Button()
950 {
951   // if we disable focused button, then focus will be lost
952   if (GetFocus() == GetItem(IDB_STOP))
953   {
954     // SendMsg_NextDlgCtl_Prev();
955     SendMsg_NextDlgCtl_CtlId(IDB_RESTART);
956   }
957   EnableItem(IDB_STOP, false);
958 }
959 
960 
OnStopButton()961 void CBenchmarkDialog::OnStopButton()
962 {
963   if (ExitWasAsked_in_GUI)
964     return;
965 
966   Disable_Stop_Button();
967 
968   WasStopped_in_GUI = true;
969   if (_thread.IsCreated())
970   {
971     SendExit_Status(L"Stop ...");
972   }
973 }
974 
975 
976 
OnCancel()977 void CBenchmarkDialog::OnCancel()
978 {
979   ExitWasAsked_in_GUI = true;
980 
981   /*
982   SendMsg_NextDlgCtl_Prev();
983   EnableItem(IDCANCEL, false);
984   */
985 
986   if (_thread.IsCreated())
987     SendExit_Status(L"Cancel ...");
988   else
989     CModalDialog::OnCancel();
990 }
991 
992 
OnHelp()993 void CBenchmarkDialog::OnHelp()
994 {
995   ShowHelpWindow(kHelpTopic);
996 }
997 
998 
999 
1000 // void GetTimeString(UInt64 timeValue, wchar_t *s);
1001 
PrintTime()1002 void CBenchmarkDialog::PrintTime()
1003 {
1004   const UInt32 curTime =
1005     _finishTime_WasSet ?
1006       _finishTime :
1007       ::GetTickCount();
1008 
1009   const UInt32 elapsedTime = (curTime - _startTime);
1010 
1011   WCHAR s[64];
1012 
1013   WCHAR *p = ConvertUInt32ToString(elapsedTime / 1000, s);
1014 
1015   if (_finishTime_WasSet)
1016   {
1017     *p++ = '.';
1018     UINT_TO_STR_3(p, elapsedTime % 1000)
1019   }
1020 
1021   // p = NumberToDot3((UInt64)elapsedTime, s);
1022 
1023   MyStringCopy(p, L" s");
1024 
1025   // if (WasStopped_in_GUI) wcscat(s, L" X"); // for debug
1026 
1027   if (s == ElapsedSec_Prev)
1028     return;
1029 
1030   ElapsedSec_Prev = s;
1031 
1032   // static cnt = 0; cnt++; wcscat(s, L" ");
1033   // UString s2; s2.Add_UInt32(cnt); wcscat(s, s2.Ptr());
1034 
1035   SetItemText(IDT_BENCH_ELAPSED_VAL, s);
1036 }
1037 
1038 
GetMips(UInt64 ips)1039 static UInt64 GetMips(UInt64 ips)
1040 {
1041   return (ips + 500000) / 1000000;
1042 }
1043 
1044 
GetUsagePercents(UInt64 usage)1045 static UInt64 GetUsagePercents(UInt64 usage)
1046 {
1047   return Benchmark_GetUsage_Percents(usage);
1048 }
1049 
1050 
GetRating(const CTotalBenchRes & info)1051 static UInt32 GetRating(const CTotalBenchRes &info)
1052 {
1053   UInt64 numIter = info.NumIterations2;
1054   if (numIter == 0)
1055     numIter = 1000000;
1056   const UInt64 rating64 = GetMips(info.Rating / numIter);
1057   // return rating64;
1058   UInt32 rating32 = (UInt32)rating64;
1059   if (rating32 != rating64)
1060     rating32 = (UInt32)(Int32)-1;
1061   return rating32;
1062 }
1063 
1064 
AddUsageString(UString & s,const CTotalBenchRes & info)1065 static void AddUsageString(UString &s, const CTotalBenchRes &info)
1066 {
1067   UInt64 numIter = info.NumIterations2;
1068   if (numIter == 0)
1069     numIter = 1000000;
1070   UInt64 usage = GetUsagePercents(info.Usage / numIter);
1071 
1072   wchar_t w[64];
1073   ConvertUInt64ToString(usage, w);
1074   unsigned len = MyStringLen(w);
1075   while (len < 5)
1076   {
1077     s.Add_Space();
1078     len++;
1079   }
1080   s += w;
1081   s += "%";
1082 }
1083 
1084 
Add_Dot3String(UString & s,UInt64 val)1085 static void Add_Dot3String(UString &s, UInt64 val)
1086 {
1087   WCHAR temp[32];
1088   NumberToDot3(val, temp);
1089   s += temp;
1090 }
1091 
1092 
AddRatingString(UString & s,const CTotalBenchRes & info)1093 static void AddRatingString(UString &s, const CTotalBenchRes &info)
1094 {
1095   // AddUsageString(s, info);
1096   // s.Add_Space();
1097   // s.Add_UInt32(GetRating(info));
1098   Add_Dot3String(s, GetRating(info));
1099 }
1100 
1101 
AddRatingsLine(UString & s,const CTotalBenchRes & enc,const CTotalBenchRes & dec,DWORD ticks)1102 static void AddRatingsLine(UString &s, const CTotalBenchRes &enc, const CTotalBenchRes &dec
1103     #ifdef PRINT_ITER_TIME
1104     , DWORD ticks
1105     #endif
1106     )
1107 {
1108   // AddUsageString(s, enc); s.Add_Space();
1109 
1110   AddRatingString(s, enc);
1111   s += "  ";
1112   AddRatingString(s, dec);
1113 
1114   CTotalBenchRes tot_BenchRes;
1115   tot_BenchRes.SetSum(enc, dec);
1116 
1117   s += "  ";
1118   AddRatingString(s, tot_BenchRes);
1119 
1120   s.Add_Space();  AddUsageString(s, tot_BenchRes);
1121 
1122 
1123   #ifdef PRINT_ITER_TIME
1124   s.Add_Space();
1125   {
1126     Add_Dot3String(s, ticks;
1127     s += " s";
1128     // s.Add_UInt32(ticks); s += " ms";
1129   }
1130   #endif
1131 }
1132 
1133 
1134 void CBenchmarkDialog::PrintRating(UInt64 rating, UINT controlID)
1135 {
1136   // SetItemText_Number(controlID, GetMips(rating), kMIPS);
1137   WCHAR s[64];
1138   MyStringCopy(NumberToDot3(GetMips(rating), s), L" GIPS");
1139   SetItemText(controlID, s);
1140 }
1141 
1142 void CBenchmarkDialog::PrintUsage(UInt64 usage, UINT controlID)
1143 {
1144   SetItemText_Number(controlID, GetUsagePercents(usage), TEXT("%"));
1145 }
1146 
1147 
1148 // void SetItemText_Number
1149 
1150 void CBenchmarkDialog::PrintBenchRes(
1151     const CTotalBenchRes2 &info,
1152     const UINT ids[])
1153 {
1154   if (info.NumIterations2 == 0)
1155     return;
1156   if (ids[1] != 0)
1157     SetItemText_Number(ids[1], (info.Speed >> 10) / info.NumIterations2, kKBs);
1158   PrintRating(info.Rating / info.NumIterations2, ids[3]);
1159   PrintRating(info.RPU / info.NumIterations2, ids[2]);
1160   PrintUsage(info.Usage / info.NumIterations2, ids[0]);
1161   if (ids[4] != 0)
1162   {
1163     UInt64 val = info.UnpackSize;
1164     LPCTSTR kPostfix;
1165     if (val >= ((UInt64)1 << 40))
1166     {
1167       kPostfix = kGB;
1168       val >>= 30;
1169     }
1170     else
1171     {
1172       kPostfix = kMB;
1173       val >>= 20;
1174     }
1175     SetItemText_Number(ids[4], val, kPostfix);
1176   }
1177 }
1178 
1179 
1180 // static UInt32 k_Message_Finished_cnt = 0;
1181 // static UInt32 k_OnTimer_cnt = 0;
1182 
1183 bool CBenchmarkDialog::OnMessage(UINT message, WPARAM wParam, LPARAM lParam)
1184 {
1185   if (message != k_Message_Finished)
1186     return CModalDialog::OnMessage(message, wParam, lParam);
1187 
1188   {
1189     if (wParam == k_Msg_WPARM_Thread_Finished)
1190     {
1191       _finishTime = GetTickCount();
1192       _finishTime_WasSet = true;
1193       MyKillTimer();
1194 
1195       if (_thread.Wait_Close() != 0)
1196       {
1197         MessageBoxError_Status(L"Thread Wait Error");
1198       }
1199 
1200       if (!WasStopped_in_GUI)
1201       {
1202         WasStopped_in_GUI = true;
1203         Disable_Stop_Button();
1204       }
1205 
1206       HRESULT res = Sync.BenchFinish_Thread_HRESULT;
1207       if (res != S_OK)
1208       // if (!ExitWasAsked_in_GUI || res != E_ABORT)
1209         MessageBoxError_Status(HResultToMessage(res));
1210 
1211       if (ExitWasAsked_in_GUI)
1212       {
1213         // SetItemText(IDT_BENCH_ERROR_MESSAGE, "before CModalDialog::OnCancel()");
1214         // Sleep (2000);
1215         // MessageBoxError(L"test");
1216         CModalDialog::OnCancel();
1217         return true;
1218       }
1219 
1220       SetItemText_Empty(IDT_BENCH_ERROR_MESSAGE);
1221 
1222       res = Sync.BenchFinish_Task_HRESULT;
1223       if (res != S_OK)
1224       {
1225         if (!WasStopped_in_GUI || res != E_ABORT)
1226         {
1227           UString m;
1228           if (res == S_FALSE)
1229             m = "Decoding error";
1230           else if (res == CLASS_E_CLASSNOTAVAILABLE)
1231             m = "Can't find 7z.dll";
1232           else
1233             m = HResultToMessage(res);
1234           MessageBoxError_Status(m);
1235         }
1236       }
1237 
1238       if (NeedRestart)
1239       {
1240         StartBenchmark();
1241         return true;
1242       }
1243     }
1244     // k_Message_Finished_cnt++;
1245     UpdateGui();
1246     return true;
1247   }
1248 }
1249 
1250 
1251 bool CBenchmarkDialog::OnTimer(WPARAM timerID, LPARAM /* callback */)
1252 {
1253   // k_OnTimer_cnt++;
1254   if (timerID == kTimerID)
1255     UpdateGui();
1256   return true;
1257 }
1258 
1259 
1260 void CBenchmarkDialog::UpdateGui()
1261 {
1262   PrintTime();
1263 
1264   if (TotalMode)
1265   {
1266     bool wasChanged = false;
1267     {
1268       NWindows::NSynchronization::CCriticalSectionLock lock(Sync.CS);
1269 
1270       if (Sync.TextWasChanged)
1271       {
1272         wasChanged = true;
1273         Bench2Text += Sync.Text;
1274         Sync.Text.Empty();
1275         Sync.TextWasChanged = false;
1276       }
1277     }
1278     if (wasChanged)
1279       _consoleEdit.SetText(Bench2Text);
1280     return;
1281   }
1282 
1283   CSyncData sd;
1284   CRecordVector<CBenchPassResult> RatingVector;
1285 
1286   {
1287     NWindows::NSynchronization::CCriticalSectionLock lock(Sync.CS);
1288     sd = Sync.sd;
1289 
1290     if (sd.NeedPrint_RatingVector)
1291       RatingVector = Sync.RatingVector;
1292 
1293     if (sd.NeedPrint_Freq)
1294     {
1295       Sync.FreqString_GUI = Sync.FreqString_Sync;
1296       sd.NeedPrint_RatingVector = true;
1297     }
1298 
1299     Sync.sd.NeedPrint_RatingVector = false;
1300     Sync.sd.NeedPrint_Enc_1 = false;
1301     Sync.sd.NeedPrint_Enc   = false;
1302     Sync.sd.NeedPrint_Dec_1 = false;
1303     Sync.sd.NeedPrint_Dec   = false;
1304     Sync.sd.NeedPrint_Tot   = false;
1305     Sync.sd.NeedPrint_Freq = false;
1306   }
1307 
1308   if (sd.NumPasses_Finished != NumPasses_Finished_Prev)
1309   {
1310     SetItemText_Number(IDT_BENCH_PASSES_VAL, sd.NumPasses_Finished, TEXT(" /"));
1311     NumPasses_Finished_Prev = sd.NumPasses_Finished;
1312   }
1313 
1314   if (sd.NeedPrint_Enc_1) PrintBenchRes(sd.Enc_BenchRes_1, k_Ids_Enc_1);
1315   if (sd.NeedPrint_Enc)   PrintBenchRes(sd.Enc_BenchRes,   k_Ids_Enc);
1316   if (sd.NeedPrint_Dec_1) PrintBenchRes(sd.Dec_BenchRes_1, k_Ids_Dec_1);
1317   if (sd.NeedPrint_Dec)   PrintBenchRes(sd.Dec_BenchRes,   k_Ids_Dec);
1318 
1319   if (sd.BenchWasFinished && sd.NeedPrint_Tot)
1320   {
1321     CTotalBenchRes2 tot_BenchRes = sd.Enc_BenchRes;
1322     tot_BenchRes.Update_With_Res2(sd.Dec_BenchRes);
1323     PrintBenchRes(tot_BenchRes, k_Ids_Tot);
1324   }
1325 
1326 
1327   if (sd.NeedPrint_RatingVector)
1328   // for (unsigned k = 0; k < 1; k++)
1329   {
1330     UString s;
1331     s += Sync.FreqString_GUI;
1332     if (!RatingVector.IsEmpty())
1333     {
1334       if (!s.IsEmpty())
1335         s.Add_LF();
1336       s += "Compr Decompr Total   CPU"
1337           #ifdef PRINT_ITER_TIME
1338           "   Time"
1339           #endif
1340           ;
1341       s.Add_LF();
1342     }
1343     // s += "GIPS    GIPS   GIPS    %   s"; s.Add_LF();
1344     for (unsigned i = 0; i < RatingVector.Size(); i++)
1345     {
1346       if (i != 0)
1347         s.Add_LF();
1348       if ((int)i == sd.RatingVector_DeletedIndex)
1349       {
1350         s += "...";
1351         s.Add_LF();
1352       }
1353       const CBenchPassResult &pair = RatingVector[i];
1354       /*
1355         s += "g:"; s.Add_UInt32((UInt32)pair.EncInfo.GlobalTime);
1356         s += " u:"; s.Add_UInt32((UInt32)pair.EncInfo.UserTime);
1357         s.Add_Space();
1358       */
1359       AddRatingsLine(s, pair.Enc, pair.Dec
1360             #ifdef PRINT_ITER_TIME
1361             , pair.Ticks
1362             #endif
1363             );
1364       /*
1365       {
1366         UInt64 v = i + 1;
1367         if (sd.RatingVector_DeletedIndex >= 0 && i >= (unsigned)sd.RatingVector_DeletedIndex)
1368           v += sd.RatingVector_NumDeleted;
1369         char temp[64];
1370         ConvertUInt64ToString(v, temp);
1371         s += " : ";
1372         s += temp;
1373       }
1374       */
1375     }
1376 
1377     if (sd.BenchWasFinished)
1378     {
1379       s.Add_LF();
1380       s += "-------------";
1381       s.Add_LF();
1382       {
1383         // average time is not correct because of freq detection in first iteration
1384         AddRatingsLine(s, sd.Enc_BenchRes, sd.Dec_BenchRes
1385               #ifdef PRINT_ITER_TIME
1386               , (DWORD)(sd.TotalTicks / (sd.NumPasses_Finished ? sd.NumPasses_Finished : 1))
1387               #endif
1388               );
1389       }
1390     }
1391     // s.Add_LF(); s += "OnTimer: "; s.Add_UInt32(k_OnTimer_cnt);
1392     // s.Add_LF(); s += "finished Message: "; s.Add_UInt32(k_Message_Finished_cnt);
1393     // static cnt = 0; cnt++; s.Add_LF(); s += "Print: "; s.Add_UInt32(cnt);
1394     // s.Add_LF(); s += "NumEncProgress: "; s.Add_UInt32((UInt32)sd.NumEncProgress);
1395     // s.Add_LF(); s += "NumDecProgress: "; s.Add_UInt32((UInt32)sd.NumDecProgress);
1396     SetItemText(IDT_BENCH_LOG, s);
1397   }
1398 }
1399 
1400 
1401 bool CBenchmarkDialog::OnCommand(unsigned code, unsigned itemID, LPARAM lParam)
1402 {
1403   if (code == CBN_SELCHANGE &&
1404       (itemID == IDC_BENCH_DICTIONARY ||
1405        itemID == IDC_BENCH_NUM_PASSES ||
1406        itemID == IDC_BENCH_NUM_THREADS))
1407   {
1408     RestartBenchmark();
1409     return true;
1410   }
1411   return CModalDialog::OnCommand(code, itemID, lParam);
1412 }
1413 
1414 
1415 bool CBenchmarkDialog::OnButtonClicked(unsigned buttonID, HWND buttonHWND)
1416 {
1417   switch (buttonID)
1418   {
1419     case IDB_RESTART:
1420       RestartBenchmark();
1421       return true;
1422     case IDB_STOP:
1423       OnStopButton();
1424       return true;
1425   }
1426   return CModalDialog::OnButtonClicked(buttonID, buttonHWND);
1427 }
1428 
1429 
1430 
1431 
1432 
1433 // ---------- Benchmark Thread ----------
1434 
1435 struct CBenchCallback Z7_final: public IBenchCallback
1436 {
1437   UInt64 dictionarySize;
1438   CBenchProgressSync *Sync;
1439   CBenchmarkDialog *BenchmarkDialog;
1440 
1441   HRESULT SetEncodeResult(const CBenchInfo &info, bool final) Z7_override;
1442   HRESULT SetDecodeResult(const CBenchInfo &info, bool final) Z7_override;
1443 };
1444 
1445 HRESULT CBenchCallback::SetEncodeResult(const CBenchInfo &info, bool final)
1446 {
1447   bool needPost = false;
1448   {
1449     NSynchronization::CCriticalSectionLock lock(Sync->CS);
1450     if (Sync->Exit)
1451       return E_ABORT;
1452     CSyncData &sd = Sync->sd;
1453     // sd.NumEncProgress++;
1454     CTotalBenchRes2 &br = sd.Enc_BenchRes_1;
1455     {
1456       UInt64 dictSize = Sync->DictSize;
1457       if (final)
1458       {
1459         // sd.EncInfo = info;
1460       }
1461       else
1462       {
1463         /* if (!final), then CBenchInfo::NumIterations means totalNumber of threads.
1464            so we can reduce the dictionary */
1465         if (dictSize > info.UnpackSize)
1466           dictSize = info.UnpackSize;
1467       }
1468       br.Rating = info.GetRating_LzmaEnc(dictSize);
1469     }
1470     br.SetFrom_BenchInfo(info);
1471     sd.NeedPrint_Enc_1 = true;
1472     if (final)
1473     {
1474       sd.Enc_BenchRes.Update_With_Res2(br);
1475       sd.NeedPrint_Enc = true;
1476       needPost = true;
1477     }
1478   }
1479 
1480   if (needPost)
1481     BenchmarkDialog->PostMsg(k_Message_Finished, k_Msg_WPARM_Enc1_Finished);
1482 
1483   return S_OK;
1484 }
1485 
1486 
1487 HRESULT CBenchCallback::SetDecodeResult(const CBenchInfo &info, bool final)
1488 {
1489   NSynchronization::CCriticalSectionLock lock(Sync->CS);
1490   if (Sync->Exit)
1491     return E_ABORT;
1492   CSyncData &sd = Sync->sd;
1493   // sd.NumDecProgress++;
1494   CTotalBenchRes2 &br = sd.Dec_BenchRes_1;
1495   br.Rating = info.GetRating_LzmaDec();
1496   br.SetFrom_BenchInfo(info);
1497   sd.NeedPrint_Dec_1 = true;
1498   if (final)
1499     sd.Dec_BenchRes.Update_With_Res2(br);
1500   return S_OK;
1501 }
1502 
1503 
1504 struct CBenchCallback2 Z7_final: public IBenchPrintCallback
1505 {
1506   CBenchProgressSync *Sync;
1507   bool TotalMode;
1508 
1509   void Print(const char *s) Z7_override;
1510   void NewLine() Z7_override;
1511   HRESULT CheckBreak() Z7_override;
1512 };
1513 
1514 void CBenchCallback2::Print(const char *s)
1515 {
1516   if (TotalMode)
1517   {
1518     NSynchronization::CCriticalSectionLock lock(Sync->CS);
1519     Sync->Text += s;
1520     Sync->TextWasChanged = true;
1521   }
1522 }
1523 
1524 void CBenchCallback2::NewLine()
1525 {
1526   Print("\xD\n");
1527 }
1528 
1529 HRESULT CBenchCallback2::CheckBreak()
1530 {
1531   if (Sync->Exit)
1532     return E_ABORT;
1533   return S_OK;
1534 }
1535 
1536 
1537 
1538 struct CFreqCallback Z7_final: public IBenchFreqCallback
1539 {
1540   CBenchmarkDialog *BenchmarkDialog;
1541 
1542   virtual HRESULT AddCpuFreq(unsigned numThreads, UInt64 freq, UInt64 usage) Z7_override;
1543   virtual HRESULT FreqsFinished(unsigned numThreads) Z7_override;
1544 };
1545 
1546 HRESULT CFreqCallback::AddCpuFreq(unsigned numThreads, UInt64 freq, UInt64 usage)
1547 {
1548   HRESULT res;
1549   {
1550     CBenchProgressSync &sync = BenchmarkDialog->Sync;
1551     NSynchronization::CCriticalSectionLock lock(sync.CS);
1552     UString &s = sync.FreqString_Sync;
1553     if (sync.NumFreqThreadsPrev != numThreads)
1554     {
1555       sync.NumFreqThreadsPrev = numThreads;
1556       if (!s.IsEmpty())
1557         s.Add_LF();
1558       s.Add_UInt32(numThreads);
1559       s += "T Frequency (MHz):";
1560       s.Add_LF();
1561     }
1562     s.Add_Space();
1563     if (numThreads != 1)
1564     {
1565       s.Add_UInt64(GetUsagePercents(usage));
1566       s.Add_Char('%');
1567       s.Add_Space();
1568     }
1569     s.Add_UInt64(GetMips(freq));
1570     // BenchmarkDialog->Sync.sd.NeedPrint_Freq = true;
1571     res = sync.Exit ? E_ABORT : S_OK;
1572   }
1573   // BenchmarkDialog->PostMsg(k_Message_Finished, k_Msg_WPARM_Enc1_Finished);
1574   return res;
1575 }
1576 
1577 HRESULT CFreqCallback::FreqsFinished(unsigned /* numThreads */)
1578 {
1579   HRESULT res;
1580   {
1581     CBenchProgressSync &sync = BenchmarkDialog->Sync;
1582     NSynchronization::CCriticalSectionLock lock(sync.CS);
1583     sync.sd.NeedPrint_Freq = true;
1584     BenchmarkDialog->PostMsg(k_Message_Finished, k_Msg_WPARM_Enc1_Finished);
1585     res = sync.Exit ? E_ABORT : S_OK;
1586   }
1587   BenchmarkDialog->PostMsg(k_Message_Finished, k_Msg_WPARM_Enc1_Finished);
1588   return res;
1589 }
1590 
1591 
1592 
1593 // define USE_DUMMY only for debug
1594 // #define USE_DUMMY
1595 #ifdef USE_DUMMY
1596 static unsigned dummy = 1;
1597 static unsigned Dummy(unsigned limit)
1598 {
1599   unsigned sum = 0;
1600   for (unsigned k = 0; k < limit; k++)
1601   {
1602     sum += dummy;
1603     if (sum == 0)
1604       break;
1605   }
1606   return sum;
1607 }
1608 #endif
1609 
1610 
1611 HRESULT CThreadBenchmark::Process()
1612 {
1613   /* the first benchmark pass can be slow,
1614      if we run benchmark while the window is being created,
1615      and (no freq detecion loop) && (dictionary is small) (-mtic is small) */
1616 
1617   // Sleep(300); // for debug
1618   #ifdef USE_DUMMY
1619   Dummy(1000 * 1000 * 1000); // for debug
1620   #endif
1621 
1622   CBenchProgressSync &sync = BenchmarkDialog->Sync;
1623   HRESULT finishHRESULT = S_OK;
1624 
1625   try
1626   {
1627     for (UInt32 passIndex = 0;; passIndex++)
1628     {
1629       // throw 1; // to debug
1630       // throw CSystemException(E_INVALIDARG); // to debug
1631 
1632       UInt64 dictionarySize;
1633       UInt32 numThreads;
1634       {
1635         NSynchronization::CCriticalSectionLock lock(sync.CS);
1636         if (sync.Exit)
1637           break;
1638         dictionarySize = sync.DictSize;
1639         numThreads = sync.NumThreads;
1640       }
1641 
1642       #ifdef PRINT_ITER_TIME
1643       const DWORD startTick = GetTickCount();
1644       #endif
1645 
1646       CBenchCallback callback;
1647 
1648       callback.dictionarySize = dictionarySize;
1649       callback.Sync = &sync;
1650       callback.BenchmarkDialog = BenchmarkDialog;
1651 
1652       CBenchCallback2 callback2;
1653       callback2.TotalMode = BenchmarkDialog->TotalMode;
1654       callback2.Sync = &sync;
1655 
1656       CFreqCallback freqCallback;
1657       freqCallback.BenchmarkDialog = BenchmarkDialog;
1658 
1659       HRESULT result;
1660 
1661       try
1662       {
1663         CObjectVector<CProperty> props;
1664 
1665         props = BenchmarkDialog->Props;
1666 
1667         if (BenchmarkDialog->TotalMode)
1668         {
1669           props = BenchmarkDialog->Props;
1670         }
1671         else
1672         {
1673           {
1674             CProperty prop;
1675             prop.Name = "mt";
1676             prop.Value.Add_UInt32(numThreads);
1677             props.Add(prop);
1678           }
1679           {
1680             CProperty prop;
1681             prop.Name = 'd';
1682             prop.Name.Add_UInt32((UInt32)(dictionarySize >> 10));
1683             prop.Name.Add_Char('k');
1684             props.Add(prop);
1685           }
1686         }
1687 
1688         result = Bench(EXTERNAL_CODECS_LOC_VARS
1689             BenchmarkDialog->TotalMode ? &callback2 : NULL,
1690             BenchmarkDialog->TotalMode ? NULL : &callback,
1691             props, 1, false,
1692             (!BenchmarkDialog->TotalMode) && passIndex == 0 ? &freqCallback: NULL);
1693 
1694         // result = S_FALSE; // for debug;
1695         // throw 1;
1696       }
1697       catch(...)
1698       {
1699         result = E_FAIL;
1700       }
1701 
1702       #ifdef PRINT_ITER_TIME
1703       const DWORD numTicks = GetTickCount() - startTick;
1704       #endif
1705 
1706       bool finished = true;
1707 
1708       NSynchronization::CCriticalSectionLock lock(sync.CS);
1709 
1710       if (result != S_OK)
1711       {
1712         sync.BenchFinish_Task_HRESULT = result;
1713         break;
1714       }
1715 
1716       {
1717         CSyncData &sd = sync.sd;
1718 
1719         sd.NumPasses_Finished++;
1720         #ifdef PRINT_ITER_TIME
1721         sd.TotalTicks += numTicks;
1722         #endif
1723 
1724         if (BenchmarkDialog->TotalMode)
1725           break;
1726 
1727         {
1728           CTotalBenchRes tot_BenchRes = sd.Enc_BenchRes_1;
1729           tot_BenchRes.Update_With_Res(sd.Dec_BenchRes_1);
1730 
1731           sd.NeedPrint_RatingVector = true;
1732           {
1733             CBenchPassResult pair;
1734             // pair.EncInfo = sd.EncInfo; // for debug
1735             pair.Enc = sd.Enc_BenchRes_1;
1736             pair.Dec = sd.Dec_BenchRes_1;
1737             #ifdef PRINT_ITER_TIME
1738             pair.Ticks = numTicks;
1739             #endif
1740             sync.RatingVector.Add(pair);
1741             // pair.Dec_Defined = true;
1742           }
1743         }
1744 
1745         sd.NeedPrint_Dec = true;
1746         sd.NeedPrint_Tot = true;
1747 
1748         if (sync.RatingVector.Size() > kRatingVector_NumBundlesMax)
1749         {
1750           // sd.RatingVector_NumDeleted++;
1751           sd.RatingVector_DeletedIndex = (int)(kRatingVector_NumBundlesMax / 4);
1752           sync.RatingVector.Delete((unsigned)(sd.RatingVector_DeletedIndex));
1753         }
1754 
1755         if (sync.sd.NumPasses_Finished < sync.NumPasses_Limit)
1756           finished = false;
1757         else
1758         {
1759           sync.sd.BenchWasFinished = true;
1760           // BenchmarkDialog->_finishTime = GetTickCount();
1761           // return 0;
1762         }
1763       }
1764 
1765       if (BenchmarkDialog->TotalMode)
1766         break;
1767 
1768       /*
1769       if (newTick - prevTick < 1000)
1770         numSameTick++;
1771       if (numSameTick > 5 || finished)
1772       {
1773         prevTick = newTick;
1774         numSameTick = 0;
1775       */
1776       // for (unsigned i = 0; i < 1; i++)
1777       {
1778         // we suppose that PostMsg messages will be processed in order.
1779         if (!BenchmarkDialog->PostMsg_Finish(k_Msg_WPARM_Iter_Finished))
1780         {
1781           finished = true;
1782           finishHRESULT = E_FAIL;
1783           // throw 1234567;
1784         }
1785       }
1786       if (finished)
1787         break;
1788     }
1789     // return S_OK;
1790   }
1791   catch(CSystemException &e)
1792   {
1793     finishHRESULT = e.ErrorCode;
1794     // BenchmarkDialog->MessageBoxError(HResultToMessage(e.ErrorCode));
1795     // return E_FAIL;
1796   }
1797   catch(...)
1798   {
1799     finishHRESULT = E_FAIL;
1800     // BenchmarkDialog->MessageBoxError(HResultToMessage(E_FAIL));
1801     // return E_FAIL;
1802   }
1803 
1804   if (finishHRESULT != S_OK)
1805   {
1806     NSynchronization::CCriticalSectionLock lock(sync.CS);
1807     sync.BenchFinish_Thread_HRESULT = finishHRESULT;
1808   }
1809   if (!BenchmarkDialog->PostMsg_Finish(k_Msg_WPARM_Thread_Finished))
1810   {
1811     // sync.BenchFinish_Thread_HRESULT = E_FAIL;
1812   }
1813   return 0;
1814 }
1815 
1816 
1817 
1818 static void ParseNumberString(const UString &s, NCOM::CPropVariant &prop)
1819 {
1820   const wchar_t *end;
1821   UInt64 result = ConvertStringToUInt64(s, &end);
1822   if (*end != 0 || s.IsEmpty())
1823     prop = s;
1824   else if (result <= (UInt32)0xFFFFFFFF)
1825     prop = (UInt32)result;
1826   else
1827     prop = result;
1828 }
1829 
1830 
1831 HRESULT Benchmark(
1832     DECL_EXTERNAL_CODECS_LOC_VARS
1833     const CObjectVector<CProperty> &props, UInt32 numIterations, HWND hwndParent)
1834 {
1835   CBenchmarkDialog bd;
1836 
1837   bd.TotalMode = false;
1838   bd.Props = props;
1839   if (numIterations == 0)
1840     numIterations = 1;
1841   bd.Sync.NumPasses_Limit = numIterations;
1842   bd.Sync.DictSize = (UInt64)(Int64)-1;
1843   bd.Sync.NumThreads = (UInt32)(Int32)-1;
1844   bd.Sync.Level = -1;
1845 
1846   COneMethodInfo method;
1847 
1848   UInt32 numCPUs = 1;
1849   #ifndef Z7_ST
1850   numCPUs = NSystem::GetNumberOfProcessors();
1851   #endif
1852   UInt32 numThreads = numCPUs;
1853 
1854   FOR_VECTOR (i, props)
1855   {
1856     const CProperty &prop = props[i];
1857     UString name = prop.Name;
1858     name.MakeLower_Ascii();
1859     if (name.IsEqualTo_Ascii_NoCase("m") && prop.Value == L"*")
1860     {
1861       bd.TotalMode = true;
1862       continue;
1863     }
1864 
1865     NCOM::CPropVariant propVariant;
1866     if (!prop.Value.IsEmpty())
1867       ParseNumberString(prop.Value, propVariant);
1868     if (name.IsPrefixedBy(L"mt"))
1869     {
1870       #ifndef Z7_ST
1871       RINOK(ParseMtProp(name.Ptr(2), propVariant, numCPUs, numThreads))
1872       if (numThreads != numCPUs)
1873         bd.Sync.NumThreads = numThreads;
1874       #endif
1875       continue;
1876     }
1877     /*
1878     if (name.IsEqualTo("time"))
1879     {
1880       // UInt32 testTime = 4;
1881       // RINOK(ParsePropToUInt32(L"", propVariant, testTime));
1882       continue;
1883     }
1884     RINOK(method.ParseMethodFromPROPVARIANT(name, propVariant));
1885     */
1886     // here we need to parse DictSize property, and ignore unknown properties
1887     method.ParseMethodFromPROPVARIANT(name, propVariant);
1888   }
1889 
1890   if (bd.TotalMode)
1891   {
1892     // bd.Bench2Text.Empty();
1893     bd.Bench2Text = "7-Zip " MY_VERSION_CPU;
1894     // bd.Bench2Text.Add_Char((char)0xD);
1895     bd.Bench2Text.Add_LF();
1896   }
1897 
1898   {
1899     UInt64 dict;
1900     if (method.Get_DicSize(dict))
1901       bd.Sync.DictSize = dict;
1902   }
1903   bd.Sync.Level = (int)method.GetLevel();
1904 
1905   // Dummy(1000 * 1000 * 1);
1906 
1907   {
1908     CThreadBenchmark &benchmarker = bd._threadBenchmark;
1909     #ifdef Z7_EXTERNAL_CODECS
1910     benchmarker._externalCodecs = _externalCodecs;
1911     #endif
1912     benchmarker.BenchmarkDialog = &bd;
1913   }
1914 
1915   bd.Create(hwndParent);
1916 
1917   return S_OK;
1918 }
1919 
1920 
1921 CBenchmarkDialog::~CBenchmarkDialog()
1922 {
1923   if (_thread.IsCreated())
1924   {
1925     /* the following code will be not executed in normal code flow.
1926        it can be called, if there is some internal failure in dialog code. */
1927     Attach(NULL);
1928     MessageBoxError(L"The flaw in benchmark thread code");
1929     Sync.SendExit();
1930     _thread.Wait_Close();
1931   }
1932 }
1933