GrPPI  0.2
Generic and Reusable Parallel Pattern Interface
Farm pattern

The farm pattern (or task-farm) is a streaming pattern that applies an operation to every element in a stream.

The interface to the farm pattern is provided by function grppi::farm(). As all functions in GrPPI, this function takes as its first argument an execution policy.

grppi::farm(exec, other_arguments...);

Farm variants

There are several variants:

Key elements in a farm

The key elements of a farm are the Generator, the Transformer, and the Consumer.

The central element in a farm is the Transformer. The operation may be any C++ callable entity. This operation, is a unary operation taking a data item and returning its transformation. Thus, a transformer op is any operation that, given an input value x of type T makes valid the following:

U res{transformer(x)};

A stand-alone farm also has a Generator. The generator may be any C++ callable entity that produces values in a way that allows to signal an indication of end-of stream. For this purpose, the farm requires that the generator produces values of any type that is convertible to bool and can be dereferenced:

auto r = generator(); // r is a value generated by a generator.
if (!r) { /* ... */ } // !r is convertible to bool
if (r) { // r is convertible to bool
auto y = *r; // r can be dereferenced.
}

Additionally a farm may have a Consumer. The consumer may be any C++ callable entity that takes values of the result type of the Transformer.

auto t = transformer(x);
consumer(t);

Details on farm variants

Stand-alone transforming farm

A stand-alone transforming farm transforms each generated element in a stream and then consumes it.


Example: Generate a sequence of integers and print the square of each value.

using namespace std;
using namespace experimental;
//...
int n = 10;
[]() -> optional<int> {
n--;
if (n>0) return n;
else {};
},
[](int x) { return x*x; },
[](int x) {
cout << x << " ";
}
);

Stand-alone identity farm

A stand-alone identity farm generates a stream of data items and then consumes them.


Example: Read a file and write to the standard output one word per line.

using namespace std;
using namespace experimental;
//...
ifstream file{"file.txt"};
[&file]() -> optional<string> {
string word;
file >> word;
if (file) return word;
else return {};
},
[](auto word) {
cout << word << " , " << word.length() << endl;
},
);

Composable farm

A composable farm applies a Transformer to each data item in a stream. The farm can be inserted into another upper level pattern (which will be responsible for generation and consumption)


Example: Use a farm as a stage of a composed pipeline.

stageA,
stageB,
grppi::farm(exec, [](auto x) {
return x.length();
}),
stageC
);

Note: For brevity we do not show here the details of other stages.

For composing complex patterns, the farm() function may be used to create an object that may be used later in the composition.


Example: Build a farm as a composable pattern and use later as a stage of a pipeline.

auto print_long_words = grppi::farm(exec, [](auto x) {
if (x.length() > 4) std::cout << x << std::endl;
});
stageA,
stageB,
print_long_words,
stageC
);

Note: For brevity we do not show here the details of other stages.