Skip to content

Commit 9ca1fd0

Browse files
authored
Merge pull request #3 from euphoricpoptarts/main
CMake Package and File Organization
2 parents f2e40d4 + 8d83557 commit 9ca1fd0

26 files changed

+1266
-818
lines changed

CMakeLists.txt

+6-39
Original file line numberDiff line numberDiff line change
@@ -1,47 +1,14 @@
1-
cmake_minimum_required(VERSION 3.18)
2-
project(jetpartition CXX)
1+
cmake_minimum_required(VERSION 3.23)
2+
project(jetpartition LANGUAGES CXX VERSION 1.1.0)
33
set(CMAKE_CXX_STANDARD 17)
44
set(CMAKE_CXX_STANDARD_REQUIRED True)
55
if(NOT CMAKE_BUILD_TYPE)
66
set(CMAKE_BUILD_TYPE Release)
77
endif()
8-
# GKlib is a dependency of Metis when built from its github repo
9-
# Unfortunately there is no simple way to avoid also linking GKlib in this case
10-
# Older distributions of metis do not create this dependency
11-
SET(LINK_GKLIB False CACHE BOOL "Newer Metis distributions require us to link GKlib")
128

9+
# both the apps and the library depend on kokkos and kokkoskernels
10+
# find KokkosKernels also handles kokkos
1311
find_package(KokkosKernels REQUIRED)
1412
add_compile_options(-Wall -Wextra -Wshadow)
15-
16-
# This is used by the build script
17-
# to avoid putting metis and gklib in the global path
18-
include_directories(${METIS_DIR}/include)
19-
link_directories(${METIS_DIR}/lib)
20-
21-
add_executable(jet partition.cpp)
22-
add_executable(jet4 partition.cpp)
23-
add_executable(jet2 partition.cpp)
24-
add_executable(jet_host partition.cpp)
25-
add_executable(jet_import import_coarse.cpp)
26-
add_executable(jet_export partition.cpp)
27-
add_executable(jet_serial partition.cpp)
28-
add_executable(pstat part_eval.cpp)
29-
30-
target_compile_definitions(jet PUBLIC HASHMAP_P)
31-
target_compile_definitions(jet4 PUBLIC HASHMAP_P FOUR9)
32-
target_compile_definitions(jet2 PUBLIC HASHMAP_P TWO9)
33-
target_compile_definitions(jet_host PUBLIC HASHMAP_P HOST)
34-
target_compile_definitions(jet_import PUBLIC HASHMAP_P HOST)
35-
target_compile_definitions(jet_export PUBLIC HASHMAP_P HOST EXP)
36-
target_compile_definitions(jet_serial PUBLIC HASHMAP_P SERIAL)
37-
foreach(prog jet jet4 jet2 jet_host jet_import jet_export jet_serial pstat)
38-
target_link_libraries(${prog} Kokkos::kokkos Kokkos::kokkoskernels)
39-
endforeach(prog)
40-
foreach(prog jet jet4 jet2 jet_host jet_export jet_serial)
41-
target_link_libraries(${prog} metis)
42-
endforeach(prog)
43-
if(LINK_GKLIB)
44-
foreach(prog jet jet4 jet2 jet_host jet_export jet_serial)
45-
target_link_libraries(${prog} GKlib)
46-
endforeach(prog)
47-
endif()
13+
add_subdirectory(src)
14+
add_subdirectory(app)

README.md

+10-4
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@
22

33
The Jet Partitioner is a parallel graph partitioner that runs on most CPU and GPU systems (via Kokkos, a required dependency).
44
This partitioner was developed in a collaboration between Sandia National Labs and Pennsylvania State University.
5-
For details about the algorithm, please see https://arxiv.org/abs/2304.13194
5+
For details about the algorithm, please see https://arxiv.org/abs/2304.13194
6+
If you intend to cite this partitioner in a publication, please cite https://doi.org/10.1137/23M1559129
67

78
## Dependencies
89

