Commit c9ce99c0 authored by Tom Jackson's avatar Tom Jackson Committed by Jordan DeLong

Composed, for lightweight operator composition

Summary:
Sometimes it'll be handy to have a custom operator which is little more than the
composition of a few base operators. This makes that really easy to do, as shown
in examples, tests, and benchmarks.

Test Plan: Unit tests, benchmarks

Reviewed By: rsagula@fb.com

FB internal diff: D617152
parent 24f6247d
...@@ -92,6 +92,16 @@ class Operator : public FBounded<Self> { ...@@ -92,6 +92,16 @@ class Operator : public FBounded<Self> {
class Value, class Value,
class ResultGen = void> class ResultGen = void>
ResultGen compose(const GenImpl<Value, Source>& source) const; ResultGen compose(const GenImpl<Value, Source>& source) const;
/**
* operator|() - For composing two operators without binding it to a
* particular generator.
*/
template<class Next,
class Composed = detail::Composed<Self, Next>>
Composed operator|(const Operator<Next>& op) const {
return Composed(this->self(), op.self());
}
protected: protected:
Operator() = default; Operator() = default;
Operator(const Operator&) = default; Operator(const Operator&) = default;
...@@ -779,6 +789,39 @@ class Order : public Operator<Order<Selector, Comparer>> { ...@@ -779,6 +789,39 @@ class Order : public Operator<Order<Selector, Comparer>> {
} }
}; };
/**
* Composed - For building up a pipeline of operations to perform, absent any
* particular source generator. Useful for building up custom pipelines.
*
* This type is usually used by just piping two operators together:
*
* auto valuesOf = filter([](Optional<int>& o) { return o.hasValue(); })
* | map([](Optional<int>& o) -> int& { return o.value(); });
*
* auto valuesIncluded = from(optionals) | valuesOf | as<vector>();
*/
template<class First,
class Second>
class Composed : public Operator<Composed<First, Second>> {
const First first_;
const Second second_;
public:
Composed() {}
Composed(const First& first, const Second& second)
: first_(first)
, second_(second) {}
template<class Source,
class Value,
class FirstRet = decltype(std::declval<First>()
.compose(std::declval<Source>())),
class SecondRet = decltype(std::declval<Second>()
.compose(std::declval<FirstRet>()))>
SecondRet compose(const GenImpl<Value, Source>& source) const {
return second_.compose(first_.compose(source.self()));
}
};
/* /*
* Sinks * Sinks
*/ */
......
...@@ -194,6 +194,8 @@ class Skip; ...@@ -194,6 +194,8 @@ class Skip;
template<class Selector, class Comparer = Less> template<class Selector, class Comparer = Less>
class Order; class Order;
template<class First, class Second>
class Composed;
/* /*
* Sinks * Sinks
......
...@@ -128,7 +128,6 @@ BENCHMARK(Fib_Sum_NoGen, iters) { ...@@ -128,7 +128,6 @@ BENCHMARK(Fib_Sum_NoGen, iters) {
}; };
for (auto& v : fib(testSize.load())) { for (auto& v : fib(testSize.load())) {
s += v; s += v;
v = s;
} }
} }
folly::doNotOptimizeAway(s); folly::doNotOptimizeAway(s);
...@@ -166,7 +165,7 @@ BENCHMARK_RELATIVE(Fib_Sum_Gen_Static, iters) { ...@@ -166,7 +165,7 @@ BENCHMARK_RELATIVE(Fib_Sum_Gen_Static, iters) {
int s = 0; int s = 0;
while (iters--) { while (iters--) {
auto fib = generator<int>(FibYielder()); auto fib = generator<int>(FibYielder());
s += fib | take(30) | sum; s += fib | take(testSize.load()) | sum;
} }
folly::doNotOptimizeAway(s); folly::doNotOptimizeAway(s);
} }
...@@ -239,31 +238,64 @@ BENCHMARK_RELATIVE(Concat_Gen, iters) { ...@@ -239,31 +238,64 @@ BENCHMARK_RELATIVE(Concat_Gen, iters) {
folly::doNotOptimizeAway(s); folly::doNotOptimizeAway(s);
} }
BENCHMARK_DRAW_LINE()
BENCHMARK(Composed_NoGen, iters) {
int s = 0;
while (iters--) {
for (auto& i : testVector) {
s += i * i;
}
}
folly::doNotOptimizeAway(s);
}
BENCHMARK_RELATIVE(Composed_Gen, iters) {
int s = 0;
auto sumSq = map(square) | sum;
while (iters--) {
s += from(testVector) | sumSq;
}
folly::doNotOptimizeAway(s);
}
BENCHMARK_RELATIVE(Composed_GenRegular, iters) {
int s = 0;
while (iters--) {
s += from(testVector) | map(square) | sum;
}
folly::doNotOptimizeAway(s);
}
// Results from a dual core Xeon L5520 @ 2.27GHz: // Results from a dual core Xeon L5520 @ 2.27GHz:
// //
// ============================================================================ // ============================================================================
// folly/experimental/test/GenBenchmark.cpp relative time/iter iters/s // folly/experimental/test/GenBenchmark.cpp relative time/iter iters/s
// ============================================================================ // ============================================================================
// Sum_Basic_NoGen 301.60ns 3.32M // Sum_Basic_NoGen 301.60ns 3.32M
// Sum_Basic_Gen 103.20% 292.24ns 3.42M // Sum_Basic_Gen 104.27% 289.24ns 3.46M
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
// Sum_Vector_NoGen 200.33ns 4.99M // Sum_Vector_NoGen 200.33ns 4.99M
// Sum_Vector_Gen 99.44% 201.45ns 4.96M // Sum_Vector_Gen 99.81% 200.70ns 4.98M
// ----------------------------------------------------------------------------
// Count_Vector_NoGen 12.37us 80.84K
// Count_Vector_Gen 103.09% 12.00us 83.33K
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
// Count_Vector_NoGen 19.07fs 52.43T // Fib_Sum_NoGen 3.66us 273.21K
// Count_Vector_Gen 166.67% 11.44fs 87.38T // Fib_Sum_Gen 43.06% 8.50us 117.65K
// Fib_Sum_Gen_Static 87.81% 4.17us 239.89K
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
// Fib_Sum_NoGen 4.15us 241.21K // VirtualGen_0Virtual 10.04us 99.61K
// Fib_Sum_Gen 48.75% 8.50us 117.58K // VirtualGen_1Virtual 29.59% 33.93us 29.47K
// Fib_Sum_Gen_Static 113.24% 3.66us 273.16K // VirtualGen_2Virtual 20.45% 49.10us 20.37K
// VirtualGen_3Virtual 15.49% 64.82us 15.43K
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
// VirtualGen_0Virtual 10.05us 99.48K // Concat_NoGen 2.50us 400.37K
// VirtualGen_1Virtual 29.63% 33.93us 29.47K // Concat_Gen 102.50% 2.44us 410.37K
// VirtualGen_2Virtual 20.47% 49.09us 20.37K
// VirtualGen_3Virtual 15.30% 65.68us 15.23K
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
// Concat_NoGen 2.34us 427.15K // Composed_NoGen 549.54ns 1.82M
// Concat_Gen 90.04% 2.60us 384.59K // Composed_Gen 101.39% 542.00ns 1.85M
// Composed_GenRegular 99.66% 551.40ns 1.81M
// ============================================================================ // ============================================================================
int main(int argc, char *argv[]) { int main(int argc, char *argv[]) {
......
...@@ -166,6 +166,19 @@ TEST(Gen, Until) { ...@@ -166,6 +166,19 @@ TEST(Gen, Until) {
EXPECT_EQ(31, gen | count); EXPECT_EQ(31, gen | count);
} }
TEST(Gen, Composed) {
// Operator, Operator
auto valuesOf =
filter([](Optional<int>& o) { return o.hasValue(); })
| map([](Optional<int>& o) -> int& { return o.value(); });
std::vector<Optional<int>> opts {
none, 4, none, 6, none
};
EXPECT_EQ(4 * 4 + 6 * 6, from(opts) | valuesOf | map(square) | sum);
// Operator, Sink
auto sumOpt = valuesOf | sum;
EXPECT_EQ(10, from(opts) | sumOpt);
}
TEST(Gen, Chain) { TEST(Gen, Chain) {
std::vector<int> nums {2, 3, 5, 7}; std::vector<int> nums {2, 3, 5, 7};
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment