controller.cc 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982
  1. extern "C" {
  2. #include <event2/listener.h>
  3. #include <event2/bufferevent.h>
  4. #include <event2/buffer.h>
  5. }
  6. #include <NTL/vec_ZZ.h>
  7. #include <NTL/ZZ.h>
  8. #include <NTL/ZZ_p.h>
  9. #include <sys/socket.h>
  10. #include <netinet/in.h>
  11. #include <arpa/inet.h>
  12. #include <sys/time.h>
  13. #include <unistd.h>
  14. #include <fstream>
  15. #include <vector>
  16. #include <set>
  17. #include <map>
  18. #include <string.h>
  19. #include "evutils.h"
  20. #include "subproblem.h"
  21. #include "controller.h"
  22. NTL_CLIENT
  23. // #undef VERBOSE
  24. // Used to prefix output from this process
  25. std::string output_prefix;
  26. struct SubproblemProgress;
  27. typedef std::set<struct bufferevent *> BESet;
  28. typedef std::map<struct bufferevent *, SubproblemProgress *> BEMap;
  29. static void besetdump(const BESet &bes, ostream &os)
  30. {
  31. BESet::const_iterator besit;
  32. os << hex << " ";
  33. for (besit = bes.begin(); besit != bes.end(); ++besit) {
  34. os << *besit << " ";
  35. }
  36. os << dec << "\n";
  37. }
  38. static void besetfree(BESet &bes)
  39. {
  40. BESet::iterator besit;
  41. for (besit = bes.begin(); besit != bes.end(); ++besit) {
  42. bufferevent_free(*besit);
  43. }
  44. bes.clear();
  45. }
  46. static void bemapdump(const BEMap &bem, ostream &os)
  47. {
  48. BEMap::const_iterator bemit;
  49. os << hex << " ";
  50. for (bemit = bem.begin(); bemit != bem.end(); ++bemit) {
  51. os << bemit->first << "->" << bemit->second << " ";
  52. }
  53. os << dec << "\n";
  54. }
  55. static void bemapfree(BEMap &bem)
  56. {
  57. BEMap::iterator bemit;
  58. for (bemit = bem.begin(); bemit != bem.end(); ++bemit) {
  59. bufferevent_free(bemit->first);
  60. }
  61. bem.clear();
  62. }
  63. struct Statuses {
  64. BESet idle;
  65. BEMap working;
  66. // Dump the state for debug purposes
  67. void dump(ostream &os) const {
  68. os << " idle (" << idle.size() << "):\n";
  69. besetdump(idle, os);
  70. os << " working (" << working.size() << "):\n";
  71. bemapdump(working, os);
  72. }
  73. void free(void) {
  74. besetfree(idle);
  75. bemapfree(working);
  76. }
  77. };
  78. struct FactorDecomp {
  79. ZZ factor;
  80. vec_ZZ fvec;
  81. };
  82. static void vsppdump(const vector<SubproblemProgress> &spv, ostream &os);
  83. static unsigned short curproblemid = 0;
  84. static struct ControllerState {
  85. ZZ rho;
  86. FactorDecomp p, q;
  87. ZZ base, target;
  88. bool working;
  89. struct timeval started_working;
  90. vector<SubproblemProgress> subproblems_p, subproblems_q;
  91. Statuses dpnodes, workers;
  92. unsigned int num_unsolved_subproblems;
  93. Worklist worklist;
  94. struct evconnlistener *listener;
  95. unsigned int problemid;
  96. unsigned int dpnode_count;
  97. unsigned long long total_kernel_launches;
  98. unsigned long long total_worker_time_ms;
  99. ControllerState() : working(false), num_unsolved_subproblems(0),
  100. listener(NULL), problemid(0), dpnode_count(0) {}
  101. // Reset the state for a new problem with the same modulus
  102. void reset(void) {
  103. base = 0;
  104. target = 0;
  105. working = false;
  106. subproblems_p.clear();
  107. subproblems_q.clear();
  108. num_unsolved_subproblems = 0;
  109. started_working.tv_sec = 0;
  110. started_working.tv_usec = 0;
  111. total_kernel_launches = 0;
  112. total_worker_time_ms = 0;
  113. problemid = curproblemid++;
  114. }
  115. // Dump the state for debug purposes
  116. void dump(ostream &os) const {
  117. if (!working) {
  118. os << "Not working\n";
  119. return;
  120. }
  121. os << "P:\n";
  122. vsppdump(subproblems_p, os);
  123. os << "Q:\n";
  124. vsppdump(subproblems_q, os);
  125. os << "dpnodes:\n";
  126. dpnodes.dump(os);
  127. os << "workers:\n";
  128. workers.dump(os);
  129. }
  130. } ctrlstate;
  131. struct IPPort {
  132. unsigned char ipport[6];
  133. IPPort(unsigned char *ipp) {
  134. memmove(ipport, ipp, 6);
  135. }
  136. void dump(ostream &os) const {
  137. os << int(ipport[0]) << "." << int(ipport[1]) << "." <<
  138. int(ipport[2]) << "." << int(ipport[3]) << ":" <<
  139. ((ipport[4] << 8) + ipport[5]) << " ";
  140. }
  141. };
  142. typedef vector<IPPort> IPPortSet;
  143. static void ipportsetdump(const IPPortSet &ipps, ostream &os)
  144. {
  145. IPPortSet::const_iterator ippsit;
  146. os << " ";
  147. for (ippsit = ipps.begin(); ippsit != ipps.end(); ++ippsit) {
  148. ippsit->dump(os);
  149. }
  150. os << "\n";
  151. }
  152. void custom_desired_resources(const ZZ &order,
  153. unsigned short &desired_dpnodes, unsigned int &max_workers,
  154. unsigned int &dpfreq);
  155. struct SubproblemProgress : Subproblem {
  156. // The sets of dpnodes and workers currently working on this subproblem
  157. BESet dpnodes, workers;
  158. // The dpnode IPPorts registered for this subproblem
  159. IPPortSet ipports;
  160. // The desired number of DPnodes for this subproblem
  161. unsigned short desired_dpnodes;
  162. // The maximum number of workers useful for this subproblem
  163. unsigned int max_workers;
  164. // Have we found a solution?
  165. bool solved;
  166. // The solution, if found.
  167. ZZ solution;
  168. unsigned int kernel_launch_count;
  169. unsigned long long worker_time_ms;
  170. unsigned int num_workers_replied;
  171. unsigned int worker_id_counter;
  172. SubproblemProgress(unsigned short id, const ZZ &b, const ZZ &t,
  173. const ZZ &m, const ZZ &o) :
  174. // By default, 1 in 1000 points are distinguihed points. The
  175. // number in the next line is 2^32/1000
  176. Subproblem(id, b, t, m, o, 4294967), solved(false), kernel_launch_count(0),
  177. worker_time_ms(0), num_workers_replied(0), worker_id_counter(0) {
  178. custom_desired_resources(order, desired_dpnodes, max_workers, dpfreq);
  179. // We need to update the binary description of the subproblem,
  180. // as we may have just changed dpfreq.
  181. updatedesc();
  182. }
  183. // Stop all dpnodes and workers
  184. void stop(void) {
  185. BESet::iterator iter;
  186. unsigned char stopcmd[1] = { 'S' };
  187. for (BESet::iterator iter = dpnodes.begin(); iter != dpnodes.end();
  188. ++iter) {
  189. bufferevent_write(*iter, stopcmd, 1);
  190. }
  191. for (BESet::iterator iter = workers.begin(); iter != workers.end();
  192. ++iter) {
  193. bufferevent_write(*iter, stopcmd, 1);
  194. }
  195. }
  196. void reset(void) {
  197. BESet::iterator iter;
  198. for (BESet::iterator iter = dpnodes.begin(); iter != dpnodes.end();
  199. ++iter) {
  200. ctrlstate.dpnodes.working.erase(*iter);
  201. ctrlstate.dpnodes.idle.insert(*iter);
  202. }
  203. for (BESet::iterator iter = workers.begin(); iter != workers.end();
  204. ++iter) {
  205. ctrlstate.workers.working.erase(*iter);
  206. ctrlstate.workers.idle.insert(*iter);
  207. }
  208. dpnodes.clear();
  209. workers.clear();
  210. ipports.clear();
  211. }
  212. // Dump for debugging purposes
  213. void dump(ostream &os) const {
  214. os << " Subproblem " << problemid << "\n";
  215. os << " dpnodes (" << dpnodes.size() << "):\n";
  216. besetdump(dpnodes, os);
  217. os << " workers (" << workers.size() << "):\n";
  218. besetdump(workers, os);
  219. os << " ipports (" << ipports.size() << "):\n";
  220. ipportsetdump(ipports, os);
  221. if (solved) {
  222. os << " solution: " << solution << "\n\n";
  223. }
  224. }
  225. void worker_write(struct bufferevent *bev) {
  226. bev_write(bev);
  227. bufferevent_write(bev, &worker_id_counter, sizeof(unsigned int));
  228. worker_id_counter++;
  229. unsigned short num_ipports = ipports.size();
  230. bufferevent_write(bev, &num_ipports, 2);
  231. for (unsigned short i = 0; i < num_ipports; ++i) {
  232. bufferevent_write(bev, ipports[i].ipport, 6);
  233. }
  234. #ifdef VERBOSE
  235. cerr << "Added worker " << bev << " to subproblem "
  236. << problemid << "\n";
  237. #endif
  238. }
  239. };
  240. // Dump the state for debug purposes
  241. static void vsppdump(const vector<SubproblemProgress> &spv, ostream &os)
  242. {
  243. vector<SubproblemProgress>::const_iterator spiter;
  244. int count = 0;
  245. for (spiter = spv.begin(); spiter != spv.end(); ++spiter) {
  246. ++count;
  247. os << " " << count << ":\n";
  248. spiter->dump(os);
  249. }
  250. os << "\n";
  251. }
  252. // Find a subproblem in the given vector that could use another DPnode,
  253. // and give it one of the idle ones. Only allocate it to a subproblem
  254. // with no current DPnodes if consider_empty is true.
  255. static void find_subproblem_for_dpnode(vector<SubproblemProgress> &spv,
  256. bool consider_empty)
  257. {
  258. vector<SubproblemProgress>::iterator spiter;
  259. for (spiter = spv.begin(); spiter != spv.end(); ++spiter) {
  260. if (spiter->solved) continue;
  261. if (spiter->dpnodes.size() == 0 && consider_empty == false) continue;
  262. // How many DPnodes would we like to have for this subproblem?
  263. while (spiter->dpnodes.size() < spiter->desired_dpnodes &&
  264. ctrlstate.dpnodes.idle.size() > 0) {
  265. // Get the first idle DPnode
  266. BESet::iterator beviter = ctrlstate.dpnodes.idle.begin();
  267. struct bufferevent *firstbev = *beviter;
  268. // Allocate it to the subproblem
  269. spiter->dpnodes.insert(firstbev);
  270. ctrlstate.dpnodes.working[firstbev] = &(*spiter);
  271. ctrlstate.dpnodes.idle.erase(firstbev);
  272. // Tell it to start listening for DPs
  273. spiter->bev_write(firstbev);
  274. }
  275. }
  276. }
  277. // Find a subproblem in the given vector that has all of its DPnodes and
  278. // could use another worker, and give it one of the idle ones.
  279. static void find_subproblem_for_worker(vector<SubproblemProgress> &spv)
  280. {
  281. vector<SubproblemProgress>::iterator spiter;
  282. for (spiter = spv.begin(); spiter != spv.end(); ++spiter) {
  283. if (spiter->solved) continue;
  284. while (spiter->ipports.size() == spiter->desired_dpnodes &&
  285. spiter->workers.size() < spiter->max_workers &&
  286. ctrlstate.workers.idle.size() > 0) {
  287. // Get the first idle worker
  288. BESet::iterator beviter = ctrlstate.workers.idle.begin();
  289. struct bufferevent *firstbev = *beviter;
  290. // Allocate it to the subproblem
  291. spiter->workers.insert(firstbev);
  292. ctrlstate.workers.working[firstbev] = &(*spiter);
  293. ctrlstate.workers.idle.erase(firstbev);
  294. // Tell it to start working on the subproblem
  295. spiter->worker_write(firstbev);
  296. }
  297. }
  298. }
  299. // See if there are any idle DPnodes or workers we can put to use
  300. static void schedule(void)
  301. {
  302. // Check the DPnodes
  303. // Iterate through the subproblems, looking for one that can use
  304. // another DPnode. First look for subproblems that already have
  305. // some, but not all, of their DPnodes
  306. if (ctrlstate.dpnodes.idle.size() > 0) {
  307. find_subproblem_for_dpnode(ctrlstate.subproblems_p, false);
  308. }
  309. if (ctrlstate.dpnodes.idle.size() > 0) {
  310. find_subproblem_for_dpnode(ctrlstate.subproblems_q, false);
  311. }
  312. // If there are still more dpnodes to place, start assigning them to
  313. // subproblems with no current dpnodes
  314. if (ctrlstate.dpnodes.idle.size() > 0) {
  315. find_subproblem_for_dpnode(ctrlstate.subproblems_p, true);
  316. }
  317. if (ctrlstate.dpnodes.idle.size() > 0) {
  318. find_subproblem_for_dpnode(ctrlstate.subproblems_q, true);
  319. }
  320. // Check the workers
  321. // Iterate through the subproblems, looking for one that can use
  322. // another worker.
  323. if (ctrlstate.workers.idle.size() > 0) {
  324. find_subproblem_for_worker(ctrlstate.subproblems_p);
  325. }
  326. if (ctrlstate.workers.idle.size() > 0) {
  327. find_subproblem_for_worker(ctrlstate.subproblems_q);
  328. }
  329. // cerr << "After schedule:\n"; ctrlstate.dump(cerr);
  330. }
  331. static ZZ computation_complete_p(const vector<SubproblemProgress> &v)
  332. {
  333. ZZ curmodulus, curexp;
  334. curmodulus = 2;
  335. curexp = 0;
  336. vector<SubproblemProgress>::const_iterator vit;
  337. for(vit = v.begin(); vit != v.end(); ++vit) {
  338. CRT(curexp, curmodulus, vit->solution, vit->order);
  339. }
  340. if (curexp < 0) {
  341. curexp += curmodulus;
  342. }
  343. return curexp;
  344. }
  345. // All subproblems are solved. Combine the results.
  346. static void computation_complete(void)
  347. {
  348. ZZ exp_p = computation_complete_p(ctrlstate.subproblems_p);
  349. ZZ exp_q = computation_complete_p(ctrlstate.subproblems_q);
  350. ZZ pm1 = (ctrlstate.p.factor - 1)/2;
  351. ZZ qm1 = (ctrlstate.q.factor - 1)/2;
  352. CRT(exp_p, pm1, exp_q, qm1);
  353. if (exp_p < 0) {
  354. exp_p += pm1;
  355. }
  356. ZZ& expon = exp_p;
  357. struct timeval ended_working;
  358. gettimeofday(&ended_working, NULL);
  359. unsigned long long computation_length_ms =
  360. (ended_working.tv_sec - ctrlstate.started_working.tv_sec) * 1000 +
  361. (ended_working.tv_usec - ctrlstate.started_working.tv_usec) / 1000;
  362. char length_buf[50];
  363. sprintf(length_buf, "%lld.%03lld s", computation_length_ms / 1000,
  364. computation_length_ms % 1000);
  365. char worker_time_sec_buf[50];
  366. sprintf(worker_time_sec_buf, "%lld.%03lld",
  367. ctrlstate.total_worker_time_ms / 1000,
  368. ctrlstate.total_worker_time_ms % 1000);
  369. char timestamp[20];
  370. sprintf(timestamp, "%d.%06d",
  371. (int)ended_working.tv_sec, (int)ended_working.tv_usec);
  372. cout << "Totals (problemid, launches, worker_time): "
  373. << ctrlstate.problemid << ", "
  374. << ctrlstate.total_kernel_launches << ", "
  375. << worker_time_sec_buf << "\n";
  376. cout << timestamp << ":" << output_prefix << ": " <<
  377. "expon = " << expon << "\n";
  378. cout << timestamp << ":" << output_prefix << ": " <<
  379. ctrlstate.worklist[0].first << " ";
  380. ZZ base_exp = PowerMod(ctrlstate.base, expon, ctrlstate.rho);
  381. if (base_exp == ctrlstate.target) {
  382. cout << "CORRECT in " << length_buf << "\n";
  383. } else {
  384. cout << "INCORRECT in " << length_buf << ":\n";
  385. cout << "base^exp = " << base_exp << "\n";
  386. cout << "target = " << ctrlstate.target << "\n";
  387. }
  388. cout.flush();
  389. ctrlstate.reset();
  390. }
  391. static unsigned short cursubproblemid = 0;
  392. // Take base and target mod f.factor, then decompose that into small
  393. // subproblems given our knowledge of the factors of phi(f.factor)
  394. static vector<SubproblemProgress> decomp(const ZZ_p &base, const ZZ_p &target,
  395. const FactorDecomp &f)
  396. {
  397. vector<SubproblemProgress> ret;
  398. // Compute phi(factor)
  399. const int fveclen = f.fvec.length();
  400. ZZ phi = to_ZZ(2);
  401. for (int i = 0; i < fveclen; ++i) {
  402. phi *= f.fvec[i];
  403. }
  404. ZZ_pPush push; // This will backup and restore the ZZ_p at the end of the function
  405. ZZ_p::init(f.factor);
  406. for (int i = 0; i < fveclen; ++i) {
  407. const ZZ& order = f.fvec[i];
  408. ZZ quotient = phi / order;
  409. ZZ_p subgroup_base = to_ZZ_p(rep(base));
  410. subgroup_base = power(subgroup_base, quotient);
  411. ZZ_p subgroup_target = to_ZZ_p(rep(target));
  412. subgroup_target = power(subgroup_target, quotient);
  413. if (subgroup_base == 1) {
  414. // The original base wasn't a generator of the whole group
  415. if (subgroup_target == 1) {
  416. // But the target is in the subgroup. Lucky us.
  417. continue;
  418. } else {
  419. ret.clear();
  420. return ret;
  421. }
  422. }
  423. ++cursubproblemid;
  424. ret.push_back(SubproblemProgress(cursubproblemid-1, rep(subgroup_base),
  425. rep(subgroup_target), f.factor, order));
  426. }
  427. return ret;
  428. }
  429. // Read the modulus (and the factorization of the modulus and its
  430. // totient) from the given file. "-" means cin. Returns true if
  431. // successful.
  432. static bool read_modulus(const char *filename)
  433. {
  434. if (strcmp(filename, "-")) {
  435. ifstream ins(filename);
  436. if (!ins.good()) return false;
  437. ins >> ctrlstate.rho >> ctrlstate.p.factor >> ctrlstate.p.fvec >>
  438. ctrlstate.q.factor >> ctrlstate.q.fvec;
  439. ins.close();
  440. } else {
  441. cin >> ctrlstate.rho >> ctrlstate.p.factor >> ctrlstate.p.fvec >>
  442. ctrlstate.q.factor >> ctrlstate.q.fvec;
  443. }
  444. return true;
  445. }
  446. static int generate_problem()
  447. {
  448. // If there's already a problem on the go, don't generate another one
  449. if (ctrlstate.working == true) {
  450. return -1;
  451. }
  452. while (ctrlstate.worklist[0].second == 0) {
  453. ctrlstate.worklist.erase(ctrlstate.worklist.begin());
  454. if (ctrlstate.worklist.size() > 0) {
  455. read_modulus(ctrlstate.worklist[0].first);
  456. } else {
  457. break;
  458. }
  459. }
  460. // If there are no more problems to generate, close the listener
  461. if (ctrlstate.worklist.size() == 0) {
  462. evconnlistener_free(ctrlstate.listener);
  463. ctrlstate.dpnodes.free();
  464. ctrlstate.workers.free();
  465. return -1;
  466. }
  467. ctrlstate.working = true;
  468. int num_subproblems_p = 0;
  469. int num_subproblems_q = 0;
  470. // Generate a DLP mod rho (in the large odd-order subgroup)
  471. ZZ_p::init(ctrlstate.rho);
  472. unsigned int attempt = 0;
  473. do {
  474. ZZ_p base;
  475. ZZ_p target;
  476. {
  477. #ifdef DERANDOMIZE
  478. RandomStreamPush push_seed;
  479. // the seed will be reset to its original value
  480. // once we exit this scope
  481. SetSeed(ctrlstate.rho*ctrlstate.p.factor*ctrlstate.q.factor+attempt);
  482. #endif
  483. base = power(random_ZZ_p(), 2);
  484. target = power(random_ZZ_p(), 2);
  485. }
  486. gettimeofday(&ctrlstate.started_working, NULL);
  487. ctrlstate.base = rep(base);
  488. ctrlstate.target = rep(target);
  489. // Decompose it mod p and mod q
  490. ctrlstate.subproblems_p = decomp(base, target, ctrlstate.p);
  491. ctrlstate.subproblems_q = decomp(base, target, ctrlstate.q);
  492. num_subproblems_p = ctrlstate.subproblems_p.size();
  493. num_subproblems_q = ctrlstate.subproblems_q.size();
  494. attempt++;
  495. } while (num_subproblems_p == 0 || num_subproblems_q == 0);
  496. ctrlstate.num_unsolved_subproblems =
  497. num_subproblems_p + num_subproblems_q;
  498. schedule();
  499. --(ctrlstate.worklist[0].second);
  500. return 0;
  501. }
  502. typedef enum {
  503. CCSTATE_START,
  504. CCSTATE_DPWAITRESP,
  505. CCSTATE_DPLISTENING,
  506. CCSTATE_DPEXPON,
  507. CCSTATE_END
  508. } CCState;
  509. struct ControllerConnInfo {
  510. CCState state;
  511. ControllerConnInfo() : state(CCSTATE_DPWAITRESP) {}
  512. };
  513. static void controller_dpnode_reader(struct bufferevent *bev, void *ctx)
  514. {
  515. struct evbuffer *input = bufferevent_get_input(bev);
  516. ControllerConnInfo *info = (ControllerConnInfo *)ctx;
  517. unsigned char cmd[1];
  518. ZZ expon;
  519. while(1) {
  520. size_t len = evbuffer_get_length(input);
  521. switch (info->state) {
  522. case CCSTATE_START:
  523. case CCSTATE_DPWAITRESP:
  524. if (len < 1) return;
  525. bufferevent_read(bev, cmd, 1);
  526. switch (cmd[0]) {
  527. case 'L':
  528. info->state = CCSTATE_DPLISTENING;
  529. break;
  530. case 'E':
  531. info->state = CCSTATE_DPEXPON;
  532. break;
  533. default:
  534. /* Unknown DPnode command received */
  535. fprintf(stderr, "Unknown command in "
  536. "controller_dpnode_reader: "
  537. "%c\n", cmd[0]);
  538. info->state = CCSTATE_END;
  539. break;
  540. }
  541. break;
  542. case CCSTATE_DPLISTENING:
  543. // Read 6 bytes
  544. if (len < 6) return;
  545. unsigned char ipport[6];
  546. unsigned int DPip;
  547. unsigned short DPport;
  548. bufferevent_read(bev, ipport, 6);
  549. memmove(&DPip, ipport, 4);
  550. memmove(&DPport, ipport+4, 2);
  551. {
  552. #ifdef VERBOSE
  553. struct in_addr DPaddr = { DPip };
  554. fprintf(stderr, "DP node at %s:%d\n", inet_ntoa(DPaddr), ntohs(DPport));
  555. #endif
  556. if (ctrlstate.dpnodes.working.count(bev) > 0) {
  557. ctrlstate.dpnodes.working[bev]->ipports.push_back(
  558. IPPort(ipport));
  559. schedule();
  560. }
  561. }
  562. info->state = CCSTATE_DPWAITRESP;
  563. break;
  564. case CCSTATE_DPEXPON:
  565. // Read the subproblemid and the answer to the subproblem
  566. if (len < 2 + 3*sizeof(unsigned int)) return;
  567. unsigned char exponbytes[2 + 3*sizeof(unsigned int)];
  568. unsigned short problemid;
  569. bufferevent_read(bev, exponbytes, 2 + 3*sizeof(unsigned int));
  570. memmove(&problemid, exponbytes, 2);
  571. ZZFromBytes(expon, exponbytes+2, 3*sizeof(unsigned int));
  572. // Find the subproblem and check the answer
  573. if (ctrlstate.dpnodes.working.count(bev) > 0) {
  574. SubproblemProgress *spp = ctrlstate.dpnodes.working[bev];
  575. if (spp->problemid == problemid &&
  576. spp->solved == false &&
  577. spp->target ==
  578. PowerMod(spp->base, expon, spp->modulus)) {
  579. // Subproblem solved!
  580. ctrlstate.num_unsolved_subproblems--;
  581. spp->solution = expon;
  582. spp->solved = true;
  583. spp->stop();
  584. schedule();
  585. }
  586. }
  587. info->state = CCSTATE_DPWAITRESP;
  588. break;
  589. case CCSTATE_END:
  590. // Shut down the connection
  591. delete info;
  592. bufferevent_free(bev);
  593. return;
  594. }
  595. }
  596. }
  597. static void controller_worker_reader(struct bufferevent *bev, void *ctx)
  598. {
  599. struct evbuffer *input = bufferevent_get_input(bev);
  600. ControllerConnInfo *info = (ControllerConnInfo *)ctx;
  601. while(1) {
  602. unsigned int kernel_launch_count = 0;
  603. unsigned long long worker_time_ms = 0;
  604. size_t len = evbuffer_get_length(input);
  605. if (len < sizeof(kernel_launch_count)+sizeof(worker_time_ms)) return;
  606. bufferevent_read(bev, &kernel_launch_count, sizeof(kernel_launch_count));
  607. bufferevent_read(bev, &worker_time_ms, sizeof(worker_time_ms));
  608. // this assumes that workers never leave a subproblem (never crash or get re-assigned)
  609. // otherwise we'll miss out on the kernel_launch_count for some workers
  610. SubproblemProgress *spp = ctrlstate.workers.working[bev];
  611. spp->kernel_launch_count += kernel_launch_count;
  612. spp->worker_time_ms += worker_time_ms;
  613. spp->num_workers_replied += 1;
  614. if (spp->num_workers_replied == spp->workers.size()) {
  615. char worker_time_sec_buf[50];
  616. sprintf(worker_time_sec_buf, "%lld.%03lld",
  617. spp->worker_time_ms / 1000,
  618. spp->worker_time_ms % 1000);
  619. cout << "Timing (name, problemid, subproblemid, launches, worker_time): "
  620. << ctrlstate.worklist[0].first << ", "
  621. << ctrlstate.problemid << ", "
  622. << spp->problemid << ", "
  623. << spp->kernel_launch_count << ", "
  624. << worker_time_sec_buf << "\n";
  625. cout.flush();
  626. ctrlstate.total_kernel_launches += spp->kernel_launch_count;
  627. ctrlstate.total_worker_time_ms += spp->worker_time_ms;
  628. spp->reset();
  629. schedule();
  630. }
  631. if (ctrlstate.num_unsolved_subproblems == 0 && ctrlstate.workers.working.size() == 0) {
  632. computation_complete();
  633. generate_problem();
  634. }
  635. }
  636. }
  637. static void controller_dpnode_event_cb(struct bufferevent *bev, short events,
  638. void *ctx)
  639. {
  640. if (events & (BEV_EVENT_EOF|BEV_EVENT_ERROR)) {
  641. ControllerConnInfo *info = (ControllerConnInfo*)ctx;
  642. fprintf(stderr, "Closing dpnode connection\n");
  643. if (ctrlstate.dpnodes.working.count(bev)) {
  644. // If we lose a dpnode from an active computation, the
  645. // computation is useless.
  646. SubproblemProgress *spp = ctrlstate.dpnodes.working[bev];
  647. ctrlstate.dpnodes.working.erase(bev);
  648. spp->dpnodes.erase(bev);
  649. spp->stop();
  650. spp->reset();
  651. } else {
  652. ctrlstate.dpnodes.idle.erase(bev);
  653. }
  654. delete info;
  655. bufferevent_free(bev);
  656. schedule();
  657. }
  658. }
  659. static void controller_worker_event_cb(struct bufferevent *bev, short events,
  660. void *ctx)
  661. {
  662. if (events & (BEV_EVENT_EOF|BEV_EVENT_ERROR)) {
  663. ControllerConnInfo *info = (ControllerConnInfo*)ctx;
  664. fprintf(stderr, "Closing worker connection\n");
  665. if (ctrlstate.workers.working.count(bev)) {
  666. SubproblemProgress *spp = ctrlstate.workers.working[bev];
  667. ctrlstate.workers.working.erase(bev);
  668. spp->workers.erase(bev);
  669. } else {
  670. ctrlstate.workers.idle.erase(bev);
  671. }
  672. delete info;
  673. bufferevent_free(bev);
  674. schedule();
  675. }
  676. }
  677. static void controller_event_cb(struct bufferevent *bev, short events,
  678. void *ctx)
  679. {
  680. if (events & (BEV_EVENT_EOF|BEV_EVENT_ERROR)) {
  681. fprintf(stderr, "Closing connection\n");
  682. ControllerConnInfo *info = (ControllerConnInfo*)ctx;
  683. delete info;
  684. bufferevent_free(bev);
  685. }
  686. }
  687. // We're just going to read a single byte that will tell us whether the
  688. // peer is a DPnode or a Worker
  689. static void controller_master_reader(struct bufferevent *bev, void *ctx)
  690. {
  691. struct evbuffer *input = bufferevent_get_input(bev);
  692. size_t len = evbuffer_get_length(input);
  693. if (len < 1) return;
  694. char msg_id = 'I';
  695. char indata[1];
  696. bufferevent_read(bev, indata, 1);
  697. switch(indata[0]) {
  698. case 'D':
  699. /* Add this DPnode to the list of available ones */
  700. ctrlstate.dpnodes.idle.insert(bev);
  701. bufferevent_setcb(bev, controller_dpnode_reader, NULL,
  702. controller_dpnode_event_cb, ctx);
  703. controller_dpnode_reader(bev, ctx);
  704. bufferevent_write(bev, &msg_id, sizeof(msg_id));
  705. bufferevent_write(bev, &ctrlstate.dpnode_count, sizeof(ctrlstate.dpnode_count));
  706. ctrlstate.dpnode_count++;
  707. schedule();
  708. return;
  709. case 'W':
  710. ctrlstate.workers.idle.insert(bev);
  711. bufferevent_enable(bev, EV_WRITE);
  712. bufferevent_setcb(bev, controller_worker_reader, NULL,
  713. controller_worker_event_cb, ctx);
  714. controller_worker_reader(bev, ctx);
  715. schedule();
  716. return;
  717. default:
  718. fprintf(stderr, "Unknown command in controller_master_reader: "
  719. "%c\n", indata[0]);
  720. ControllerConnInfo *info = (ControllerConnInfo*)ctx;
  721. delete info;
  722. bufferevent_free(bev);
  723. return;
  724. }
  725. }
  726. static void controller_accept_cb(struct evconnlistener *listener,
  727. evutil_socket_t fd, struct sockaddr *address, int socklen,
  728. void *ctx)
  729. {
  730. // Create the state of the new connection
  731. ControllerConnInfo *info = new ControllerConnInfo();
  732. // Create a bufferevent for the new connection
  733. struct event_base *base = evconnlistener_get_base(listener);
  734. struct bufferevent *bev = bufferevent_socket_new(
  735. base, fd, BEV_OPT_CLOSE_ON_FREE);
  736. bufferevent_setcb(bev, controller_master_reader, NULL,
  737. controller_event_cb, info);
  738. bufferevent_enable(bev, EV_READ|EV_WRITE);
  739. }
  740. // Create a new controller socket. bindport is the port to bind to (in
  741. // host byte order), or 0 if any port will do. ip and boundport are set
  742. // to the IP and port of the socket, in network byte order.
  743. static struct evconnlistener *controller_create(struct event_base *evbase,
  744. unsigned short bindport, unsigned int *ip, unsigned short *boundport)
  745. {
  746. return listener_create(evbase, bindport, controller_accept_cb, NULL,
  747. ip, boundport, false);
  748. }
  749. int controller_parse_args(int argc, char **argv, unsigned short &bindport,
  750. Worklist &worklist, unsigned short &total_workers, unsigned short &total_nodes,
  751. unsigned short &GB_mem_per_node)
  752. {
  753. bindport = 0;
  754. total_workers = 1;
  755. total_nodes = 1;
  756. GB_mem_per_node = 1;
  757. int reps = 1;
  758. while (argc > 2 && argv[1][0] == '-') {
  759. if (!strncmp(argv[1], "-p", 2)) {
  760. // A port number was specified
  761. bindport = strtoul(argv[2], NULL, 10);
  762. argc -= 2;
  763. argv += 2;
  764. } else if (!strncmp(argv[1], "-w", 2)) {
  765. total_workers = strtoul(argv[2], NULL, 10);
  766. argc -= 2;
  767. argv += 2;
  768. } else if (!strncmp(argv[1], "-n", 2)) {
  769. total_nodes = strtoul(argv[2], NULL, 10);
  770. argc -= 2;
  771. argv += 2;
  772. } else if (!strncmp(argv[1], "-m", 2)) {
  773. GB_mem_per_node = strtoul(argv[2], NULL, 10);
  774. argc -= 2;
  775. argv += 2;
  776. } else if (!strncmp(argv[1], "-r", 2)) {
  777. reps = strtoul(argv[2], NULL, 10);
  778. argc -= 2;
  779. argv += 2;
  780. }
  781. }
  782. if (argc < 3 || (argc % 2 == 0)) {
  783. return 1;
  784. }
  785. for (int r=0;r<reps;++r) {
  786. for (int i=1;i<argc;i+=2) {
  787. worklist.push_back(
  788. Workentry(argv[i], strtoul(argv[i+1], NULL, 10)));
  789. }
  790. }
  791. return 0;
  792. }
  793. int controller_main(const Worklist &worklist, unsigned short bindport,
  794. void (*boundcb)(const char *boundaddr, unsigned short boundport))
  795. {
  796. // Initialize the prng with some randomness from the kernel
  797. unsigned char randbuf[1024];
  798. ifstream urand("/dev/urandom");
  799. urand.read((char *)randbuf, sizeof(randbuf));
  800. urand.close();
  801. ZZ randzz = ZZFromBytes(randbuf, sizeof(randbuf));
  802. SetSeed(randzz);
  803. if (worklist.size() == 0) {
  804. return 0;
  805. }
  806. // Initialize the output prefix
  807. char prefix[300];
  808. gethostname(prefix, 256);
  809. strcat(prefix, "-C");
  810. output_prefix = std::string(prefix);
  811. ctrlstate.worklist = worklist;
  812. // Read the modulus and the factorization of its totient from the
  813. // specified file
  814. if (!read_modulus(worklist[0].first)) {
  815. cerr << "Unable to read file " << worklist[0].first << "\n";
  816. return 1;
  817. }
  818. struct event_base *evbase = event_base_new();
  819. unsigned int myip;
  820. unsigned short myport;
  821. ctrlstate.listener = controller_create(evbase, bindport, &myip, &myport);
  822. if (ctrlstate.listener && boundcb) {
  823. struct in_addr myaddr = { myip };
  824. boundcb(inet_ntoa(myaddr), ntohs(myport));
  825. }
  826. // Kick off the first problem to solve
  827. generate_problem();
  828. event_base_dispatch(evbase);
  829. return 0;
  830. }