|
|
b579b3 |
#ifndef COMMON_INC_CPP
|
|
|
b579b3 |
#define COMMON_INC_CPP
|
|
|
b579b3 |
|
|
|
b579b3 |
|
|
|
b579b3 |
#include <cmath></cmath>
|
|
|
b579b3 |
#include <cstdio></cstdio>
|
|
|
b579b3 |
#include <cstdlib></cstdlib>
|
|
|
b579b3 |
#include <cstring></cstring>
|
|
|
b579b3 |
#include <cassert></cassert>
|
|
|
b579b3 |
|
|
|
b579b3 |
#include <atomic></atomic>
|
|
|
b579b3 |
#include <vector></vector>
|
|
|
b579b3 |
#include <string></string>
|
|
|
b579b3 |
#include <chrono></chrono>
|
|
|
b579b3 |
#include <algorithm></algorithm>
|
|
|
b579b3 |
|
|
|
e4740d |
#include <mutex></mutex>
|
|
|
e4740d |
#include <thread></thread>
|
|
|
e4740d |
#include <condition_variable></condition_variable>
|
|
|
e4740d |
|
|
|
b579b3 |
|
|
|
b579b3 |
#include "layout.inc.cpp"
|
|
|
b579b3 |
|
|
|
b579b3 |
|
|
|
b579b3 |
typedef double WeightReal;
|
|
|
b579b3 |
typedef double NeuronReal;
|
|
|
b579b3 |
typedef double AccumReal;
|
|
|
b579b3 |
|
|
|
b579b3 |
typedef int WeightInt;
|
|
|
b579b3 |
typedef int AccumInt;
|
|
|
b579b3 |
|
|
|
b579b3 |
|
|
|
b579b3 |
|
|
|
b579b3 |
#define RANDOM_MAX 0x7fffffff
|
|
|
b579b3 |
inline unsigned int randomNext(unsigned int prev)
|
|
|
b579b3 |
{ return (1103515245*prev + 12345) & RANDOM_MAX; }
|
|
|
b579b3 |
inline unsigned int randomBranch(unsigned int seed)
|
|
|
b579b3 |
{ return randomNext(seed + 1); }
|
|
|
b579b3 |
|
|
|
b579b3 |
inline void busyloop(unsigned int count)
|
|
|
b579b3 |
{ while(count--) __asm__ __volatile__(""); }
|
|
|
e4740d |
inline void sleep()
|
|
|
e4740d |
{ std::this_thread::sleep_for(std::chrono::nanoseconds(0)); }
|
|
|
e4740d |
inline void sleepUs(long long us)
|
|
|
e4740d |
{ std::this_thread::sleep_for(std::chrono::microseconds(us)); }
|
|
|
b579b3 |
|
|
|
b579b3 |
|
|
|
b579b3 |
inline long long timeUs() {
|
|
|
b579b3 |
static std::chrono::steady_clock::time_point begin = std::chrono::steady_clock::now();
|
|
|
b579b3 |
return (long long)std::chrono::duration_cast<std::chrono::microseconds>( std::chrono::steady_clock::now() - begin ).count();</std::chrono::microseconds>
|
|
|
b579b3 |
}
|
|
|
b579b3 |
|
|
|
b579b3 |
|
|
|
b579b3 |
|
|
|
b579b3 |
struct Accum {
|
|
|
b579b3 |
union { AccumReal v; AccumInt i; };
|
|
|
b579b3 |
};
|
|
|
b579b3 |
|
|
|
b579b3 |
|
|
|
b579b3 |
struct Neuron {
|
|
|
b579b3 |
NeuronReal v, d;
|
|
|
b579b3 |
Accum a;
|
|
|
b579b3 |
};
|
|
|
b579b3 |
|
|
|
b579b3 |
|
|
|
b579b3 |
struct Weight {
|
|
|
b579b3 |
union { WeightReal w; WeightInt i; };
|
|
|
b579b3 |
};
|
|
|
b579b3 |
|
|
|
b579b3 |
|
|
|
b579b3 |
struct Iter {
|
|
|
b579b3 |
typedef Accum AccumType;
|
|
|
b579b3 |
typedef NeuronReal* DataType;
|
|
|
b579b3 |
typedef AccumType DataAccumType;
|
|
|
b579b3 |
static inline void init(Neuron&, AccumType&) { }
|
|
|
b579b3 |
static inline void iter(Neuron&, Weight&, AccumType&) { }
|
|
|
b579b3 |
static inline void done(Neuron&, AccumType&) { }
|
|
|
b579b3 |
static inline void iter2(Neuron&, Neuron&, Weight&) { }
|
|
|
b579b3 |
static inline void iter3(Neuron&) { }
|
|
|
b579b3 |
static inline void iter4(Neuron&, DataType, DataAccumType&) { }
|
|
|
b579b3 |
};
|
|
|
b579b3 |
|
|
|
b579b3 |
|
|
|
b579b3 |
|
|
|
e4740d |
class Barrier;
|
|
|
e4740d |
|
|
|
e4740d |
class ThreadControl {
|
|
|
e4740d |
private:
|
|
|
e4740d |
friend class Barrier;
|
|
|
e4740d |
|
|
|
e4740d |
std::mutex mutex;
|
|
|
e4740d |
std::condition_variable cond;
|
|
|
e4740d |
std::atomic<unsigned int=""> counter;</unsigned>
|
|
|
e4740d |
std::vector<std::thread*> threads;</std::thread*>
|
|
|
e4740d |
unsigned int commonSeed;
|
|
|
e4740d |
|
|
|
e4740d |
void runSingleThread(unsigned int tid, unsigned int seed);
|
|
|
e4740d |
|
|
|
e4740d |
protected:
|
|
|
e4740d |
virtual void threadFunc(Barrier&) { }
|
|
|
e4740d |
|
|
|
e4740d |
public:
|
|
|
e4740d |
ThreadControl(): counter(0), commonSeed() { }
|
|
|
e4740d |
void runThreads(unsigned int threadsCount = 1) {
|
|
|
e4740d |
assert(threadsCount);
|
|
|
e4740d |
counter = 0;
|
|
|
e4740d |
threads.clear();
|
|
|
e4740d |
threads.resize(threadsCount);
|
|
|
e4740d |
commonSeed = rand();
|
|
|
e4740d |
for(unsigned int i = 1; i < threadsCount; ++i)
|
|
|
e4740d |
threads[i] = new std::thread(&ThreadControl::runSingleThread, this, i, rand());
|
|
|
e4740d |
runSingleThread(0, rand());
|
|
|
e4740d |
for(unsigned int i = 1; i < threadsCount; ++i)
|
|
|
e4740d |
{ threads[i]->join(); delete threads[i]; }
|
|
|
e4740d |
threads.clear();
|
|
|
e4740d |
}
|
|
|
e4740d |
};
|
|
|
e4740d |
|
|
|
e4740d |
|
|
|
b579b3 |
class Barrier {
|
|
|
b579b3 |
private:
|
|
|
e4740d |
ThreadControl &owner;
|
|
|
b579b3 |
unsigned int next;
|
|
|
b579b3 |
unsigned int busyseed;
|
|
|
b579b3 |
public:
|
|
|
b579b3 |
const unsigned int tid;
|
|
|
b579b3 |
const unsigned int threads;
|
|
|
b579b3 |
unsigned int seed;
|
|
|
e4740d |
unsigned int commonSeed;
|
|
|
b579b3 |
|
|
|
b579b3 |
Barrier(const Barrier&) = delete;
|
|
|
e4740d |
inline Barrier(ThreadControl &owner, unsigned int tid, unsigned int seed, unsigned int commonSeed):
|
|
|
e4740d |
owner(owner), next(), busyseed(randomBranch(seed)), tid(tid), threads(owner.threads.size()), seed(seed), commonSeed(commonSeed)
|
|
|
e4740d |
{ assert(tid < threads); }
|
|
|
b579b3 |
|
|
|
b579b3 |
//inline void busyloop() { }
|
|
|
b579b3 |
inline void busyloop(unsigned int maxCycles = 4096) { ::busyloop( (busyseed = randomNext(busyseed))%maxCycles ); }
|
|
|
b579b3 |
inline unsigned int rand() { return seed = randomNext(seed); }
|
|
|
e4740d |
inline unsigned int commonRand() { return commonSeed = randomNext(commonSeed); }
|
|
|
e4740d |
inline void wait() { next += threads; ++owner.counter; while(owner.counter < next) busyloop(); }
|
|
|
e4740d |
inline void subwait() { while(owner.counter < next + tid) busyloop(); }
|
|
|
e4740d |
|
|
|
e4740d |
inline void wait2() {
|
|
|
e4740d |
next += threads;
|
|
|
e4740d |
std::unique_lock<std::mutex> lock(owner.mutex);</std::mutex>
|
|
|
e4740d |
if (++owner.counter == next) owner.cond.notify_all(); else
|
|
|
e4740d |
while(owner.counter < next) owner.cond.wait(lock);
|
|
|
e4740d |
}
|
|
|
b579b3 |
|
|
|
e4740d |
inline void wait3() { next += threads; ++owner.counter; while(owner.counter < next) sleepUs(1); }
|
|
|
b579b3 |
};
|
|
|
b579b3 |
|
|
|
b579b3 |
|
|
|
e4740d |
void ThreadControl::runSingleThread(unsigned int tid, unsigned int seed) {
|
|
|
e4740d |
Barrier barrier(*this, tid, seed, commonSeed);
|
|
|
e4740d |
threadFunc(barrier);
|
|
|
e4740d |
}
|
|
|
e4740d |
|
|
|
e4740d |
|
|
|
e4740d |
|
|
|
b579b3 |
struct Stat {
|
|
|
b579b3 |
int neurons;
|
|
|
b579b3 |
int activeNeurons;
|
|
|
b579b3 |
int weights;
|
|
|
b579b3 |
int links;
|
|
|
b579b3 |
size_t memsize;
|
|
|
b579b3 |
|
|
|
b579b3 |
Stat(): neurons(), activeNeurons(), weights(), links(), memsize() { }
|
|
|
b579b3 |
|
|
|
b579b3 |
Stat& operator+= (const Stat &b) {
|
|
|
b579b3 |
neurons += b.neurons;
|
|
|
b579b3 |
activeNeurons += b.activeNeurons;
|
|
|
b579b3 |
weights += b.weights;
|
|
|
b579b3 |
links += b.links;
|
|
|
b579b3 |
memsize += b.memsize;
|
|
|
b579b3 |
return *this;
|
|
|
b579b3 |
}
|
|
|
b579b3 |
|
|
|
b579b3 |
void print(const char *prefix = nullptr) const {
|
|
|
b579b3 |
if (prefix && *prefix) printf("%s: ", prefix);
|
|
|
b579b3 |
printf("neurons: %d / %d, links %d / %d, memSize: %llu\n", activeNeurons, neurons, weights, links, (unsigned long long)memsize);
|
|
|
b579b3 |
}
|
|
|
b579b3 |
};
|
|
|
b579b3 |
|
|
|
b579b3 |
|
|
|
b579b3 |
struct Quality {
|
|
|
b579b3 |
AccumReal train;
|
|
|
b579b3 |
AccumReal human;
|
|
|
b579b3 |
|
|
|
b579b3 |
inline Quality(AccumReal train, AccumReal human): train(train), human(human) {}
|
|
|
b579b3 |
inline explicit Quality(AccumReal train = 0): Quality(train, train) {}
|
|
|
b579b3 |
inline static Quality nan() { return Quality(NAN); }
|
|
|
b579b3 |
inline static Quality bad() { return Quality(INFINITY); }
|
|
|
b579b3 |
|
|
|
b579b3 |
inline Quality& operator+=(const Quality &b)
|
|
|
b579b3 |
{ train += b.train; human += b.human; return *this; }
|
|
|
b579b3 |
inline Quality& operator*=(AccumReal x)
|
|
|
b579b3 |
{ train *= x; human *= x; return *this; }
|
|
|
b579b3 |
inline bool operator<(const Quality &b) const {
|
|
|
b579b3 |
return human < b.human ? true
|
|
|
b579b3 |
: b.human < human ? false
|
|
|
b579b3 |
: train < b.train;
|
|
|
b579b3 |
}
|
|
|
b579b3 |
};
|
|
|
b579b3 |
|
|
|
b579b3 |
|
|
|
b579b3 |
struct QualityPair {
|
|
|
b579b3 |
Quality measure;
|
|
|
b579b3 |
Quality train;
|
|
|
b579b3 |
|
|
|
b579b3 |
inline explicit QualityPair(const Quality &measure = Quality(), const Quality &train = Quality()):
|
|
|
b579b3 |
measure(measure), train(train) { }
|
|
|
b579b3 |
|
|
|
b579b3 |
inline QualityPair& operator+=(const QualityPair &b)
|
|
|
b579b3 |
{ measure += b.measure; train += b.train; return *this; }
|
|
|
b579b3 |
inline QualityPair& operator*=(AccumReal x)
|
|
|
b579b3 |
{ measure *= x; train *= x; return *this; }
|
|
|
b579b3 |
inline bool operator<(const QualityPair &b) const {
|
|
|
b579b3 |
return measure < b.measure ? true
|
|
|
b579b3 |
: b.measure < measure ? false
|
|
|
b579b3 |
: train < b.train;
|
|
|
b579b3 |
}
|
|
|
b579b3 |
};
|
|
|
b579b3 |
|
|
|
b579b3 |
|
|
|
b579b3 |
#endif
|
|
|
b579b3 |
|