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 "TempoChange.h"
36#include "WaveClip.h"
37#include "WaveTrack.h"
38#include "Sequence.h"
39#include "Prefs.h"
40#include "ProjectRate.h"
41
42#include "FileNames.h"
43#include "SelectFile.h"
44#include "AudacityMessageBox.h"
45#include "wxPanelWrapper.h"
46
47// Change these to the desired format...should probably make the
48// choice available in the dialog
49#define SampleType short
50#define SampleFormat int16Sample
51
52class BenchmarkDialog final : public wxDialogWrapper
53{
54public:
55 // constructors and destructors
56 BenchmarkDialog( wxWindow *parent, AudacityProject &project );
57
59
60private:
61 // WDR: handler declarations
62 void OnRun( wxCommandEvent &event );
63 void OnSave( wxCommandEvent &event );
64 void OnClear( wxCommandEvent &event );
65 void OnClose( wxCommandEvent &event );
66
67 void Printf(const TranslatableString &str);
68 void HoldPrint(bool hold);
69 void FlushPrint();
70
73
75 wxString mToPrint;
76
77 wxString mBlockSizeStr;
78 wxString mDataSizeStr;
79 wxString mNumEditsStr;
80 wxString mRandSeedStr;
81
84
85 wxTextCtrl *mText;
86
87private:
88 DECLARE_EVENT_TABLE()
89};
90
91void RunBenchmark( wxWindow *parent, AudacityProject &project )
92{
93 /*
94 int action = AudacityMessageBox(
95XO("This will close all project windows (without saving)\nand open the Audacity Benchmark dialog.\n\nAre you sure you want to do this?"),
96 XO("Benchmark"),
97 wxYES_NO | wxICON_EXCLAMATION,
98 NULL);
99
100 if (action != wxYES)
101 return;
102
103 for ( auto pProject : AllProjects{} )
104 GetProjectFrame( *pProject ).Close();
105 */
106
107 BenchmarkDialog dlog{ parent, project };
108
109 dlog.CentreOnParent();
110
111 dlog.ShowModal();
112}
113
114//
115// BenchmarkDialog
116//
117
118enum {
119 RunID = 1000,
128
129BEGIN_EVENT_TABLE(BenchmarkDialog, wxDialogWrapper)
135
137 wxWindow *parent, AudacityProject &project)
138 :
139 /* i18n-hint: Benchmark means a software speed test */
140 wxDialogWrapper( parent, 0, XO("Benchmark"),
141 wxDefaultPosition, wxDefaultSize,
142 wxDEFAULT_DIALOG_STYLE |
143 wxRESIZE_BORDER)
144 , mProject(project)
145 , mRate{ ProjectRate::Get(project) }
146{
147 SetName();
148
149 mBlockSizeStr = wxT("64");
150 mNumEditsStr = wxT("100");
151 mDataSizeStr = wxT("32");
152 mRandSeedStr = wxT("234657");
153
154 mBlockDetail = false;
155 mEditDetail = false;
156
157 HoldPrint(false);
158
159 MakeBenchmarkDialog();
160}
161
162// WDR: handler implementations for BenchmarkDialog
163
164void BenchmarkDialog::OnClose(wxCommandEvent & WXUNUSED(event))
165{
166 EndModal(0);
167}
168
170{
171 ShuttleGui S(this, eIsCreating);
172
173 // Strings don't need to be translated because this class doesn't
174 // ever get used in a stable release.
175
176 S.StartVerticalLay(true);
177 {
178 S.SetBorder(8);
179 S.StartMultiColumn(4);
180 {
181 //
182 S.Id(BlockSizeID)
183 .Validator<wxTextValidator>(wxFILTER_NUMERIC, &mBlockSizeStr)
184 .AddTextBox(XXO("Disk Block Size (KB):"),
185 wxT(""),
186 12);
187
188 //
189 S.Id(NumEditsID)
190 .Validator<wxTextValidator>(wxFILTER_NUMERIC, &mNumEditsStr)
191 .AddTextBox(XXO("Number of Edits:"),
192 wxT(""),
193 12);
194
195 //
196 S.Id(DataSizeID)
197 .Validator<wxTextValidator>(wxFILTER_NUMERIC, &mDataSizeStr)
198 .AddTextBox(XXO("Test Data Size (MB):"),
199 wxT(""),
200 12);
201
203 S.Id(RandSeedID)
204 .Validator<wxTextValidator>(wxFILTER_NUMERIC, &mRandSeedStr)
205 /* i18n-hint: A "seed" is a number that initializes a
206 pseudorandom number generating algorithm */
207 .AddTextBox(XXO("Random Seed:"),
208 wxT(""),
209 12);
210
211 }
212 S.EndMultiColumn();
213
214 //
215 S.Validator<wxGenericValidator>(&mBlockDetail)
216 .AddCheckBox(XXO("Show detailed info about each block file"),
217 false);
218
219 //
220 S.Validator<wxGenericValidator>(&mEditDetail)
221 .AddCheckBox(XXO("Show detailed info about each editing operation"),
222 false);
223
224 //
225 mText = S.Id(StaticTextID)
226 /* i18n-hint noun */
227 .Name(XO("Output"))
228 .Style( wxTE_MULTILINE | wxTE_READONLY | wxTE_RICH )
229 .MinSize( { 500, 200 } )
230 .AddTextWindow(wxT(""));
231
232 //
233 S.SetBorder(10);
234 S.StartHorizontalLay(wxALIGN_LEFT | wxEXPAND, false);
235 {
236 S.StartHorizontalLay(wxALIGN_LEFT, false);
237 {
238 S.Id(RunID).AddButton(XXO("Run"), wxALIGN_CENTRE, true);
239 S.Id(BSaveID).AddButton(XXO("Save"));
240 /* i18n-hint verb; to empty or erase */
241 S.Id(ClearID).AddButton(XXO("Clear"));
242 }
243 S.EndHorizontalLay();
244
245 S.StartHorizontalLay(wxALIGN_CENTER, true);
246 {
247 // Spacer
248 }
249 S.EndHorizontalLay();
250
251 S.StartHorizontalLay(wxALIGN_NOT | wxALIGN_LEFT, false);
252 {
253 /* i18n-hint verb */
254 S.Id(wxID_CANCEL).AddButton(XXO("Close"));
255 }
256 S.EndHorizontalLay();
257 }
258 S.EndHorizontalLay();
259 }
260 S.EndVerticalLay();
261
262 Fit();
263 SetSizeHints(GetSize());
264}
265
266void BenchmarkDialog::OnSave( wxCommandEvent & WXUNUSED(event))
267{
268/* i18n-hint: Benchmark means a software speed test;
269 leave untranslated file extension .txt */
270 auto fName = XO("benchmark.txt").Translation();
271
272 fName = SelectFile(FileNames::Operation::Export,
273 XO("Export Benchmark Data as:"),
274 wxEmptyString,
275 fName,
276 wxT("txt"),
278 wxFD_SAVE | wxRESIZE_BORDER,
279 this);
280
281 if (fName.empty())
282 return;
283
284 mText->SaveFile(fName);
285}
286
287void BenchmarkDialog::OnClear(wxCommandEvent & WXUNUSED(event))
288{
289 mText->Clear();
290}
291
293{
294 auto s = str.Translation();
295 mToPrint += s;
296 if (!mHoldPrint)
297 FlushPrint();
298}
299
301{
302 mHoldPrint = hold;
303
304 if (!mHoldPrint)
305 FlushPrint();
306}
307
309{
310 while(mToPrint.length() > 100) {
311 mText->AppendText(mToPrint.Left(100));
312 mToPrint = mToPrint.Right(mToPrint.length() - 100);
313 }
314 if (mToPrint.length() > 0)
315 mText->AppendText(mToPrint);
316 mToPrint = wxT("");
317}
318
319void BenchmarkDialog::OnRun( wxCommandEvent & WXUNUSED(event))
320{
321 TransferDataFromWindow();
322
323 if (!Validate())
324 return;
325
326 // This code will become part of libaudacity,
327 // and this class will be phased out.
328 long blockSize, numEdits, dataSize, randSeed;
329
330 mBlockSizeStr.ToLong(&blockSize);
331 mNumEditsStr.ToLong(&numEdits);
332 mDataSizeStr.ToLong(&dataSize);
333 mRandSeedStr.ToLong(&randSeed);
334
335 if (blockSize < 1 || blockSize > 1024) {
337 XO("Block size should be in the range 1 - 1024 KB.") );
338 return;
339 }
340
341 if (numEdits < 1 || numEdits > 10000) {
343 XO("Number of edits should be in the range 1 - 10000.") );
344 return;
345 }
346
347 if (dataSize < 1 || dataSize > 2000) {
349 XO("Test data size should be in the range 1 - 2000 MB.") );
350 return;
351 }
352
354 EditClipsCanMove.Write( false );
355
356 // Remember the old blocksize, so that we can restore it later.
357 auto oldBlockSize = Sequence::GetMaxDiskBlockSize();
358 Sequence::SetMaxDiskBlockSize(blockSize * 1024);
359
360 const auto cleanup = finally( [&] {
361 Sequence::SetMaxDiskBlockSize(oldBlockSize);
362 } );
363
364 wxBusyCursor busy;
365
366 HoldPrint(true);
367
368 const auto t =
371 .Create(SampleFormat, mRate.GetRate());
372 const auto tmp0 = TrackList::Temporary(nullptr, t);
373 const auto tempo = ProjectTimeSignature::Get(mProject).GetTempo();
374 DoProjectTempoChange(*t, tempo);
375
376 t->SetRate(1);
377
378 srand(randSeed);
379
380 uint64_t nChunks, chunkSize;
381 //chunkSize = 7500ull + (rand() % 1000ull);
382 chunkSize = 200ull + (rand() % 100ull);
383 nChunks = (dataSize * 1048576ull) / (chunkSize*sizeof(SampleType));
384 while (nChunks < 20 || chunkSize > (blockSize*1024)/4)
385 {
386 chunkSize = std::max( uint64_t(1), (chunkSize / 2) + (rand() % 100) );
387 nChunks = (dataSize * 1048576ull) / (chunkSize*sizeof(SampleType));
388 }
389
390 // The chunks are the pieces we move around in the test.
391 // They are (and are supposed to be) a different size to
392 // the blocks that make the sample blocks. That way we get to
393 // do some testing of when edit chunks cross sample block boundaries.
394 Printf( XO("Using %lld chunks of %lld samples each, for a total of %.1f MB.\n")
395 .Format( nChunks, chunkSize, nChunks*chunkSize*sizeof(SampleType)/1048576.0 ) );
396
397 int trials = numEdits;
398
399 using Samples = ArrayOf<SampleType>;
400 Samples small1{nChunks};
401 Samples block{chunkSize};
402
403 Printf( XO("Preparing...\n") );
404
405 wxTheApp->Yield();
406 FlushPrint();
407
408 int v;
409 int bad;
410 int z;
411 long elapsed;
412 wxString tempStr;
413 wxStopWatch timer;
414
415 for (uint64_t i = 0; i < nChunks; i++) {
416 v = SampleType(rand());
417 small1[i] = v;
418 for (uint64_t b = 0; b < chunkSize; b++)
419 block[b] = v;
420
421 t->Append(0, (samplePtr)block.get(), SampleFormat, chunkSize);
422 }
423 t->Flush();
424
425 // This forces the WaveTrack to flush all of the appends (which is
426 // only necessary if you want to access the Sequence class directly,
427 // as we're about to do).
428 t->GetEndTime();
429
430 if (t->GetClip(0)->GetVisibleSampleCount() != nChunks * chunkSize) {
431 Printf( XO("Expected len %lld, track len %lld.\n")
432 .Format(
433 nChunks * chunkSize,
434 t->GetClip(0)->GetVisibleSampleCount()
435 .as_long_long() ) );
436 goto fail;
437 }
438
439 Printf( XO("Performing %d edits...\n").Format( trials ) );
440 wxTheApp->Yield();
441 FlushPrint();
442
443 timer.Start();
444 for (z = 0; z < trials; z++) {
445 // First chunk to cut
446 // 0 <= x0 < nChunks
447 const uint64_t x0 = rand() % nChunks;
448
449 // Number of chunks to cut
450 // 1 <= xlen <= nChunks - x0
451 const uint64_t xlen = 1 + (rand() % (nChunks - x0));
452 if (mEditDetail)
453 Printf( XO("Cut: %lld - %lld \n")
454 .Format( x0 * chunkSize, (x0 + xlen) * chunkSize) );
455
456 Track::Holder tmp;
457 try {
458 tmp =
459 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->GetClip(0)->GetVisibleSampleCount()
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);
482 }
483 catch (const AudacityException&) {
484 Printf( XO("Trial %d\nFailed on Paste.\n").Format( z ) );
485 goto fail;
486 }
487
488 if (t->GetClip(0)->GetVisibleSampleCount() != 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->GetClip(0)->GetVisibleSampleCount()
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 // One remaining old direct use of narrow clips, only for debugging
509 auto seq = t->GetClip(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:91
#define SampleType
Definition: Benchmark.cpp:49
#define SampleFormat
Definition: Benchmark.cpp:50
@ StaticTextID
Definition: Benchmark.cpp:122
@ ClearID
Definition: Benchmark.cpp:121
@ NumEditsID
Definition: Benchmark.cpp:125
@ BlockSizeID
Definition: Benchmark.cpp:123
@ DataSizeID
Definition: Benchmark.cpp:124
@ BSaveID
Definition: Benchmark.cpp:120
@ RunID
Definition: Benchmark.cpp:119
@ RandSeedID
Definition: Benchmark.cpp:126
#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
void DoProjectTempoChange(ChannelGroup &group, double newTempo)
Definition: TempoChange.cpp:41
const auto project
#define S(N)
Definition: ToChars.cpp:64
BoolSetting EditClipsCanMove
Definition: WaveTrack.cpp:3390
This simplifies arrays of arrays, each array separately allocated with NEW[] But it might be better t...
Definition: MemoryX.h:28
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:53
void OnClose(wxCommandEvent &event)
Definition: Benchmark.cpp:164
void OnSave(wxCommandEvent &event)
Definition: Benchmark.cpp:266
void HoldPrint(bool hold)
Definition: Benchmark.cpp:300
wxString mNumEditsStr
Definition: Benchmark.cpp:79
wxString mRandSeedStr
Definition: Benchmark.cpp:80
wxString mToPrint
Definition: Benchmark.cpp:75
BenchmarkDialog(wxWindow *parent, AudacityProject &project)
Definition: Benchmark.cpp:136
wxTextCtrl * mText
Definition: Benchmark.cpp:85
void OnClear(wxCommandEvent &event)
Definition: Benchmark.cpp:287
AudacityProject & mProject
Definition: Benchmark.cpp:71
void Printf(const TranslatableString &str)
Definition: Benchmark.cpp:292
wxString mBlockSizeStr
Definition: Benchmark.cpp:77
wxString mDataSizeStr
Definition: Benchmark.cpp:78
void MakeBenchmarkDialog()
Definition: Benchmark.cpp:169
void OnRun(wxCommandEvent &event)
Definition: Benchmark.cpp:319
const ProjectRate & mRate
Definition: Benchmark.cpp:72
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:1895
static size_t GetMaxDiskBlockSize()
Definition: Sequence.cpp:1900
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:640
std::shared_ptr< Track > Holder
Definition: Track.h:202
static TrackListHolder Temporary(AudacityProject *pProject, const Track::Holder &pTrack={})
Definition: Track.cpp:858
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:870
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