Audacity  2.2.2
Benchmark.cpp
Go to the documentation of this file.
1 /**********************************************************************
2 
3  Audacity: A Digital Audio Editor
4 
5  Benchmark.cpp
6 
7  Dominic Mazzoni
8 
9 *******************************************************************//*******************************************************************/
16 
17 
18 #include "Audacity.h"
19 #include "Benchmark.h"
20 
21 #include <wx/app.h>
22 #include <wx/log.h>
23 #include <wx/textctrl.h>
24 #include <wx/button.h>
25 #include <wx/checkbox.h>
26 #include <wx/choice.h>
27 #include <wx/dialog.h>
28 #include <wx/filedlg.h>
29 #include <wx/sizer.h>
30 #include <wx/stattext.h>
31 #include <wx/timer.h>
32 #include <wx/utils.h>
33 #include <wx/valgen.h>
34 #include <wx/valtext.h>
35 #include <wx/intl.h>
36 
37 #include "ShuttleGui.h"
38 #include "Project.h"
39 #include "WaveTrack.h"
40 #include "Sequence.h"
41 #include "Prefs.h"
42 
43 #include "FileNames.h"
44 #include "widgets/ErrorDialog.h"
45 
46 class BenchmarkDialog final : public wxDialogWrapper
47 {
48 public:
49  // constructors and destructors
50  BenchmarkDialog( wxWindow *parent );
51 
52  void MakeBenchmarkDialog();
53 
54 private:
55  // WDR: handler declarations
56  void OnRun( wxCommandEvent &event );
57  void OnSave( wxCommandEvent &event );
58  void OnClear( wxCommandEvent &event );
59  void OnClose( wxCommandEvent &event );
60 
61  void Printf(const wxChar *format, ...);
62  void HoldPrint(bool hold);
63  void FlushPrint();
64 
65  bool mHoldPrint;
66  wxString mToPrint;
67 
68  wxString mBlockSizeStr;
69  wxString mDataSizeStr;
70  wxString mNumEditsStr;
71  wxString mRandSeedStr;
72 
75 
76  wxTextCtrl *mText;
77 
78 private:
79  DECLARE_EVENT_TABLE()
80 };
81 
82 void RunBenchmark(wxWindow *parent)
83 {
84  /*
85  int action = AudacityMessageBox(
86 _("This will close all project windows (without saving)\nand open the Audacity Benchmark dialog.\n\nAre you sure you want to do this?"),
87  _("Benchmark"),
88  wxYES_NO | wxICON_EXCLAMATION,
89  NULL);
90 
91  if (action != wxYES)
92  return;
93 
94  CloseAllProjects();
95  */
96 
97  BenchmarkDialog dlog(parent);
98 
99  dlog.CentreOnParent();
100 
101  dlog.ShowModal();
102 }
103 
104 //
105 // BenchmarkDialog
106 //
107 
108 enum {
109  RunID = 1000,
117 };
118 
119 BEGIN_EVENT_TABLE(BenchmarkDialog, wxDialogWrapper)
120  EVT_BUTTON( RunID, BenchmarkDialog::OnRun )
122  EVT_BUTTON( ClearID, BenchmarkDialog::OnClear )
123  EVT_BUTTON( wxID_CANCEL, BenchmarkDialog::OnClose )
125 
126 BenchmarkDialog::BenchmarkDialog(wxWindow *parent):
127 /* i18n-hint: Benchmark means a software speed test */
128  wxDialogWrapper( parent, 0, _("Benchmark"),
129  wxDefaultPosition, wxDefaultSize,
130  wxDEFAULT_DIALOG_STYLE |
131  wxRESIZE_BORDER)
132 {
133  SetName(GetTitle());
134 
135  mBlockSizeStr = wxT("64");
136  mNumEditsStr = wxT("100");
137  mDataSizeStr = wxT("32");
138  mRandSeedStr = wxT("234657");
139 
140  mBlockDetail = false;
141  mEditDetail = false;
142 
143  HoldPrint(false);
144 
145  MakeBenchmarkDialog();
146 }
147 
148 // WDR: handler implementations for BenchmarkDialog
149 
150 void BenchmarkDialog::OnClose(wxCommandEvent & WXUNUSED(event))
151 {
152  EndModal(0);
153 }
154 
156 {
157  ShuttleGui S(this, eIsCreating);
158  wxControl *item;
159 
160  // Strings don't need to be translated because this class doesn't
161  // ever get used in a stable release.
162 
163  S.StartVerticalLay(true);
164  {
165  S.SetBorder(8);
166  S.StartMultiColumn(4);
167  {
168  //
169  item = S.Id(BlockSizeID).AddTextBox(_("Disk Block Size (KB):"),
170  wxT(""),
171  12);
172  item->SetValidator(wxTextValidator(wxFILTER_NUMERIC,
173  &mBlockSizeStr));
174 
175  //
176  item = S.Id(NumEditsID).AddTextBox(_("Number of Edits:"),
177  wxT(""),
178  12);
179  item->SetValidator(wxTextValidator(wxFILTER_NUMERIC,
180  &mNumEditsStr));
181 
182  //
183  item = S.Id(DataSizeID).AddTextBox(_("Test Data Size (MB):"),
184  wxT(""),
185  12);
186  item->SetValidator(wxTextValidator(wxFILTER_NUMERIC,
187  &mDataSizeStr));
188 
190  /* i18n-hint: A "seed" is a number that initializes a
191  pseudorandom number generating algorithm */
192  item = S.Id(RandSeedID).AddTextBox(_("Random Seed:"),
193  wxT(""),
194  12);
195  item->SetValidator(wxTextValidator(wxFILTER_NUMERIC,
196  &mRandSeedStr));
197 
198  }
199  S.EndMultiColumn();
200 
201  //
202  item = S.AddCheckBox(_("Show detailed info about each block file"),
203  wxT("false"));
204  item->SetValidator(wxGenericValidator(&mBlockDetail));
205 
206  //
207  item = S.AddCheckBox(_("Show detailed info about each editing operation"),
208  wxT("false"));
209  item->SetValidator(wxGenericValidator(&mEditDetail));
210 
211  //
212  mText = S.Id(StaticTextID).AddTextWindow(wxT(""));
213  /* i18n-hint noun */
214  mText->SetName(_("Output"));
215  mText->SetSizeHints(wxSize(500,200));
216 
217  //
218  S.SetBorder(10);
219  S.StartHorizontalLay(wxALIGN_LEFT | wxEXPAND, false);
220  {
221  S.StartHorizontalLay(wxALIGN_LEFT, false);
222  {
223  S.Id(RunID).AddButton(_("Run"))->SetDefault();
224  S.Id(BSaveID).AddButton(_("Save"));
225  /* i18n-hint verb; to empty or erase */
226  S.Id(ClearID).AddButton(_("Clear"));
227  }
228  S.EndHorizontalLay();
229 
230  S.StartHorizontalLay(wxALIGN_CENTER, true);
231  {
232  // Spacer
233  }
234  S.EndHorizontalLay();
235 
236  S.StartHorizontalLay(wxALIGN_NOT | wxALIGN_LEFT, false);
237  {
238  /* i18n-hint verb */
239  S.Id(wxID_CANCEL).AddButton(_("Close"));
240  }
241  S.EndHorizontalLay();
242  }
243  S.EndHorizontalLay();
244  }
245  S.EndVerticalLay();
246 
247  Fit();
248  SetSizeHints(GetSize());
249 }
250 
251 void BenchmarkDialog::OnSave( wxCommandEvent & WXUNUSED(event))
252 {
253 /* i18n-hint: Benchmark means a software speed test;
254  leave untranslated file extension .txt */
255  wxString fName = _("benchmark.txt");
256 
258  _("Export Benchmark Data as:"),
259  wxEmptyString,
260  fName,
261  wxT("txt"),
262  wxT("*.txt"),
263  wxFD_SAVE | wxRESIZE_BORDER,
264  this);
265 
266  if (fName == wxT(""))
267  return;
268 
269  mText->SaveFile(fName);
270 }
271 
272 void BenchmarkDialog::OnClear(wxCommandEvent & WXUNUSED(event))
273 {
274  mText->Clear();
275 }
276 
277 void BenchmarkDialog::Printf(const wxChar *format, ...)
278 {
279  va_list argptr;
280  va_start(argptr, format);
281 
282  wxString s = wxString::FormatV(format, argptr);
283  mToPrint += s;
284  if (!mHoldPrint)
285  FlushPrint();
286 
287  va_end(argptr);
288 }
289 
291 {
292  mHoldPrint = hold;
293 
294  if (!mHoldPrint)
295  FlushPrint();
296 }
297 
299 {
300  while(mToPrint.Length() > 100) {
301  mText->AppendText(mToPrint.Left(100));
302  mToPrint = mToPrint.Right(mToPrint.Length() - 100);
303  }
304  if (mToPrint.Length() > 0)
305  mText->AppendText(mToPrint);
306  mToPrint = wxT("");
307 }
308 
309 void BenchmarkDialog::OnRun( wxCommandEvent & WXUNUSED(event))
310 {
311  TransferDataFromWindow();
312 
313  if (!Validate())
314  return;
315 
316  // This code will become part of libaudacity,
317  // and this class will be phased out.
318  long blockSize, numEdits, dataSize, randSeed;
319 
320  mBlockSizeStr.ToLong(&blockSize);
321  mNumEditsStr.ToLong(&numEdits);
322  mDataSizeStr.ToLong(&dataSize);
323  mRandSeedStr.ToLong(&randSeed);
324 
325  if (blockSize < 1 || blockSize > 1024) {
326  AudacityMessageBox(_("Block size should be in the range 1 - 1024 KB."));
327  return;
328  }
329 
330  if (numEdits < 1 || numEdits > 10000) {
331  AudacityMessageBox(_("Number of edits should be in the range 1 - 10000."));
332  return;
333  }
334 
335  if (dataSize < 1 || dataSize > 2000) {
336  AudacityMessageBox(_("Test data size should be in the range 1 - 2000 MB."));
337  return;
338  }
339 
340  bool editClipCanMove = true;
341  gPrefs->Read(wxT("/GUI/EditClipCanMove"), &editClipCanMove);
342  gPrefs->Write(wxT("/GUI/EditClipCanMove"), false);
343  gPrefs->Flush();
344 
345  // Rememebr the old blocksize, so that we can restore it later.
346  auto oldBlockSize = Sequence::GetMaxDiskBlockSize();
347  Sequence::SetMaxDiskBlockSize(blockSize * 1024);
348 
349  const auto cleanup = finally( [&] {
350  Sequence::SetMaxDiskBlockSize(oldBlockSize);
351  gPrefs->Write(wxT("/GUI/EditClipCanMove"), editClipCanMove);
352  gPrefs->Flush();
353  } );
354 
355  wxBusyCursor busy;
356 
357  HoldPrint(true);
358 
359  ZoomInfo zoomInfo(0.0, ZoomInfo::GetDefaultZoom());
360  auto dd = std::make_shared<DirManager>();
361  const auto t = TrackFactory{ dd, &zoomInfo }.NewWaveTrack(int16Sample);
362 
363  t->SetRate(1);
364 
365  srand(randSeed);
366 
367  size_t nChunks, chunkSize;
368  //chunkSize = 7500 + (rand() % 1000);
369  chunkSize = 200 + (rand() % 100);
370  nChunks = (dataSize * 1048576) / (chunkSize*sizeof(short));
371  while(nChunks < 20 || chunkSize > ((unsigned long)(blockSize)*1024)/4) {
372  chunkSize = std::max( size_t(1), (chunkSize / 2) + (rand() % 100) );
373  nChunks = (dataSize * 1048576) / (chunkSize*sizeof(short));
374  }
375 
376  // The chunks are the pieces we move around in the test.
377  // They are (and are supposed to be) a different size to
378  // the blocks that make the blockfiles. That way we get to
379  // do some testing of when edit chunks cross blockfile boundaries.
380  Printf(_("Using %d chunks of %d samples each, for a total of %.1f MB.\n"),
381  nChunks, chunkSize, nChunks*chunkSize*sizeof(short)/1048576.0);
382 
383  int trials = numEdits;
384 
385  using Shorts = ArrayOf < short > ;
386  Shorts small1{ nChunks };
387  Shorts block{ chunkSize };
388 
389  Printf(_("Preparing...\n"));
390 
391  wxTheApp->Yield();
392  FlushPrint();
393 
394  int v;
395  int bad;
396  int z;
397  long elapsed;
398  wxString tempStr;
399  wxStopWatch timer;
400 
401  for (size_t i = 0; i < nChunks; i++) {
402  v = short(rand());
403  small1[i] = v;
404  for (size_t b = 0; b < chunkSize; b++)
405  block[b] = v;
406 
407  t->Append((samplePtr)block.get(), int16Sample, chunkSize);
408  }
409  t->Flush();
410 
411  // This forces the WaveTrack to flush all of the appends (which is
412  // only necessary if you want to access the Sequence class directly,
413  // as we're about to do).
414  t->GetEndTime();
415 
416  if (t->GetClipByIndex(0)->GetSequence()->GetNumSamples() != nChunks * chunkSize) {
417  Printf(_("Expected len %d, track len %lld.\n"), nChunks * chunkSize,
418  t->GetClipByIndex(0)->GetSequence()->GetNumSamples().as_long_long());
419  goto fail;
420  }
421 
422  Printf(_("Performing %d edits...\n"), trials);
423  wxTheApp->Yield();
424  FlushPrint();
425 
426  timer.Start();
427  for (z = 0; z < trials; z++) {
428  // First chunk to cut
429  // 0 <= x0 < nChunks
430  const size_t x0 = rand() % nChunks;
431 
432  // Number of chunks to cut
433  // 1 <= xlen <= nChunks - x0
434  const size_t xlen = 1 + (rand() % (nChunks - x0));
435  if (mEditDetail)
436  Printf(_("Cut: %d - %d \n"), x0 * chunkSize, (x0 + xlen) * chunkSize);
437 
438  Track::Holder tmp;
439  try {
440  tmp = t->Cut(double (x0 * chunkSize), double ((x0 + xlen) * chunkSize));
441  }
442  catch (const AudacityException&) {
443  Printf(_("Trial %d\n"), z);
444  Printf(_("Cut (%d, %d) failed.\n"), (x0 * chunkSize),
445  (x0 + xlen) * chunkSize);
446  Printf(_("Expected len %d, track len %lld.\n"), nChunks * chunkSize,
447  t->GetClipByIndex(0)->GetSequence()->GetNumSamples().as_long_long());
448  goto fail;
449  }
450 
451  // Position to paste
452  // 0 <= y0 <= nChunks - xlen
453  const size_t y0 = rand() % (nChunks - xlen + 1);
454 
455  if (mEditDetail)
456  Printf(_("Paste: %d\n"), y0 * chunkSize);
457 
458  try {
459  t->Paste((double)(y0 * chunkSize), tmp.get());
460  }
461  catch (const AudacityException&) {
462  Printf(_("Trial %d\nFailed on Paste.\n"), z);
463  goto fail;
464  }
465 
466  if (t->GetClipByIndex(0)->GetSequence()->GetNumSamples() != nChunks * chunkSize) {
467  Printf(_("Trial %d\n"), z);
468  Printf(_("Expected len %d, track len %lld.\n"), nChunks * chunkSize,
469  t->GetClipByIndex(0)->GetSequence()->GetNumSamples().as_long_long());
470  goto fail;
471  }
472 
473  // Permute small1 correspondingly to the cut and paste
474  auto first = &small1[0];
475  if (x0 + xlen < nChunks)
476  std::rotate( first + x0, first + x0 + xlen, first + nChunks );
477  std::rotate( first + y0, first + nChunks - xlen, first + nChunks );
478  }
479 
480  elapsed = timer.Time();
481 
482  if (mBlockDetail) {
483  auto seq = t->GetClipByIndex(0)->GetSequence();
484  seq->DebugPrintf(seq->GetBlockArray(), seq->GetNumSamples(), &tempStr);
485  mToPrint += tempStr;
486  }
487  Printf(_("Time to perform %d edits: %ld ms\n"), trials, elapsed);
488  FlushPrint();
489  wxTheApp->Yield();
490 
491 
492 #if 0
493  Printf(_("Checking file pointer leaks:\n"));
494  Printf(_("Track # blocks: %d\n"), t->GetBlockArray()->Count());
495  Printf(_("Disk # blocks: \n"));
496  system("ls .audacity_temp/* | wc --lines");
497 #endif
498 
499  Printf(_("Doing correctness check...\n"));
500  FlushPrint();
501  wxTheApp->Yield();
502 
503  bad = 0;
504  timer.Start();
505  for (size_t i = 0; i < nChunks; i++) {
506  v = small1[i];
507  t->Get((samplePtr)block.get(), int16Sample, i * chunkSize, chunkSize);
508  for (size_t b = 0; b < chunkSize; b++)
509  if (block[b] != v) {
510  bad++;
511  if (bad < 10)
512  Printf(_("Bad: chunk %d sample %d\n"), i, b);
513  b = chunkSize;
514  }
515  }
516  if (bad == 0)
517  Printf(_("Passed correctness check!\n"));
518  else
519  Printf(_("Errors in %d/%d chunks\n"), bad, nChunks);
520 
521  elapsed = timer.Time();
522 
523  Printf(_("Time to check all data: %ld ms\n"), elapsed);
524  Printf(_("Reading data again...\n"));
525 
526  wxTheApp->Yield();
527  FlushPrint();
528 
529  timer.Start();
530 
531  for (size_t i = 0; i < nChunks; i++) {
532  v = small1[i];
533  t->Get((samplePtr)block.get(), int16Sample, i * chunkSize, chunkSize);
534  for (size_t b = 0; b < chunkSize; b++)
535  if (block[b] != v)
536  bad++;
537  }
538 
539  elapsed = timer.Time();
540 
541  Printf(_("Time to check all data (2): %ld ms\n"), elapsed);
542 
543  Printf(_("At 44100 Hz, 16-bits per sample, the estimated number of\n simultaneous tracks that could be played at once: %.1f\n"),
544  (nChunks*chunkSize/44100.0)/(elapsed/1000.0));
545 
546  goto success;
547 
548  fail:
549  Printf(_("TEST FAILED!!!\n"));
550 
551  success:
552 
553  dd.reset();
554 
555  Printf(_("Benchmark completed successfully.\n"));
556  HoldPrint(false);
557 }
AudacityPrefs * gPrefs
Definition: Prefs.cpp:73
wxString mRandSeedStr
Definition: Benchmark.cpp:71
void OnSave(const CommandContext &context)
Derived from ShuttleGuiBase, an Audacity specific class for shuttling data to and from GUI...
Definition: ShuttleGui.h:409
root of a hierarchy of classes that are thrown and caught by Audacity.
BenchmarkDialog(wxWindow *parent)
Definition: Benchmark.cpp:126
void OnClear(wxCommandEvent &event)
Definition: Benchmark.cpp:272
void RunBenchmark(wxWindow *parent)
Definition: Benchmark.cpp:82
void EndMultiColumn()
void HoldPrint(bool hold)
Definition: Benchmark.cpp:290
int AudacityMessageBox(const wxString &message, const wxString &caption=AudacityMessageBoxCaptionStr(), long style=wxOK|wxCENTRE, wxWindow *parent=NULL, int x=wxDefaultCoord, int y=wxDefaultCoord)
Definition: ErrorDialog.h:92
void MakeBenchmarkDialog()
Definition: Benchmark.cpp:155
Used to create a WaveTrack, or a LabelTrack.. Implementation of the functions of this class are dispe...
Definition: Track.h:862
void EndHorizontalLay()
void EndVerticalLay()
static size_t GetMaxDiskBlockSize()
Definition: Sequence.cpp:2016
wxTextCtrl * AddTextBox(const wxString &Caption, const wxString &Value, const int nChars)
Definition: ShuttleGui.cpp:540
wxCheckBox * AddCheckBox(const wxString &Prompt, const wxString &Selected)
Definition: ShuttleGui.cpp:298
int format
Definition: ExportPCM.cpp:56
static void SetMaxDiskBlockSize(size_t bytes)
Definition: Sequence.cpp:2011
void OnClose(const CommandContext &context)
void StartHorizontalLay(int PositionFlags=wxALIGN_CENTRE, int iProp=1)
void StartMultiColumn(int nCols, int PositionFlags=wxALIGN_LEFT)
void Printf(const wxChar *format,...)
Definition: Benchmark.cpp:277
char * samplePtr
Definition: Types.h:203
ShuttleGui & Id(int id)
wxString mToPrint
Definition: Benchmark.cpp:66
void OnSave(wxCommandEvent &event)
Definition: Benchmark.cpp:251
wxString mNumEditsStr
Definition: Benchmark.cpp:70
wxString mDataSizeStr
Definition: Benchmark.cpp:69
EVT_BUTTON(wxID_NO, DependencyDialog::OnNo) EVT_BUTTON(wxID_YES
static wxString SelectFile(Operation op, const wxString &message, const wxString &default_path, const wxString &default_filename, const wxString &default_extension, const wxString &wildcard, int flags, wxWindow *parent)
Definition: FileNames.cpp:411
_("Move Track &Down")+wxT("\t")+(GetActiveProject() -> GetCommandManager() ->GetKeyFromName(wxT("TrackMoveDown")).Raw()), OnMoveTrack) POPUP_MENU_ITEM(OnMoveTopID, _("Move Track to &Top")+wxT("\t")+(GetActiveProject() ->GetCommandManager() ->GetKeyFromName(wxT("TrackMoveTop")).Raw()), OnMoveTrack) POPUP_MENU_ITEM(OnMoveBottomID, _("Move Track to &Bottom")+wxT("\t")+(GetActiveProject() ->GetCommandManager() ->GetKeyFromName(wxT("TrackMoveBottom")).Raw()), OnMoveTrack)#define SET_TRACK_NAME_PLUGIN_SYMBOLclass SetTrackNameCommand:public AudacityCommand
void OnClose(wxCommandEvent &event)
Definition: Benchmark.cpp:150
void OnRun(wxCommandEvent &event)
Definition: Benchmark.cpp:309
wxTextCtrl * AddTextWindow(const wxString &Value)
Multiline text box that grows.
Definition: ShuttleGui.cpp:608
Memory.h template class for making an array of float, bool, etc.
Definition: MemoryX.h:86
static double GetDefaultZoom()
Definition: ViewInfo.h:85
wxString mBlockSizeStr
Definition: Benchmark.cpp:68
std::unique_ptr< Track > Holder
Definition: Track.h:263
END_EVENT_TABLE()
void SetBorder(int Border)
Definition: ShuttleGui.h:286
wxTextCtrl * mText
Definition: Benchmark.cpp:76
wxButton * AddButton(const wxString &Text, int PositionFlags=wxALIGN_CENTRE)
Definition: ShuttleGui.cpp:341
BenchmarkDialog is used for measuring performance and accuracy of the BlockFile system.
Definition: Benchmark.cpp:46
void StartVerticalLay(int iProp=1)