Replies: 10 comments 19 replies
-
The previous examples assume a flat combination of all parameters, although there is likely a need to have a layered view of parameters as in multi-layer nesting. Example sketch:
This is different than saying "all combiantions of (a,b,c) and (1,2,3)", as this simple example says nothing about the structure of traversal, ordering, expressions in the native workload, etc. |
Beta Was this translation helpful? Give feedback.
-
Example workload template for parameter expansion:
With this set of template parameters, it would be possible for a user to generate 12 distinct scenarios, each with two commands to run an activity, both of which contain the specific combination of the 12 possible from the supplied values for dataset and k. This is just to demonstrate the basic scenario of applying combinations of parameters over a single layer image of rendered templates. The equivalent long-form rendering would look like this:
|
Beta Was this translation helpful? Give feedback.
-
I think that it is important to be able to present the proper layers of the tests. The |
Beta Was this translation helpful? Give feedback.
-
I can see where this can be implemented. |
Beta Was this translation helpful? Give feedback.
-
Let's look at the following:
Would it make sense to use ITERATION instead of TEMPLATE?
|
Beta Was this translation helpful? Give feedback.
-
I think we've settled on this format for the yaml:
|
Beta Was this translation helpful? Give feedback.
-
Working on implementation in #1657 |
Beta Was this translation helpful? Give feedback.
-
How about this, which reads similarly to the original form?
|
Beta Was this translation helpful? Give feedback.
-
Each scenario step does the following in allowing key=values set on the step to supercede those set from the command line. // consume each of the parameters from the steps to produce a composited command
// order is primarily based on the step template, then on user-provided parameters
for (CmdArg cmdarg : parsedStep.values()) {
// allow user provided parameter values to override those in the template,
// if the assignment operator used in the template allows for it
if (usersCopy.containsKey(cmdarg.getName())) {
cmdarg = cmdarg.override(usersCopy.remove(cmdarg.getName()));
}
buildingCmd.put(cmdarg.getName(), cmdarg.toString());
} Is this the proper order of precedence? |
Beta Was this translation helpful? Give feedback.
-
In looking at how we are actually using dataset in existing workloads it turns out that the dataset choice should automatically set other key value pairs. For example: textembedding-gecko:
dimensions: 768
trainsize: 100000
testsize: 10000
sfunction: cosine
glove-25-angular:
dimensions: 25
trainsize: 1183514
testsize: 10000
indexname: glove25
sfunction: cosine I would like to extend NB5 as follows:
Take a look at min_version: "5.17.3"
description: |
A workload which reads ann-benchmarks vector data from HDF5 file format.
scenarios:
pinecone_vectors:
rampup: run tags==block:vector_writes cycles=TEMPLATE(trainsize) threads=TEMPLATE(rampup_threads,100) driver=pinecone apiKey=TEMPLATE(apikey) projectName=f88a480 environment=eu-west4-gcp
search_and_index: run tags='block:vector_reads' threads=TEMPLATE(search_threads,100) driver=pinecone apiKey=TEMPLATE(apikey) projectName=f88a480 environment=eu-west4-gcp stride=100 striderate=13.5
bindings:
rw_key: ToString()
train_vector: HdfFileToFloatList("testdata/TEMPLATE(dataset).hdf5", "/train");
test_vector: HdfFileToFloatList("testdata/TEMPLATE(dataset).hdf5", "/test");
validation_set: HdfFileToIntArray("testdata/TEMPLATE(dataset).hdf5", "/neighbors");
random_vector: HashedFloatVectors(TEMPLATE(dimensions));
blocks:
vector_writes:
ops:
rampup-op:
upsert: TEMPLATE(index_name)
namespace: TEMPLATE(namespace)
upsert_vectors:
- id: "{rw_key}"
values: "{train_vector}"
vector_invalidate_writes:
ops:
rampup-op:
upsert: TEMPLATE(index_name)
namespace: TEMPLATE(namespace)
upsert_vectors:
- id: "{rw_key}"
values: "{random_vector}"
vector_reads:
ops:
read-op:
query: TEMPLATE(index_name)
namespace: TEMPLATE(namespace)
vector: "{test_vector}"
top_k: TEMPLATE(select_limit,100)
include_values: true
include_metadata: true
verifier-init: |
relevancy=scriptingmetrics.newRelevancyMeasures(_parsed_op,"group","relevancy");
for (int k in List.of(100)) {
relevancy.addFunction(io.nosqlbench.engine.extensions.computefunctions.RelevancyFunctions.recall("recall",k));
relevancy.addFunction(io.nosqlbench.engine.extensions.computefunctions.RelevancyFunctions.precision("precision",k));
relevancy.addFunction(io.nosqlbench.engine.extensions.computefunctions.RelevancyFunctions.F1("F1",k));
relevancy.addFunction(io.nosqlbench.engine.extensions.computefunctions.RelevancyFunctions.reciprocal_rank("RR",k));
relevancy.addFunction(io.nosqlbench.engine.extensions.computefunctions.RelevancyFunctions.average_precision("AP",k));
}
verifier: |
// driver-specific function
actual_indices=pinecone_utils.responseIdsToIntArray(result)
// driver-agnostic function
relevancy.accept({validation_set},actual_indices);
return true; The scenario block could change like this: (I'm skipping the foreach entries) scenarios:
pinecone_vectors:
rampup: run tags==block:vector_writes cycles={trainsize} threads=TEMPLATE(rampup_threads,100) driver=pinecone apiKey=TEMPLATE(apikey) projectName=f88a480 environment=eu-west4-gcp
search_and_index: run tags='block:vector_reads' threads=TEMPLATE(search_threads,100) driver=pinecone apiKey=TEMPLATE(apikey) projectName=f88a480 environment=eu-west4-gcp stride=100 striderate=13.5 Cycles (trainsize) is a function of the chosen dataset. For bindings it's even more live data: bindings:
rw_key: ToString()
train_vector: HdfFileToFloatList("testdata/{dataset}.hdf5", "/train");
test_vector: HdfFileToFloatList("testdata/{dataset}.hdf5", "/test");
validation_set: HdfFileToIntArray("testdata/{dataset}.hdf5", "/neighbors");
random_vector: HashedFloatVectors({dimensions}); Here's a block ops with changes: vector_writes:
ops:
rampup-op:
upsert: {index_name}
namespace: TEMPLATE(namespace)
upsert_vectors:
- id: "{rw_key}"
values: "{train_vector}" Should we somehow make these into bindings for their use in ops? |
Beta Was this translation helpful? Give feedback.
-
Goals
We need to be able to iterate over a set of parameter combinations so that we can test all meaningful cases around a given set of questions.
Example question: Which tuning parameters work best across our canonical datasets to achieve a minimum recall of 90% while maintaining high performance on a standard deployment?
Practical result:
Motivating Example
Suppose you want to understand relevancy measures over a set of workloads, K, and system loads. Here are some parameters that you might use:
This yields a search space of 240 distinct combinations, which is an incredible number of tests to run for a single battery. Ideally, we do not run all of these as full-length tests. It becomes feasible to run these at this grain of detail if each combination can be test quickly and with cohesion between the parameters and the results for each.
Given that large search spaces like this yield higher value information when you measure the edges of the space first, it should be default that the bounds of each parameter are tested first. This is simply using the parameters provided while ignoring any interior value. Thus, the search space becomes 2^P (P is the set of parameter names)
Possible Approaches
template variable enhancements
We presently support
TEMPLATE(name,default)
style variables which can contain a single value. If we were allowed to provide values for these template variables as inputs, perhaps from a list form likename=[glove25,glove50,...]
then it would be simple to build an iterable form of the workload over all combinations.structural templates
Introduce a in-yaml (and in-json) templating scheme based on examples from other projects to auto-render combinations from procedural template language definitions.
jsonnet
lean on Jsonnet more to render these blocks.
Beta Was this translation helpful? Give feedback.
All reactions