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