From 2e76bfb235b3ee4dbdbc9fb4acd46f04dcc33f97 Mon Sep 17 00:00:00 2001 From: "christian.schulz.phone@gmail.com" Date: Thu, 2 Apr 2026 16:03:00 +0000 Subject: [PATCH] Multipass performance optimizations for freight_con, freight_cut, and freight_graphs ~4.3x speedup for multipass streaming partitioning while preserving bit-identical partition output. Applied to both hypergraph and graph streaming partitioners. I/O optimizations: - Cache binary input across multipass iterations (ram_stream) - Skip readFirstLineStream on subsequent ram_stream passes - Use mmap with MAP_POPULATE for zero-copy file loading (macOS compat) - Bulk-read + parse from char buffer with pre-reserved vectors - Skip per-node gettimeofday syscalls in inner loop Evaluation optimizations (hypergraphs): - Use cached data for evaluation instead of re-reading file - Replace unordered_set with flat bitset for connectivity tracking - Skip unused metric per mode (cut-only or connectivity-only) - Specialize bitset check for k<=64 (single word) - Cheap first-pass shortcuts for cut/connectivity (unweighted, k<=64) - Use local scratch vector for inter-pass eval to avoid save/restore - Skip evaluation when partition has converged Processing optimizations: - Bypass OMP critical sections when single-threaded - Eliminate valid_neighboring_nets vector for ram_stream (hypergraphs) - Prefetch stream_edges_assign entries in hot loops (hypergraphs) - Remove -fno-builtin-malloc flags - Use memset for all_blocks_to_keys initialization - Skip dead computations for default config - Add PGO support to CMakeLists.txt (GCC/Clang only) Safety: - OMP atomic for nodes_moved; first-pass bitset in sequential post-pass - Hardened mmap: fstat/tellg/offset validation with ifstream fallback - OOM guard for connectivity bitset (256 MiB cap) - PGO_GENERATE/PGO_USE mutual exclusivity enforced - Default initializers for evaluate_cut/evaluate_connectivity --- code_for_graphs/CMakeLists.txt | 19 +- code_for_graphs/app/streammultisection.cpp | 61 +++-- code_for_graphs/lib/io/graph_io_stream.h | 104 ++++++++- .../vertex_partitioning.h | 19 +- code_for_hypergraphs/CMakeLists.txt | 20 +- code_for_hypergraphs/app/configuration.h | 2 + code_for_hypergraphs/app/freight.cpp | 165 +++++++++++--- .../lib/io/graph_io_stream.cpp | 213 ++++++++++++------ code_for_hypergraphs/lib/io/graph_io_stream.h | 177 +++++++++++++-- .../vertex_partitioning.h | 19 +- .../lib/partition/partition_config.h | 2 + 11 files changed, 637 insertions(+), 164 deletions(-) diff --git a/code_for_graphs/CMakeLists.txt b/code_for_graphs/CMakeLists.txt index b3635e4..895e001 100644 --- a/code_for_graphs/CMakeLists.txt +++ b/code_for_graphs/CMakeLists.txt @@ -24,6 +24,23 @@ if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) set(CMAKE_BUILD_TYPE "Release") endif() +# Profile-Guided Optimization (PGO) support +option(PGO_GENERATE "Build with PGO instrumentation (phase 1)" OFF) +option(PGO_USE "Build with PGO optimization from collected profiles (phase 2)" OFF) +if(PGO_GENERATE AND PGO_USE) + message(FATAL_ERROR "PGO_GENERATE and PGO_USE are mutually exclusive. Use PGO_GENERATE first, run a training workload, then rebuild with PGO_USE.") +endif() +if(CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang") + if(PGO_GENERATE) + add_definitions(-fprofile-generate=${CMAKE_BINARY_DIR}/pgo_data) + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fprofile-generate=${CMAKE_BINARY_DIR}/pgo_data") + endif() + if(PGO_USE) + add_definitions(-fprofile-use=${CMAKE_BINARY_DIR}/pgo_data -fprofile-correction) + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fprofile-use=${CMAKE_BINARY_DIR}/pgo_data") + endif() +endif() + # tweak compiler flags CHECK_CXX_COMPILER_FLAG(-funroll-loops COMPILER_SUPPORTS_FUNROLL_LOOPS) if(COMPILER_SUPPORTS_FUNROLL_LOOPS) @@ -90,7 +107,7 @@ include_directories(${CMAKE_CURRENT_SOURCE_DIR}/lib/tools) -set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-builtin-malloc -fno-builtin-calloc -fno-builtin-realloc") +# Removed -fno-builtin-malloc flags to let compiler optimize allocation patterns set(LIBKAFFPA_SOURCE_FILES lib/tools/quality_metrics.cpp diff --git a/code_for_graphs/app/streammultisection.cpp b/code_for_graphs/app/streammultisection.cpp index 44726c7..ed1df76 100644 --- a/code_for_graphs/app/streammultisection.cpp +++ b/code_for_graphs/app/streammultisection.cpp @@ -88,6 +88,7 @@ int main(int argn, char **argv) { config.LogDump(stdout); config.stream_input = true; + config.graph_filename = graph_filename; [[maybe_unused]] bool already_fully_partitioned; @@ -98,27 +99,45 @@ int main(int argn, char **argv) { for (config.restream_number=0; config.restream_number 1 + config.restream_number) { + config.stream_total_upperbound = ceil(((100+1.5*config.imbalance)/100.)*(config.remaining_stream_nodes/(double)config.k)); + } else { + config.stream_total_upperbound = ceil(((100+config.imbalance)/100.)*(config.remaining_stream_nodes/(double)config.k)); + } + config.quotient_nodes = config.k; + total_edge_cut = 0; + qap = 0; + config.nmbNodes = MIN(config.stream_buffer_len, config.remaining_stream_nodes); + config.n_batches = ceil(config.remaining_stream_nodes / (double)config.nmbNodes); + config.curr_batch = 0; + } buffer_io_time += io_t.elapsed(); onepass_partitioner->instantiate_blocks(config.remaining_stream_nodes, config.remaining_stream_edges, config.k, config.imbalance); if (config.restream_number > 0 && config.use_self_sorting_array) { onepass_partitioner->reset_sorted_blocks(); } if (config.stream_rec_bisection) { - onepass_partitioner->create_problem_tree(config.remaining_stream_nodes, config.remaining_stream_edges, config.k, + onepass_partitioner->create_problem_tree(config.remaining_stream_nodes, config.remaining_stream_edges, config.k, config.enable_mapping, config.stream_rec_biss_orig_alpha, config.non_hashified_layers); } omp_set_schedule(config.omp_schedule, config.omp_chunk); omp_set_num_threads(config.parallel_nodes); -#pragma omp parallel for schedule(static) for (int i=0; i < config.parallel_nodes; i++) { config.all_blocks_to_keys[i].resize(config.k); - for (auto & b : config.all_blocks_to_keys[i]) { - b = INVALID_PARTITION; - } + memset(config.all_blocks_to_keys[i].data(), 0xFF, config.k * sizeof(PartitionID)); config.neighbor_blocks[i].resize(config.k); config.next_key[i] = 0; if (config.sample_edges) { @@ -131,16 +150,18 @@ int main(int argn, char **argv) { } processing_t.restart(); -#pragma omp parallel for schedule(runtime) +#pragma omp parallel for schedule(runtime) for (LongNodeID curr_node = 0; curr_node < config.n_batches; curr_node++) { int my_thread = omp_get_thread_num(); - io_t.restart(); - if((config.one_pass_algorithm != ONEPASS_HASHING) && (config.one_pass_algorithm != ONEPASS_HASHING_CRC32)) { - graph_io_stream::loadBufferLinesToBinary(config, input, 1); + if (!config.ram_stream) { + io_t.restart(); + if((config.one_pass_algorithm != ONEPASS_HASHING) && (config.one_pass_algorithm != ONEPASS_HASHING_CRC32)) { + graph_io_stream::loadBufferLinesToBinary(config, input, 1); + } + buffer_io_time += io_t.elapsed(); } - buffer_io_time += io_t.elapsed(); - // ***************************** perform partitioning *************************************** - t.restart(); + // ***************************** perform partitioning *************************************** + if (config.dynamic_threashold) t.restart(); graph_io_stream::readNodeOnePass(config, curr_node, my_thread, input, onepass_partitioner); PartitionID block = onepass_partitioner->solve_node(curr_node, 1, my_thread); (*config.stream_nodes_assign)[curr_node] = block; @@ -158,16 +179,20 @@ int main(int argn, char **argv) { config.sampling_threashold; config.sampling_threashold = MIN(config.sampling_threashold, 4); } - global_mapping_time += t.elapsed(); } total_time += processing_t.elapsed(); + global_mapping_time += processing_t.elapsed(); - if (config.ram_stream) { - delete input; - /* delete lines; */ + if (!config.ram_stream) { + /* Non-ram_stream: input already freed per-node */ } } + // Free cached input for ram_stream after all passes + if (config.ram_stream && input != NULL) { + delete input; + } + // output some information about the partition that we have computed std::cout << "graph has " << config.stream_nodes_assign->size() << " nodes and " << config.total_edges << " edges" << std::endl; std::cout << "Total processing time: " << total_time << std::endl; diff --git a/code_for_graphs/lib/io/graph_io_stream.h b/code_for_graphs/lib/io/graph_io_stream.h index 05ece21..506227f 100644 --- a/code_for_graphs/lib/io/graph_io_stream.h +++ b/code_for_graphs/lib/io/graph_io_stream.h @@ -19,6 +19,10 @@ #include #include #include +#include +#include +#include +#include #include "definitions.h" #include "data_structure/graph_access.h" @@ -191,20 +195,96 @@ inline void graph_io_stream::loadBufferLinesToBinary(PartitionConfig & partition inline std::vector>* graph_io_stream::loadLinesFromStreamToBinary(PartitionConfig & partition_config, LongNodeID num_lines) { std::vector>* input; input = new std::vector>(num_lines); - std::vector* lines; - lines = new std::vector(1); - LongNodeID node_counter = 0; - buffered_input *ss2 = NULL; - while( node_counter < num_lines) { - std::getline(*(partition_config.stream_in),(*lines)[0]); - if ((*lines)[0][0] == '%') { // a comment in the file - continue; + + if (partition_config.ram_stream) { + // mmap the file for zero-copy access, then parse from the mapped region + std::ifstream& in = *(partition_config.stream_in); + std::streampos cur = in.tellg(); + + int fd = open(partition_config.graph_filename.c_str(), O_RDONLY); + void* mmap_addr = MAP_FAILED; + size_t file_size = 0; + size_t offset = 0; + if (fd != -1) { + struct stat st; + if (fstat(fd, &st) == 0 && st.st_size > 0 && cur >= 0) { + file_size = (size_t)st.st_size; + offset = (size_t)cur; + if (offset <= file_size) { +#ifdef MAP_POPULATE + mmap_addr = mmap(nullptr, file_size, PROT_READ, MAP_PRIVATE | MAP_POPULATE, fd, 0); +#else + mmap_addr = mmap(nullptr, file_size, PROT_READ, MAP_PRIVATE, fd, 0); +#endif + } + } + close(fd); + } + + const char* p; + const char* pend; + size_t actual; + std::vector buf; // fallback only + if (mmap_addr != MAP_FAILED) { + p = (const char*)mmap_addr + offset; + pend = (const char*)mmap_addr + file_size; + actual = file_size - offset; + } else { + in.seekg(0, std::ios::end); + size_t remaining = (size_t)in.tellg() - offset; + in.seekg(cur); + buf.resize(remaining); + in.read(buf.data(), remaining); + actual = (size_t)in.gcount(); + p = buf.data(); + pend = p + actual; + } + + size_t avg_entries = (num_lines > 0) ? std::max((size_t)4, actual / (num_lines * 4)) : 4; + + LongNodeID node_counter = 0; + while (node_counter < num_lines && p < pend) { + if (*p == '%') { + while (p < pend && *p != '\n') p++; + if (p < pend) p++; + continue; + } + auto& vec = (*input)[node_counter]; + vec.reserve(avg_entries); + while (p < pend && *p != '\n' && *p != '\r') { + while (p < pend && *p != '\n' && *p != '\r' && (*p < '0' || *p > '9')) p++; + if (p >= pend || *p == '\n' || *p == '\r') break; + LongNodeID val = 0; + while (p < pend && *p >= '0' && *p <= '9') { + val = val * 10 + (LongNodeID)(*p - '0'); + p++; + } + vec.push_back(val); + } + if (p < pend && *p == '\r') p++; + if (p < pend && *p == '\n') p++; + node_counter++; + } + if (mmap_addr != MAP_FAILED) { + munmap(mmap_addr, file_size); + } + } else { + // Original streaming approach + std::vector* lines; + lines = new std::vector(1); + LongNodeID node_counter = 0; + buffered_input *ss2 = NULL; + while( node_counter < num_lines) { + std::getline(*(partition_config.stream_in),(*lines)[0]); + if ((*lines)[0][0] == '%') { + continue; + } + ss2 = new buffered_input(lines); + ss2->simple_scan_line((*input)[node_counter++]); + (*lines)[0].clear(); delete ss2; } - ss2 = new buffered_input(lines); - ss2->simple_scan_line((*input)[node_counter++]); - (*lines)[0].clear(); delete ss2; + delete lines; } - delete lines; return input; } diff --git a/code_for_graphs/lib/partition/onepass_partitioning/vertex_partitioning.h b/code_for_graphs/lib/partition/onepass_partitioning/vertex_partitioning.h index fb32511..8d562e3 100644 --- a/code_for_graphs/lib/partition/onepass_partitioning/vertex_partitioning.h +++ b/code_for_graphs/lib/partition/onepass_partitioning/vertex_partitioning.h @@ -174,13 +174,22 @@ inline PartitionID vertex_partitioning::solve_node(LongNodeID curr_node_id, Node switch(sampling) { case SAMPLING_INACTIVE_LINEAR_COMPLEXITY: decision = solve_linear_complexity(curr_node_id, curr_node_weight, my_thread); + // Duplicate call is intentional: #pragma omp critical cannot be conditionally skipped + if (n_threads > 1) { #pragma omp critical(update_self_sorting_vector) - this->sorted_blocks.increment(decision); + this->sorted_blocks.increment(decision); + } else { + this->sorted_blocks.increment(decision); + } break; case SAMPLING_NEIGHBORS_LINEAR_COMPLEXITY: decision = solve_sampl_neighb_linear_complex(curr_node_id, curr_node_weight, my_thread); + if (n_threads > 1) { #pragma omp critical(update_self_sorting_vector) - this->sorted_blocks.increment(decision); + this->sorted_blocks.increment(decision); + } else { + this->sorted_blocks.increment(decision); + } break; case SAMPLING_INACTIVE: decision = solve(curr_node_id, curr_node_weight, my_thread); @@ -429,8 +438,12 @@ inline void vertex_partitioning::reset_sorted_blocks() { } inline void vertex_partitioning::decrement_sorted_block(PartitionID block) { + if (n_threads > 1) { #pragma omp critical(update_self_sorting_vector) - this->sorted_blocks.decrement(block); + this->sorted_blocks.decrement(block); + } else { + this->sorted_blocks.decrement(block); + } } inline void vertex_partitioning::set_sampling_threashold(float sampling_threashold) { diff --git a/code_for_hypergraphs/CMakeLists.txt b/code_for_hypergraphs/CMakeLists.txt index f922f1a..c4792cf 100644 --- a/code_for_hypergraphs/CMakeLists.txt +++ b/code_for_hypergraphs/CMakeLists.txt @@ -24,6 +24,24 @@ if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) set(CMAKE_BUILD_TYPE "Release") endif() +# Profile-Guided Optimization (PGO) support +# Usage: cmake -DPGO_GENERATE=ON .. && make && && cmake -DPGO_USE=ON .. && make +option(PGO_GENERATE "Build with PGO instrumentation (phase 1)" OFF) +option(PGO_USE "Build with PGO optimization from collected profiles (phase 2)" OFF) +if(PGO_GENERATE AND PGO_USE) + message(FATAL_ERROR "PGO_GENERATE and PGO_USE are mutually exclusive. Use PGO_GENERATE first, run a training workload, then rebuild with PGO_USE.") +endif() +if(CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang") + if(PGO_GENERATE) + add_definitions(-fprofile-generate=${CMAKE_BINARY_DIR}/pgo_data) + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fprofile-generate=${CMAKE_BINARY_DIR}/pgo_data") + endif() + if(PGO_USE) + add_definitions(-fprofile-use=${CMAKE_BINARY_DIR}/pgo_data -fprofile-correction) + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fprofile-use=${CMAKE_BINARY_DIR}/pgo_data") + endif() +endif() + # tweak compiler flags CHECK_CXX_COMPILER_FLAG(-funroll-loops COMPILER_SUPPORTS_FUNROLL_LOOPS) if(COMPILER_SUPPORTS_FUNROLL_LOOPS) @@ -86,7 +104,7 @@ include_directories(${CMAKE_CURRENT_SOURCE_DIR}/lib/tools) -set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-builtin-malloc -fno-builtin-calloc -fno-builtin-realloc") +# Removed -fno-builtin-malloc flags to let compiler optimize allocation patterns set(LIBKAFFPA_SOURCE_FILES lib/tools/quality_metrics.cpp diff --git a/code_for_hypergraphs/app/configuration.h b/code_for_hypergraphs/app/configuration.h index a6067f6..bf24d7c 100644 --- a/code_for_hypergraphs/app/configuration.h +++ b/code_for_hypergraphs/app/configuration.h @@ -616,6 +616,8 @@ inline void configuration::standard( PartitionConfig & partition_config ) { partition_config.problem = PROBLEM_DEGREE; partition_config.enable_mapping = false; + partition_config.evaluate_cut = true; + partition_config.evaluate_connectivity = true; partition_config.ls_neighborhood = COMMUNICATIONGRAPH; partition_config.communication_neighborhood_dist = 10; partition_config.construction_algorithm = MAP_CONST_FASTHIERARCHY_TOPDOWN; diff --git a/code_for_hypergraphs/app/freight.cpp b/code_for_hypergraphs/app/freight.cpp index 8e740e5..beb07c5 100644 --- a/code_for_hypergraphs/app/freight.cpp +++ b/code_for_hypergraphs/app/freight.cpp @@ -98,6 +98,7 @@ int main(int argn, char **argv) { config.LogDump(stdout); config.stream_input = true; + config.graph_filename = graph_filename; [[maybe_unused]] bool already_fully_partitioned; @@ -113,8 +114,35 @@ int main(int argn, char **argv) { for (config.restream_number=0; config.restream_number 1 + config.restream_number) { + config.stream_total_upperbound = ceil(((100+1.5*config.imbalance)/100.)*(config.remaining_stream_nodes/(double)config.k)); + } else { + config.stream_total_upperbound = ceil(((100+config.imbalance)/100.)*(config.remaining_stream_nodes/(double)config.k)); + } + config.quotient_nodes = config.k; + total_edge_cut = 0; + qap = 0; + config.nmbNodes = MIN(config.stream_buffer_len, config.remaining_stream_nodes); + config.n_batches = ceil(config.remaining_stream_nodes / (double)config.nmbNodes); + config.curr_batch = 0; + } buffer_io_time += io_t.elapsed(); onepass_partitioner->instantiate_blocks(config.remaining_stream_nodes, config.remaining_stream_edges, config.k, config.imbalance); if (config.restream_number > 0 && config.use_self_sorting_array) { @@ -128,12 +156,9 @@ int main(int argn, char **argv) { omp_set_schedule(config.omp_schedule, config.omp_chunk); omp_set_num_threads(config.parallel_nodes); -#pragma omp parallel for schedule(static) for (int i=0; i < config.parallel_nodes; i++) { config.all_blocks_to_keys[i].resize(config.k); - for (auto & b : config.all_blocks_to_keys[i]) { - b = INVALID_PARTITION; - } + memset(config.all_blocks_to_keys[i].data(), 0xFF, config.k * sizeof(PartitionID)); config.neighbor_blocks[i].resize(config.k); config.next_key[i] = 0; if (config.sample_edges) { @@ -145,25 +170,38 @@ int main(int argn, char **argv) { } } + LongNodeID nodes_moved = 0; +#if defined MODE_CONNECTIVITY && defined MODE_NETLIST + // First pass: maintain bitset inline for cheap connectivity computation + // Only safe for k <= 64 (single word per net) and unweighted edges + bool use_first_pass_bitset = (config.restream_number == 0 && passes > 1 + && config.ram_stream && config.k <= 64 && !config.read_ew); + size_t n_edges_for_bits = use_first_pass_bitset ? config.stream_edges_assign->size() : 0; + std::vector first_pass_bits(n_edges_for_bits, 0); + double first_pass_connectivity = 0; +#endif processing_t.restart(); -#pragma omp parallel for schedule(runtime) +#pragma omp parallel for schedule(runtime) for (LongNodeID curr_node = 0; curr_node < config.n_batches; curr_node++) { int my_thread = omp_get_thread_num(); - io_t.restart(); - if((config.one_pass_algorithm != ONEPASS_HASHING) && (config.one_pass_algorithm != ONEPASS_HASHING_CRC32)) { - graph_io_stream::loadBufferLinesToBinary(config, input, 1); + if (!config.ram_stream) { + io_t.restart(); + if((config.one_pass_algorithm != ONEPASS_HASHING) && (config.one_pass_algorithm != ONEPASS_HASHING_CRC32)) { + graph_io_stream::loadBufferLinesToBinary(config, input, 1); + } + buffer_io_time += io_t.elapsed(); } - buffer_io_time += io_t.elapsed(); // ***************************** perform partitioning *************************************** - t.restart(); + if (config.dynamic_threashold) t.restart(); // Restreaming: remove vertex from its old block before re-evaluating + PartitionID old_block_for_move_check = INVALID_PARTITION; if (config.restream_number > 0) { - PartitionID old_block = (*config.stream_nodes_assign)[curr_node]; - if (old_block != INVALID_PARTITION) { - (*config.stream_blocks_weight)[old_block] -= 1; - onepass_partitioner->remove_nodeweight(old_block, 1); + old_block_for_move_check = (*config.stream_nodes_assign)[curr_node]; + if (old_block_for_move_check != INVALID_PARTITION) { + (*config.stream_blocks_weight)[old_block_for_move_check] -= 1; + onepass_partitioner->remove_nodeweight(old_block_for_move_check, 1); if (config.use_self_sorting_array) { - onepass_partitioner->decrement_sorted_block(old_block); + onepass_partitioner->decrement_sorted_block(old_block_for_move_check); } } } @@ -173,7 +211,15 @@ int main(int argn, char **argv) { graph_io_stream::readNodeOnePass_netl(config, curr_node, my_thread, input, onepass_partitioner); #endif PartitionID block = onepass_partitioner->solve_node(curr_node, 1, my_thread); - graph_io_stream::register_result(config, curr_node, block, my_thread); + if (config.ram_stream) { + graph_io_stream::register_result_from_input(config, curr_node, block, (*input)[curr_node]); + } else { + graph_io_stream::register_result(config, curr_node, block, my_thread); + } + if (config.restream_number > 0 && block != old_block_for_move_check) { +#pragma omp atomic + nodes_moved++; + } #if defined MODE_NETLIST if(config.dynamic_threashold) { if (config.step_sampled) { @@ -189,33 +235,85 @@ int main(int argn, char **argv) { config.sampling_threashold = MIN(config.sampling_threashold, 4); } #endif - global_mapping_time += t.elapsed(); } total_time += processing_t.elapsed(); + global_mapping_time += processing_t.elapsed(); - if (config.ram_stream) { - delete input; - /* delete lines; */ + if (!config.ram_stream) { + /* Non-ram_stream: input already freed per-node in readNodeOnePass */ } +#if defined MODE_CONNECTIVITY && defined MODE_NETLIST + // First pass: compute connectivity from bitset (sequential, after parallel loop) + if (n_edges_for_bits > 0 && config.ram_stream) { + PartitionID* nodes_data = config.stream_nodes_assign->data(); + for (LongNodeID node = 0; node < config.n_batches; node++) { + PartitionID block = nodes_data[node]; + auto& ln = (*input)[node]; + LongNodeID cc = config.read_nw ? 1 : 0; + LongNodeID ls = ln.size(); + PartitionID sf = 1 + (PartitionID)config.read_ew; + while (cc < ls) { + size_t net_idx = ln[cc] - 1; + cc += sf; + uint64_t bit = 1ULL << (block % 64); + uint64_t old_word = first_pass_bits[net_idx]; + if (!(old_word & bit)) { + if (old_word != 0) first_pass_connectivity += 1; + first_pass_bits[net_idx] = old_word | bit; + } + } + } + } +#endif + // Evaluate this pass and track best partition - if (passes > 1) { + // Skip evaluation if no nodes moved (partition unchanged from previous pass) + if (passes > 1 && (config.restream_number == 0 || nodes_moved > 0)) { double pass_cut = 0; [[maybe_unused]] double pass_con = 0; [[maybe_unused]] EdgeWeight pass_qap = 0; [[maybe_unused]] LongNodeID pass_pins = 0; - // Save stream_edges_assign before evaluation (evaluation modifies it) + + // First-pass shortcuts (only for unweighted, ram_stream) + bool used_shortcut = false; #if defined MODE_NETLIST - std::vector saved_edges_assign(*config.stream_edges_assign); + if (config.restream_number == 0 && config.ram_stream && !config.read_ew) { +#if defined MODE_CONNECTIVITY + if (use_first_pass_bitset) { + pass_con = first_pass_connectivity; + used_shortcut = true; + } +#else + // Cut mode: count CUT_NET entries (accurate on first pass, no carry-over) + for (const auto& entry : *config.stream_edges_assign) { + if (entry == CUT_NET) pass_cut += 1; + } + pass_qap = pass_cut; + used_shortcut = true; +#endif + } + if (!used_shortcut) { +#endif + // For inter-pass evaluation, only compute the objective metric needed +#if defined MODE_CONNECTIVITY + config.evaluate_cut = false; + config.evaluate_connectivity = true; +#else + config.evaluate_cut = true; + config.evaluate_connectivity = false; #endif #if defined MODE_PINSETLIST graph_io_stream::streamEvaluateHPartition_pinsl(config, graph_filename, pass_cut, pass_con, pass_qap, pass_pins); #elif defined MODE_NETLIST - graph_io_stream::streamEvaluateHPartition_netl(config, graph_filename, pass_cut, pass_con, pass_qap, pass_pins); + graph_io_stream::streamEvaluateHPartition_netl(config, graph_filename, pass_cut, pass_con, pass_qap, pass_pins, + config.ram_stream ? input : nullptr); #endif - // Restore stream_edges_assign + // Restore full evaluation flags + config.evaluate_cut = true; + config.evaluate_connectivity = true; #if defined MODE_NETLIST - *config.stream_edges_assign = saved_edges_assign; + } #endif #if defined MODE_CONNECTIVITY double pass_objective = pass_con; @@ -244,11 +342,20 @@ int main(int argn, char **argv) { if(config.parallel_nodes < 2) { std::cout << "time spent for integrated mapping: " << global_mapping_time << std::endl; } + // Always run full evaluation for final output (both cut and connectivity) + config.evaluate_cut = true; + config.evaluate_connectivity = true; #if defined MODE_PINSETLIST graph_io_stream::streamEvaluateHPartition_pinsl(config, graph_filename, total_edge_cut, connectivity, qap, pin_count); #elif defined MODE_NETLIST - graph_io_stream::streamEvaluateHPartition_netl(config, graph_filename, total_edge_cut, connectivity, qap, pin_count); + graph_io_stream::streamEvaluateHPartition_netl(config, graph_filename, total_edge_cut, connectivity, qap, pin_count, + config.ram_stream ? input : nullptr, false); #endif + // Free cached input for ram_stream after all evaluations are done + if (config.ram_stream && input != NULL) { + delete input; + input = NULL; + } std::cout << "nanoseconds / pin for integrated mapping: " << global_mapping_time*1000000000./pin_count << std::endl; std::cout << "pin count: \t" << pin_count << std::endl; std::cout << "connectivity " << connectivity << std::endl; diff --git a/code_for_hypergraphs/lib/io/graph_io_stream.cpp b/code_for_hypergraphs/lib/io/graph_io_stream.cpp index c65694e..b9f9e67 100644 --- a/code_for_hypergraphs/lib/io/graph_io_stream.cpp +++ b/code_for_hypergraphs/lib/io/graph_io_stream.cpp @@ -6,6 +6,7 @@ *****************************************************************************/ #include +#include #include #include "graph_io_stream.h" #include "timer.h" @@ -134,42 +135,49 @@ void graph_io_stream::streamEvaluateHPartition_pinsl(PartitionConfig & config, c delete lines; } -void graph_io_stream::streamEvaluateHPartition_netl(PartitionConfig & config, const std::string & filename, double& cutNet, double& connectivity, - EdgeWeight& qap, LongNodeID& pin_count) { - std::vector>* input; - std::vector* lines; - lines = new std::vector(1); - LongNodeID node_counter = 0; +void graph_io_stream::streamEvaluateHPartition_netl(PartitionConfig & config, const std::string & filename, double& cutNet, double& connectivity, + EdgeWeight& qap, LongNodeID& pin_count, + std::vector>* cached_input, + bool use_local_scratch) { + bool use_cached = (cached_input != nullptr); + std::vector>* input = nullptr; + std::vector* lines = nullptr; buffered_input *ss2 = NULL; - std::string line; - std::ifstream in(filename.c_str()); - if (!in) { - std::cerr << "Error opening " << filename << std::endl; - return; - } - long nmbNodes; - long nmbEdges; - int ew = 0; - std::getline(in,(*lines)[0]); - while ((*lines)[0][0] == '%') std::getline(in,(*lines)[0]); // a comment in the file - - std::stringstream ss((*lines)[0]); - ss >> nmbNodes; - ss >> nmbEdges; - ss >> ew; + std::ifstream in; bool read_ew = false; bool read_nw = false; - if(ew == 1) { - read_ew = true; - } else if (ew == 11) { - read_ew = true; - read_nw = true; - } else if (ew == 10) { - read_nw = true; - } - [[maybe_unused]] NodeID target; - [[maybe_unused]] NodeWeight total_nodeweight = 0; - [[maybe_unused]] EdgeWeight total_edgeweight = 0; + long nmbNodes = 0; + + if (use_cached) { + nmbNodes = cached_input->size(); + read_ew = config.read_ew; + read_nw = config.read_nw; + } else { + lines = new std::vector(1); + in.open(filename.c_str()); + if (!in) { + std::cerr << "Error opening " << filename << std::endl; + return; + } + int ew = 0; + std::getline(in,(*lines)[0]); + while ((*lines)[0][0] == '%') std::getline(in,(*lines)[0]); + std::stringstream ss((*lines)[0]); + ss >> nmbNodes; + long nmbEdges; + ss >> nmbEdges; + ss >> ew; + if(ew == 1) { + read_ew = true; + } else if (ew == 11) { + read_ew = true; + read_nw = true; + } else if (ew == 10) { + read_nw = true; + } + } + + [[maybe_unused]] NodeWeight total_nodeweight = 0; cutNet = 0; connectivity = 0; qap = 0; @@ -184,22 +192,58 @@ void graph_io_stream::streamEvaluateHPartition_netl(PartitionConfig & config, co D->setPartitionConfig(config); } - // erase nets for evaluation only - for (auto& net : (*config.stream_edges_assign) ) { - net = INVALID_PARTITION; - } - std::vector net_seen(config.stream_edges_assign->size(), false); + size_t n_edges = config.stream_edges_assign->size(); pin_count = 0; - std::vector> touched_nets_per_block(config.k); - while( std::getline(in, (*lines)[0])) { - if ((*lines)[0][0] == '%') continue; // a comment in the file - NodeID node = node_counter++; - PartitionID node_block = (*config.stream_nodes_assign)[node]; - input = new std::vector>(1); - ss2 = new buffered_input(lines); - ss2->simple_scan_line((*input)[0]); - std::vector &line_numbers = (*input)[0]; + // Use runtime flag to determine which metrics to compute + // (compile-time #ifdefs don't work here because this .cpp is compiled once for both modes) + bool compute_cut = config.evaluate_cut; + bool compute_connectivity = config.evaluate_connectivity; + + // For cut tracking: use local scratch (inter-pass) or stream_edges_assign directly (final) + std::vector eval_edges; + PartitionID* edges_data = nullptr; + if (compute_cut) { + if (use_local_scratch) { + eval_edges.assign(n_edges, INVALID_PARTITION); + edges_data = eval_edges.data(); + } else { + std::memset(config.stream_edges_assign->data(), 0xFF, n_edges * sizeof(PartitionID)); + edges_data = config.stream_edges_assign->data(); + } + } + size_t words_per_net = ((size_t)config.k + 63) / 64; + std::vector net_block_bits; + if (compute_connectivity) { + // Guard against excessive memory usage for large k * n_edges + size_t total_words = n_edges * words_per_net; + const size_t MAX_BITSET_WORDS = (size_t(256) * 1024 * 1024) / sizeof(uint64_t); // 256 MiB cap + if (words_per_net != 0 && n_edges <= MAX_BITSET_WORDS / words_per_net) { + net_block_bits.assign(total_words, 0); + } else { + compute_connectivity = false; // too large, skip connectivity computation + } + } + + PartitionID* nodes_assign_data = config.stream_nodes_assign->data(); + uint64_t* bits_data = compute_connectivity ? net_block_bits.data() : nullptr; + + for (long node = 0; node < nmbNodes; node++) { + std::vector* line_numbers_ptr; + if (use_cached) { + line_numbers_ptr = &(*cached_input)[node]; + } else { + if (!std::getline(in, (*lines)[0])) break; + if ((*lines)[0][0] == '%') { node--; continue; } + input = new std::vector>(1); + ss2 = new buffered_input(lines); + ss2->simple_scan_line((*input)[0]); + line_numbers_ptr = &(*input)[0]; + } + + std::vector &line_numbers = *line_numbers_ptr; + LongNodeID line_size = line_numbers.size(); + PartitionID node_block = nodes_assign_data[node]; LongNodeID col_counter = 0; NodeWeight weight = 1; @@ -207,44 +251,67 @@ void graph_io_stream::streamEvaluateHPartition_netl(PartitionConfig & config, co weight = line_numbers[col_counter++]; total_nodeweight += weight; } - while (col_counter < line_numbers.size()) { + while (col_counter < line_size) { LongEdgeID net = line_numbers[col_counter++]; pin_count++; - PartitionID old_net_block = (*config.stream_edges_assign)[net-1]; - PartitionID new_net_block = (old_net_block==node_block || old_net_block==INVALID_PARTITION) ? node_block : CUT_NET; - (*config.stream_edges_assign)[net-1] = (old_net_block==COMPUTED_CUT_NET) ? COMPUTED_CUT_NET : new_net_block; EdgeWeight edge_weight = 1; if( read_ew ) { edge_weight = line_numbers[col_counter++]; } - if (new_net_block == CUT_NET && old_net_block != CUT_NET && old_net_block != COMPUTED_CUT_NET) { - cutNet += edge_weight; - (*config.stream_edges_assign)[net-1] = COMPUTED_CUT_NET; - if (config.enable_mapping) { - EdgeWeight comm_vol = edge_weight; - NodeID perm_rank_node = (*perm_rank)[node_block]; - NodeID perm_rank_target = (*perm_rank)[old_net_block]; - EdgeWeight cur_vol = comm_vol*(D->get_xy(perm_rank_node, perm_rank_target)); - qap += cur_vol; + if (compute_cut) { + // Cut tracking via eval_edges + if (col_counter < line_size) { + __builtin_prefetch(&edges_data[line_numbers[col_counter]-1], 1, 1); + } + PartitionID old_net_block = edges_data[net-1]; + PartitionID new_net_block = (old_net_block==node_block || old_net_block==INVALID_PARTITION) ? node_block : CUT_NET; + edges_data[net-1] = (old_net_block==COMPUTED_CUT_NET) ? COMPUTED_CUT_NET : new_net_block; + if (new_net_block == CUT_NET && old_net_block != CUT_NET && old_net_block != COMPUTED_CUT_NET) { + cutNet += edge_weight; + edges_data[net-1] = COMPUTED_CUT_NET; + if (config.enable_mapping) { + EdgeWeight comm_vol = edge_weight; + NodeID perm_rank_node = (*perm_rank)[node_block]; + NodeID perm_rank_target = (*perm_rank)[old_net_block]; + EdgeWeight cur_vol = comm_vol*(D->get_xy(perm_rank_node, perm_rank_target)); + qap += cur_vol; + } } } - if (touched_nets_per_block[node_block].find(net) == touched_nets_per_block[node_block].end()) { - connectivity += edge_weight; - touched_nets_per_block[node_block].insert(net); - } - if (!net_seen[net]) { - net_seen[net] = true; - connectivity -= edge_weight; // subtract net weight once per net to compute connectivity correctly + if (compute_connectivity) { + // Connectivity tracking via bitset + size_t net_idx = net - 1; + uint64_t bit_mask = 1ULL << (node_block % 64); + if (words_per_net == 1) { + // Fast path for k <= 64: single word per net + uint64_t old_word = bits_data[net_idx]; + if (!(old_word & bit_mask)) { + if (old_word != 0) connectivity += edge_weight; + bits_data[net_idx] = old_word | bit_mask; + } + } else { + size_t base = net_idx * words_per_net; + size_t word_offset = node_block / 64; + if (!(bits_data[base + word_offset] & bit_mask)) { + bool first_touch = true; + for (size_t w = 0; w < words_per_net; w++) { + if (bits_data[base + w] != 0) { first_touch = false; break; } + } + if (!first_touch) { + connectivity += edge_weight; + } + bits_data[base + word_offset] |= bit_mask; + } + } } } - (*lines)[0].clear(); delete ss2; - delete input; - if(in.eof()) { - break; - } + if (!use_cached) { + (*lines)[0].clear(); delete ss2; + delete input; + } } if (!config.enable_mapping) qap = cutNet; - delete lines; + if (lines) delete lines; } void graph_io_stream::streamEvaluateEdgePartition_netl(PartitionConfig & config, const std::string & filename, double& cutNet, double& connectivity, diff --git a/code_for_hypergraphs/lib/io/graph_io_stream.h b/code_for_hypergraphs/lib/io/graph_io_stream.h index 1a969cd..a9b392a 100644 --- a/code_for_hypergraphs/lib/io/graph_io_stream.h +++ b/code_for_hypergraphs/lib/io/graph_io_stream.h @@ -20,6 +20,10 @@ #include #include #include +#include +#include +#include +#include #include "definitions.h" #include "data_structure/hypergraph.h" @@ -69,8 +73,10 @@ class graph_io_stream { EdgeWeight& qap, LongNodeID& pin_count); static - void streamEvaluateHPartition_netl(PartitionConfig & config, const std::string & filename, double& cutNet, double& connectivity, - EdgeWeight& qap, LongNodeID& pin_count); + void streamEvaluateHPartition_netl(PartitionConfig & config, const std::string & filename, double& cutNet, double& connectivity, + EdgeWeight& qap, LongNodeID& pin_count, + std::vector>* cached_input = nullptr, + bool use_local_scratch = true); static void streamEvaluateEdgePartition_netl(PartitionConfig & config, const std::string & filename, double& cutNet, double& connectivity, @@ -118,6 +124,10 @@ class graph_io_stream { static void register_result(PartitionConfig & config, LongNodeID curr_node, PartitionID assigned_block, int my_thread); + static + void register_result_from_input(PartitionConfig & config, LongNodeID curr_node, PartitionID assigned_block, + std::vector &line_numbers); + static void readPartition(PartitionConfig & config, const std::string & filename); }; @@ -208,7 +218,9 @@ inline void graph_io_stream::readNodeOnePass_pinsl (PartitionConfig & config, Lo /* config.total_stream_nodecounter += nmbNodes; */ /* config.total_stream_nodeweight += total_nodeweight; */ /* config.remaining_stream_nodes -= nmbNodes; */ - config.remaining_stream_nodes--; + // For ram_stream, remaining_stream_nodes is unused during the loop (reset each pass + // by readFirstLineStream) so skip the decrement. Non-ram_stream needs it for streaming I/O. + if (!config.ram_stream) config.remaining_stream_nodes--; } inline void graph_io_stream::readNodeOnePass_netl (PartitionConfig & config, LongNodeID curr_node, int my_thread, @@ -230,9 +242,12 @@ inline void graph_io_stream::readNodeOnePass_netl (PartitionConfig & config, Lon auto& next_key = config.next_key[my_thread]; auto& neighbor_blocks = config.neighbor_blocks[my_thread]; auto& sampled_edges = config.sampled_edges[my_thread]; - auto& valid_neighboring_nets = config.valid_neighboring_nets[my_thread]; - valid_neighboring_nets.clear(); + // For ram_stream, register_result_from_input reads nets directly from cached data, + // so we skip building valid_neighboring_nets + if (!config.ram_stream) { + config.valid_neighboring_nets[my_thread].clear(); + } onepass_partitioner->clear_edgeweight_blocks(neighbor_blocks, next_key, my_thread); next_key = 0; @@ -242,9 +257,11 @@ inline void graph_io_stream::readNodeOnePass_netl (PartitionConfig & config, Lon /* total_nodeweight += weight; */ + float scaling_factor = 1; PartitionID selecting_factor = (1+(PartitionID)read_ew); - config.edges = (line_numbers.size()-col_counter) / selecting_factor; - float scaling_factor = 1; + if(config.sample_edges || config.dynamic_threashold) { + config.edges = (line_numbers.size()-col_counter) / selecting_factor; + } if(config.sample_edges) { EdgeID n_sampled_edges = (config.sampling_threashold*config.stream_sampling < config.edges) ? config.stream_sampling : config.edges; bool sampling_active = n_sampled_edges < config.edges; @@ -257,7 +274,7 @@ inline void graph_io_stream::readNodeOnePass_netl (PartitionConfig & config, Lon sampled_edges[i].first = (*config.stream_edges_assign)[net-1]; sampled_edges[i].second = (read_ew) ? line_numbers[edge_pos+1] : 1; - if(sampled_edges[i].first != CUT_NET) valid_neighboring_nets.push_back(net-1); + if(sampled_edges[i].first != CUT_NET && !config.ram_stream) config.valid_neighboring_nets[my_thread].push_back(net-1); } for (PartitionID i=0; idata(); + LongNodeID line_size = line_numbers.size(); + while (col_counter < line_size) { net = line_numbers[col_counter++]; EdgeWeight edge_weight = (read_ew) ? line_numbers[col_counter++] : 1; - PartitionID targetGlobalPar = (*config.stream_edges_assign)[net-1]; - if(targetGlobalPar != CUT_NET) valid_neighboring_nets.push_back(net-1); + // Prefetch the next net's assignment to hide memory latency + if (col_counter < line_size) { + __builtin_prefetch(&edges_assign_data[line_numbers[col_counter]-1], 0, 1); + } + PartitionID targetGlobalPar = edges_assign_data[net-1]; + if(!config.ram_stream && targetGlobalPar != CUT_NET) config.valid_neighboring_nets[my_thread].push_back(net-1); if(targetGlobalPar != INVALID_PARTITION && targetGlobalPar != CUT_NET) { PartitionID key = all_blocks_to_keys[targetGlobalPar]; if (key >= next_key || neighbor_blocks[key].first != targetGlobalPar) { @@ -305,7 +328,6 @@ inline void graph_io_stream::readNodeOnePass_netl (PartitionConfig & config, Lon /* config.total_stream_nodecounter += nmbNodes; */ /* config.total_stream_nodeweight += total_nodeweight; */ /* config.remaining_stream_nodes -= nmbNodes; */ - config.remaining_stream_nodes--; } inline void graph_io_stream::loadRemainingLinesToBinary(PartitionConfig & partition_config, std::vector>* &input) { @@ -323,20 +345,104 @@ inline void graph_io_stream::loadBufferLinesToBinary(PartitionConfig & partition inline std::vector>* graph_io_stream::loadLinesFromStreamToBinary(PartitionConfig & partition_config, LongNodeID num_lines) { std::vector>* input; input = new std::vector>(num_lines); - std::vector* lines; - lines = new std::vector(1); - LongNodeID node_counter = 0; - buffered_input *ss2 = NULL; - while( node_counter < num_lines) { - std::getline(*(partition_config.stream_in),(*lines)[0]); - if ((*lines)[0][0] == '%') { // a comment in the file - continue; + + if (partition_config.ram_stream) { + // Get current stream position (after header) + std::ifstream& in = *(partition_config.stream_in); + std::streampos cur = in.tellg(); + + // mmap the entire file for zero-copy access (POSIX: Linux + macOS) + int fd = open(partition_config.graph_filename.c_str(), O_RDONLY); + void* mmap_addr = MAP_FAILED; + size_t file_size = 0; + size_t offset = 0; + if (fd != -1) { + struct stat st; + if (fstat(fd, &st) == 0 && st.st_size > 0 && cur >= 0) { + file_size = (size_t)st.st_size; + offset = (size_t)cur; + if (offset <= file_size) { +#ifdef MAP_POPULATE + mmap_addr = mmap(nullptr, file_size, PROT_READ, MAP_PRIVATE | MAP_POPULATE, fd, 0); +#else + mmap_addr = mmap(nullptr, file_size, PROT_READ, MAP_PRIVATE, fd, 0); +#endif + } + } + close(fd); + } + + const char* p; + const char* pend; + size_t actual; + std::vector buf; // fallback only + if (mmap_addr != MAP_FAILED) { + p = (const char*)mmap_addr + offset; + pend = (const char*)mmap_addr + file_size; + actual = file_size - offset; + } else { + // Fallback to ifstream::read + in.seekg(0, std::ios::end); + size_t remaining = (size_t)in.tellg() - offset; + in.seekg(cur); + buf.resize(remaining); + in.read(buf.data(), remaining); + actual = (size_t)in.gcount(); + p = buf.data(); + pend = p + actual; + } + + // Estimate average entries per line for pre-reservation + size_t avg_entries = (num_lines > 0) ? std::max((size_t)4, actual / (num_lines * 4)) : 4; + + // Parse integers directly from the buffer + LongNodeID node_counter = 0; + while (node_counter < num_lines && p < pend) { + // Skip comment lines + if (*p == '%') { + while (p < pend && *p != '\n') p++; + if (p < pend) p++; + continue; + } + auto& vec = (*input)[node_counter]; + vec.reserve(avg_entries); + while (p < pend && *p != '\n' && *p != '\r') { + // Skip non-digit + while (p < pend && *p != '\n' && *p != '\r' && (*p < '0' || *p > '9')) p++; + if (p >= pend || *p == '\n' || *p == '\r') break; + // Parse integer + LongNodeID val = 0; + while (p < pend && *p >= '0' && *p <= '9') { + val = val * 10 + (LongNodeID)(*p - '0'); + p++; + } + vec.push_back(val); + } + // Skip line ending + if (p < pend && *p == '\r') p++; + if (p < pend && *p == '\n') p++; + node_counter++; + } + if (mmap_addr != MAP_FAILED) { + munmap(mmap_addr, file_size); + } + } else { + // Original streaming approach + std::vector* lines; + lines = new std::vector(1); + LongNodeID node_counter = 0; + buffered_input *ss2 = NULL; + while( node_counter < num_lines) { + std::getline(*(partition_config.stream_in),(*lines)[0]); + if ((*lines)[0][0] == '%') { + continue; + } + ss2 = new buffered_input(lines); + ss2->simple_scan_line((*input)[node_counter++]); + (*lines)[0].clear(); delete ss2; } - ss2 = new buffered_input(lines); - ss2->simple_scan_line((*input)[node_counter++]); - (*lines)[0].clear(); delete ss2; + delete lines; } - delete lines; return input; } @@ -355,6 +461,29 @@ inline void graph_io_stream::register_result(PartitionConfig & config, LongNodeI #endif } +inline void graph_io_stream::register_result_from_input(PartitionConfig & config, LongNodeID curr_node, PartitionID assigned_block, + std::vector &line_numbers) { + (*config.stream_nodes_assign)[curr_node] = assigned_block; + (*config.stream_blocks_weight)[assigned_block] += 1; +#if defined MODE_NETLIST + PartitionID* edges_data = config.stream_edges_assign->data(); + LongNodeID col_counter = config.read_nw ? 1 : 0; + LongNodeID line_size = line_numbers.size(); + PartitionID selecting_factor = 1 + (PartitionID)config.read_ew; + while (col_counter < line_size) { + LongEdgeID net = line_numbers[col_counter]; + col_counter += selecting_factor; + PartitionID & old_block = edges_data[net-1]; + if (old_block == CUT_NET) continue; +#if defined MODE_CONNECTIVITY + old_block = assigned_block; +#else + old_block = (old_block==assigned_block || old_block==INVALID_PARTITION) ? assigned_block : CUT_NET; +#endif + } +#endif +} + inline void graph_io_stream::readFirstLineStream(PartitionConfig & partition_config, std::string graph_filename, double& total_edge_cut, EdgeWeight& qap) { if (partition_config.stream_in != NULL) { delete partition_config.stream_in; diff --git a/code_for_hypergraphs/lib/partition/onepass_partitioning/vertex_partitioning.h b/code_for_hypergraphs/lib/partition/onepass_partitioning/vertex_partitioning.h index 654c941..834f29e 100644 --- a/code_for_hypergraphs/lib/partition/onepass_partitioning/vertex_partitioning.h +++ b/code_for_hypergraphs/lib/partition/onepass_partitioning/vertex_partitioning.h @@ -173,13 +173,22 @@ inline PartitionID vertex_partitioning::solve_node(LongNodeID curr_node_id, Node switch(sampling) { case SAMPLING_INACTIVE_LINEAR_COMPLEXITY: decision = solve_linear_complexity(curr_node_id, curr_node_weight, my_thread); + // Duplicate call is intentional: #pragma omp critical cannot be conditionally skipped + if (n_threads > 1) { #pragma omp critical(update_self_sorting_vector) - this->sorted_blocks.increment(decision); + this->sorted_blocks.increment(decision); + } else { + this->sorted_blocks.increment(decision); + } break; case SAMPLING_NEIGHBORS_LINEAR_COMPLEXITY: decision = solve_sampl_neighb_linear_complex(curr_node_id, curr_node_weight, my_thread); + if (n_threads > 1) { #pragma omp critical(update_self_sorting_vector) - this->sorted_blocks.increment(decision); + this->sorted_blocks.increment(decision); + } else { + this->sorted_blocks.increment(decision); + } break; case SAMPLING_INACTIVE: decision = solve(curr_node_id, curr_node_weight, my_thread); @@ -428,8 +437,12 @@ inline void vertex_partitioning::reset_sorted_blocks() { } inline void vertex_partitioning::decrement_sorted_block(PartitionID block) { + if (n_threads > 1) { #pragma omp critical(update_self_sorting_vector) - this->sorted_blocks.decrement(block); + this->sorted_blocks.decrement(block); + } else { + this->sorted_blocks.decrement(block); + } } inline void vertex_partitioning::set_sampling_threashold(float sampling_threashold) { diff --git a/code_for_hypergraphs/lib/partition/partition_config.h b/code_for_hypergraphs/lib/partition/partition_config.h index f6c7452..fc9dadf 100644 --- a/code_for_hypergraphs/lib/partition/partition_config.h +++ b/code_for_hypergraphs/lib/partition/partition_config.h @@ -399,6 +399,8 @@ struct PartitionConfig int max_recursion_levels_construction; bool enable_mapping; + bool evaluate_cut = true; + bool evaluate_connectivity = true; //=======================================