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"
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
371 t->SetRate(1);
372
373 srand(randSeed);
374
375 uint64_t nChunks, chunkSize;
376 //chunkSize = 7500ull + (rand() % 1000ull);
377 chunkSize = 200ull + (rand() % 100ull);
378 nChunks = (dataSize * 1048576ull) / (chunkSize*sizeof(SampleType));
379 while (nChunks < 20 || chunkSize > (blockSize*1024)/4)
380 {
381 chunkSize = std::max( uint64_t(1), (chunkSize / 2) + (rand() % 100) );
382 nChunks = (dataSize * 1048576ull) / (chunkSize*sizeof(SampleType));
383 }
384
385 // The chunks are the pieces we move around in the test.
386 // They are (and are supposed to be) a different size to
387 // the blocks that make the sample blocks. That way we get to
388 // do some testing of when edit chunks cross sample block boundaries.
389 Printf( XO("Using %lld chunks of %lld samples each, for a total of %.1f MB.\n")
390 .Format( nChunks, chunkSize, nChunks*chunkSize*sizeof(SampleType)/1048576.0 ) );
391
392 int trials = numEdits;
393
394 using Samples = ArrayOf<SampleType>;
395 Samples small1{nChunks};
396 Samples block{chunkSize};
397
398 Printf( XO("Preparing...\n") );
399
400 wxTheApp->Yield();
401 FlushPrint();
402
403 int v;
404 int bad;
405 int z;
406 long elapsed;
407 wxString tempStr;
408 wxStopWatch timer;
409
410 for (uint64_t i = 0; i < nChunks; i++) {
411 v = SampleType(rand());
412 small1[i] = v;
413 for (uint64_t b = 0; b < chunkSize; b++)
414 block[b] = v;
415
416 t->Append((samplePtr)block.get(), SampleFormat, chunkSize);
417 }
418 t->Flush();
419
420 // This forces the WaveTrack to flush all of the appends (which is
421 // only necessary if you want to access the Sequence class directly,
422 // as we're about to do).
423 t->GetEndTime();
424
425 if (t->GetClipByIndex(0)->GetPlaySamplesCount() != nChunks * chunkSize) {
426 Printf( XO("Expected len %lld, track len %lld.\n")
427 .Format(
428 nChunks * chunkSize,
429 t->GetClipByIndex(0)->GetPlaySamplesCount()
430 .as_long_long() ) );
431 goto fail;
432 }
433
434 Printf( XO("Performing %d edits...\n").Format( trials ) );
435 wxTheApp->Yield();
436 FlushPrint();
437
438 timer.Start();
439 for (z = 0; z < trials; z++) {
440 // First chunk to cut
441 // 0 <= x0 < nChunks
442 const uint64_t x0 = rand() % nChunks;
443
444 // Number of chunks to cut
445 // 1 <= xlen <= nChunks - x0
446 const uint64_t xlen = 1 + (rand() % (nChunks - x0));
447 if (mEditDetail)
448 Printf( XO("Cut: %lld - %lld \n")
449 .Format( x0 * chunkSize, (x0 + xlen) * chunkSize) );
450
451 Track::Holder tmp;
452 try {
453 tmp = t->Cut(double (x0 * chunkSize), double ((x0 + xlen) * chunkSize));
454 }
455 catch (const AudacityException&) {
456 Printf( XO("Trial %d\n").Format( z ) );
457 Printf( XO("Cut (%lld, %lld) failed.\n")
458 .Format( (x0 * chunkSize), (x0 + xlen) * chunkSize) );
459 Printf( XO("Expected len %lld, track len %lld.\n")
460 .Format(
461 nChunks * chunkSize,
462 t->GetClipByIndex(0)->GetPlaySamplesCount()
463 .as_long_long() ) );
464 goto fail;
465 }
466
467 // Position to paste
468 // 0 <= y0 <= nChunks - xlen
469 const uint64_t y0 = rand() % (nChunks - xlen + 1);
470
471 if (mEditDetail)
472 Printf( XO("Paste: %lld\n").Format( y0 * chunkSize ) );
473
474 try {
475 t->Paste((double)(y0 * chunkSize), tmp.get());
476 }
477 catch (const AudacityException&) {
478 Printf( XO("Trial %d\nFailed on Paste.\n").Format( z ) );
479 goto fail;
480 }
481
482 if (t->GetClipByIndex(0)->GetPlaySamplesCount() != nChunks * chunkSize) {
483 Printf( XO("Trial %d\n").Format( z ) );
484 Printf( XO("Expected len %lld, track len %lld.\n")
485 .Format(
486 nChunks * chunkSize,
487 t->GetClipByIndex(0)->GetPlaySamplesCount()
488 .as_long_long() ) );
489 goto fail;
490 }
491
492 // Permute small1 correspondingly to the cut and paste
493 auto first = &small1[0];
494 if (x0 + xlen < nChunks)
495 std::rotate( first + x0, first + x0 + xlen, first + nChunks );
496 std::rotate( first + y0, first + nChunks - xlen, first + nChunks );
497 }
498
499 elapsed = timer.Time();
500
501 if (mBlockDetail) {
502 auto seq = t->GetClipByIndex(0)->GetSequence();
503 seq->DebugPrintf(seq->GetBlockArray(), seq->GetNumSamples(), &tempStr);
504 mToPrint += tempStr;
505 }
506 Printf( XO("Time to perform %d edits: %ld ms\n").Format( trials, elapsed ) );
507 FlushPrint();
508 wxTheApp->Yield();
509
510
511#if 0
512 Printf( XO("Checking file pointer leaks:\n") );
513 Printf( XO("Track # blocks: %ld\n").Format( t->GetBlockArray()->size() ) );
514 Printf( XO("Disk # blocks: \n") );
515 system("ls .audacity_temp/* | wc --lines");
516#endif
517
518 Printf( XO("Doing correctness check...\n") );
519 FlushPrint();
520 wxTheApp->Yield();
521
522 bad = 0;
523 timer.Start();
524 for (uint64_t i = 0; i < nChunks; i++) {
525 v = small1[i];
526 t->Get((samplePtr)block.get(), SampleFormat, i * chunkSize, chunkSize);
527 for (uint64_t b = 0; b < chunkSize; b++)
528 if (block[b] != v) {
529 bad++;
530 if (bad < 10)
531 Printf( XO("Bad: chunk %lld sample %lld\n").Format( i, b ) );
532 b = chunkSize;
533 }
534 }
535 if (bad == 0)
536 Printf( XO("Passed correctness check!\n") );
537 else
538 Printf( XO("Errors in %d/%lld chunks\n").Format( bad, nChunks ) );
539
540 elapsed = timer.Time();
541
542 Printf( XO("Time to check all data: %ld ms\n").Format( elapsed ) );
543 Printf( XO("Reading data again...\n") );
544
545 wxTheApp->Yield();
546 FlushPrint();
547
548 timer.Start();
549
550 for (uint64_t i = 0; i < nChunks; i++) {
551 v = small1[i];
552 t->Get((samplePtr)block.get(), SampleFormat, i * chunkSize, chunkSize);
553 for (uint64_t b = 0; b < chunkSize; b++)
554 if (block[b] != v)
555 bad++;
556 }
557
558 elapsed = timer.Time();
559
560 Printf( XO("Time to check all data (2): %ld ms\n").Format( elapsed ) );
561
562 Printf( XO("At 44100 Hz, %d bytes per sample, the estimated number of\n simultaneous tracks that could be played at once: %.1f\n" )
563 .Format( SAMPLE_SIZE(SampleFormat), (nChunks*chunkSize/44100.0)/(elapsed/1000.0) ) );
564
565 goto success;
566
567 fail:
568 Printf( XO("TEST FAILED!!!\n") );
569
570 success:
571
572 Printf( XO("Benchmark completed successfully.\n") );
573 HoldPrint(false);
574}
wxT("CloseDown"))
int AudacityMessageBox(const TranslatableString &message, const TranslatableString &caption, long style, wxWindow *parent, int x, int y)
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)
static TransactionScope::Factory::Scope scope
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:39
#define S(N)
Definition: ToChars.cpp:64
BoolSetting EditClipsCanMove
Definition: WaveTrack.cpp:2857
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:1840
static size_t GetMaxDiskBlockSize()
Definition: Sequence.cpp:1845
bool Write(const T &value)
Write value to config and return true if successful.
Definition: Prefs.h:252
Makes temporary changes to preferences, then rolls them back at destruction.
Definition: Prefs.h:115
Derived from ShuttleGuiBase, an Audacity specific class for shuttling data to and from GUI.
Definition: ShuttleGui.h:628
std::shared_ptr< Track > Holder
Definition: Track.h:368
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:620