foundationdb/fdbrpc/dsltest.actor.cpp

1469 lines
35 KiB
C++

/*
* dsltest.actor.cpp
*
* This source file is part of the FoundationDB open source project
*
* Copyright 2013-2018 Apple Inc. and the FoundationDB project authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <iostream>
#include <algorithm>
#include "flow/FastRef.h"
#undef ERROR
#include "fdbrpc/simulator.h"
#include "fdbrpc/ActorFuzz.h"
#include "flow/DeterministicRandom.h"
#include "flow/ThreadHelper.actor.h"
#include "flow/actorcompiler.h" // This must be the last #include.
using std::cout;
using std::endl;
using std::vector;
void* allocateLargePages( int total );
bool testFuzzActor( Future<int>(*actor)(FutureStream<int> const&, PromiseStream<int> const&, Future<Void> const&), const char* desc, vector<int> const& expectedOutput ) {
// Run the test 5 times with different "timing"
int i, outCount;
bool ok = true;
for(int trial=0; trial<5; trial++) {
PromiseStream<int> in, out;
Promise<Void> err;
int before = deterministicRandom()->randomInt(0, 4);
int errorBefore = before + deterministicRandom()->randomInt(0, 4);
//printf("\t\tTrial #%d: %d, %d\n", trial, before, errorBefore);
if (errorBefore <= before) err.sendError( operation_failed() );
for(i=0; i<before; i++) {
in.send( (i+1) * 1000 );
}
Future<int> ret = (*actor)(in.getFuture(), out, err.getFuture());
while (i < 1000000 && !ret.isReady()) {
i++;
if (errorBefore == i) err.sendError( operation_failed() );
in.send( i * 1000 );
}
if (ret.isReady()) {
if (ret.isError())
out.send( ret.getError().code() );
else
out.send( ret.get() );
} else {
printf("\tERROR: %s did not return after consuming %d input values\n", desc, i);
if (trial) printf("\t\tResult was inconsistent between runs! (Trial %d)\n", trial);
ok = false;
//return false;
}
outCount = -1;
while (out.getFuture().isReady()) {
int o = out.getFuture().pop();
outCount++;
if (outCount < expectedOutput.size() && expectedOutput[outCount] != o) {
printf("\tERROR: %s output #%d incorrect: %d != expected %d\n", desc, outCount, o, expectedOutput[outCount]);
if (trial) printf("\t\tResult was inconsistent between runs!\n");
ok = false;
//return false;
}
}
if (outCount+1 != expectedOutput.size()) {
printf("\tERROR: %s output length incorrect: %d != expected %zu\n", desc, outCount+1, expectedOutput.size());
if (trial) printf("\t\tResult was inconsistent between runs!\n");
ok = false;
//return false;
}
// We might have put in values that weren't actually consumed...
while (in.getFuture().isReady()) {
in.getFuture().pop();
i--;
}
}
//printf("\t%s: OK, %d input values -> %d output values\n", desc, i, outCount);
return ok;
}
#if 0
void memoryTest2() {
const int Size = 2000 << 20;
const int Reads = 4 << 20;
const int MaxThreads = 4;
char* block = new char[Size];
memset(block, 0, Size);
char** random = new char*[ Reads * MaxThreads ];
random[0] = block;
for(int i=1; i<Reads*MaxThreads; ) {
char *s = &block[ deterministicRandom()->randomInt(0, Size) ];
random[i++] = s;
/*for(int j=0; j<10 && i<Reads*MaxThreads; j++,i++) {
random[i] = s + deterministicRandom()->randomInt(0, 4096);
if (random[i] >= block+Size) random[i] -= Size;
}*/
}
for(int threads=1; threads<=MaxThreads; threads++) {
double tstart = timer();
vector<ThreadFuture<Void>> done;
for(int t=0; t<threads; t++) {
char** r = random + Reads*t;
done.push_back(
inThread<Void>( [r,Reads] () -> Void {
for(int i=0; i<Reads; i++)
if ( *r[i] )
cout << "Does not happen" << endl;
return Void();
}));
}
waitForAll(done).getBlocking();
double duration = timer() - tstart;
cout << format("%d threads: %f sec, %0.2fM/sec", threads, duration, Reads*threads/1e6/duration) << endl;
}
}
#endif
enum {MaxTraversalsPerThread = 64};
void showNumaStatus();
void* numaAllocate(size_t size);
#if 0
void memoryTest() {
//memoryTest2();
//return;
showNumaStatus();
const int N = 128<<20; // 128 = 1GB
const int N2 = 8<<20;
cout << "Preparing memory test with " << N / 1e6 * sizeof(void*) << " MB" << endl;
void **x;
if (0) {
cout << " NUMA large pages" << endl;
x = (void**)numaAllocate(size_t(N)*sizeof(void*));
} else if (1) {
cout << " Normal pages" << endl;
x = new void*[ N ];
printf(" at %p\n", x);
} else {
cout << " Large pages" << endl;
x = (void**)allocate(N*sizeof(void*), true);
}
memset(x, 0, ((int64_t)N) * sizeof(void*));
showNumaStatus();
if (1) {
cout <<" Random permutation" << endl;
// Random cyclic permutation
for(int i=0; i<N; i++)
x[i] = &x[i];
// Sattolo's algorithm
for(int n = N-1; n >= 1; n--) {
int k = deterministicRandom()->randomInt(0, n); //random.IRandomX(0, n-1);
std::swap( x[k], x[n] );
}
} else {
cout <<" Sequential permutation" << endl;
// Sequential
for(int i=0; i<N-1; i++)
x[i] = &x[i+1];
x[N-1] = &x[0];
}
void **p = x;
for(int i=0; i<N; i++) {
p = (void**)*p;
if (p == x) {
cout << "Cycle " << i << endl;
if (i != N-1) terminate();
}
}
const int MT = 16;
for(int TraversalsPerThread = 1; TraversalsPerThread <= MaxTraversalsPerThread; TraversalsPerThread *= 2)
{
const int PseudoThreads = MT * TraversalsPerThread;
void **starts[MT*MaxTraversalsPerThread];
for(int t=0; t<PseudoThreads; t++) {
starts[t] = &x[ N/PseudoThreads * t ];
//starts[t] = &x[ deterministicRandom()->randomInt(0,N) ];
}
for(int T=1; T<=MT; T+=T) {
double start = timer();
vector< Future<double> > done;
for(int t=0; t<T; t++) {
void*** start = starts + t*TraversalsPerThread;
done.push_back(
inThread<double>( [start,N2,TraversalsPerThread] () -> double {
void **p[MaxTraversalsPerThread];
for(int j=0; j<TraversalsPerThread; j++)
p[j] = start[j];
for(int i=0; i<N2; i++)
for(int j=0; j<TraversalsPerThread; j++) {
p[j] = (void**)*p[j];
if (TraversalsPerThread > 1)
_mm_prefetch( (const char*)p[j], _MM_HINT_T0 );
}
for(int j=0; j<TraversalsPerThread; j++)
if (p[j] == p[(j+1)%TraversalsPerThread])
cout << "N";
return timer();
}));
}
double firstEnd = 1e30;
for(int t=0; t<T; t++) {
done[t].getBlocking();
firstEnd = std::min(firstEnd, done[t].get());
}
double end = timer();
printf(" %2dx%2d traversals: %5.3fs, %6.1f M/sec, %4.1f%%\n", T, (int)TraversalsPerThread, end-start,
N2 / 1e6 * (T*TraversalsPerThread) / (end-start),
(firstEnd-start)/(end-start)*100.0);
}
}
//delete[] x; // TODO: Free large pages
}
#endif
ACTOR template <int N, class X>
[[flow_allow_discard]] Future<X> addN(Future<X> in) {
X i = wait( in );
return i + N;
}
ACTOR template <class A, class B>
[[flow_allow_discard]] Future<Void> switchTest(FutureStream<A> as, Future<B> oneb) {
loop choose {
when (A a = waitNext( as )) { cout << "A " << a << endl; }
when (B b = wait( oneb )) { cout << "B " << b << endl; break; }
}
loop {
cout << "Done!" << endl;
return Void();
}
}
class TestBuffer : public ReferenceCounted<TestBuffer> {
public:
static TestBuffer* create( int length ) {
#if defined(__INTEL_COMPILER)
return new TestBuffer(length);
#else
auto b = (TestBuffer*)new int[ (length+7)/4 ];
new (b) TestBuffer(length);
return b;
#endif
}
#if !defined(__INTEL_COMPILER)
void operator delete( void* buf ) {
cout << "Freeing buffer" << endl;
delete[] (int*)buf;
}
#endif
int size() const { return length; }
uint8_t* begin() { return data; }
uint8_t* end() { return data+length; }
const uint8_t* begin() const { return data; }
const uint8_t* end() const { return data+length; }
private:
TestBuffer(int length) noexcept : length(length) {}
int length;
uint8_t data[1];
};
int fastKeyCount = 0;
class FastKey : public FastAllocated<FastKey>, public ReferenceCounted<FastKey>
{
public:
FastKey() : length(0) {}
FastKey(char* b, int length) : length(length) {
ASSERT (length <= sizeof(data));
memcpy(data, b, length);
}
~FastKey() { fastKeyCount++; }
int size() const { return length; }
uint8_t* begin() { return data; }
uint8_t* end() { return data+length; }
const uint8_t* begin() const { return data; }
const uint8_t* end() const { return data+length; }
private:
int length;
uint8_t data[252];
};
struct TestB : FastAllocated<TestB> {
char x[65];
};
void fastAllocTest() {
double t;
std::vector<void*> d;
for(int i=0; i<1000000;i++) {
d.push_back( FastAllocator<64>::allocate() );
int r = deterministicRandom()->randomInt(0,1000000);
if (r < d.size()) {
FastAllocator<64>::release(d[r]);
d[r] = d.back();
d.pop_back();
}
}
std::sort(d.begin(), d.end());
if (std::unique(d.begin(),d.end())!=d.end())
cout << "Pointer returned twice!?" << endl;
for(int i=0; i<2; i++) {
void *p = FastAllocator<64>::allocate();
void *q = FastAllocator<64>::allocate();
cout << (intptr_t)p << " " << (intptr_t)q << endl;
FastAllocator<64>::release(p);
FastAllocator<64>::release(q);
}
t = timer();
for (int i = 0; i < 1000000; i++) (void)FastAllocator<64>::allocate();
t = timer()-t;
cout << "Allocations: " << (1/t) << "M/sec" << endl;
t = timer();
for(int i=0; i<1000000; i++)
FastAllocator<64>::release( FastAllocator<64>::allocate() );
t = timer()-t;
cout << "Allocate/Release pairs: " << (1/t) << "M/sec" << endl;
t = timer();
void* pp[100];
for(int i=0; i<10000; i++) {
for(int j=0; j<100; j++)
pp[j] = FastAllocator<64>::allocate();
for(int j=0; j<100; j++)
FastAllocator<64>::release(pp[j]);
}
t = timer()-t;
cout << "Allocate/Release interleaved(100): " << (1/t) << "M/sec" << endl;
t = timer();
for(int i=0; i<1000000; i++)
delete new TestB;
t = timer()-t;
cout << "Allocate/Release TestB pairs: " << (1/t) << "M/sec" << endl;
#if FLOW_THREAD_SAFE
t = timer();
std::vector< Future<bool> > results;
for(int i=0; i<4; i++)
results.push_back(
inThread<bool>( [] ()->bool {
TestB* pp[100];
for(int i=0; i<10000; i++) {
for(int j=0; j<100; j++)
pp[j] = new TestB;
for(int j=0; j<100; j++)
delete pp[j];
}
return true;
}));
waitForAll( results ).getBlocking();
t = timer()-t;
cout << "Threaded Allocate/Release TestB interleaved (100): " << results.size() << " x " << (1/t) << "M/sec" << endl;
#endif
volatile int32_t v = 0;
t = timer();
for(int i=0; i<10000000; i++)
interlockedIncrement(&v);
t = timer()-t;
cout << "interlocked increment: " << 10.0/t << "M/sec " << v << endl;
v = 5;
t = timer();
for(int i=0; i<10000000; i++) {
interlockedCompareExchange(&v, 5, 5);
}
t = timer()-t;
cout << "1 state machine: " << 10.0/t << "M/sec " << v << endl;
v=0;
t = timer();
for(int i=0; i<10000000; i++)
v++;
t = timer()-t;
cout << "volatile increment: " << 10.0/t << "M/sec " << v << endl;
{
Reference<TestBuffer> b( TestBuffer::create(1000) );
memcpy(b->begin(), "Hello, world!", 14);
t = timer();
for(int i=0; i<10000000; i++) {
Reference<TestBuffer> r = std::move(b);
b = std::move(r);
}
t = timer()-t;
cout << "move Reference<Buffer>: " << 10.0/t << "M/sec " << endl;
t = timer();
for(int i=0; i<10000000; i++) {
Reference<TestBuffer> r = b;
}
t = timer()-t;
cout << "copy (1) Reference<Buffer>: " << 10.0/t << "M/sec " << endl;
Reference<TestBuffer> c = b;
t = timer();
for(int i=0; i<10000000; i++) {
Reference<TestBuffer> r = b;
}
t = timer()-t;
cout << "copy (2) Reference<Buffer>: " << 10.0/t << "M/sec " << endl;
cout << (const char*)b->begin() << endl;
}
t = timer();
for(int i=0; i<10000000; i++) {
delete new FastKey;
}
t = timer()-t;
cout << "delete new FastKey: " << 10.0/t << "M/sec " << fastKeyCount << endl;
t = timer();
for(int i=0; i<10000000; i++) {
Reference<FastKey> r( new FastKey );
}
t = timer()-t;
cout << "new Reference<FastKey>: " << 10.0/t << "M/sec " << fastKeyCount << endl;
}
template <class PromiseT>
Future<Void> threadSafetySender( vector<PromiseT>& v, Event &start, Event &ready, int iterations ) {
for (int i = 0; i < iterations; i++) {
start.block();
if (v.size() == 0) return Void();
for(int i=0; i<v.size(); i++)
v[i].send( Void() );
ready.set();
}
return Void();
}
ACTOR [[flow_allow_discard]] void threadSafetyWaiter(Future<Void> f, int32_t* count) {
wait(f);
interlockedIncrement(count);
}
ACTOR [[flow_allow_discard]] void threadSafetyWaiter(FutureStream<Void> f, int n, int32_t* count) {
while (n--) {
waitNext(f);
interlockedIncrement(count);
}
}
#if 0
void threadSafetyTest() {
double t = timer();
int N = 10000, V = 100;
vector<Promise<Void>> v;
Event start, ready;
Future<Void> sender = inThread<Void>( [&] { return threadSafetySender( v, start, ready, N ); } );
for(int i=0; i<N; i++) {
v.clear();
for (int j = 0; j < V; j++)
v.push_back(Promise<Void>());
vector<Future<Void>> f( v.size() );
for(int i=0; i<v.size(); i++)
f[i] = v[i].getFuture();
std::random_shuffle( f.begin(), f.end() );
start.set();
int32_t count = 0;
for(int i=0; i<f.size(); i++)
threadSafetyWaiter( f[i], &count );
ready.block();
if (count != V)
cout << "Thread safety error: " << count << endl;
}
t = timer()-t;
cout << "Thread safety test (2t): " << (V*N/1e6/t) << "M/sec" << endl;
}
void threadSafetyTest2() {
double t = timer();
int N = 1000, V = 100;
// vector<PromiseStream<Void>> streams( 100 );
vector<PromiseStream<Void>> streams;
for (int i = 0; i < 100; i++)
streams.push_back(PromiseStream<Void>());
vector<PromiseStream<Void>> v;
Event start, ready;
Future<Void> sender = inThread<Void>( [&] { return threadSafetySender( v, start, ready, N ); } );
for(int i=0; i<N; i++) {
vector<int> counts( streams.size() );
v.clear();
for(int k=0; k<V; k++) {
int i = deterministicRandom()->randomInt(0, (int)streams.size());
counts[i]++;
v.push_back( streams[i] );
}
start.set();
int32_t count = 0;
for(int i=0; i<streams.size(); i++)
threadSafetyWaiter( streams[i].getFuture(), counts[i], &count );
ready.block();
if (count != V)
cout << "Thread safety error: " << count << endl;
}
t = timer()-t;
cout << "Thread safety test 2 (2t): " << (V*N/1e6/t) << "M/sec" << endl;
}
volatile int32_t cancelled = 0, returned = 0;
ACTOR [[flow_allow_discard]] Future<Void> returnCancelRacer( Future<Void> f ) {
try {
wait(f);
} catch ( Error& ) {
interlockedIncrement( &cancelled );
throw;
}
interlockedIncrement( &returned );
return Void();
}
void returnCancelRaceTest() {
int N = 100, M = 100;
for(int i=0; i<N; i++) {
vector< Promise<Void> > promises;
vector< Future<Void> > futures;
for(int i=0; i < M; i++) {
promises.push_back( Promise<Void>() );
futures.push_back( returnCancelRacer( promises.back().getFuture() ) );
}
std::random_shuffle( futures.begin(), futures.end() );
// FIXME: Doesn't work as written with auto-reset
// events. Probably not particularly racy as written. Test may
// FAIL or PASS at whim.
Event ev1, ev2;
ThreadFuture<Void> b = inThread<Void>( [&] ()->Void {
ev1.block();
for(int i=0; i<promises.size(); i++)
futures[i] = Future<Void>();
return Void();
} );
ThreadFuture<Void> a = inThread<Void>([&]()->Void {
ev2.block();
for(int i=0; i<promises.size(); i++) {
promises[i].send(Void());
for( volatile int32_t dummy = 0; dummy < 10; dummy ++ );
}
return Void();
} );
ev1.set(); ev2.set();
a.getBlocking();
b.getBlocking();
}
bool ok = cancelled && returned && cancelled+returned == N*M;
printf("ReturnCancelRaceTest: %s\n", ok ? "PASS" : "FAIL");
printf(" %d cancelled, %d returned\n", cancelled, returned);
}
#endif
ACTOR [[flow_allow_discard]] Future<int> chooseTest(Future<int> a, Future<int> b) {
choose {
when( int A = wait( a ) ) { return A; }
when( int B = wait( b ) ) { return B; }
}
}
void showArena( ArenaBlock* a, ArenaBlock* parent) {
printf("ArenaBlock %p (<-%p): %d bytes, %d refs\n", a, parent, a->size(), a->debugGetReferenceCount());
if (!a->isTiny()) {
int o = a->nextBlockOffset;
while (o) {
ArenaBlockRef* r = (ArenaBlockRef*)((char*)a->getData() + o);
showArena( r->next, a );
o = r->nextBlockOffset;
}
}
}
void arenaTest() {
BinaryWriter wr(AssumeVersion(currentProtocolVersion));
{
Arena arena;
VectorRef<StringRef> test;
test.push_back( arena, StringRef(arena, LiteralStringRef("Hello")) );
test.push_back( arena, StringRef(arena, LiteralStringRef(", ")) );
test.push_back( arena, StringRef(arena, LiteralStringRef("World!")) );
for(auto i = test.begin(); i != test.end(); ++i)
for(auto j = i->begin(); j != i->end(); ++j)
cout << *j;
cout << endl;
wr << test;
}
{
Arena arena2;
VectorRef<StringRef> test2;
BinaryReader reader(wr.getData(),wr.getLength(), AssumeVersion(currentProtocolVersion));
reader >> test2 >> arena2;
for(auto i = test2.begin(); i != test2.end(); ++i)
for(auto j = i->begin(); j != i->end(); ++j)
cout << *j;
cout << endl;
}
double t = timer();
for(int i=0; i<100; i++) {
Arena ar;
for(int i=0; i<10000000; i++)
new (ar) char[10];
}
printf("100 x 10M x 10B allocated+freed from Arenas: %f sec\n", timer()-t);
//printf("100M x 8bytes allocations: %d bytes used\n", 0);//ar.getSize());
//showArena( ar.impl.getPtr(), 0 );
};
ACTOR [[flow_allow_discard]] void testStream(FutureStream<int> xs) {
loop {
int x = waitNext(xs);
cout << x << endl;
}
}
ACTOR [[flow_allow_discard]] Future<Void> actorTest1(bool b) {
printf("1");
if (b)
throw future_version();
return Void();
}
ACTOR [[flow_allow_discard]] void actorTest2(bool b) {
printf("2");
if (b)
throw future_version();
}
ACTOR [[flow_allow_discard]] Future<Void> actorTest3(bool b) {
try {
if (b)
throw future_version();
} catch (Error&) {
printf("3");
return Void();
}
printf("\nactorTest3 failed\n");
return Void();
}
ACTOR [[flow_allow_discard]] Future<Void> actorTest4(bool b) {
state double tstart = now();
try {
if (b)
throw operation_failed();
} catch (...) {
wait( delay(1) );
}
if (now() < tstart + 1)
printf("actorTest4 failed");
else
printf("4");
return Void();
}
ACTOR [[flow_allow_discard]] Future<bool> actorTest5() {
state bool caught = false;
loop {
loop {
state bool inloop = false;
if (caught) {
printf("5");
return true;
}
try {
loop {
if (inloop) { printf("\nactorTest5 failed\n"); return false; }
inloop = true;
if (1) throw operation_failed();
}
} catch (Error&) {
caught = true;
}
}
}
}
ACTOR [[flow_allow_discard]] Future<bool> actorTest6() {
state bool caught = false;
loop {
if (caught) { printf("6"); return true; }
try {
if (1) throw operation_failed();
} catch (Error&) {
caught = true;
}
}
}
ACTOR [[flow_allow_discard]] Future<bool> actorTest7() {
try {
loop {
loop {
if (1) throw operation_failed();
if (1) { printf("actorTest7 failed (1)\n"); return false; }
if (0) break;
}
if (1) { printf("actorTest7 failed (2)\n"); return false; }
}
} catch (Error&) {
printf("7");
return true;
}
}
ACTOR [[flow_allow_discard]] Future<bool> actorTest8() {
state bool caught = false;
state Future<bool> set = true;
loop {
state bool inloop = false;
if (caught) {
printf("8");
return true;
}
try {
loop {
if (inloop) { printf("\nactorTest8 failed\n"); return false; }
bool b = wait( set );
inloop = true;
if (1) throw operation_failed();
}
} catch (Error&) {
caught = true;
}
}
}
ACTOR [[flow_allow_discard]] Future<bool> actorTest9A(Future<Void> setAfterCalling) {
state int count = 0;
loop {
if (count == 4) { printf("9"); return true; }
if (count && count!=4) { printf("\nactorTest9 failed\n"); return false; }
loop {
loop {
wait( setAfterCalling );
loop {
loop {
count++;
break;
}
wait( Future<Void>(Void()) );
count++;
break;
}
count++;
break;
}
count++;
break;
}
// loopDepth < 0 ???
}
}
Future<bool> actorTest9() {
Promise<Void> p;
Future<bool> f = actorTest9A(p.getFuture());
p.send(Void());
return f;
}
ACTOR [[flow_allow_discard]] Future<Void> actorTest10A(FutureStream<int> inputStream, Future<Void> go) {
state int i;
for(i = 0; i < 5; i++) {
wait( go );
int input = waitNext( inputStream );
}
return Void();
}
void actorTest10() {
PromiseStream<int> ins;
Promise<Void> go;
for(int x=0; x<2; x++)
ins.send(x);
Future<Void> a = actorTest10A( ins.getFuture(), go.getFuture() );
go.send(Void());
for(int x=0; x<3; x++)
ins.send(x);
if (!a.isReady())
printf("\nactorTest10 failed\n");
else
printf("10");
}
ACTOR [[flow_allow_discard]] Future<Void> cancellable() {
wait( Never() );
return Void();
}
ACTOR [[flow_allow_discard]] Future<Void> simple() {
return Void();
}
ACTOR [[flow_allow_discard]] Future<Void> simpleWait() {
wait( Future<Void>(Void()) );
return Void();
}
ACTOR [[flow_allow_discard]] Future<int> simpleRet(Future<int> x) {
int i = wait(x);
return i;
}
template <int i> Future<int> chain( Future<int> const& x );
ACTOR template <int i>
[[flow_allow_discard]] Future<int> achain(Future<int> x) {
int k = wait( chain<i>(x) );
return k+1;
}
template <int i> Future<int> chain( Future<int> const& x ) {
return achain<i-1>(x);
}
template<> Future<int> chain<0>( Future<int> const& x ) {
return x;
}
ACTOR [[flow_allow_discard]] Future<int> chain2(Future<int> x, int i);
ACTOR [[flow_allow_discard]] Future<int> chain2(Future<int> x, int i) {
if (i>1) {
int k = wait( chain2(x, i-1) );
return k+1;
} else {
int k = wait(x);
return k+i;
}
}
ACTOR [[flow_allow_discard]] Future<Void> cancellable2() {
try {
wait( Never() );
return Void();
} catch (Error& e) {
throw;
}
}
using std::string;
ACTOR [[flow_allow_discard]] Future<int> introLoadValueFromDisk(Future<string> filename) {
string file = wait( filename );
if (file == "/dev/threes")
return 3;
else
ASSERT(false);
return 0; // does not happen
}
ACTOR [[flow_allow_discard]] Future<int> introAdd(Future<int> a, Future<int> b) {
state int x = wait(a);
int y = wait(b);
return x + y; // x would be undefined here if it was not "state"
}
ACTOR [[flow_allow_discard]] Future<int> introFirst(Future<int> a, Future<int> b) {
choose {
when( int x = wait(a) ) {
return x;
}
when( int x = wait(b) ) {
return x;
}
}
}
struct AddReply {
int sum;
AddReply() {}
AddReply(int x) : sum(x) {}
template <class Ar>
void serialize(Ar& ar) {
serializer(ar, sum);
}
};
struct AddRequest {
int a, b;
Promise< AddReply > reply; // Self-addressed envelope
AddRequest() {}
AddRequest(int a, int b) : a(a), b(b) {}
template <class Ar>
void serialize(Ar& ar) {
serializer(ar, a, b, reply);
}
};
ACTOR [[flow_allow_discard]] void introAddServer(PromiseStream<AddRequest> add) {
loop choose {
when ( AddRequest req = waitNext(add.getFuture()) ) {
printf("%d + %d = %d\n", req.a, req.b, req.a+req.b);
req.reply.send( req.a + req.b );
}
}
}
void introPromiseFuture() {
Promise<int> myPromise;
Future<int> myFuture = myPromise.getFuture();
myPromise.send( 12345 );
ASSERT( myFuture.isReady() && myFuture.get() == 12345 );
}
void introActor() {
Future<int> f = introLoadValueFromDisk( std::string("/dev/threes") );
ASSERT( f.get() == 3 );
Promise<int> a, b;
Future<int> sum = introAdd(a.getFuture(), b.getFuture());
b.send(3);
ASSERT( !sum.isReady() );
a.send(2);
ASSERT( sum.get() == 5 );
Promise<int> c,d;
Future<int> first = introFirst( c.getFuture(), d.getFuture() );
ASSERT( !first.isReady() );
//d.send(100);
d.sendError( operation_failed() );
ASSERT( first.isError() && first.getError().code() == error_code_operation_failed );
//ASSERT( first.getBlocking() == 100 );
PromiseStream<AddRequest> addInterface;
introAddServer( addInterface );
Future<AddReply> reply = addInterface.getReply( AddRequest(5,2) );
ASSERT( reply.get().sum == 7 );
printf("OK\n");
}
template <int N>
void chainTest() {
auto startt = timer();
for(int i=0; i<100000; i++) {
Promise<int> p;
Future<int> f = chain<N>(p.getFuture());
p.send(i);
ASSERT( f.get() == i+N );
}
auto endt = timer();
printf("chain<%d>: %0.3f M/sec\n", N, 0.1/(endt-startt));
startt = timer();
for(int i=0; i<100000; i++) {
Promise<int> p;
Future<int> f = chain2(p.getFuture(), N);
p.send(i);
ASSERT( f.get() == i+N );
}
endt = timer();
printf("chain2<%d>: %0.3f M/sec\n", N, 0.1/(endt-startt));
}
ACTOR [[flow_allow_discard]] void cycle(FutureStream<Void> in, PromiseStream<Void> out, int* ptotal) {
loop{
waitNext(in);
(*ptotal)++;
out.send(Void());
}
}
ACTOR [[flow_allow_discard]] Future<Void> cycleTime(int nodes, int times) {
state vector<PromiseStream<Void>> n(nodes);
state int total = 0;
// 1->2, 2->3, ..., n-1->0
for (int i=1;i<nodes;i++)
cycle(n[i].getFuture(), n[(i+1)%nodes], &total);
state double startT = timer();
n[1].send(Void());
loop {
waitNext(n[0].getFuture());
if (!--times) break;
n[1].send(Void());
}
printf("Ring test: %d nodes, %d total ops, %.3f seconds\n", nodes, total, timer()-startT);
return Void();
}
void sleeptest() {
#ifdef __linux__
int times[] = {0, 100, 500, 1000, 5000, 100000, 500000, 1000000};
for(int j=0; j<8; j++) {
double b = timer();
int n = std::min(100, 4000000/(1+times[j]));
for(int i=0; i<n; i++) {
timespec ts;
ts.tv_sec = times[j] / 1000000;
ts.tv_nsec = (times[j] % 1000000)*1000;
clock_nanosleep(CLOCK_MONOTONIC, 0, &ts, NULL);
//nanosleep(&ts, NULL);
}
double t = timer() - b;
printf("Sleep test (%dus x %d): %0.1f\n", times[j], n, double(t)/n*1e6);
}
#endif
}
void asyncMapTest() {
Future<Void> c;
{
AsyncMap<int, int> m1;
m1.set( 10, 1 );
ASSERT( m1.get(10) == 1 );
ASSERT( m1.get(20) == 0 );
Future<Void> a = m1.onChange( 10 );
Future<Void> b = m1.onChange( 20 );
c = m1.onChange( 30 );
ASSERT( !a.isReady() && !b.isReady() );
m1.set(10, 0);
ASSERT( a.isReady() && !a.isError() && !b.isReady() && m1.get(10) == 0 );
m1.set(20, 5);
ASSERT( b.isReady() && !b.isError() && m1.get(20)==5 );
a = m1.onChange(10);
b = m1.onChange(20);
m1.triggerRange( 15, 25 );
ASSERT( !a.isReady() && b.isReady() && !b.isError() && m1.get(20) == 5 );
}
ASSERT( c.isReady() && c.isError() && c.getError().code() == error_code_broken_promise );
printf("AsyncMap: OK\n");
double startt;
AsyncMap<int,int> m2;
startt = timer();
for(int i=0; i<1000000; i++) {
m2.set(5, 0);
m2.set(5, 1);
}
printf(" set(not present/present): %0.1fM/sec\n", 2.0 / (timer()-startt));
startt = timer();
for(int i=0; i<1000000; i++) {
m2.set(5, 1);
m2.set(5, 2);
}
printf(" set(present/present): %0.1fM/sec\n", 2.0 / (timer()-startt));
startt = timer();
for(int i=0; i<1000000; i++) {
m2.set(5, 1);
}
printf(" set(no change): %0.1fM/sec\n", 1.0 / (timer()-startt));
m2.set(5, 5);
startt = timer();
for(int i=0; i<1000000; i++)
m2.onChange(5);
printf(" onChange(present, cancelled): %0.1fM/sec\n", 1.0 / (timer()-startt));
startt = timer();
for(int i=0; i<1000000; i++)
m2.onChange(10);
printf(" onChange(not present, cancelled): %0.1fM/sec\n", 1.0 / (timer()-startt));
startt = timer();
for(int i=0; i<1000000; i++) {
auto f = m2.onChange(10);
m2.set(10, 1);
m2.set(10, 0);
}
printf(" onChange(not present, set): %0.1fM/sec\n", 1.0 / (timer()-startt));
startt = timer();
for(int i=0; i<1000000; i++) {
auto f = m2.onChange(5);
m2.set(5, i+1);
}
printf(" onChange(present, set): %0.1fM/sec\n", 1.0 / (timer()-startt));
}
extern void net2_test();
void dsltest() {
double startt, endt;
setThreadLocalDeterministicRandomSeed(40);
asyncMapTest();
net2_test();
//sleeptest();
Future<Void> ctf = cycleTime(1000,1000);
ctf.get();
introPromiseFuture();
introActor();
// return;
printf("Actor control flow tests: ");
actorTest1(true);
actorTest2(true);
actorTest3(true);
//if (g_network == &g_simulator)
//g_simulator.run( actorTest4(true) );
actorTest5();
actorTest6();
actorTest7();
actorTest8();
actorTest9();
actorTest10();
printf("\n");
printf("Running actor fuzz tests:\n");
// Only include this test outside of Windows because of MSVC compiler bug
#ifndef WIN32
auto afResults = actorFuzzTests();
#else
std::pair<int, int> afResults(0,0);
#endif
printf("Actor fuzz tests: %d/%d passed\n", afResults.first, afResults.second);
startt = timer();
for(int i=0; i<1000000; i++)
deterministicRandom()->random01();
endt = timer();
printf("Random01: %0.2f M/sec\n", 1.0/(endt-startt));
startt = timer();
for(int i=0; i<1000000; i++)
Promise<Void>();
endt = timer();
printf("Promises: %0.2f M/sec\n", 1.0/(endt-startt));
startt = timer();
for(int i=0; i<1000000; i++)
Promise<Void>().send(Void());
endt = timer();
printf("Promises (with send): %0.2f M/sec\n", 1.0/(endt-startt));
startt = timer();
for(int i=0; i<1000000; i++) {
Promise<Void> p;
Future<Void> f = p.getFuture();
p.send(Void());
f.get();
}
endt = timer();
printf("Promise/Future/send roundtrip: %0.2f M/sec\n", 1.0/(endt-startt));
Promise<Void> p;
startt = timer();
for(int i=0; i<1000000; i++)
p.getFuture();
endt = timer();
printf("Futures: %0.2f M/sec\n", 1.0/(endt-startt));
startt = timer();
for (int i = 0; i<1000000; i++)
PromiseStream<Void>();
endt = timer();
printf("PromiseStreams: %0.2f M/sec\n", 1.0 / (endt - startt));
startt = timer();
for (int i = 0; i < 1000000; i++)
PromiseStream<Void>().send(Void());
endt = timer();
printf("PromiseStreams (with send): %0.2f M/sec\n", 1.0 / (endt - startt));
startt = timer();
for (int i = 0; i<1000000; i++) {
PromiseStream<Void> p;
FutureStream<Void> f = p.getFuture();
p.send(Void());
f.pop();
}
endt = timer();
printf("PromiseStream/FutureStream/send/popBlocking roundtrip: %0.2f M/sec\n", 1.0 / (endt - startt));
startt = timer();
{
PromiseStream<int> ps;
for (int i = 0; i < 1000000; i++) {
ps.send(i);
}
}
endt = timer();
printf("PromiseStream queued send: %0.2f M/sec\n", 1.0 / (endt - startt));
startt = timer();
for(int i=0; i<1000000; i++)
cancellable();
endt = timer();
printf("Cancellations: %0.2f M/sec\n", 1.0/(endt-startt));
startt = timer();
for(int i=0; i<1000000; i++)
cancellable2();
endt = timer();
printf("Cancellations with catch: %0.2f M/sec\n", 1.0/(endt-startt));
startt = timer();
for(int i=0; i<1000000; i++)
simple();
endt = timer();
printf("Actor creation: %0.2f M/sec\n", 1.0/(endt-startt));
startt = timer();
for(int i=0; i<1000000; i++)
simpleWait();
endt = timer();
printf("With trivial wait: %0.2f M/sec\n", 1.0/(endt-startt));
startt = timer();
for(int i=0; i<1000000; i++) {
Promise<int> p;
Future<int> f = simpleRet(p.getFuture());
p.send(i);
ASSERT( f.get() == i );
}
endt = timer();
printf("Bounce int through actor: %0.2f M/sec\n", 1.0/(endt-startt));
startt = timer();
for(int i=0; i<1000000; i++) {
Promise<int> p;
Future<int> f = simpleRet(p.getFuture());
Future<int> g = simpleRet(p.getFuture());
p.send(i);
ASSERT( f.get() == i );
ASSERT( g.get() == i );
}
endt = timer();
printf("Bounce int through two actors in parallel: %0.2f M/sec\n", 1.0/(endt-startt));
/*chainTest<1>();
chainTest<4>();
chainTest<16>();
chainTest<64>();
startt = timer();
for(int i=0; i<1000000; i++)
try {
throw success();
} catch (Error&) {
}
endt = timer();
printf("C++ exception: %0.2f M/sec\n", 1.0/(endt-startt));*/
arenaTest();
{
Promise<int> a, b;
Future<int> c = chooseTest( a.getFuture(), b.getFuture() );
a.send(1);
b.send(2);
cout << "c=" << c.get() << endl;
}
{
Promise<double> i;
Future<double> d = addN<20>(i.getFuture());
i.send( 1.1 );
cout << d.get() << endl;
}
{
Promise<double> i;
i.sendError( operation_failed() );
Future<double> d = addN<20>(i.getFuture());
if (d.isError() && d.getError().code() == error_code_operation_failed)
cout << "Error transmitted OK" << endl;
else
cout << "Error not transmitted!" << endl;
}
/*{
int na = Actor::allActors.size();
PromiseStream<int> t;
testStream(t.getFuture());
if (Actor::allActors.size() != na+1)
cout << "Actor not created!" << endl;
t = PromiseStream<int>();
if (Actor::allActors.size() != na)
cout << "Actor not cleaned up!" << endl;
}*/
PromiseStream<int> as;
Promise<double> bs;
as.send( 4 );
Future<Void> sT = switchTest( as.getFuture(), bs.getFuture() );
as.send( 5 );
//sT = move(Future<Void>());
as.send( 6 );
bs.send( 10.1 );
as.send( 7 );
fastAllocTest();
#if FLOW_THREAD_SAFE
returnCancelRaceTest();
threadSafetyTest();
threadSafetyTest2();
#else
printf("Thread safety disabled.\n");
#endif
}
/*ACTOR Future<Void> pingServer( FutureStream<Promise<bool>> requests, int rate ) {
state int count = 0;
loop {
Promise<bool> req = waitNext( requests );
req.send( (++count)%rate != 0 );
}
}
ACTOR Future<int> ping( PromiseStream<Promise<bool>> server ) {
state int count = 0;
loop {
bool result = wait( server.getReply<bool>() );
count++;
if (!result)
break;
}
return count;
}
void pingtest() {
double start = timer();
PromiseStream<Promise<bool>> serverInterface;
Future<Void> pS = pingServer( serverInterface.getFuture(), 5000000 );
Future<int> count = ping( serverInterface );
double end = timer();
cout << count.get() << " pings completed in " << (end-start) << " sec" << endl;
}*/
void copyTest() {
double start, elapsed;
Arena arena;
StringRef s( new (arena) uint8_t[ 10<<20 ], 10<<20 );
{
start = timer();
for (int i = 0; i < 100; i++) {
StringRef k = s;
(void)k;
}
elapsed = timer() - start;
printf("StringRef->StringRef: %fs/GB\n", elapsed);
}
{
start = timer();
for(int i=0; i<100; i++)
Standalone<StringRef> a = s;
elapsed = timer() - start;
printf("StringRef->Standalone: %fs/GB\n", elapsed);
}
{
Standalone<StringRef> sa = s;
start = timer();
for(int i=0; i<100; i++)
Standalone<StringRef> a = sa;
elapsed = timer() - start;
printf("Standalone->Standalone: %fs/GB\n", elapsed);
}
{
Standalone<StringRef> sa = s, sb;
start = timer();
for(int i=0; i<50; i++) {
sb = std::move(sa);
sa = std::move(sb);
}
elapsed = timer() - start;
printf("move(Standalone)->Standalone: %fs/GB\n", elapsed);
}
}
/*ACTOR void badTest( FutureStream<int> is ) {
state PromiseStream<int> js;
loop choose {
when( int j = waitNext( js.getFuture() ) ) {
cout << "J" << j << endl;
}
when( int i = waitNext( is ) ) {
cout << "I" << i << endl;
js.send( i );
cout << "-I" << i << endl;
}
}
}
void dsltest() {
PromiseStream<int> is;
badTest( is.getFuture() );
is.send(1);
is.send(2);
is.send(3);
throw not_implemented();
}
void pingtest() {}*/