worker.cc 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367
  1. #include <pthread.h>
  2. extern "C" {
  3. #include <event2/thread.h>
  4. #include <event2/bufferevent.h>
  5. #include <event2/buffer.h>
  6. #include <event2/event.h>
  7. #include <event2/util.h>
  8. }
  9. #include <NTL/ZZ_p.h>
  10. #include <fstream>
  11. #include <vector>
  12. #include <stdio.h>
  13. #include <errno.h>
  14. #include <signal.h>
  15. #include <cuda_runtime.h>
  16. #include "cudadl.h"
  17. #include "evutils.h"
  18. #include "subproblem.h"
  19. #include "worker.h"
  20. NTL_CLIENT
  21. #undef VERBOSE
  22. static int cuda_device_id = -1;
  23. typedef enum {
  24. WRKCCSTATE_AWAITCMD,
  25. WRKCCSTATE_RDPROBLEM,
  26. WRKCCSTATE_RDDPNODES,
  27. WRKCCSTATE_END
  28. } WrkCCState;
  29. struct WrkControllerConnInfo {
  30. WrkCCState state;
  31. unsigned short num_dpnodes;
  32. WrkControllerConnInfo() : state(WRKCCSTATE_AWAITCMD), num_dpnodes(0) {}
  33. };
  34. typedef enum { WT_NOT_RUNNING, WT_RUNNING, WT_SHOULD_STOP} WTState;
  35. static struct WrkControllerState {
  36. Subproblem *current_problem;
  37. unsigned short num_expected_dpnodes;
  38. vector<struct bufferevent *> dpnodes;
  39. unsigned short num_connected_dpnodes;
  40. WTState worker_thread_state;
  41. pthread_t worker_thread;
  42. unsigned int kernel_launch_count;
  43. struct timeval time_started_calculations;
  44. unsigned int worker_id;
  45. WrkControllerState(): current_problem(NULL),
  46. worker_thread_state(WT_NOT_RUNNING) {}
  47. } wrkctrlstate;
  48. // ----- Below this line are the functions running in the worker thread.
  49. #if WORDS > 1
  50. #define DEMUXWORD 8
  51. #else
  52. #define DEMUXWORD 7
  53. #endif
  54. // This function is called from inside cuda_dl for each DP it encounters.
  55. // It calls the function named "dpcallback" directly. It would be
  56. // cleaner if this were passed as a function pointer to cuda_dl, but
  57. // that makes nvcc 3.1 segfault. :-p
  58. // dp points to an array of WORDS+7 unsigned ints:
  59. // - 1 word of threadID/blockID
  60. // - WORDS words of the dp value
  61. // - 3 words of a
  62. // - 3 words of b
  63. bool dpcallback(void *cbdata, unsigned int *dpwords)
  64. {
  65. unsigned int demux = dpwords[DEMUXWORD];
  66. struct bufferevent *bev =
  67. wrkctrlstate.dpnodes[demux % wrkctrlstate.num_connected_dpnodes];
  68. bufferevent_write(bev, dpwords+1, (WORDS+6)*sizeof(unsigned int));
  69. // If worker_thread_state changes to WT_SHOULD_STOP, then signal to
  70. // stop computation by returning true. If for some reason, it
  71. // becomes WT_NOT_RUNNING (which it shouldn't), stop as well.
  72. return wrkctrlstate.worker_thread_state != WT_RUNNING;
  73. }
  74. static void *worker_thread_start(void *data)
  75. {
  76. ZZ_p::init(wrkctrlstate.current_problem->modulus);
  77. cudaError_t cudares = cudaSetDevice(cuda_device_id);
  78. if (cudares != cudaSuccess) {
  79. cerr << "Error setting CUDA device: " << cudaGetErrorString(cudares) << "\n";
  80. return NULL;
  81. }
  82. bool filled_dp_buffer = false;
  83. gettimeofday(&wrkctrlstate.time_started_calculations, NULL);
  84. {
  85. #ifdef DERANDOMIZE
  86. RandomStreamPush push_seed;
  87. // the seed will be reset to its original value
  88. // once we exit this scope
  89. SetSeed(wrkctrlstate.current_problem->base*
  90. wrkctrlstate.current_problem->target*
  91. wrkctrlstate.current_problem->order*
  92. wrkctrlstate.current_problem->modulus+wrkctrlstate.worker_id);
  93. #endif
  94. cuda_dl(to_ZZ_p(wrkctrlstate.current_problem->base),
  95. to_ZZ_p(wrkctrlstate.current_problem->target),
  96. wrkctrlstate.current_problem->order,
  97. wrkctrlstate.current_problem->modulus,
  98. wrkctrlstate.current_problem->dpfreq, NULL,
  99. &wrkctrlstate.kernel_launch_count, &filled_dp_buffer);
  100. }
  101. #ifdef SAVE_DPS
  102. if (filled_dp_buffer) {
  103. cerr << "Warning: The device dp buffer was filled, so some points were not recorded.\n";
  104. cerr << "These points will not be reproducible.\n";
  105. }
  106. #endif
  107. return NULL;
  108. }
  109. // ----- Above this line are the functions running in the worker thread.
  110. // Below are the functions running in the main (communication) thread.
  111. static void stop_working(void)
  112. {
  113. #ifdef VERBOSE
  114. cerr << "Stopping work\n";
  115. #endif
  116. if (wrkctrlstate.worker_thread_state != WT_NOT_RUNNING) {
  117. // Tell the worker thread to stop after its next kernel launch
  118. wrkctrlstate.worker_thread_state = WT_SHOULD_STOP;
  119. pthread_join(wrkctrlstate.worker_thread, NULL);
  120. wrkctrlstate.worker_thread_state = WT_NOT_RUNNING;
  121. }
  122. // Close the connections to the dpnodes
  123. vector<struct bufferevent *>::iterator bevit;
  124. for (bevit = wrkctrlstate.dpnodes.begin();
  125. bevit != wrkctrlstate.dpnodes.end(); ++bevit) {
  126. #ifdef VERBOSE
  127. cerr << "Closing connection to " << *bevit << "\n";
  128. #endif
  129. bufferevent_free(*bevit);
  130. }
  131. wrkctrlstate.dpnodes.clear();
  132. wrkctrlstate.num_connected_dpnodes = 0;
  133. wrkctrlstate.num_expected_dpnodes = 0;
  134. wrkctrlstate.worker_id = 0;
  135. // Careful! Subproblem uses NTL, so we must be sure we're not
  136. // multithreaded at this point.
  137. delete wrkctrlstate.current_problem;
  138. wrkctrlstate.current_problem = NULL;
  139. }
  140. static void start_working(void)
  141. {
  142. #ifdef VERBOSE
  143. cerr << "Starting work (worker id: " << wrkctrlstate.worker_id << ")\n";
  144. #endif
  145. struct timeval now;
  146. gettimeofday(&now, NULL);
  147. char timestamp[20];
  148. sprintf(timestamp, "%d.%06d", (int)now.tv_sec, (int)now.tv_usec);
  149. cout << timestamp << ":" << output_prefix << ": Subproblem " << wrkctrlstate.current_problem->problemid << "\n";
  150. cout.flush();
  151. wrkctrlstate.kernel_launch_count = 0;
  152. wrkctrlstate.worker_thread_state = WT_RUNNING;
  153. if (pthread_create(&wrkctrlstate.worker_thread, NULL,
  154. worker_thread_start, NULL)) {
  155. wrkctrlstate.worker_thread = WT_NOT_RUNNING;
  156. cerr << "Could not start worker thread\n";
  157. }
  158. char thread_name[16];
  159. snprintf(thread_name, sizeof(thread_name), "prob:%2d wrkr:%2d", wrkctrlstate.current_problem->problemid, wrkctrlstate.worker_id);
  160. pthread_setname_np(wrkctrlstate.worker_thread, thread_name);
  161. }
  162. static void dpconn_event_cb(struct bufferevent *bev, short events,
  163. void *ctx)
  164. {
  165. if (events & BEV_EVENT_CONNECTED) {
  166. // We have successfully connected to the dpnode
  167. #ifdef VERBOSE
  168. cerr << "Connection established to dpnode " << bev << "\n";
  169. #endif
  170. bufferevent_enable(bev, EV_WRITE);
  171. ++wrkctrlstate.num_connected_dpnodes;
  172. if (wrkctrlstate.num_connected_dpnodes ==
  173. wrkctrlstate.num_expected_dpnodes) {
  174. start_working();
  175. }
  176. } else if (events & (BEV_EVENT_EOF|BEV_EVENT_ERROR)) {
  177. #ifdef VERBOSE
  178. cerr << "Closing connection to dpnode " << bev << " ";
  179. if (events & BEV_EVENT_EOF) {
  180. cerr << "EOF";
  181. }
  182. if (events & BEV_EVENT_ERROR) {
  183. cerr << "ERR (" << evutil_socket_error_to_string(EVUTIL_SOCKET_ERROR()) << ")";
  184. }
  185. cerr << "\n";
  186. #endif
  187. stop_working();
  188. }
  189. }
  190. static void controllerconn_reader(struct bufferevent *bev, void *ctx)
  191. {
  192. struct evbuffer *input = bufferevent_get_input(bev);
  193. WrkControllerConnInfo *info = (WrkControllerConnInfo *)ctx;
  194. unsigned char cmd[1];
  195. unsigned char subproblem[SUBPROBLEM_DESC_LEN];
  196. struct timeval ended_working;
  197. unsigned long long computation_length_ms;
  198. while(1) {
  199. size_t len = evbuffer_get_length(input);
  200. switch(info->state) {
  201. case WRKCCSTATE_AWAITCMD:
  202. if (len < 1) return;
  203. bufferevent_read(bev, cmd, 1);
  204. #ifdef VERBOSE
  205. cerr << "Command " << cmd[0] << " received\n";
  206. #endif
  207. switch(cmd[0]) {
  208. case 'P':
  209. info->state = WRKCCSTATE_RDPROBLEM;
  210. break;
  211. case 'S':
  212. stop_working();
  213. gettimeofday(&ended_working, NULL);
  214. computation_length_ms =
  215. (ended_working.tv_sec - wrkctrlstate.time_started_calculations.tv_sec) * 1000 +
  216. (ended_working.tv_usec - wrkctrlstate.time_started_calculations.tv_usec) / 1000;
  217. bufferevent_write(bev, &(wrkctrlstate.kernel_launch_count), sizeof(wrkctrlstate.kernel_launch_count));
  218. bufferevent_write(bev, &computation_length_ms, sizeof(computation_length_ms));
  219. cout << "Launch count: " << wrkctrlstate.kernel_launch_count << "\n";
  220. cout.flush();
  221. break;
  222. default:
  223. /* Unknown command received */
  224. fprintf(stderr, "Unknown command in "
  225. "controllerconn_reader: %c\n", cmd[0]);
  226. info->state = WRKCCSTATE_END;
  227. break;
  228. }
  229. break;
  230. case WRKCCSTATE_RDPROBLEM:
  231. if (len < SUBPROBLEM_DESC_LEN+sizeof(unsigned int)+2) return;
  232. stop_working();
  233. bufferevent_read(bev, subproblem, SUBPROBLEM_DESC_LEN);
  234. bufferevent_read(bev, &wrkctrlstate.worker_id, sizeof(unsigned int));
  235. bufferevent_read(bev, &(info->num_dpnodes), 2);
  236. // Careful! Subproblem uses NTL, so we must be sure
  237. // we're not multithreaded at this point.
  238. wrkctrlstate.current_problem = new Subproblem(subproblem);
  239. info->state = WRKCCSTATE_RDDPNODES;
  240. /* FALLTHROUGH */
  241. case WRKCCSTATE_RDDPNODES:
  242. if (len < 6*(info->num_dpnodes)) return;
  243. wrkctrlstate.num_expected_dpnodes = info->num_dpnodes;
  244. {
  245. unsigned short i;
  246. for(i=0;i<info->num_dpnodes;++i) {
  247. unsigned char ipport[6];
  248. bufferevent_read(bev, ipport, 6);
  249. #ifdef VERBOSE
  250. cerr << "Connecting to DPnode " <<
  251. int(ipport[0]) << "." <<
  252. int(ipport[1]) << "." <<
  253. int(ipport[2]) << "." <<
  254. int(ipport[3]) << ":" <<
  255. ((ipport[4] << 8) + ipport[5]) <<
  256. "\n";
  257. #endif
  258. struct bufferevent *dpbev = client_create(
  259. bufferevent_get_base(bev), ipport,
  260. dpconn_event_cb, true);
  261. #ifdef VERBOSE
  262. cerr << "Starting connection to dpnode " << dpbev << "\n";
  263. #endif
  264. if (dpbev) {
  265. wrkctrlstate.dpnodes.push_back(dpbev);
  266. } else {
  267. stop_working();
  268. }
  269. }
  270. }
  271. info->state = WRKCCSTATE_AWAITCMD;
  272. break;
  273. case WRKCCSTATE_END:
  274. // Shut down
  275. delete info;
  276. event_base_loopbreak(bufferevent_get_base(bev));
  277. #ifdef VERBOSE
  278. cerr << "Closing connection to " << bev << "\n";
  279. #endif
  280. bufferevent_free(bev);
  281. return;
  282. }
  283. }
  284. }
  285. static void controllerconn_event_cb(struct bufferevent *bev, short events,
  286. void *ctx)
  287. {
  288. if (events & BEV_EVENT_CONNECTED) {
  289. // We have successfully connected to the controller
  290. char id[1] = { 'W' };
  291. bufferevent_enable(bev, EV_READ|EV_WRITE);
  292. bufferevent_write(bev, id, 1);
  293. bufferevent_setcb(bev, controllerconn_reader, NULL,
  294. controllerconn_event_cb, new WrkControllerConnInfo());
  295. } else if (events & (BEV_EVENT_EOF|BEV_EVENT_ERROR)) {
  296. fprintf(stderr, "Closing connection to controller and exiting\n");
  297. event_base_loopbreak(bufferevent_get_base(bev));
  298. #ifdef VERBOSE
  299. cerr << "Closing connection to " << bev << "\n";
  300. #endif
  301. bufferevent_free(bev);
  302. }
  303. }
  304. int worker_main(const char *controller_host, unsigned short controller_port, int gpu_id)
  305. {
  306. // Initialize the prng with some randomness from the kernel
  307. unsigned char randbuf[1024];
  308. ifstream urand("/dev/urandom");
  309. urand.read((char *)randbuf, sizeof(randbuf));
  310. urand.close();
  311. ZZ randzz = ZZFromBytes(randbuf, sizeof(randbuf));
  312. SetSeed(randzz);
  313. evthread_use_pthreads();
  314. signal(SIGPIPE, SIG_IGN);
  315. cuda_device_id = gpu_id;
  316. return controller_client(controller_host, controller_port,
  317. controllerconn_event_cb, true, NULL);
  318. }