Allow workload modules of different workers and rounds to share lightweight data
Please share the technical limitation of Caliper that you encountered.
Workload modules of different rounds and workers require some common data to be shared (e.g., a common seed for random number generators). This data might/should be different for every Caliper run, possibly calculated during a workload module run (thus it can't be part of the benchmark configuration file, or it would decrease usability/flexibility if doing so).
Please detail your feature idea that could alleviate the limitation.
A feature similar to the connectors' would solve this problem. Before starting any round, the manager service could instantiate the workload modules of all rounds, and call an optional "prepare" function, allowing the modules to fill a provided object with custom data. Later on, this object is also distributed to all workers for all rounds when executing the actual workload.
This (unpersisted) mechanism is intended to be used only for small, configuration-like data sharing, and not as full-fledged state management.
Please share some details about your use case if possible, and how the new feature would make Caliper a better performance benchmarking framework.
The use case is implementing a benchmark that requires a random seed that must be consistent across all workers during the entire Caliper run. The proposed state sharing feature would enable Caliper to support more advanced workloads that require lightweight coordination when used in distributed mode (i.e., with more than one worker).
Please share any suggestions about the new feature's code/configuration API (using formatted YAML segments or pseudo-code).
The workload module interface could be extended with the following parts (while retaining backward compatibility):
class WorkloadModuleInterface {
async prepareWorkerParameters(workerParameters) {
// no-op, for backward compatibility
}
async initializeWorkloadModule(workerIndex, totalWorkers, roundIndex, roundArguments, sutAdapter, sutContext, workerParameters) {
// last parameter, for backward compatibility
throw new Error('WorkloadModuleInterface.initializeWorkloadModule() must be implemented in derived class');
}
// other functions ....
}
Then, the manager service could seed the calls with an initially empty "state", like this:
let workerParameters = {};
for (let workloadModule of workloadModules) {
let wm = require(workloadModule);
if (wm.prepareWorkerParameters && typeof wm.prepareWorkerParameters === 'function') {
// workload modules manipulate the same state
await wm.prepareWorkerParameters(workerParameters);
}
}