tracktion-engine 3.0-10-g034fdde4aa5
Tracktion Engine — High level data model for audio applications

« « « Anklang Documentation
Loading...
Searching...
No Matches
tracktion_BenchmarkUtilities.h
Go to the documentation of this file.
1 /*
2 ,--. ,--. ,--. ,--.
3 ,-' '-.,--.--.,--,--.,---.| |,-.,-' '-.`--' ,---. ,--,--, Copyright 2024
4 '-. .-'| .--' ,-. | .--'| /'-. .-',--.| .-. || \ Tracktion Software
5 | | | | \ '-' \ `--.| \ \ | | | |' '-' '| || | Corporation
6 `---' `--' `--`--'`---'`--'`--' `---' `--' `---' `--''--' www.tracktion.com
7
8 Tracktion Engine uses a GPL/commercial licence - see LICENCE.md for details.
9*/
10
11#pragma once
12
14#include "../../../tracktion_core/utilities/tracktion_Benchmark.h"
15#include "../../playback/graph/tracktion_TracktionEngineNode.h"
16#include "../../playback/graph/tracktion_TracktionNodePlayer.h"
17#include "../../playback/graph/tracktion_MultiThreadedNodePlayer.h"
18
19namespace tracktion { inline namespace engine
20{
21
22namespace benchmark_utilities
23{
24 //==============================================================================
25 enum class MultiThreaded { no, yes };
26
27 enum class LockFree { no, yes };
28
29 enum class PoolMemoryAllocations { no, yes };
30
31 enum class ShareNodeMemory { no, yes };
32
34 {
35 Edit* edit = nullptr;
36 juce::String editName;
38 MultiThreaded isMultiThreaded;
39 LockFree isLockFree;
41 PoolMemoryAllocations poolMemoryAllocations = PoolMemoryAllocations::no;
42 ShareNodeMemory shareNodeMemory = ShareNodeMemory::no;
43 };
44
45 inline juce::String getDescription (const BenchmarkOptions& opts)
46 {
47 using namespace tracktion::graph;
48 auto s = graph::test_utilities::getDescription (opts.testSetup)
49 + juce::String (opts.isMultiThreaded == MultiThreaded::yes ? ", MT" : ", ST");
50
51 if (opts.isMultiThreaded == MultiThreaded::yes && opts.isLockFree == LockFree::yes)
52 s << ", lock-free";
53
54 if (opts.poolMemoryAllocations == PoolMemoryAllocations::yes)
55 s << ", pooled-memory";
56
57 if (opts.shareNodeMemory == ShareNodeMemory::yes)
58 s << ", share-node-memory";
59
60 if (opts.isMultiThreaded == MultiThreaded::yes)
61 s << ", " + graph::test_utilities::getName (opts.poolType);
62
63 return s;
64 }
65
66 //==============================================================================
67 inline std::unique_ptr<tracktion::graph::Node> createNode (Edit& edit, ProcessState& processState,
68 double sampleRate, int blockSize)
69 {
70 CreateNodeParams params { processState };
71 params.sampleRate = sampleRate;
72 params.blockSize = blockSize;
73 params.forRendering = true; // Required for audio files to be read
74 return createNodeForEdit (edit, params);
75 }
76
77 template<typename NodePlayerType>
78 void prepareRenderAndDestroy (juce::UnitTest& ut, juce::String editName, juce::String description,
81 MultiThreaded isMultiThreaded)
82 {
83 description += ", " + juce::String (testContext.getDescription());
84 ut.beginTest (editName + " - preparing: " + description);
85
86 if (isMultiThreaded == MultiThreaded::no)
87 testContext.getNodePlayer().setNumThreads (0);
88
89 testContext.setPlayHead (&playHeadState.playHead);
90 playHeadState.playHead.playSyncedToRange ({});
91 ut.expect (true);
92
93 ut.beginTest (editName + " - memory use: " + description);
94 {
95 const auto nodes = tracktion::graph::getNodes (testContext.getNode(), tracktion::graph::VertexOrdering::postordering);
96 const auto sizeInBytes = tracktion::graph::test_utilities::getMemoryUsage (nodes);
97
98 BenchmarkResult bmr { createBenchmarkDescription ("Node", (editName + ": memory use").toStdString(), description.toStdString()) };
99 bmr.totalSeconds = static_cast<double> (sizeInBytes);
100 bmr.totalCycles = static_cast<std::uint64_t> (sizeInBytes);
102
103 std::cout << "Num nodes: " << nodes.size() << "\n";
104 std::cout << juce::File::descriptionOfSizeInBytes ((int64_t) sizeInBytes) << "\n";
105 }
106 ut.expect (true);
107
108 ut.beginTest (editName + " - rendering: " + description);
109 const StopwatchTimer sw;
110 auto result = testContext.processAll();
111 const auto stats = testContext.getStatisticsAndReset();
112
114 (editName + ": rendering").toStdString(),
115 description.toStdString()),
116 stats));
117
118 std::cout << sw.getDescription() << "\n";
119 std::cout << stats.toString (testContext.getPerformanceMeasurement().getName()) << "\n";
120 ut.expect (true);
121
122 ut.beginTest (editName + " - destroying: " + description);
123 {
124 ScopedBenchmark sb (createBenchmarkDescription ("Node", (editName + ": destroying").toStdString(), description.toStdString()));
125 result.reset();
126 }
127 ut.expect (true);
128 }
129
130 inline void renderEdit (juce::UnitTest& ut, BenchmarkOptions opts)
131 {
132 assert (opts.edit != nullptr);
133 assert (opts.shareNodeMemory == ShareNodeMemory::no || opts.isLockFree == LockFree::yes); // Only supported in the lock-free player atm
134 const auto description = getDescription (opts);
135
137 tracktion::graph::PlayHeadState playHeadState { playHead };
138 ProcessState processState { playHeadState, opts.edit->tempoSequence };
139
140 //===
141 ut.beginTest (opts.editName + " - building: " + description);
142 auto sb = std::make_unique<ScopedBenchmark> (createBenchmarkDescription ("Node", (opts.editName + ": building").toStdString(), description.toStdString()));
143 auto node = createNode (*opts.edit, processState, opts.testSetup.sampleRate, opts.testSetup.blockSize);
144 sb.reset();
145 ut.expect (node != nullptr);
146
147 //===
148 if (opts.isLockFree == LockFree::yes)
149 {
150 // N.B. Don't set the Node until after the pooled and shared options or they won't get picked up
152 processState, opts.testSetup.sampleRate, opts.testSetup.blockSize,
153 tracktion::graph::getPoolCreatorFunction (opts.poolType)),
154 opts.testSetup, 2, opts.edit->getLength().inSeconds(), false);
155
156 if (opts.poolMemoryAllocations == PoolMemoryAllocations::yes)
157 testContext.getNodePlayer().enablePooledMemoryAllocations (true);
158
159 if (opts.shareNodeMemory == ShareNodeMemory::yes)
160 testContext.getNodePlayer().enableNodeMemorySharing (true);
161
162 {
163 const ScopedBenchmark sb2 (createBenchmarkDescription ("Node", (opts.editName + ": setting node").toStdString(), description.toStdString()));
164 testContext.setNode(std::move(node));
165 }
166
167 prepareRenderAndDestroy (ut, opts.editName, description, testContext, playHeadState, opts.isMultiThreaded);
168 }
169 else
170 {
171 tracktion::graph::test_utilities::TestProcess<MultiThreadedNodePlayer> testContext (std::make_unique<engine::MultiThreadedNodePlayer> (std::move (node), processState, opts.testSetup.sampleRate, opts.testSetup.blockSize),
172 opts.testSetup, 2, opts.edit->getLength().inSeconds(), false);
173 prepareRenderAndDestroy (ut, opts.editName, description, testContext, playHeadState, opts.isMultiThreaded);
174 }
175
176 ut.beginTest (opts.editName + " - cleanup: " + description);
177 // This is deliberately empty as RAII will take care of cleanup
178 ut.expect (true);
179 }
180
181 inline void renderEdit (juce::UnitTest& ut,
182 juce::String editName,
183 Edit& edit,
185 MultiThreaded isMultiThreaded,
186 LockFree isLockFree,
188 {
189 renderEdit (ut, { &edit, editName, ts, isMultiThreaded, isLockFree, poolType });
190 }
191
192 //==============================================================================
193 inline std::unique_ptr<Edit> openEditfromArchiveData (Engine& engine, const char* data, int size)
194 {
196 juce::TemporaryFile tempArchiveFile, tempDir;
197
198 {
199 const auto res = tempDir.getFile().createDirectory();
200 jassert (res);
201 ignoreUnused (res);
202 }
203
204 {
205 tempArchiveFile.getFile().replaceWithData (data, (size_t) size);
206 TracktionArchiveFile archive (engine, tempArchiveFile.getFile());
207
208 juce::Array<juce::File> createdFiles;
209 const auto res = archive.extractAll (tempDir.getFile(), createdFiles);
210 jassert (res);
211 juce::ignoreUnused (res);
212
213 for (const auto& f : createdFiles)
214 {
215 if (isTracktionEditFile (f))
216 {
217 edit = loadEditFromFile (engine, f);
218 break;
219 }
220 }
221 }
222
223 tempDir.getFile().deleteRecursively();
224
225 return edit;
226 }
227
230 {
231 auto id = ProjectItemID::fromProperty (editState, IDs::projectID);
232
233 if (! id.isValid())
235
237 {
238 engine,
239 editState,
240 id,
242 nullptr,
244 {},
245 {},
246 0
247 });
248 }
249
251 inline std::unique_ptr<Edit> openEditFromZipData (Engine& engine, const void* data, size_t numBytes)
252 {
253 return loadEditFromValueTree (engine, juce::ValueTree::readFromGZIPData (data, numBytes));
254 }
255}
256
257}} // namespace tracktion { inline namespace engine
assert
bool deleteRecursively(bool followSymlinks=false) const
bool replaceWithData(const void *dataToWrite, size_t numberOfBytes) const
static String descriptionOfSizeInBytes(int64 bytes)
Result createDirectory() const
const File & getFile() const noexcept
void beginTest(const String &testName)
void expect(bool testResult, const String &failureMessage=String())
static BenchmarkList & getInstance()
Gets the singleton instance.
void addResult(BenchmarkResult r)
Adds a result to the list.
The Tracktion Edit class!
static std::unique_ptr< Edit > createEdit(Options)
Creates an Edit for the given options.
@ forEditing
Creates an Edit for normal use.
static int getDefaultNumUndoLevels() noexcept
Returns the default number of undo levels that should be used.
Determines how the Edit will be created.
The Engine is the central class for all tracktion sessions.
static ProjectItemID createNewID(int projectID) noexcept
Generates a new ID for a given project.
Determines how this block releates to other previous render blocks and if the play head has jumped in...
Converts a monotonically increasing reference range in to a timeline range.
void playSyncedToRange(juce::Range< int64_t > rangeToPlay)
Takes the play position directly from the playout range.
T is_pointer_v
#define jassert(expression)
void ignoreUnused(Types &&...) noexcept
juce::ValueTree loadEditFromFile(Engine &e, const juce::File &f, ProjectItemID itemID)
Legacy, will be deprecated soon.
std::unique_ptr< tracktion::graph::Node > createNodeForEdit(EditPlaybackContext &epc, std::atomic< double > &audibleTimeToUpdate, const CreateNodeParams &params)
Creates a Node to play back an Edit with live inputs and outputs.
BenchmarkDescription createBenchmarkDescription(std::string category, std::string name, std::string description)
Creates a description by hashing the name and description fields.
BenchmarkResult createBenchmarkResult(BenchmarkDescription description, const tracktion::graph::PerformanceMeasurement::Statistics &stats)
Creates a BenchmarkResult from a set of Statistics.
Holds the duration a benchmark took to run.
Contains options for Edit Node content creation.
ThreadPoolStrategy
Available strategies for thread pools.
Holds the state of a process call.
Helper class for measuring a benchmark and adding it to the singleton BenchmarkList list.
std::string getDescription() const
Returns a description of the number of channels and length of rendering.
std::unique_ptr< Edit > openEditFromZipData(Engine &engine, const void *data, size_t numBytes)
Loads an Edit that was saved directly from the state to a GZip stream.
std::unique_ptr< Edit > loadEditFromValueTree(Engine &engine, const juce::ValueTree &editState)
Loads an Edit from a value tree with no Project file references.