// Notes: // - Each task receives distinct copy of parent // - Copy of child is shallow, be careful with `state` member static long visit(node_t parent) { node_t child; uint64_t *child_descendants = calloc(sizeof(long), parent.num_children); CILK_C_REDUCER_OPADD(num_descendants, ulong, 0); uint64_t tmp; // Spawn children, if any for (int i = 0; i < parent.num_children; i++) { child.height = parent.height + 1; for (int j = 0; j < num_samples; j++) { rng_spawn(parent.state.state, child.state.state, i); } child.num_children = calc_num_children(&child); child_descendants[i] = _Cilk_spawn visit(child); } _Cilk_sync; CILK_C_REGISTER_REDUCER(num_descendants); _Cilk_for(int i = 0; i < parent.num_children; i++) { REDUCER_VIEW(num_descendants) += child_descendants[i]; } tmp = 1 + REDUCER_VIEW(num_descendants); CILK_C_UNREGISTER_REDUCER(num_descendants); return tmp; }
// Gets the local instance of a trace local variable void* get_trace_local(tlv_id id) { int worker_id = 0; int steal_count = __cilkrts_get_steal_count(&worker_id); if (worker_id == 0) { printf("User thread???\n"); exit(1); } else { // Shift back from [1, P] to [0, P-1] worker_id -= 1; } worker_table_t* our_table = &worker_tables[worker_id]; if (our_table->wtrace_info[id].steal_count == steal_count && our_table->wtrace_info[id].view != NULL) { return our_table->wtrace_info[id].view; } trace_initializer initializer = global_table.gtrace_info[id].initializer; void* new_view = (*initializer)(); our_table->wtrace_info[id].view = new_view; tc_add(&REDUCER_VIEW(global_table.gtrace_info[id].reducer), new_view); our_table->wtrace_info[id].steal_count = steal_count; return new_view; }
// Collects all the instances of the variable in the execution strand trace_collection collect_trace_local(tlv_id id) { return REDUCER_VIEW(global_table.gtrace_info[id].reducer); }
return; } void board_reducer_identity(void *key, void* value) { full_board_t* board = (full_board_t*) value; memcpy(board, &base_board, sizeof(full_board_t)); board->is_identity = true; } void board_reducer_destroy(void *key, void *value) { return; } void initialize_reducer() { full_board_reducer_t reducer = CILK_C_INIT_REDUCER(full_board_t, board_reducer_reduce, board_reducer_identity, board_reducer_destroy, (full_board_t) { .board = {0}, .pieces = {{0}, {0}}, .pawn_count = {0, 0}, .rank_major = {0}, .file_major = {0}, .ply = 0, .is_identity = 0}); CILK_C_REDUCER_OPADD(ncr, ulong, 0); node_count_reducer = ncr; board_reducer = reducer; CILK_C_REGISTER_REDUCER(board_reducer); board_reducer_identity(NULL, &REDUCER_VIEW(board_reducer)); REDUCER_VIEW(board_reducer).is_identity = false; } void destroy_reducer() { CILK_C_UNREGISTER_REDUCER(board_reducer); CILK_C_UNREGISTER_REDUCER(node_count_reducer); } #include "./end_game.c"
static score_t scout_search(searchNode *node, int depth, uint64_t *node_count_serial, full_board_t* board) { // Initialize the search node. initialize_scout_node(node, depth); // check whether we should abort if (should_abort_check() || parallel_parent_aborted(node)) { return 0; } // Pre-evaluate this position. leafEvalResult pre_evaluation_result = evaluate_as_leaf(node, SEARCH_SCOUT, board); // If we decide to stop searching, return the pre-evaluation score. if (pre_evaluation_result.type == MOVE_EVALUATED) { return pre_evaluation_result.score; } // Populate some of the fields of this search node, using some // of the information provided by the pre-evaluation. int hash_table_move = pre_evaluation_result.hash_table_move; node->best_score = pre_evaluation_result.score; node->quiescence = pre_evaluation_result.should_enter_quiescence; // Grab the killer-moves for later use. move_t killer_a = killer[KMT(node->ply, 0)]; move_t killer_b = killer[KMT(node->ply, 1)]; // Store the sorted move list on the stack. // MAX_NUM_MOVES is all that we need. sortable_move_t move_list[MAX_NUM_MOVES]; // Obtain the sorted move list. int num_of_moves = get_sortable_move_list(node, move_list, hash_table_move, board); int num_of_special_moves = move_list[num_of_moves]; int num_of_nonzero_moves = move_list[num_of_moves + 1]; if (num_of_special_moves == 0) { selectionSort(move_list, num_of_moves, 1); num_of_special_moves = 1; } int number_of_moves_evaluated = 0; bool generated_early_cutoff = false; for (int mv_index = 0; mv_index < num_of_special_moves; mv_index++) { int local_index = number_of_moves_evaluated++; move_t mv = get_move(move_list[local_index]); // increase node count REDUCER_VIEW(node_count_reducer)++; smallMoveEvaluationResult result = evaluateMove(node, mv, killer_a, killer_b, SEARCH_SCOUT, node_count_serial); if (result.type == MOVE_ILLEGAL || result.type == MOVE_IGNORE) { continue; } if (abortf || parallel_parent_aborted(node)) { return 0; } // A legal move is a move that's not KO, but when we are in quiescence // we only want to count moves that has a capture. if (result.type == MOVE_EVALUATED) { node->legal_move_count++; } // process the score. Note that this mutates fields in node. bool cutoff = search_process_score(node, mv, local_index, &result, SEARCH_SCOUT); if (cutoff) { generated_early_cutoff = true; break; } } // ABOVE IS YOUNG BROTHER'S WAIT CODE. IT SHOULD RUN IN SERIAL // // BELOW IS PARALLELIZED SCOUT SEARCH. IT SHOULD RUN IN PARALLEL // if this happens, we generated an early cutoff in young brother's wait and can just skip to the end if (generated_early_cutoff) goto finish_scout_search; sort_incremental(move_list + num_of_special_moves, num_of_nonzero_moves - num_of_special_moves); // A simple mutex. See simple_mutex.h for implementation details. simple_mutex_t node_mutex; init_simple_mutex(&node_mutex); // TODO: see if changing this depth is better if (depth >= 2) { cilk_for (int mv_index = num_of_special_moves; mv_index < num_of_moves; mv_index++) { do { if (node->abort) continue; int local_index = __sync_fetch_and_add(&number_of_moves_evaluated, 1); move_t mv = get_move(move_list[local_index]); // increase node count REDUCER_VIEW(node_count_reducer)++; smallMoveEvaluationResult result = evaluateMove(node, mv, killer_a, killer_b, SEARCH_SCOUT, node_count_serial); // TODO: change this to break if we want to abort if (result.type == MOVE_ILLEGAL || result.type == MOVE_IGNORE) { continue; } if (abortf || parallel_parent_aborted(node)) { node->abort = true; continue; } // A legal move is a move that's not KO, but when we are in quiescence // we only want to count moves that has a capture. if (result.type == MOVE_EVALUATED) { __sync_fetch_and_add(&node->legal_move_count, 1); } bool cutoff = false; if (result.score > node->best_score) { simple_acquire(&node_mutex); // process the score. Note that this mutates fields in node. cutoff = search_process_score(node, mv, local_index, &result, SEARCH_SCOUT); simple_release(&node_mutex); } if (cutoff) { node->abort = true; continue; } } while (false); } } else {