@@ -15,7 +16,7 @@ Metis (https://github.com/KarypisLab/METIS): Used for initial partitioning of co
1516

1617
### Building
1718

18-
Standard CMake build process. If your Metis build requires GKlib, add `-DLINK_GKLIB=True` to your cmake command when building Jet. Example build scripts are provided for macOS with OpenMP and Linux systems with Cuda. These scripts handle all required dependencies.
19+
Standard CMake build process. CMake version >= 3.23 required. If your Metis build requires GKlib, add `-DLINK_GKLIB=True` to your cmake command when building Jet. You can add `-DMETIS_HINT=<metis install location>` to help cmake find metis (and gklib if it has the same install location) if you have not added metis to your relevant path variables. Example build scripts are provided for macOS with OpenMP and Linux systems with Cuda. These scripts handle all required dependencies.
1920

2021
### Executables
2122

@@ -30,13 +31,18 @@ jet\_serial: jet but runs on the host on a single thread.
3031
pstat: Given a metis graph file, partition file, and k-value, will print out quality information on the partition.
3132

3233
### Using Jet Partitioner in Your Code
33-
We can not provide an option to compile a library due to the use of templates. However, you can import "jet.hpp" into your code to use the partitioner via the "jet\_partitioner::partition" method. Note that this requires you to add our source directory to your include path and also to link our dependencies. This method currently always uses the default coarsening algorithm.
34+
We provide a cmake package that you can install on your system. Add `find_package(jet CONFIG REQUIRED)` to your project's CMakeLists.txt file and link your executable/s to `jet::jet`. Include `jet.h` in your code to use one of the provided partitioning functions. Each function is distinguished by the target Kokkos execution space it will run in and the type of KokkosKernels CrsMatrix which it accepts. Reference `jet_defs.h` for the relevant template definitions of these parameters. You can set the desired part count and imbalance values on the input config_t struct (see `jet_config.h` for other parameters).
35+
36+
#### Tips
37+
On Linux systems, you can create a file `~/.cmake/packages/jet/find.txt` that cmake will automatically use to find the jet cmake package.
38+
Inside this file, add the full path to the jet install directory.
3439

3540
### Input Format
36-
We do not yet support vertex weights within metis graph files.
41+
The partitioner executables accept graphs stored in the metis graph file format. We do not yet support vertex weights within metis graph files.
3742

3843
### Config File format:
3944
\<Coarsening algorithm\> (0 for 2-hop matching)/(1 for HEC)/(2 for pure matching)/(default is 2-hop matching)
4045
\<Number of parts\>
4146
\<Partitioning attempts\>
4247
\<Imbalance value\>
48+
\<Ultra quality settings\> (Optional) 1 to enable, 0 to disable (default)

app/CMakeLists.txt

+30
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
# jet executables
2+
add_executable(jet_ex driver.cpp)
3+
set_property(TARGET jet_ex PROPERTY OUTPUT_NAME jet)
4+
add_executable(jet4 driver.cpp)
5+
add_executable(jet2 driver.cpp)
6+
add_executable(jet_host driver.cpp)
7+
add_executable(jet_import import_coarse.cpp)
8+
add_executable(jet_export driver.cpp)
9+
add_executable(jet_serial driver.cpp)
10+
add_executable(pstat part_eval.cpp)
11+
12+
foreach(prog jet_import pstat)
13+
target_include_directories(${prog} PRIVATE ${CMAKE_SOURCE_DIR}/header ${CMAKE_SOURCE_DIR}/src)
14+
endforeach(prog)
15+
16+
# compile definitions to set exe behavior
17+
target_compile_definitions(jet4 PUBLIC FOUR9)
18+
target_compile_definitions(jet2 PUBLIC TWO9)
19+
target_compile_definitions(jet_host PUBLIC HOST)
20+
target_compile_definitions(jet_import PUBLIC HOST)
21+
target_compile_definitions(jet_export PUBLIC HOST EXP)
22+
target_compile_definitions(jet_serial PUBLIC SERIAL)
23+
24+
# link executables
25+
target_link_libraries(jet_import Kokkos::kokkos Kokkos::kokkoskernels)
26+
target_link_libraries(pstat Kokkos::kokkos Kokkos::kokkoskernels)
27+
# other executables get the kokkos dependencies via jet
28+
foreach(prog jet_ex jet4 jet2 jet_host jet_export jet_serial)
29+
target_link_libraries(${prog} jet)
30+
endforeach(prog)

import_coarse.cpp app/driver.cpp

+59-50
Original file line numberDiff line numberDiff line change
@@ -36,86 +36,93 @@
3636
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
3737
//
3838
// ************************************************************************
39-
#include "contract.hpp"
40-
#include "uncoarsen.hpp"
41-
#include "defs.h"
39+
40+
#include "jet_defs.h"
4241
#include "io.hpp"
42+
#include "jet.h"
43+
#include "jet_config.h"
4344
#include <limits>
45+
#include <vector>
46+
#include <algorithm>
4447

4548
using namespace jet_partitioner;
4649

47-
part_vt partition(value_t& edge_cut,
48-
const config_t& config,
49-
ExperimentLoggerUtil<value_t>& experiment) {
50-
51-
using coarsener_t = contracter<matrix_t>;
52-
using uncoarsener_t = uncoarsener<matrix_t, part_t>;
53-
using coarse_level_triple = typename coarsener_t::coarse_level_triple;
54-
55-
std::list<coarse_level_triple> cg_list = load_coarse();
56-
Kokkos::fence();
57-
Kokkos::Timer t;
58-
double start_time = t.seconds();
59-
part_t k = config.num_parts;
60-
double fin_coarsening_time = t.seconds();
61-
double imb_ratio = config.max_imb_ratio;
62-
part_vt coarsest_p = load_coarse_part(cg_list.back().mtx.numRows());
63-
Kokkos::fence();
64-
experiment.addMeasurement(Measurement::InitPartition, t.seconds() - fin_coarsening_time);
65-
part_vt part = uncoarsener_t::uncoarsen(cg_list, coarsest_p, k, imb_ratio
66-
, edge_cut, experiment);
67-
68-
Kokkos::fence();
69-
double fin_uncoarsening = t.seconds();
70-
cg_list.clear();
71-
Kokkos::fence();
72-
double fin_time = t.seconds();
73-
experiment.addMeasurement(Measurement::Total, fin_time - start_time);
74-
experiment.addMeasurement(Measurement::Coarsen, fin_coarsening_time - start_time);
75-
experiment.addMeasurement(Measurement::FreeGraph, fin_time - fin_uncoarsening);
76-
77-
experiment.refinementReport();
78-
experiment.verboseReport();
79-
80-
return part;
81-
}
82-
83-
void degree_weighting(const matrix_t& g, wgt_view_t vweights){
50+
void degree_weighting(const matrix_t& g, wgt_vt vweights){
8451
Kokkos::parallel_for("set v weights", r_policy(0, g.numRows()), KOKKOS_LAMBDA(const ordinal_t i){
8552
vweights(i) = g.graph.row_map(i + 1) - g.graph.row_map(i);
8653
});
8754
}
8855

56+
value_t median(std::vector<value_t>& cuts){
57+
std::sort(cuts.begin(), cuts.end());
58+
int count = cuts.size();
59+
if(count % 2 == 0){
60+
return (cuts[count / 2] + cuts[(count / 2) - 1]) / 2;
61+
} else {
62+
return cuts[count / 2];
63+
}
64+
}
65+
8966
int main(int argc, char **argv) {
9067

91-
if (argc < 2) {
68+
if (argc < 3) {
9269
std::cerr << "Insufficient number of args provided" << std::endl;
93-
std::cerr << "Usage: " << argv[0] << " <config_file> <optional partition_output_filename> <optional metrics_filename>" << std::endl;
70+
std::cerr << "Usage: " << argv[0] << " <metis_graph_file> <config_file> <optional partition_output_filename> <optional metrics_filename>" << std::endl;
9471
return -1;
9572
}
9673
config_t config;
97-
if(!load_config(config, argv[1])) return -1;
74+
char *filename = argv[1];
75+
if(!load_config(config, argv[2])) return -1;
9876
char *part_file = nullptr;
9977
char *metrics = nullptr;
100-
if(argc >= 3){
101-
part_file = argv[2];
102-
}
10378
if(argc >= 4){
104-
metrics = argv[3];
79+
part_file = argv[3];
80+
}
81+
if(argc >= 5){
82+
metrics = argv[4];
10583
}
84+
#ifdef FOUR9
85+
config.refine_tolerance = 0.9999;
86+
#elif defined TWO9
87+
config.refine_tolerance = 0.99;
88+
#endif
89+
#ifdef EXP
90+
config.dump_coarse = true;
91+
#endif
92+
config.verbose = true;
10693

10794
Kokkos::initialize();
10895
//must scope kokkos-related data
10996
//so that it falls out of scope b4 finalize
11097
{
98+
matrix_t g;
99+
bool uniform_ew = false;
100+
if(!load_metis_graph(g, uniform_ew, filename)) return -1;
101+
std::cout << "vertices: " << g.numRows() << "; edges: " << g.nnz() / 2 << std::endl;
102+
wgt_vt vweights("vertex weights", g.numRows());
103+
Kokkos::deep_copy(vweights, 1);
104+
111105
part_vt best_part;
112106

113107
value_t edgecut_min = std::numeric_limits<value_t>::max();
108+
std::vector<value_t> cuts;
109+
int64_t avg = 0;
114110
for (int i=0; i < config.num_iter; i++) {
115111
Kokkos::fence();
116112
value_t edgecut = 0;
117-
ExperimentLoggerUtil<value_t> experiment;
118-
part_vt part = partition(edgecut, config, experiment);
113+
experiment_data<value_t> experiment;
114+
#ifdef HOST
115+
part_vt part = partition_host(edgecut, config, g, vweights, uniform_ew,
116+
experiment);
117+
#elif defined SERIAL
118+
part_vt part = partition_serial(edgecut, config, g, vweights, uniform_ew,
119+
experiment);
120+
#else
121+
part_vt part = partition(edgecut, config, g, vweights, uniform_ew,
122+
experiment);
123+
#endif
124+
avg += edgecut;
125+
cuts.push_back(edgecut);
119126

120127
if (edgecut < edgecut_min) {
121128
edgecut_min = edgecut;
@@ -130,7 +137,9 @@ int main(int argc, char **argv) {
130137
}
131138
if(metrics != nullptr) experiment.log(metrics, first, last);
132139
}
133-
std::cout << "Imported coarse graphs, min edgecut found is " << edgecut_min << std::endl;
140+
std::cout << "graph " << filename << ", min edgecut found is " << edgecut_min << std::endl;
141+
std::cout << "average edgecut: " << (avg / config.num_iter) << std::endl;
142+
std::cout << "median edgecut: " << median(cuts) << std::endl;
134143

135144
if(part_file != nullptr && config.num_iter > 0) write_part(best_part, part_file);
136145
}

0 commit comments

Comments
 (0)