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 "Project.h"
33#include "SampleBlock.h"
34#include "ShuttleGui.h"
35#include "WaveClip.h"
36#include "WaveTrack.h"
37#include "Sequence.h"
38#include "Prefs.h"
39#include "ProjectRate.h"
40
41#include "FileNames.h"
42#include "SelectFile.h"
43#include "AudacityMessageBox.h"
44#include "wxPanelWrapper.h"
45
46// Change these to the desired format...should probably make the
47// choice available in the dialog
48#define SampleType short
49#define SampleFormat int16Sample
50
51class BenchmarkDialog final : public wxDialogWrapper
52{
53public:
54 // constructors and destructors
55 BenchmarkDialog( wxWindow *parent, AudacityProject &project );
56
58
59private:
60 // WDR: handler declarations
61 void OnRun( wxCommandEvent &event );
62 void OnSave( wxCommandEvent &event );
63 void OnClear( wxCommandEvent &event );
64 void OnClose( wxCommandEvent &event );
65
66 void Printf(const TranslatableString &str);
67 void HoldPrint(bool hold);
68 void FlushPrint();
69
72
74 wxString mToPrint;
75
76 wxString mBlockSizeStr;
77 wxString mDataSizeStr;
78 wxString mNumEditsStr;
79 wxString mRandSeedStr;
80
83
84 wxTextCtrl *mText;
85
86private:
87 DECLARE_EVENT_TABLE()
88};
89
90void RunBenchmark( wxWindow *parent, AudacityProject &project )
91{
92 /*
93 int action = AudacityMessageBox(
94XO("This will close all project windows (without saving)\nand open the Audacity Benchmark dialog.\n\nAre you sure you want to do this?"),
95 XO("Benchmark"),
96 wxYES_NO | wxICON_EXCLAMATION,
97 NULL);
98
99 if (action != wxYES)
100 return;
101
102 for ( auto pProject : AllProjects{} )
103 GetProjectFrame( *pProject ).Close();
104 */
105
106 BenchmarkDialog dlog{ parent, project };
107
108 dlog.CentreOnParent();
109
110 dlog.ShowModal();
111}
112
113//
114// BenchmarkDialog
115//
116
117enum {
118 RunID = 1000,
127
128BEGIN_EVENT_TABLE(BenchmarkDialog, wxDialogWrapper)
134
136 wxWindow *parent, AudacityProject &project)
137 :
138 /* i18n-hint: Benchmark means a software speed test */
139 wxDialogWrapper( parent, 0, XO("Benchmark"),
140 wxDefaultPosition, wxDefaultSize,
141 wxDEFAULT_DIALOG_STYLE |
142 wxRESIZE_BORDER)
143 , mProject(project)
144 , mRate{ ProjectRate::Get(project) }
145{
146 SetName();
147
148 mBlockSizeStr = wxT("64");
149 mNumEditsStr = wxT("100");
150 mDataSizeStr = wxT("32");
151 mRandSeedStr = wxT("234657");
152
153 mBlockDetail = false;
154 mEditDetail = false;
155
156 HoldPrint(false);
157
158 MakeBenchmarkDialog();
159}
160
161// WDR: handler implementations for BenchmarkDialog
162
163void BenchmarkDialog::OnClose(wxCommandEvent & WXUNUSED(event))
164{
165 EndModal(0);
166}
167
169{
170 ShuttleGui S(this, eIsCreating);
171
172 // Strings don't need to be translated because this class doesn't
173 // ever get used in a stable release.
174
175 S.StartVerticalLay(true);
176 {
177 S.SetBorder(8);
178 S.StartMultiColumn(4);
179 {
180 //
181 S.Id(BlockSizeID)
182 .Validator<wxTextValidator>(wxFILTER_NUMERIC, &mBlockSizeStr)
183 .AddTextBox(XXO("Disk Block Size (KB):"),
184 wxT(""),
185 12);
186
187 //
188 S.Id(NumEditsID)
189 .Validator<wxTextValidator>(wxFILTER_NUMERIC, &mNumEditsStr)
190 .AddTextBox(XXO("Number of Edits:"),
191 wxT(""),
192 12);
193
194 //
195 S.Id(DataSizeID)
196 .Validator<wxTextValidator>(wxFILTER_NUMERIC, &mDataSizeStr)
197 .AddTextBox(XXO("Test Data Size (MB):"),
198 wxT(""),
199 12);
200
202 S.Id(RandSeedID)
203 .Validator<wxTextValidator>(wxFILTER_NUMERIC, &mRandSeedStr)
204 /* i18n-hint: A "seed" is a number that initializes a
205 pseudorandom number generating algorithm */
206 .AddTextBox(XXO("Random Seed:"),
207 wxT(""),
208 12);
209
210 }
211 S.EndMultiColumn();
212
213 //
214 S.Validator<wxGenericValidator>(&mBlockDetail)
215 .AddCheckBox(XXO("Show detailed info about each block file"),
216 false);
217
218 //
219 S.Validator<wxGenericValidator>(&mEditDetail)
220 .AddCheckBox(XXO("Show detailed info about each editing operation"),
221 false);
222
223 //
224 mText = S.Id(StaticTextID)
225 /* i18n-hint noun */
226 .Name(XO("Output"))
227 .Style( wxTE_MULTILINE | wxTE_READONLY | wxTE_RICH )
228 .MinSize( { 500, 200 } )
229 .AddTextWindow(wxT(""));
230
231 //
232 S.SetBorder(10);
233 S.StartHorizontalLay(wxALIGN_LEFT | wxEXPAND, false);
234 {
235 S.StartHorizontalLay(wxALIGN_LEFT, false);
236 {
237 S.Id(RunID).AddButton(XXO("Run"), wxALIGN_CENTRE, true);
238 S.Id(BSaveID).AddButton(XXO("Save"));
239 /* i18n-hint verb; to empty or erase */
240 S.Id(ClearID).AddButton(XXO("Clear"));
241 }
242 S.EndHorizontalLay();
243
244 S.StartHorizontalLay(wxALIGN_CENTER, true);
245 {
246 // Spacer
247 }
248 S.EndHorizontalLay();
249
250 S.StartHorizontalLay(wxALIGN_NOT | wxALIGN_LEFT, false);
251 {
252 /* i18n-hint verb */
253 S.Id(wxID_CANCEL).AddButton(XXO("Close"));
254 }
255 S.EndHorizontalLay();
256 }
257 S.EndHorizontalLay();
258 }
259 S.EndVerticalLay();
260
261 Fit();
262 SetSizeHints(GetSize());
263}
264
265void BenchmarkDialog::OnSave( wxCommandEvent & WXUNUSED(event))
266{
267/* i18n-hint: Benchmark means a software speed test;
268 leave untranslated file extension .txt */
269 auto fName = XO("benchmark.txt").Translation();
270
271 fName = SelectFile(FileNames::Operation::Export,
272 XO("Export Benchmark Data as:"),
273 wxEmptyString,
274 fName,
275 wxT("txt"),
277 wxFD_SAVE | wxRESIZE_BORDER,
278 this);
279
280 if (fName.empty())
281 return;
282
283 mText->SaveFile(fName);
284}
285
286void BenchmarkDialog::OnClear(wxCommandEvent & WXUNUSED(event))
287{
288 mText->Clear();
289}
290
292{
293 auto s = str.Translation();
294 mToPrint += s;
295 if (!mHoldPrint)
296 FlushPrint();
297}
298
300{
301 mHoldPrint = hold;
302
303 if (!mHoldPrint)
304 FlushPrint();
305}
306
308{
309 while(mToPrint.length() > 100) {
310 mText->AppendText(mToPrint.Left(100));
311 mToPrint = mToPrint.Right(mToPrint.length() - 100);
312 }
313 if (mToPrint.length() > 0)
314 mText->AppendText(mToPrint);
315 mToPrint = wxT("");
316}
317
318void BenchmarkDialog::OnRun( wxCommandEvent & WXUNUSED(event))
319{
320 TransferDataFromWindow();
321
322 if (!Validate())
323 return;
324
325 // This code will become part of libaudacity,
326 // and this class will be phased out.
327 long blockSize, numEdits, dataSize, randSeed;
328
329 mBlockSizeStr.ToLong(&blockSize);
330 mNumEditsStr.ToLong(&numEdits);
331 mDataSizeStr.ToLong(&dataSize);
332 mRandSeedStr.ToLong(&randSeed);
333
334 if (blockSize < 1 || blockSize > 1024) {
336 XO("Block size should be in the range 1 - 1024 KB.") );
337 return;
338 }
339
340 if (numEdits < 1 || numEdits > 10000) {
342 XO("Number of edits should be in the range 1 - 10000.") );
343 return;
344 }
345
346 if (dataSize < 1 || dataSize > 2000) {
348 XO("Test data size should be in the range 1 - 2000 MB.") );
349 return;
350 }
351
353 EditClipsCanMove.Write( false );
354
355 // Remember the old blocksize, so that we can restore it later.
356 auto oldBlockSize = Sequence::GetMaxDiskBlockSize();
357 Sequence::SetMaxDiskBlockSize(blockSize * 1024);
358
359 const auto cleanup = finally( [&] {
360 Sequence::SetMaxDiskBlockSize(oldBlockSize);
361 } );
362
363 wxBusyCursor busy;
364
365 HoldPrint(true);
366
367 const auto t =
370 .Create(SampleFormat, mRate.GetRate());
371 const auto tmp0 = TrackList::Temporary(nullptr, t, nullptr);
372 const auto tempo = ProjectTimeSignature::Get(mProject).GetTempo();
373 t->OnProjectTempoChange(tempo);
374
375 assert(t->IsLeader()); // because it's new and not grouped
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)->GetVisibleSampleCount() != nChunks * chunkSize) {
432 Printf( XO("Expected len %lld, track len %lld.\n")
433 .Format(
434 nChunks * chunkSize,
435 t->GetClipByIndex(0)->GetVisibleSampleCount()
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 TrackListHolder tmp;
458 try {
459 tmp =
460 t->Cut(double (x0 * chunkSize), double ((x0 + xlen) * chunkSize));
461 }
462 catch (const AudacityException&) {
463 Printf( XO("Trial %d\n").Format( z ) );
464 Printf( XO("Cut (%lld, %lld) failed.\n")
465 .Format( (x0 * chunkSize), (x0 + xlen) * chunkSize) );
466 Printf( XO("Expected len %lld, track len %lld.\n")
467 .Format(
468 nChunks * chunkSize,
469 t->GetClipByIndex(0)->GetVisibleSampleCount()
470 .as_long_long() ) );
471 goto fail;
472 }
473
474 // Position to paste
475 // 0 <= y0 <= nChunks - xlen
476 const uint64_t y0 = rand() % (nChunks - xlen + 1);
477
478 if (mEditDetail)
479 Printf( XO("Paste: %lld\n").Format( y0 * chunkSize ) );
480
481 try {
482 t->Paste((double)(y0 * chunkSize), *tmp);
483 }
484 catch (const AudacityException&) {
485 Printf( XO("Trial %d\nFailed on Paste.\n").Format( z ) );
486 goto fail;
487 }
488
489 if (t->GetClipByIndex(0)->GetVisibleSampleCount() != nChunks * chunkSize) {
490 Printf( XO("Trial %d\n").Format( z ) );
491 Printf( XO("Expected len %lld, track len %lld.\n")
492 .Format(
493 nChunks * chunkSize,
494 t->GetClipByIndex(0)->GetVisibleSampleCount()
495 .as_long_long() ) );
496 goto fail;
497 }
498
499 // Permute small1 correspondingly to the cut and paste
500 auto first = &small1[0];
501 if (x0 + xlen < nChunks)
502 std::rotate( first + x0, first + x0 + xlen, first + nChunks );
503 std::rotate( first + y0, first + nChunks - xlen, first + nChunks );
504 }
505
506 elapsed = timer.Time();
507
508 if (mBlockDetail) {
509 auto seq = t->GetClipByIndex(0)->GetSequence(0);
510 seq->DebugPrintf(seq->GetBlockArray(), seq->GetNumSamples(), &tempStr);
511 mToPrint += tempStr;
512 }
513 Printf( XO("Time to perform %d edits: %ld ms\n").Format( trials, elapsed ) );
514 FlushPrint();
515 wxTheApp->Yield();
516
517
518#if 0
519 Printf( XO("Checking file pointer leaks:\n") );
520 Printf( XO("Track # blocks: %ld\n").Format( t->GetBlockArray()->size() ) );
521 Printf( XO("Disk # blocks: \n") );
522 system("ls .audacity_temp/* | wc --lines");
523#endif
524
525 Printf( XO("Doing correctness check...\n") );
526 FlushPrint();
527 wxTheApp->Yield();
528
529 bad = 0;
530 timer.Start();
531 for (uint64_t i = 0; i < nChunks; i++) {
532 v = small1[i];
533 auto pBlock = reinterpret_cast<samplePtr>(block.get());
534 constexpr auto backwards = false;
535 t->DoGet(0, 1, &pBlock, SampleFormat, i * chunkSize, chunkSize, backwards);
536 for (uint64_t b = 0; b < chunkSize; b++)
537 if (block[b] != v) {
538 bad++;
539 if (bad < 10)
540 Printf( XO("Bad: chunk %lld sample %lld\n").Format( i, b ) );
541 b = chunkSize;
542 }
543 }
544 if (bad == 0)
545 Printf( XO("Passed correctness check!\n") );
546 else
547 Printf( XO("Errors in %d/%lld chunks\n").Format( bad, nChunks ) );
548
549 elapsed = timer.Time();
550
551 Printf( XO("Time to check all data: %ld ms\n").Format( elapsed ) );
552 Printf( XO("Reading data again...\n") );
553
554 wxTheApp->Yield();
555 FlushPrint();
556
557 timer.Start();
558
559 for (uint64_t i = 0; i < nChunks; i++) {
560 v = small1[i];
561 auto pBlock = reinterpret_cast<samplePtr>(block.get());
562 constexpr auto backwards = false;
563 t->DoGet(0, 1, &pBlock, SampleFormat, i * chunkSize, chunkSize, backwards);
564 for (uint64_t b = 0; b < chunkSize; b++)
565 if (block[b] != v)
566 bad++;
567 }
568
569 elapsed = timer.Time();
570
571 Printf( XO("Time to check all data (2): %ld ms\n").Format( elapsed ) );
572
573 Printf( XO("At 44100 Hz, %d bytes per sample, the estimated number of\n simultaneous tracks that could be played at once: %.1f\n" )
574 .Format( SAMPLE_SIZE(SampleFormat), (nChunks*chunkSize/44100.0)/(elapsed/1000.0) ) );
575
576 goto success;
577
578 fail:
579 Printf( XO("TEST FAILED!!!\n") );
580
581 success:
582
583 Printf( XO("Benchmark completed successfully.\n") );
584 HoldPrint(false);
585}
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:90
#define SampleType
Definition: Benchmark.cpp:48
#define SampleFormat
Definition: Benchmark.cpp:49
@ StaticTextID
Definition: Benchmark.cpp:121
@ ClearID
Definition: Benchmark.cpp:120
@ NumEditsID
Definition: Benchmark.cpp:124
@ BlockSizeID
Definition: Benchmark.cpp:122
@ DataSizeID
Definition: Benchmark.cpp:123
@ BSaveID
Definition: Benchmark.cpp:119
@ RunID
Definition: Benchmark.cpp:118
@ RandSeedID
Definition: Benchmark.cpp:125
#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:57
#define SAMPLE_SIZE(SampleFormat)
Definition: SampleFormat.h:52
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:42
BoolSetting EditClipsCanMove
Definition: WaveTrack.cpp:4587
This simplifies arrays of arrays, each array separately allocated with NEW[] But it might be better t...
Definition: MemoryX.h:26
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:52
void OnClose(wxCommandEvent &event)
Definition: Benchmark.cpp:163
void OnSave(wxCommandEvent &event)
Definition: Benchmark.cpp:265
void HoldPrint(bool hold)
Definition: Benchmark.cpp:299
wxString mNumEditsStr
Definition: Benchmark.cpp:78
wxString mRandSeedStr
Definition: Benchmark.cpp:79
wxString mToPrint
Definition: Benchmark.cpp:74
BenchmarkDialog(wxWindow *parent, AudacityProject &project)
Definition: Benchmark.cpp:135
wxTextCtrl * mText
Definition: Benchmark.cpp:84
void OnClear(wxCommandEvent &event)
Definition: Benchmark.cpp:286
AudacityProject & mProject
Definition: Benchmark.cpp:70
void Printf(const TranslatableString &str)
Definition: Benchmark.cpp:291
wxString mBlockSizeStr
Definition: Benchmark.cpp:76
wxString mDataSizeStr
Definition: Benchmark.cpp:77
void MakeBenchmarkDialog()
Definition: Benchmark.cpp:168
void OnRun(wxCommandEvent &event)
Definition: Benchmark.cpp:318
const ProjectRate & mRate
Definition: Benchmark.cpp:71
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 ProjectTimeSignature & Get(AudacityProject &project)
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:259
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:1418
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:1279
static CommandContext::TargetFactory::SubstituteInUnique< InteractiveOutputTargets > scope
void rotate(const float *oldPhase, const float *newPhase, std::complex< float > *dst, int32_t n)
Definition: VectorOps.h:140