Loading...
Searching...
No Matches
speculationExecutorEngine.h
Go to the documentation of this file.
1//
2// Copyright 2025 Pixar
3//
4// Licensed under the terms set forth in the LICENSE.txt file available at
5// https://openusd.org/license.
6//
7#ifndef PXR_EXEC_VDF_SPECULATION_EXECUTOR_ENGINE_H
8#define PXR_EXEC_VDF_SPECULATION_EXECUTOR_ENGINE_H
9
11
12#include "pxr/pxr.h"
13
15
20#include "pxr/exec/vdf/node.h"
23
24PXR_NAMESPACE_OPEN_SCOPE
25
27
37template <typename DataManagerType>
39 public VdfPullBasedExecutorEngine<DataManagerType>
40{
41 // Base type definition
42 typedef
44 Base;
45
46public:
47
51 const VdfSpeculationExecutorBase &speculationExecutor,
52 DataManagerType *dataManager) :
53 VdfPullBasedExecutorEngine<DataManagerType>(
54 speculationExecutor, dataManager),
55 _writeBackExecutor(const_cast<VdfExecutorInterface *>(
56 speculationExecutor.GetNonSpeculationParentExecutor())) {
57 TF_VERIFY(_writeBackExecutor);
58 }
59
64 const VdfSchedule &schedule,
65 const VdfRequest &computeRequest,
66 VdfExecutorErrorLogger *errorLogger) {
68 schedule, computeRequest, errorLogger,
69 [](const VdfMaskedOutput &, size_t){});
70 }
71
77 template <typename F>
78 void RunSchedule(
79 const VdfSchedule &schedule,
80 const VdfRequest &computeRequest,
81 VdfExecutorErrorLogger *errorLogger,
82 F &&callback);
83
84private:
85 // MSVC errors out if Base::ExecutionStageStart is accessed in the
86 // _OutputToExecute constructor, because the enumerator is protected in the
87 // base class. GCC and clang do not consider this an error. To work around
88 // this issue, we loft the enumerator into the namespace of the derived
89 // class where _OutputToExecute() has the proper privileges across all
90 // compilers.
91 static constexpr typename Base::_ExecutionStage _ExecutionStageStart =
92 Base::ExecutionStageStart;
93
94 // This struct contains the necessary state to compute an output.
95 //
96 struct _OutputToExecute {
97
98 // Constructor that takes schedule node and output.
99 _OutputToExecute(const VdfSchedule::OutputId &outputId) :
100 outputId(outputId),
101 stage(_ExecutionStageStart),
102 numPushed(0),
103 inputsSpeculate(false)
104 { }
105
106 // The schedule identifier for the output to execute.
107 VdfSchedule::OutputId outputId;
108
109 // The current phase of this output in the execution stack.
110 typename Base::_ExecutionStage stage;
111
112 // The number of inputs that this output is waiting on.
113 int numPushed;
114
115 // Whether or not our read inputs speculate.
116 bool inputsSpeculate;
117 };
118
119 // This method adds \p output to the \p outputs vector.
120 // Returns \c true if it added a new output and \c false otherwise.
121 //
122 bool _PushBackOutputForSpeculation(
123 std::vector< _OutputToExecute > *outputs,
124 const VdfOutput &output,
125 const VdfSchedule &schedule);
126
127 // Method that makes sure that data is available for the given \p output
128 // before returning.
129 //
130 void _ExecuteOutputForSpeculation(
131 const VdfEvaluationState &state,
132 const VdfOutput &output,
133 TfBits *executedNodes,
134 TfBits *speculatedNodes);
135
136 // Write the computed output back to the write-back executor.
137 void _WriteBackComputedOutput(
138 const VdfOutput &output,
139 const VdfSchedule::OutputId &outputId,
140 const VdfSchedule &schedule);
141
142 // The parent executor this speculation engine is going to write back to.
143 VdfExecutorInterface *_writeBackExecutor;
144
145};
146
148
149template <typename DataManagerType>
150bool
152 std::vector< _OutputToExecute > *outputs,
153 const VdfOutput &output,
154 const VdfSchedule &schedule)
155{
156 VdfSchedule::OutputId outputId = schedule.GetOutputId(output);
157
158 if (outputId.IsValid()) {
159 outputs->push_back(_OutputToExecute(outputId));
160 return true;
161 }
162
163 // The output to push is not actually scheduled, which guarantees
164 // that is value will never be needed by any computations. So
165 // just skip it.
166 return false;
167}
168
169template <typename DataManagerType>
170template <typename F>
171void
173 const VdfSchedule &schedule,
174 const VdfRequest &computeRequest,
175 VdfExecutorErrorLogger *errorLogger,
176 F &&callback)
177{
178 TRACE_FUNCTION();
179
180 // Make sure the executor data manager is appropriately sized.
181 Base::_GetDataManager()->Resize(*schedule.GetNetwork());
182
183 const size_t numNodes = schedule.GetScheduleNodeVector().size();
184
185 // Has a bit set for any node that has already been run.
186 TfBits executedNodes(numNodes);
187
188 // Has a bit set for any node, which had one ore more inputs speculated.
189 TfBits speculatedNodes(numNodes);
190
191 // The persistent evaluation state
192 VdfEvaluationState state(Base::_GetExecutor(), schedule, errorLogger);
193
194 // Now execute the uncached, requested outputs.
195 VdfRequest::IndexedView requestView(computeRequest);
196 for (size_t i = 0; i < requestView.GetSize(); ++i) {
197 // Skip outputs not included in the request.
198 const VdfMaskedOutput *maskedOutput = requestView.Get(i);
199 if (!maskedOutput) {
200 continue;
201 }
202
203 // Skip outputs that have already been cached. However, we must invoke
204 // the callback to notify the client side that evaluation of the
205 // requested output has completed.
206 const VdfOutput &output = *maskedOutput->GetOutput();
207 const VdfMask &mask = maskedOutput->GetMask();
208 if (Base::_GetExecutor().GetOutputValue(output, mask)) {
209 callback(*maskedOutput, i);
210 continue;
211 }
212
213 _ExecuteOutputForSpeculation(
214 state, output, &executedNodes, &speculatedNodes);
215
216 // If we've been interrupted, bail out.
217 if (Base::_GetExecutor().HasBeenInterrupted()) {
218 break;
219 }
220
221 // Invoke the callback once the output has been evaluated, but only
222 // if the executor has not been interrupted.
223 else {
224 callback(*maskedOutput, i);
225 }
226 }
227}
228
229template <typename DataManagerType>
230void
232 const VdfEvaluationState &state,
233 const VdfOutput &output,
234 TfBits *executedNodes,
235 TfBits *speculatedNodes)
236{
237 TF_DEBUG(VDF_SEE_TRACE).Msg("{ SpeculationOutputExecuteBegin();\n");
238
239 // The current schedule
240 const VdfSchedule &schedule = state.GetSchedule();
241
242 // This is the stack of the outputs currently in the process of execution.
243 std::vector< _OutputToExecute > outputsStack;
244
245 // This is a stack used for the return values of outputs. A return value
246 // of true means that the output couldn't be evaluated due to speculation.
247 // XXX:optimization
248 // It's possible to get rid of this vector all together if outputs were
249 // allowed to write directly into their caller's stack space.
250 std::vector< bool > speculated;
251
252 // Add the initial output to start executing. This call will check for
253 // already cached values.
254 _PushBackOutputForSpeculation(&outputsStack, output, schedule);
255
256 bool hasBeenInterrupted = Base::_GetExecutor().HasBeenInterrupted();
257
258 while (!outputsStack.empty() && !hasBeenInterrupted) {
259
260 const VdfSchedule::OutputId &outputId = outputsStack.back().outputId;
261 const int scheduleNodeIndex = schedule.GetScheduleNodeIndex(outputId);
262 const VdfNode &node = *schedule.GetNode(outputId);
263 typename Base::_ExecutionStage stage = outputsStack.back().stage;
264 const size_t outputIndex = outputsStack.size() - 1;
265
266 bool affective = schedule.IsAffective(outputId);
267
268 // Pop all the return values from our inputs and check to see
269 // if any of them were 'true' (meaning that they hit a speculation
270 // path).
271 bool previousStageSpeculated = false;
272 while (outputsStack.back().numPushed) {
273 outputsStack.back().numPushed--;
274 previousStageSpeculated |= speculated.back();
275 speculated.pop_back();
276 }
277
278 switch (stage) {
279
280 case Base::ExecutionStageStart:
281
282 TF_DEBUG(VDF_SEE_TRACE)
283 .Msg("{ SpeculationBeginNode(%p, \"%s\");\n",
284 &node, node.GetDebugName().c_str());
285
286 // If this is the node that started the speculation, we need to
287 // skip it. Note that this means we encountered a true data
288 // dependency cycle and have a bad result. Additionally, we may
289 // write back the bad result to any parent executors.
290 if (static_cast<const VdfSpeculationExecutorBase &>(
291 Base::_GetExecutor()).IsSpeculatingNode(&node)) {
292 speculated.push_back(true);
293 outputsStack.pop_back();
294
295 TF_DEBUG(VDF_SEE_TRACE)
296 .Msg("SpeculationEndNodeSpeculationNode(); (cycle) }\n");
297
298 continue;
299 }
300
301 // If this node has already been executed, do not run it a second
302 // time. However, make sure to push the right value onto the
303 // speculated stack, based on whether the node had inputs we
304 // speculated about, the last time it was run.
305 if (executedNodes->IsSet(scheduleNodeIndex)) {
306 speculated.push_back(
307 speculatedNodes->IsSet(scheduleNodeIndex));
308 outputsStack.pop_back();
309
310 TF_DEBUG(VDF_SEE_TRACE)
311 .Msg("SpeculationEndNodeRedundantCompute(); }\n");
312
313 continue;
314 }
315
316 // If we are already cached for this output (or if our parent
317 // executor is), then we can provide a value, we can return early.
318 if (Base::_GetExecutor().GetOutputValue(
319 *schedule.GetOutput(outputId),
320 schedule.GetRequestMask(outputId))) {
321 speculated.push_back(false);
322 outputsStack.pop_back();
323
324 TF_DEBUG(VDF_SEE_TRACE)
325 .Msg("SpeculationEndNodeFoundCache(); }\n");
326
327 continue;
328 }
329
330 TF_DEV_AXIOM(outputsStack.back().numPushed == 0);
331
332 // The first stage of computation is to execute all the
333 // prerequisites for current output.
334 outputsStack.back().stage = Base::ExecutionStagePreRequisitesDone;
335
336 // Push back all the prerequisites
337 if (affective) {
338 for (const VdfScheduleInput &input : schedule.GetInputs(node)) {
339 if (input.input->GetSpec().IsPrerequisite()) {
340 const bool pushed = _PushBackOutputForSpeculation(
341 &outputsStack, *input.source, schedule);
342 outputsStack[outputIndex].numPushed += pushed;
343 }
344 }
345 }
346
347 // Little optimization to not go back to the top of the loop
348 // for no reason.
349 if (outputsStack[outputIndex].numPushed > 0) {
350 break;
351 } // else fall through to the next stage.
352
353
354 case Base::ExecutionStagePreRequisitesDone:
355
356
357 // Now that our prerequisites are done, unroll our return stack.
358
359
360 // Update whether or not our previousStageSpeculated
361 outputsStack.back().inputsSpeculate |= previousStageSpeculated;
362
363 // The second stage of computation is to use the prerequisites
364 // to determine what other inputs need to run to satisfy the
365 // current output.
366
367 // Mark that the next stage of computation
368 outputsStack.back().stage = Base::ExecutionStageReadsDone;
369
370 // Only need to run the reads of an output that will do something
371 // and if our pre-requisites were computed without speculation.
372 if (affective && !previousStageSpeculated) {
373
374 // Get the list of required inputs based on the prerequisite
375 // computations.
376 VdfRequiredInputsPredicate inputsPredicate =
377 node.GetRequiredInputsPredicate(VdfContext(state, node));
378
379 // Run the required reads first.
380 // Here we try to run the "read" inputs before the "read/write"
381 // inputs so that we can maximize the chance of being able to
382 // re-use the buffer.
383 if (inputsPredicate.HasRequiredReads()) {
384 for (const VdfScheduleInput &input :
385 schedule.GetInputs(node)) {
386 if (inputsPredicate.IsRequiredRead(*input.input)) {
387 const bool pushed = _PushBackOutputForSpeculation(
388 &outputsStack, *input.source, schedule);
389 outputsStack[outputIndex].numPushed += pushed;
390 }
391 }
392 }
393 }
394
395 // Little optimization to not go back to the top of the loop
396 // for no reason.
397 if (outputsStack[outputIndex].numPushed > 0) {
398 break;
399 } // else fall through to the next stage.
400
401 case Base::ExecutionStageReadsDone:
402
403 // Mark that the next stage of computation
404 outputsStack.back().stage = Base::ExecutionStageCompute;
405
406 // Mark whether or not our read inputs depend on a speculation.
407 outputsStack.back().inputsSpeculate |= previousStageSpeculated;
408
409 // Now run the read/writes last.
410 for (const VdfScheduleInput &input : schedule.GetInputs(node)) {
411 const VdfOutput *assocOutput =
412 input.input->GetAssociatedOutput();
413 if (!assocOutput) {
414 continue;
415 }
416
417 // Does this output have a pass-through scheduled?
418 const VdfSchedule::OutputId &assocOutputId =
419 schedule.GetOutputId(*assocOutput);
420 if (assocOutputId.IsValid()) {
421 if (const VdfOutput *fromBufferOutput =
422 schedule.GetFromBufferOutput(assocOutputId)) {
423 const bool pushed = _PushBackOutputForSpeculation(
424 &outputsStack, *fromBufferOutput, schedule);
425 outputsStack[outputIndex].numPushed += pushed;
426 continue;
427 }
428 }
429
430 // If the associated output is not scheduled, or it does not
431 // have a pass-through scheduled, we need to consider all
432 // connected source outputs!
433 const bool pushed = _PushBackOutputForSpeculation(
434 &outputsStack, *input.source, schedule);
435 outputsStack[outputIndex].numPushed += pushed;
436 }
437
438 // Little optimization to not go back to the top of the loop
439 // for no reason.
440 if (outputsStack[outputIndex].numPushed > 0) {
441 break;
442 } // else fall through to the next stage.
443
444
445 default:
446
447 // Mark whether or not our read/write inputs depend on
448 // a speculation.
449 outputsStack.back().inputsSpeculate |= previousStageSpeculated;
450
451 // Set a bit indicating that this node has been executed.
452 executedNodes->Set(scheduleNodeIndex);
453
454 // If any of our inputs speculated, there is nothing we can do.
455 // Skip this node, but make sure to still touch its outputs.
456 if (outputsStack.back().inputsSpeculate) {
457
458 TF_DEBUG(VDF_SEE_TRACE)
459 .Msg("SpeculationSkipNode (cycle) (\"%s\"); }\n",
460 node.GetDebugName().c_str());
461
462 // This node has speculated inputs
463 speculatedNodes->Set(scheduleNodeIndex);
464 speculated.push_back(true);
465
466 // Compute this node, if it is affective, or pass-through if any
467 // of the reads speculated.
468 } else if (affective) {
469 // None of our inputs speculated, we can just compute as
470 // normal.
471 Base::_ComputeNode(state, node);
472 speculated.push_back(false);
473
474 TF_DEBUG(VDF_SEE_TRACE)
475 .Msg("SpeculationComputedNode(\"%s\"); }\n",
476 node.GetDebugName().c_str());
477
478 // The node is not affective, and none of its reads or read/writes
479 // did speculate.
480 } else {
481 // None of the outputs on this node contribute to the
482 // results in the request, so we will skip over this node
483 // by passing through all the outputs with associated
484 // inputs and use the fallback value for all the outputs
485 // that don't.
486 TF_DEBUG(VDF_SEE_TRACE)
487 .Msg("SpeculationPassThrough(\"%s\"); }\n",
488 node.GetDebugName().c_str());
489
490 Base::_PassThroughNode(schedule, node);
491 speculated.push_back(false);
492
493 }
494
495 // Check interruption.
496 hasBeenInterrupted = Base::_GetExecutor().HasBeenInterrupted();
497
498 // Mark that we've visited these outputs in our parent
499 // executor. We need to tell the parent executor that
500 // we've visited this node so that we receive invalidation
501 // the next time it is required. If we don't mark the
502 // output as needing invalidation and the main executor
503 // never needs to execute it, then it will never get
504 // invalidated.
505 // Also write back any computed or pass-through data to the
506 // write back executor, so that the data can be picked up by
507 // another executor. Note, that we do NOT want to write back
508 // any data after interruption, because the buffers may
509 // contain junk.
510 VDF_FOR_EACH_SCHEDULED_OUTPUT_ID(outputId, schedule, node) {
511 const VdfOutput& output = *schedule.GetOutput(outputId);
512 if (_writeBackExecutor &&
513 !hasBeenInterrupted &&
514 !outputsStack.back().inputsSpeculate) {
515 _WriteBackComputedOutput(output, outputId, schedule);
516 }
517 Base::_GetExecutor()._TouchOutput(output);
518 }
519
520 outputsStack.pop_back();
521 }
522 }
523
524 TF_DEBUG(VDF_SEE_TRACE).Msg("SpeculationOutputExecuteEnd(); }\n");
525}
526
527template <typename DataManagerType>
528void
530 const VdfOutput &output,
531 const VdfSchedule::OutputId &outputId,
532 const VdfSchedule &schedule)
533{
534 // Retrieve the data handle.
535 const typename Base::_DataHandle dataHandle =
536 Base::_GetDataManager()->GetDataHandle(output.GetId());
537 if (!Base::_GetDataManager()->IsValidDataHandle(dataHandle)) {
538 return;
539 }
540
541 // Get the buffer data associated with the data handle.
542 VdfExecutorBufferData *bufferData =
543 Base::_GetDataManager()->GetBufferData(dataHandle);
544
545 // Get the output vector and computed output mask
546 const VdfVector *value = bufferData->GetExecutorCache();
547
548 // If the data is not available we are done. This can happen with
549 // node that manage their own buffers and choose to leave them empty.
550 if (!value) {
551 return;
552 }
553
554 const VdfMask &computedMask = bufferData->GetExecutorCacheMask();
555
556 // If the computed output mask is empty, we can bail out early. This may
557 // happen if, for example, the executor was interrupted and opted for not
558 // writing a computed output mask for the current node.
559 // Don't even bother writing back an all-zeros mask.
560 if (computedMask.IsEmpty() || computedMask.IsAllZeros()) {
561 return;
562 }
563
564 // If the output does not pass its data, we can write the full output value
565 // back to the write executor.
566 if (!output.GetAssociatedInput()) {
567 _writeBackExecutor->SetOutputValue(output, *value, computedMask);
568
569 // Reclaim locally, so that future cache lookups result in hits on
570 // the parent executor, but not the local executor.
571 //
572 // XXX
573 // This guards against client callbacks that mutate cached values (which
574 // is something we have encountered in practice), causing output values
575 // to change after the node has already run. By removing the buffer
576 // locally, we ensure that the next time we access the buffer we get it
577 // from the parent executor and modify it there. We would prefer to not
578 // support this client behavior, but for now, we choose to keep this,
579 // since it's not expensive, and safer.
580 bufferData->ResetExecutorCache();
581 }
582
583 // If the output passes its data, we may still be able to write back some
584 // or all of it.
585 else {
586 // If this output is not scheduled to pass its data, we can simply copy
587 // the entire executor cache. Alternatively, if the output is scheduled
588 // to pass its data, we can at least copy anything that will be kept at
589 // the output. Unless, however, invalidation entered somewhere between
590 // this output, and the output we are going to pass the data to. If this
591 // is the case, we want to write back the entire cache to the write back
592 // executor, making this algorithm the equivalent of mung buffer locking
593 // on the main executor!
594 const VdfMask *writeBackMask = &computedMask;
595 const VdfOutput *passToOutput = schedule.GetPassToOutput(outputId);
596 if (passToOutput &&
597 !_writeBackExecutor->HasInvalidationTimestampMismatch(
598 output, *passToOutput)) {
599 writeBackMask = &schedule.GetKeepMask(outputId);
600 }
601
602 if (!writeBackMask->IsEmpty()) {
603 _writeBackExecutor->SetOutputValue(output, *value, *writeBackMask);
604 }
605 }
606}
607
608PXR_NAMESPACE_CLOSE_SCOPE
609
610#endif
Fast bit array that keeps track of the number of bits set and can find the next set in a timely manne...
Definition: bits.h:49
void Set(size_t index)
Sets bit # index to one.
Definition: bits.h:377
bool IsSet(size_t index) const
Returns true, if bit # index is set.
Definition: bits.h:412
A context is the parameter bundle passed to callbacks of computations.
Definition: context.h:40
This object holds state that remains persistent during one round of network evaluation.
const VdfSchedule & GetSchedule() const
The schedule used for evaluation.
This object is responsible for storing the executor buffer data, comprised of the executor cache vect...
void ResetExecutorCache(const VdfMask &mask)
Reset the executor cache without releasing any memory and set the executor cache mask to mask.
const VdfMask & GetExecutorCacheMask() const
Get the available mask.
VdfVector * GetExecutorCache() const
Returns the executor cache stored at this buffer data instance.
A client may instantiate an object of this class and set it in an executor, to collect errors that ma...
Abstract base class for classes that execute a VdfNetwork to compute a requested set of values.
A VdfMask is placed on connections to specify the data flowing through them.
Definition: mask.h:37
bool IsAllZeros() const
Returns true if this mask has all entries unset.
Definition: mask.h:206
bool IsEmpty() const
Returns true if this mask is empty, i.e.
Definition: mask.h:168
Class to hold on to an externally owned output and a mask.
Definition: maskedOutput.h:32
VdfOutput * GetOutput() const
Returns the VdfOutput.
Definition: maskedOutput.h:52
const VdfMask & GetMask() const
Returns the VdfMask.
Definition: maskedOutput.h:64
This is the base class for all nodes in a VdfNetwork.
Definition: node.h:53
VDF_API const std::string GetDebugName() const
Returns the debug name for this node, if one is registered.
virtual VDF_API VdfRequiredInputsPredicate GetRequiredInputsPredicate(const VdfContext &context) const
Returns a predicate, determining whether a given input and its connections are required in order to f...
A VdfOutput represents an output on a node.
Definition: output.h:32
VdfId GetId() const
The unique id of this output.
Definition: output.h:100
const VdfInput * GetAssociatedInput() const
Returns the in/out connector associated with this output.
Definition: output.h:76
This class is a collection of common functions used by pulled based executors.
_ExecutionStage
This enum describes the stages that a node goes through in execution.
This predicate determines whether a given input value is needed to fulfill the input dependencies req...
bool IsRequiredRead(const VdfInput &input) const
Is this input a required read? Note that read/writes as well as prerequisite inputs are not required ...
bool HasRequiredReads() const
Are any inputs required?
An OutputId is a small key object that, once obtained for a particular VdfOutput, can be used to quer...
Definition: schedule.h:91
bool IsValid() const
Returns whether this OutputId can be used to make queries about an output's scheduling.
Definition: schedule.h:97
Contains a specification of how to execute a particular VdfNetwork.
Definition: schedule.h:41
const VdfNetwork * GetNetwork() const
Returns the network for this schedule.
Definition: schedule.h:178
VDF_API OutputId GetOutputId(const VdfOutput &output) const
Returns a small, cheap OutputId, which can be passed to other Get* methods in this class to efficient...
VDF_API const VdfMask & GetRequestMask(const OutputId &outputId) const
Returns the request mask associated with the given OutputId.
VDF_API const VdfOutput * GetFromBufferOutput(const OutputId &outputId) const
Returns the "from buffer's" output associated with the given OutputId.
VDF_API InputsRange GetInputs(const VdfNode &node) const
Returns a range of inputs scheduled for the given node.
int GetScheduleNodeIndex(const OutputId &outputId) const
Returns the node index of the schedule node associated with the given outputId.
Definition: schedule.h:514
VDF_API const VdfMask & GetKeepMask(const OutputId &outputId) const
Returns the keep mask associated with the given OutputId.
VDF_API const VdfOutput * GetPassToOutput(const OutputId &outputId) const
Returns the "pass to" output associated with the given OutputId.
VDF_API const VdfNode * GetNode(const OutputId &outputId) const
Returns the VdfNode that owns the VdfOutput associated with the given outputId.
ScheduleNodeVector & GetScheduleNodeVector()
Returns the vector of schedule nodes in this schedule.
Definition: schedule.h:503
VDF_API bool IsAffective(const OutputId &outputId) const
Returns true if the output is expected to have an effect on its corresponding input,...
VDF_API const VdfOutput * GetOutput(const OutputId &outputId) const
Returns the scheduled VdfOutput associated with the given OutputId.
This class provides an executor engine to the speculation executor.
VdfSpeculationExecutorEngine(const VdfSpeculationExecutorBase &speculationExecutor, DataManagerType *dataManager)
Constructs an engine used by the speculation executor.
void RunSchedule(const VdfSchedule &schedule, const VdfRequest &computeRequest, VdfExecutorErrorLogger *errorLogger)
Executes the given schedule with a computeRequest and an optional /p errorLogger.
This class is used to abstract away knowledge of the cache data used for each node.
Definition: vector.h:56
#define TF_DEBUG(enumVal)
Evaluate and print debugging message msg if enumVal is enabled for debugging.
Definition: debug.h:484
#define TF_DEV_AXIOM(cond)
The same as TF_AXIOM, but compiled only in dev builds.
Definition: diagnostic.h:205
#define TF_VERIFY(cond, format,...)
Checks a condition and reports an error if it evaluates false.
Definition: diagnostic.h:266
This class contains scheduling information for an input.
Definition: scheduleNode.h:73