Audacity 3.2.0
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
19#include "Benchmark.h"
20
21#include <wx/app.h>
22#include <wx/log.h>
23#include <wx/textctrl.h>
24#include <wx/checkbox.h>
25#include <wx/choice.h>
26#include <wx/stattext.h>
27#include <wx/stopwatch.h>
28#include <wx/valgen.h>
29#include <wx/valtext.h>
30
31#include "SampleBlock.h"
32#include "ShuttleGui.h"
33#include "Project.h"
34#include "WaveClip.h"
35#include "WaveTrack.h"
36#include "Sequence.h"
37#include "Prefs.h"
38#include "ProjectRate.h"
39
40#include "FileNames.h"
41#include "SelectFile.h"
42#include "AudacityMessageBox.h"
43#include "wxPanelWrapper.h"
44
45// Change these to the desired format...should probably make the
46// choice available in the dialog
47#define SampleType short
48#define SampleFormat int16Sample
49
50class BenchmarkDialog final : public wxDialogWrapper
51{
52public:
53 // constructors and destructors
54 BenchmarkDialog( wxWindow *parent, AudacityProject &project );
55
57
58private:
59 // WDR: handler declarations
60 void OnRun( wxCommandEvent &event );
61 void OnSave( wxCommandEvent &event );
62 void OnClear( wxCommandEvent &event );
63 void OnClose( wxCommandEvent &event );
64
65 void Printf(const TranslatableString &str);
66 void HoldPrint(bool hold);
67 void FlushPrint();
68
71
73 wxString mToPrint;
74
75 wxString mBlockSizeStr;
76 wxString mDataSizeStr;
77 wxString mNumEditsStr;
78 wxString mRandSeedStr;
79
82
83 wxTextCtrl *mText;
84
85private:
86 DECLARE_EVENT_TABLE()
87};
88
89void RunBenchmark( wxWindow *parent, AudacityProject &project )
90{
91 /*
92 int action = AudacityMessageBox(
93XO("This will close all project windows (without saving)\nand open the Audacity Benchmark dialog.\n\nAre you sure you want to do this?"),
94 XO("Benchmark"),
95 wxYES_NO | wxICON_EXCLAMATION,
96 NULL);
97
98 if (action != wxYES)
99 return;
100
101 for ( auto pProject : AllProjects{} )
102 GetProjectFrame( *pProject ).Close();
103 */
104
105 BenchmarkDialog dlog{ parent, project };
106
107 dlog.CentreOnParent();
108
109 dlog.ShowModal();
110}
111
112//
113// BenchmarkDialog
114//
115
116enum {
117 RunID = 1000,
126
127BEGIN_EVENT_TABLE(BenchmarkDialog, wxDialogWrapper)
133
135 wxWindow *parent, AudacityProject &project)
136 :
137 /* i18n-hint: Benchmark means a software speed test */
138 wxDialogWrapper( parent, 0, XO("Benchmark"),
139 wxDefaultPosition, wxDefaultSize,
140 wxDEFAULT_DIALOG_STYLE |
141 wxRESIZE_BORDER)
142 , mProject(project)
143 , mRate{ ProjectRate::Get(project) }
144{
145 SetName();
146
147 mBlockSizeStr = wxT("64");
148 mNumEditsStr = wxT("100");
149 mDataSizeStr = wxT("32");
150 mRandSeedStr = wxT("234657");
151
152 mBlockDetail = false;
153 mEditDetail = false;
154
155 HoldPrint(false);
156
157 MakeBenchmarkDialog();
158}
159
160// WDR: handler implementations for BenchmarkDialog
161
162void BenchmarkDialog::OnClose(wxCommandEvent & WXUNUSED(event))
163{
164 EndModal(0);
165}
166
168{
169 ShuttleGui S(this, eIsCreating);
170
171 // Strings don't need to be translated because this class doesn't
172 // ever get used in a stable release.
173
174 S.StartVerticalLay(true);
175 {
176 S.SetBorder(8);
177 S.StartMultiColumn(4);
178 {
179 //
180 S.Id(BlockSizeID)
181 .Validator<wxTextValidator>(wxFILTER_NUMERIC, &mBlockSizeStr)
182 .AddTextBox(XXO("Disk Block Size (KB):"),
183 wxT(""),
184 12);
185
186 //
187 S.Id(NumEditsID)
188 .Validator<wxTextValidator>(wxFILTER_NUMERIC, &mNumEditsStr)
189 .AddTextBox(XXO("Number of Edits:"),
190 wxT(""),
191 12);
192
193 //
194 S.Id(DataSizeID)
195 .Validator<wxTextValidator>(wxFILTER_NUMERIC, &mDataSizeStr)
196 .AddTextBox(XXO("Test Data Size (MB):"),
197 wxT(""),
198 12);
199
201 S.Id(RandSeedID)
202 .Validator<wxTextValidator>(wxFILTER_NUMERIC, &mRandSeedStr)
203 /* i18n-hint: A "seed" is a number that initializes a
204 pseudorandom number generating algorithm */
205 .AddTextBox(XXO("Random Seed:"),
206 wxT(""),
207 12);
208
209 }
210 S.EndMultiColumn();
211
212 //
213 S.Validator<wxGenericValidator>(&mBlockDetail)
214 .AddCheckBox(XXO("Show detailed info about each block file"),
215 false);
216
217 //
218 S.Validator<wxGenericValidator>(&mEditDetail)
219 .AddCheckBox(XXO("Show detailed info about each editing operation"),
220 false);
221
222 //
223 mText = S.Id(StaticTextID)
224 /* i18n-hint noun */
225 .Name(XO("Output"))
226 .Style( wxTE_MULTILINE | wxTE_READONLY | wxTE_RICH )
227 .MinSize( { 500, 200 } )
228 .AddTextWindow(wxT(""));
229
230 //
231 S.SetBorder(10);
232 S.StartHorizontalLay(wxALIGN_LEFT | wxEXPAND, false);
233 {
234 S.StartHorizontalLay(wxALIGN_LEFT, false);
235 {
236 S.Id(RunID).AddButton(XXO("Run"), wxALIGN_CENTRE, true);
237 S.Id(BSaveID).AddButton(XXO("Save"));
238 /* i18n-hint verb; to empty or erase */
239 S.Id(ClearID).AddButton(XXO("Clear"));
240 }
241 S.EndHorizontalLay();
242
243 S.StartHorizontalLay(wxALIGN_CENTER, true);
244 {
245 // Spacer
246 }
247 S.EndHorizontalLay();
248
249 S.StartHorizontalLay(wxALIGN_NOT | wxALIGN_LEFT, false);
250 {
251 /* i18n-hint verb */
252 S.Id(wxID_CANCEL).AddButton(XXO("Close"));
253 }
254 S.EndHorizontalLay();
255 }
256 S.EndHorizontalLay();
257 }
258 S.EndVerticalLay();
259
260 Fit();
261 SetSizeHints(GetSize());
262}
263
264void BenchmarkDialog::OnSave( wxCommandEvent & WXUNUSED(event))
265{
266/* i18n-hint: Benchmark means a software speed test;
267 leave untranslated file extension .txt */
268 auto fName = XO("benchmark.txt").Translation();
269
270 fName = SelectFile(FileNames::Operation::Export,
271 XO("Export Benchmark Data as:"),
272 wxEmptyString,
273 fName,
274 wxT("txt"),
276 wxFD_SAVE | wxRESIZE_BORDER,
277 this);
278
279 if (fName.empty())
280 return;
281
282 mText->SaveFile(fName);
283}
284
285void BenchmarkDialog::OnClear(wxCommandEvent & WXUNUSED(event))
286{
287 mText->Clear();
288}
289
291{
292 auto s = str.Translation();
293 mToPrint += s;
294 if (!mHoldPrint)
295 FlushPrint();
296}
297
299{
300 mHoldPrint = hold;
301
302 if (!mHoldPrint)
303 FlushPrint();
304}
305
307{
308 while(mToPrint.length() > 100) {
309 mText->AppendText(mToPrint.Left(100));
310 mToPrint = mToPrint.Right(mToPrint.length() - 100);
311 }
312 if (mToPrint.length() > 0)
313 mText->AppendText(mToPrint);
314 mToPrint = wxT("");
315}
316
317void BenchmarkDialog::OnRun( wxCommandEvent & WXUNUSED(event))
318{
319 TransferDataFromWindow();
320
321 if (!Validate())
322 return;
323
324 // This code will become part of libaudacity,
325 // and this class will be phased out.
326 long blockSize, numEdits, dataSize, randSeed;
327
328 mBlockSizeStr.ToLong(&blockSize);
329 mNumEditsStr.ToLong(&numEdits);
330 mDataSizeStr.ToLong(&dataSize);
331 mRandSeedStr.ToLong(&randSeed);
332
333 if (blockSize < 1 || blockSize > 1024) {
335 XO("Block size should be in the range 1 - 1024 KB.") );
336 return;
337 }
338
339 if (numEdits < 1 || numEdits > 10000) {
341 XO("Number of edits should be in the range 1 - 10000.") );
342 return;
343 }
344
345 if (dataSize < 1 || dataSize > 2000) {
347 XO("Test data size should be in the range 1 - 2000 MB.") );
348 return;
349 }
350
352 EditClipsCanMove.Write( false );
353
354 // Remember the old blocksize, so that we can restore it later.
355 auto oldBlockSize = Sequence::GetMaxDiskBlockSize();
356 Sequence::SetMaxDiskBlockSize(blockSize * 1024);
357
358 const auto cleanup = finally( [&] {
359 Sequence::SetMaxDiskBlockSize(oldBlockSize);
360 } );
361
362 wxBusyCursor busy;
363
364 HoldPrint(true);
365
366 const auto t =
369 .Create(SampleFormat, mRate.GetRate());
370 const auto tmp0 = TrackList::Temporary(nullptr, t, nullptr);
371 assert(t->IsLeader()); // because it's new and not grouped
372
373 t->SetRate(1);
374
375 srand(randSeed);
376
377 uint64_t nChunks, chunkSize;
378 //chunkSize = 7500ull + (rand() % 1000ull);
379 chunkSize = 200ull + (rand() % 100ull);
380 nChunks = (dataSize * 1048576ull) / (chunkSize*sizeof(SampleType));
381 while (nChunks < 20 || chunkSize > (blockSize*1024)/4)
382 {
383 chunkSize = std::max( uint64_t(1), (chunkSize / 2) + (rand() % 100) );
384 nChunks = (dataSize * 1048576ull) / (chunkSize*sizeof(SampleType));
385 }
386
387 // The chunks are the pieces we move around in the test.
388 // They are (and are supposed to be) a different size to
389 // the blocks that make the sample blocks. That way we get to
390 // do some testing of when edit chunks cross sample block boundaries.
391 Printf( XO("Using %lld chunks of %lld samples each, for a total of %.1f MB.\n")
392 .Format( nChunks, chunkSize, nChunks*chunkSize*sizeof(SampleType)/1048576.0 ) );
393
394 int trials = numEdits;
395
396 using Samples = ArrayOf<SampleType>;
397 Samples small1{nChunks};
398 Samples block{chunkSize};
399
400 Printf( XO("Preparing...\n") );
401
402 wxTheApp->Yield();
403 FlushPrint();
404
405 int v;
406 int bad;
407 int z;
408 long elapsed;
409 wxString tempStr;
410 wxStopWatch timer;
411
412 for (uint64_t i = 0; i < nChunks; i++) {
413 v = SampleType(rand());
414 small1[i] = v;
415 for (uint64_t b = 0; b < chunkSize; b++)
416 block[b] = v;
417
418 t->Append((samplePtr)block.get(), SampleFormat, chunkSize);
419 }
420 t->Flush();
421
422 // This forces the WaveTrack to flush all of the appends (which is
423 // only necessary if you want to access the Sequence class directly,
424 // as we're about to do).
425 t->GetEndTime();
426
427 if (t->GetClipByIndex(0)->GetVisibleSampleCount() != nChunks * chunkSize) {
428 Printf( XO("Expected len %lld, track len %lld.\n")
429 .Format(
430 nChunks * chunkSize,
431 t->GetClipByIndex(0)->GetVisibleSampleCount()
432 .as_long_long() ) );
433 goto fail;
434 }
435
436 Printf( XO("Performing %d edits...\n").Format( trials ) );
437 wxTheApp->Yield();
438 FlushPrint();
439
440 timer.Start();
441 for (z = 0; z < trials; z++) {
442 // First chunk to cut
443 // 0 <= x0 < nChunks
444 const uint64_t x0 = rand() % nChunks;
445
446 // Number of chunks to cut
447 // 1 <= xlen <= nChunks - x0
448 const uint64_t xlen = 1 + (rand() % (nChunks - x0));
449 if (mEditDetail)
450 Printf( XO("Cut: %lld - %lld \n")
451 .Format( x0 * chunkSize, (x0 + xlen) * chunkSize) );
452
453 TrackListHolder tmp;
454 try {
455 tmp =
456 t->Cut(double (x0 * chunkSize), double ((x0 + xlen) * chunkSize));
457 }
458 catch (const AudacityException&) {
459 Printf( XO("Trial %d\n").Format( z ) );
460 Printf( XO("Cut (%lld, %lld) failed.\n")
461 .Format( (x0 * chunkSize), (x0 + xlen) * chunkSize) );
462 Printf( XO("Expected len %lld, track len %lld.\n")
463 .Format(
464 nChunks * chunkSize,
465 t->GetClipByIndex(0)->GetVisibleSampleCount()
466 .as_long_long() ) );
467 goto fail;
468 }
469
470 // Position to paste
471 // 0 <= y0 <= nChunks - xlen
472 const uint64_t y0 = rand() % (nChunks - xlen + 1);
473
474 if (mEditDetail)
475 Printf( XO("Paste: %lld\n").Format( y0 * chunkSize ) );
476
477 try {
478 t->Paste((double)(y0 * chunkSize), *tmp);
479 }
480 catch (const AudacityException&) {
481 Printf( XO("Trial %d\nFailed on Paste.\n").Format( z ) );
482 goto fail;
483 }
484
485 if (t->GetClipByIndex(0)->GetVisibleSampleCount() != nChunks * chunkSize) {
486 Printf( XO("Trial %d\n").Format( z ) );
487 Printf( XO("Expected len %lld, track len %lld.\n")
488 .Format(
489 nChunks * chunkSize,
490 t->GetClipByIndex(0)->GetVisibleSampleCount()
491 .as_long_long() ) );
492 goto fail;
493 }
494
495 // Permute small1 correspondingly to the cut and paste
496 auto first = &small1[0];
497 if (x0 + xlen < nChunks)
498 std::rotate( first + x0, first + x0 + xlen, first + nChunks );
499 std::rotate( first + y0, first + nChunks - xlen, first + nChunks );
500 }
501
502 elapsed = timer.Time();
503
504 if (mBlockDetail) {
505 auto seq = t->GetClipByIndex(0)->GetSequence(0);
506 seq->DebugPrintf(seq->GetBlockArray(), seq->GetNumSamples(), &tempStr);
507 mToPrint += tempStr;
508 }
509 Printf( XO("Time to perform %d edits: %ld ms\n").Format( trials, elapsed ) );
510 FlushPrint();
511 wxTheApp->Yield();
512
513
514#if 0
515 Printf( XO("Checking file pointer leaks:\n") );
516 Printf( XO("Track # blocks: %ld\n").Format( t->GetBlockArray()->size() ) );
517 Printf( XO("Disk # blocks: \n") );
518 system("ls .audacity_temp/* | wc --lines");
519#endif
520
521 Printf( XO("Doing correctness check...\n") );
522 FlushPrint();
523 wxTheApp->Yield();
524
525 bad = 0;
526 timer.Start();
527 for (uint64_t i = 0; i < nChunks; i++) {
528 v = small1[i];
529 auto pBlock = reinterpret_cast<samplePtr>(block.get());
530 constexpr auto backwards = false;
531 t->Get(0, 1, &pBlock, SampleFormat, i * chunkSize, chunkSize, backwards);
532 for (uint64_t b = 0; b < chunkSize; b++)
533 if (block[b] != v) {
534 bad++;
535 if (bad < 10)
536 Printf( XO("Bad: chunk %lld sample %lld\n").Format( i, b ) );
537 b = chunkSize;
538 }
539 }
540 if (bad == 0)
541 Printf( XO("Passed correctness check!\n") );
542 else
543 Printf( XO("Errors in %d/%lld chunks\n").Format( bad, nChunks ) );
544
545 elapsed = timer.Time();
546
547 Printf( XO("Time to check all data: %ld ms\n").Format( elapsed ) );
548 Printf( XO("Reading data again...\n") );
549
550 wxTheApp->Yield();
551 FlushPrint();
552
553 timer.Start();
554
555 for (uint64_t i = 0; i < nChunks; i++) {
556 v = small1[i];
557 auto pBlock = reinterpret_cast<samplePtr>(block.get());
558 constexpr auto backwards = false;
559 t->Get(0, 1, &pBlock, SampleFormat, i * chunkSize, chunkSize, backwards);
560 for (uint64_t b = 0; b < chunkSize; b++)
561 if (block[b] != v)
562 bad++;
563 }
564
565 elapsed = timer.Time();
566
567 Printf( XO("Time to check all data (2): %ld ms\n").Format( elapsed ) );
568
569 Printf( XO("At 44100 Hz, %d bytes per sample, the estimated number of\n simultaneous tracks that could be played at once: %.1f\n" )
570 .Format( SAMPLE_SIZE(SampleFormat), (nChunks*chunkSize/44100.0)/(elapsed/1000.0) ) );
571
572 goto success;
573
574 fail:
575 Printf( XO("TEST FAILED!!!\n") );
576
577 success:
578
579 Printf( XO("Benchmark completed successfully.\n") );
580 HoldPrint(false);
581}
wxT("CloseDown"))
int AudacityMessageBox(const TranslatableString &message, const TranslatableString &caption, long style, wxWindow *parent, int x, int y)
static AudioUnitEffectsModule::Factory::SubstituteInUnique< AudioUnitEffect > scope
END_EVENT_TABLE()
void RunBenchmark(wxWindow *parent, AudacityProject &project)
Definition: Benchmark.cpp:89
#define SampleType
Definition: Benchmark.cpp:47
#define SampleFormat
Definition: Benchmark.cpp:48
@ StaticTextID
Definition: Benchmark.cpp:120
@ ClearID
Definition: Benchmark.cpp:119
@ NumEditsID
Definition: Benchmark.cpp:123
@ BlockSizeID
Definition: Benchmark.cpp:121
@ DataSizeID
Definition: Benchmark.cpp:122
@ BSaveID
Definition: Benchmark.cpp:118
@ RunID
Definition: Benchmark.cpp:117
@ RandSeedID
Definition: Benchmark.cpp:124
#define str(a)
EVT_BUTTON(wxID_NO, DependencyDialog::OnNo) EVT_BUTTON(wxID_YES
XO("Cut/Copy/Paste")
XXO("&Cut/Copy/Paste Toolbar")
an object holding per-project preferred sample rate
char * samplePtr
Definition: SampleFormat.h:55
#define SAMPLE_SIZE(SampleFormat)
Definition: SampleFormat.h:50
FilePath SelectFile(FileNames::Operation op, const TranslatableString &message, const FilePath &default_path, const FilePath &default_filename, const FileExtension &default_extension, const FileTypes &fileTypes, int flags, wxWindow *parent)
Definition: SelectFile.cpp:17
@ eIsCreating
Definition: ShuttleGui.h:37
const auto project
#define S(N)
Definition: ToChars.cpp:64
std::shared_ptr< TrackList > TrackListHolder
Definition: Track.h:43
BoolSetting EditClipsCanMove
Definition: WaveTrack.cpp:4150
This simplifies arrays of arrays, each array separately allocated with NEW[] But it might be better t...
Definition: MemoryX.h:27
Base class for exceptions specially processed by the application.
The top-level handle to an Audacity project. It serves as a source of events that other objects can b...
Definition: Project.h:90
BenchmarkDialog is used for measuring performance and accuracy of sample block storage.
Definition: Benchmark.cpp:51
void OnClose(wxCommandEvent &event)
Definition: Benchmark.cpp:162
void OnSave(wxCommandEvent &event)
Definition: Benchmark.cpp:264
void HoldPrint(bool hold)
Definition: Benchmark.cpp:298
wxString mNumEditsStr
Definition: Benchmark.cpp:77
wxString mRandSeedStr
Definition: Benchmark.cpp:78
wxString mToPrint
Definition: Benchmark.cpp:73
BenchmarkDialog(wxWindow *parent, AudacityProject &project)
Definition: Benchmark.cpp:134
wxTextCtrl * mText
Definition: Benchmark.cpp:83
void OnClear(wxCommandEvent &event)
Definition: Benchmark.cpp:285
AudacityProject & mProject
Definition: Benchmark.cpp:69
void Printf(const TranslatableString &str)
Definition: Benchmark.cpp:290
wxString mBlockSizeStr
Definition: Benchmark.cpp:75
wxString mDataSizeStr
Definition: Benchmark.cpp:76
void MakeBenchmarkDialog()
Definition: Benchmark.cpp:167
void OnRun(wxCommandEvent &event)
Definition: Benchmark.cpp:317
const ProjectRate & mRate
Definition: Benchmark.cpp:70
FILES_API const FileType TextFiles
Definition: FileNames.h:73
Abstract base class used in importing a file.
Holds project sample rate.
Definition: ProjectRate.h:24
static ProjectRate & Get(AudacityProject &project)
Definition: ProjectRate.cpp:28
double GetRate() const
Definition: ProjectRate.cpp:53
static SampleBlockFactoryPtr New(AudacityProject &project)
Definition: SampleBlock.cpp:16
static void SetMaxDiskBlockSize(size_t bytes)
Definition: Sequence.cpp:1865
static size_t GetMaxDiskBlockSize()
Definition: Sequence.cpp:1870
bool Write(const T &value)
Write value to config and return true if successful.
Definition: Prefs.h:257
Makes temporary changes to preferences, then rolls them back at destruction.
Definition: Prefs.h:120
Derived from ShuttleGuiBase, an Audacity specific class for shuttling data to and from GUI.
Definition: ShuttleGui.h:630
static TrackListHolder Temporary(AudacityProject *pProject, const Track::Holder &left={}, const Track::Holder &right={})
Definition: Track.cpp:1435
Holds a msgid for the translation catalog; may also bind format arguments.
Used to create or clone a WaveTrack, with appropriate context from the project that will own the trac...
Definition: WaveTrack.h:1166