├── CMakeLists.txt ├── LICENSE.txt ├── Makefile ├── README.md ├── cmdline.c ├── conf ├── check_thread_storage.c └── gkbuild.cmake ├── defs.h ├── io.c ├── main.c ├── proto.h ├── ptc.c ├── struct.h ├── tc.h └── test └── p2p-Gnutella31.metis /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.8) 2 | project(TC C) 3 | 4 | set(SHARED FALSE CACHE BOOL "build a shared library") 5 | 6 | if(MSVC) 7 | set(TC_INSTALL FALSE) 8 | else() 9 | set(TC_INSTALL TRUE) 10 | endif() 11 | 12 | # Add GK's standard cmake settings 13 | include(./conf/gkbuild.cmake) 14 | 15 | # Source files 16 | file(GLOB tc_sources *.c) 17 | 18 | # Include directories 19 | include_directories(.) 20 | include_directories(~/local/include) 21 | if(GKLIB_PATH) 22 | include_directories(${GKLIB_PATH}/include) 23 | endif(GKLIB_PATH) 24 | 25 | # Link directories 26 | link_directories(~/local/lib) 27 | if(GKLIB_PATH) 28 | link_directories(${GKLIB_PATH}/lib) 29 | endif(GKLIB_PATH) 30 | 31 | # Build 32 | add_executable(${PKGNAME} ${tc_sources}) 33 | 34 | foreach(prog ${PKGNAME}) 35 | target_link_libraries(${prog} GKlib m) 36 | endforeach(prog) 37 | 38 | # Install 39 | if(TC_INSTALL) 40 | install(TARGETS ${PKGNAME} 41 | RUNTIME DESTINATION bin) 42 | endif() 43 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | 2 | Copyright & License Notice 3 | --------------------------- 4 | 5 | Copyright 2017-2018, Regents of the University of Minnesota 6 | 7 | Licensed under the Apache License, Version 2.0 (the "License"); 8 | you may not use this file except in compliance with the License. 9 | You may obtain a copy of the License at 10 | 11 | http://www.apache.org/licenses/LICENSE-2.0 12 | 13 | Unless required by applicable law or agreed to in writing, software 14 | distributed under the License is distributed on an "AS IS" BASIS, 15 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 16 | implied. See the License for the specific language governing 17 | permissions and limitations under the License. 18 | 19 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # Configuration options. 2 | cc = gcc 3 | openmp = set 4 | prefix = ~/local 5 | gdb = not-set 6 | assert = not-set 7 | assert2 = not-set 8 | debug = not-set 9 | gprof = not-set 10 | shared = not-set 11 | gklib_path = not-set 12 | 13 | 14 | # Basically proxies everything to the builddir cmake. 15 | 16 | cputype = $(shell uname -m | sed "s/\\ /_/g") 17 | systype = $(shell uname -s) 18 | 19 | BUILDDIR = build/$(systype)-$(cputype) 20 | 21 | # Process configuration options. 22 | CONFIG_FLAGS = -DCMAKE_VERBOSE_MAKEFILE=1 23 | ifneq ($(gklib_path), not-set) 24 | CONFIG_FLAGS += -DGKLIB_PATH=$(abspath $(gklib_path)) 25 | endif 26 | ifneq ($(gdb), not-set) 27 | CONFIG_FLAGS += -DGDB=$(gdb) 28 | endif 29 | ifneq ($(assert), not-set) 30 | CONFIG_FLAGS += -DASSERT=$(assert) 31 | endif 32 | ifneq ($(assert2), not-set) 33 | CONFIG_FLAGS += -DASSERT2=$(assert2) 34 | endif 35 | ifneq ($(debug), not-set) 36 | CONFIG_FLAGS += -DDEBUG=$(debug) 37 | endif 38 | ifneq ($(gprof), not-set) 39 | CONFIG_FLAGS += -DGPROF=$(gprof) 40 | endif 41 | ifneq ($(openmp), not-set) 42 | CONFIG_FLAGS += -DOPENMP=$(openmp) 43 | endif 44 | ifneq ($(prefix), not-set) 45 | CONFIG_FLAGS += -DCMAKE_INSTALL_PREFIX=$(prefix) 46 | endif 47 | ifneq ($(shared), not-set) 48 | CONFIG_FLAGS += -DSHARED=1 49 | endif 50 | ifneq ($(cc), not-set) 51 | CONFIG_FLAGS += -DCMAKE_C_COMPILER=$(cc) 52 | endif 53 | 54 | CONFIG_FLAGS += -DPKGNAME=gktc 55 | 56 | define run-config 57 | mkdir -p $(BUILDDIR) 58 | cd $(BUILDDIR) && cmake $(CURDIR) $(CONFIG_FLAGS) 59 | endef 60 | 61 | all clean install: 62 | @if [ ! -f $(BUILDDIR)/Makefile ]; then \ 63 | more README.md; \ 64 | else \ 65 | make -C $(BUILDDIR) $@ $(MAKEFLAGS); \ 66 | fi 67 | 68 | uninstall: 69 | xargs rm < $(BUILDDIR)/install_manifest.txt 70 | 71 | config: distclean 72 | $(run-config) 73 | 74 | distclean: 75 | rm -rf $(BUILDDIR) 76 | 77 | remake: 78 | find . -name CMakeLists.txt -exec touch {} ';' 79 | 80 | .PHONY: config distclean all clean install uninstall remake 81 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # TriangleCounting 2 | OpenMP-based parallel program for counting the number of triangles in a sparse graph. 3 | 4 | 5 | ## Build requirements 6 | - CMake 2.8, found at http://www.cmake.org/, as well as GNU make. 7 | - Download, build, and install [GKlib](https://github.com/KarypisLab/GKlib). 8 | 9 | Assuming that the above are available, two commands should suffice to 10 | build the software: 11 | ``` 12 | make config 13 | make 14 | ``` 15 | 16 | ## Configuring the build 17 | It is primarily configured by passing options to make config. For example: 18 | ``` 19 | make config cc=icc 20 | ``` 21 | 22 | would configure it to be built using icc. 23 | 24 | Configuration options are: 25 | ``` 26 | cc=[compiler] - The C compiler to use [default: gcc] 27 | prefix=[PATH] - Set the installation prefix [default: ~/local] 28 | gklib_path=[PATH] - Where GKlib was installed [default: ~/local] 29 | openmp=not-set - To build a serial version 30 | ``` 31 | 32 | ## Building and installing 33 | To build and install, run the following 34 | ``` 35 | make 36 | make install 37 | ``` 38 | 39 | ## Other make commands 40 | make uninstall 41 | Removes all files installed by 'make install'. 42 | 43 | make clean 44 | Removes all object files but retains the configuration options. 45 | 46 | make distclean 47 | Performs clean and completely removes the build directory. 48 | 49 | 50 | ## Runing the program 51 | By default, the binary, called _gktc_, will be installed in ~/local/bin. 52 | For usage information just type 53 | ``` 54 | gktc -help 55 | 56 | Usage: gktc [options] infile 57 | 58 | Options 59 | -iftype=text 60 | Specifies the format of the input file. 61 | Possible values are: 62 | metis Metis format [default] 63 | tsv tsv format (i, j, v) 64 | 65 | -nthreads=int 66 | Specifies the number of threads to use. 67 | The default value is set to the value returned by omp_get_max_threads(). 68 | 69 | -help 70 | Prints this message. 71 | ``` 72 | 73 | The program supports two formats for its input files: 74 | - The one used by the [Metis](http://www.cs.umn.edu/~metis) graph 75 | partitioning program. 76 | - The tsv format used by the graphs in the 77 | [GraphChallenge 2017](http://graphchallenge.mit.edu/) competition. 78 | Note that the graph has to be undirected and it needs to include both pairs of 79 | edges (i.e., (u,v) and (v,u)). 80 | 81 | Here is the output of a sample run: 82 | ``` 83 | gktc -nthreads=4 test/p2p-Gnutella31.metis 84 | Reading graph test/p2p-Gnutella31.metis... 85 | 86 | ----------------- 87 | infile: test/p2p-Gnutella31.metis 88 | #nvtxs: 62586 89 | #nedges: 295784 90 | nthreads: 4 91 | 92 | & compatible maxhmsize: 255, startv: 23 93 | 94 | Results... 95 | #triangles: 2024; #probes: 209251; rate: 248.56 MP/sec 96 | 97 | Timings... 98 | preprocessing: 0.002s 99 | triangle counting: 0.001s 100 | total (/x i/o): 0.003s 101 | ----------------- 102 | ``` 103 | 104 | 105 | ## Performance 106 | The following shows a sample of gktc's performance on Intel's Knights Landing 107 | processor: 108 | 109 | ``` 110 | #p: # of threads (this KNL has 68 cores) 111 | total: total time excluding I/O 112 | ppt: pre-processing time 113 | tct: triangle counting time 114 | speedup: relative to p=1 115 | 116 | rmat scale25 117 | ---------------------------------------- 118 | #p total ppt tct speedup 119 | 1 767.4s 141.9s 625.5s 120 | 5 154.4s 29.1s 125.2s 5.0x 121 | 10 77.2s 14.5s 62.6s 9.9x 122 | 20 38.6s 7.3s 31.2s 19.9x 123 | 40 19.6s 3.7s 16.1s 39.2x 124 | 68 12.2s 2.2s 9.9s 62.9x 125 | 136 9.3s 1.7s 7.6s 82.5x 126 | 272 10.1s 1.5s 8.6s 76.0x 127 | 128 | twitter 129 | ---------------------------------------- 130 | #p total ppt tct speedup 131 | 1 1422.6s 307.7s 1114.9s 132 | 5 285.7s 62.3s 223.2s 5.0x 133 | 10 143.4s 31.2s 112.1s 9.9x 134 | 20 71.4s 15.6s 55.8s 19.9x 135 | 40 37.1s 7.9s 29.1s 38.4x 136 | 68 23.1s 4.8s 18.3s 61.6x 137 | 136 17.1s 3.4s 13.6s 83.2x 138 | 272 19.3s 3.2s 16.0s 73.7x 139 | 140 | friendster 141 | ---------------------------------------- 142 | #p total ppt tct speedup 143 | 1 1618.5s 421.3s 1196.7s 144 | 5 316.8s 84.8s 231.5s 5.1x 145 | 10 159.0s 42.5s 116.1s 10.2x 146 | 20 79.5s 21.3s 57.8s 20.4x 147 | 40 40.6s 10.7s 29.4s 39.9x 148 | 68 25.5s 6.5s 18.4s 63.5x 149 | 136 16.9s 4.5s 11.7s 95.8x 150 | 272 13.5s 3.6s 8.9s 119.9x 151 | ``` 152 | 153 | ## Citing 154 | The parallel algorithm implemented is based on the one described in 155 | 156 | [__"Exploring Optimizations on Shared-memory Platforms for Parallel Triangle Counting 157 | Algorithms."__ Ancy Sarah Tom, Narayanan Sundaram, Nesreen K. Ahmed, Shaden Smith, 158 | Stijn Eyerman, Midhunchandra Kodiyath, Ibrahim Hur, Fabrizio Petrini, and George 159 | Karypis. IEEE High Performance Extreme Computing Conference (HPEC), 160 | 2017](http://glaros.dtc.umn.edu/gkhome/node/1214) 161 | 162 | This was one of the finalists for the [GraphChallenge 163 | 2017](http://graphchallenge.mit.edu/) competition. 164 | 165 | -------------------------------------------------------------------------------- /cmdline.c: -------------------------------------------------------------------------------- 1 | /*! 2 | \file 3 | \brief Parsing of command-line arguments 4 | 5 | \date Started 5/10/17 6 | \author George 7 | \version\verbatim $Id: cmdline.c 21866 2018-03-04 22:27:13Z karypis $ \endverbatim 8 | */ 9 | 10 | 11 | #include "tc.h" 12 | 13 | /*------------------------------------------------------------------- 14 | * Command-line options 15 | *-------------------------------------------------------------------*/ 16 | static struct gk_option long_options[] = { 17 | {"iftype", 1, 0, CMD_IFTYPE}, 18 | {"nthreads", 1, 0, CMD_NTHREADS}, 19 | {"help", 0, 0, CMD_HELP}, 20 | {0, 0, 0, 0} 21 | }; 22 | 23 | 24 | static gk_StringMap_t iftype_options[] = { 25 | {"tsv", IFTYPE_TSV}, 26 | {"metis", IFTYPE_METIS}, 27 | {NULL, 0} 28 | }; 29 | 30 | 31 | /*------------------------------------------------------------------- 32 | * Mini help 33 | *-------------------------------------------------------------------*/ 34 | static char helpstr[][100] = 35 | { 36 | " ", 37 | "Usage: gktc [options] infile", 38 | " ", 39 | " Options", 40 | " -iftype=text", 41 | " Specifies the format of the input file. ", 42 | " Possible values are:", 43 | " metis Metis format [default]", 44 | " tsv tsv format (i, j, v)", 45 | " ", 46 | " -nthreads=int", 47 | " Specifies the number of threads to use.", 48 | " The default value is set to the value returned by omp_get_max_threads().", 49 | " ", 50 | " -help", 51 | " Prints this message.", 52 | "" 53 | }; 54 | 55 | 56 | 57 | /*************************************************************************/ 58 | /*! This is the entry point of the command-line argument parser */ 59 | /*************************************************************************/ 60 | params_t *getcmdline_params(int argc, char *argv[]) 61 | { 62 | gk_idx_t i, j, k; 63 | int type=0; 64 | int c, option_index; 65 | params_t *params; 66 | 67 | params = (params_t *)gk_malloc(sizeof(params_t), "cmdline_parse: params"); 68 | memset(params, 0, sizeof(params_t)); 69 | 70 | /* print the command line */ 71 | for (i=0; iinfile = NULL; 77 | params->iftype = IFTYPE_METIS; 78 | params->nthreads = 1; 79 | 80 | #if defined(_OPENMP) 81 | params->nthreads = omp_get_max_threads(); 82 | #endif 83 | 84 | /* Parse the command line arguments */ 85 | while ((c = gk_getopt_long_only(argc, argv, "", long_options, &option_index)) != -1) { 86 | switch (c) { 87 | case CMD_IFTYPE: 88 | if (gk_optarg) { 89 | if ((params->iftype = gk_GetStringID(iftype_options, gk_optarg)) == -1) 90 | errexit("Invalid iftype of %s.\n", gk_optarg); 91 | } 92 | break; 93 | 94 | case CMD_NTHREADS: 95 | if (gk_optarg) { 96 | if ((params->nthreads = atoi(gk_optarg)) < 1) 97 | errexit("The -nthreads must be greater than 0.\n"); 98 | } 99 | break; 100 | 101 | case CMD_HELP: 102 | for (i=0; strlen(helpstr[i]) > 0; i++) 103 | printf("%s\n", helpstr[i]); 104 | exit(EXIT_SUCCESS); 105 | break; 106 | 107 | default: 108 | printf("Illegal command-line option(s)\nUse %s -help for a summary of the options.\n", argv[0]); 109 | exit(EXIT_FAILURE); 110 | } 111 | } 112 | 113 | /* Get the operation to be performed */ 114 | if (argc-gk_optind != 1) { 115 | printf("Missing required parameters.\n Use %s -help for a summary of the options.\n", argv[0]); 116 | exit(EXIT_FAILURE); 117 | } 118 | 119 | params->infile = gk_strdup(argv[gk_optind++]); 120 | 121 | return params; 122 | } 123 | -------------------------------------------------------------------------------- /conf/check_thread_storage.c: -------------------------------------------------------------------------------- 1 | extern __thread int x; 2 | 3 | int main(int argc, char **argv) { 4 | return 0; 5 | } 6 | -------------------------------------------------------------------------------- /conf/gkbuild.cmake: -------------------------------------------------------------------------------- 1 | # Helper modules. 2 | include(CheckFunctionExists) 3 | include(CheckIncludeFile) 4 | 5 | # Setup options. 6 | option(GDB "enable use of GDB" OFF) 7 | option(ASSERT "turn asserts on" OFF) 8 | option(ASSERT2 "additional assertions" OFF) 9 | option(DEBUG "add debugging support" OFF) 10 | option(GPROF "add gprof support" OFF) 11 | option(OPENMP "enable OpenMP support" OFF) 12 | option(PCRE "enable PCRE support" OFF) 13 | option(GKREGEX "enable GKREGEX support" OFF) 14 | option(GKRAND "enable GKRAND support" OFF) 15 | 16 | # Add compiler flags. 17 | if(MSVC) 18 | set(GK_COPTS "/Ox") 19 | set(GK_COPTIONS "-DWIN32 -DMSC -D_CRT_SECURE_NO_DEPRECATE -DUSE_GKREGEX") 20 | elseif(MINGW) 21 | set(GK_COPTS "-DUSE_GKREGEX") 22 | else() 23 | set(GK_COPTIONS "-DLINUX -D_FILE_OFFSET_BITS=64") 24 | endif(MSVC) 25 | if(CYGWIN) 26 | set(GK_COPTIONS "${GK_COPTIONS} -DCYGWIN") 27 | endif(CYGWIN) 28 | if(CMAKE_COMPILER_IS_GNUCC) 29 | # GCC opts. 30 | set(GK_COPTIONS "${GK_COPTIONS} -std=c99 -fno-strict-aliasing") 31 | set(GK_COPTIONS "${GK_COPTIONS} -march=native") 32 | if(NOT MINGW) 33 | set(GK_COPTIONS "${GK_COPTIONS} -fPIC") 34 | endif(NOT MINGW) 35 | # GCC warnings. 36 | set(GK_COPTIONS "${GK_COPTIONS} -Werror -Wall -pedantic -Wno-unused-function -Wno-unused-but-set-variable -Wno-unused-variable -Wno-unknown-pragmas") 37 | elseif(${CMAKE_C_COMPILER_ID} MATCHES "Sun") 38 | # Sun insists on -xc99. 39 | set(GK_COPTIONS "${GK_COPTIONS} -xc99") 40 | endif(CMAKE_COMPILER_IS_GNUCC) 41 | 42 | if(${CMAKE_C_COMPILER_ID} STREQUAL "Intel") 43 | set(GK_COPTIONS "${GK_COPTIONS} -xHost") 44 | # set(GK_COPTIONS "${GK_COPTIONS} -fast") 45 | endif() 46 | 47 | # Add support for the Accelerate framework in OS X 48 | if(APPLE) 49 | set(GK_COPTIONS "${GK_COPTIONS} -framework Accelerate") 50 | endif(APPLE) 51 | 52 | # Find OpenMP if it is requested. 53 | if(OPENMP) 54 | include(FindOpenMP) 55 | if(OPENMP_FOUND) 56 | set(GK_COPTIONS "${GK_COPTIONS} -D__OPENMP__ ${OpenMP_C_FLAGS}") 57 | else() 58 | message(WARNING "OpenMP was requested but support was not found") 59 | endif(OPENMP_FOUND) 60 | endif(OPENMP) 61 | 62 | 63 | # Add various definitions. 64 | if(GDB) 65 | set(GK_COPTS "${GK_COPTS} -g") 66 | set(GK_COPTIONS "${GK_COPTIONS} -Werror") 67 | else() 68 | set(GK_COPTS "-O3") 69 | endif(GDB) 70 | 71 | 72 | if(DEBUG) 73 | set(GK_COPTS "-Og") 74 | set(GK_COPTIONS "${GK_COPTIONS} -DDEBUG") 75 | endif(DEBUG) 76 | 77 | if(GPROF) 78 | set(GK_COPTS "-pg") 79 | endif(GPROF) 80 | 81 | if(NOT ASSERT) 82 | set(GK_COPTIONS "${GK_COPTIONS} -DNDEBUG") 83 | endif(NOT ASSERT) 84 | 85 | if(NOT ASSERT2) 86 | set(GK_COPTIONS "${GK_COPTIONS} -DNDEBUG2") 87 | endif(NOT ASSERT2) 88 | 89 | 90 | # Add various options 91 | if(PCRE) 92 | set(GK_COPTIONS "${GK_COPTIONS} -D__WITHPCRE__") 93 | endif(PCRE) 94 | 95 | if(GKREGEX) 96 | set(GK_COPTIONS "${GK_COPTIONS} -DUSE_GKREGEX") 97 | endif(GKREGEX) 98 | 99 | if(GKRAND) 100 | set(GK_COPTIONS "${GK_COPTIONS} -DUSE_GKRAND") 101 | endif(GKRAND) 102 | 103 | 104 | # Check for features. 105 | check_include_file(execinfo.h HAVE_EXECINFO_H) 106 | if(HAVE_EXECINFO_H) 107 | set(GK_COPTIONS "${GK_COPTIONS} -DHAVE_EXECINFO_H") 108 | endif(HAVE_EXECINFO_H) 109 | 110 | check_function_exists(getline HAVE_GETLINE) 111 | if(HAVE_GETLINE) 112 | set(GK_COPTIONS "${GK_COPTIONS} -DHAVE_GETLINE") 113 | endif(HAVE_GETLINE) 114 | 115 | 116 | # Custom check for TLS. 117 | if(MSVC) 118 | set(GK_COPTIONS "${GK_COPTIONS} -D__thread=__declspec(thread)") 119 | 120 | # This if checks if that value is cached or not. 121 | if("${HAVE_THREADLOCALSTORAGE}" MATCHES "^${HAVE_THREADLOCALSTORAGE}$") 122 | try_compile(HAVE_THREADLOCALSTORAGE 123 | ${CMAKE_BINARY_DIR} 124 | ${CMAKE_SOURCE_DIR}/conf/check_thread_storage.c) 125 | if(HAVE_THREADLOCALSTORAGE) 126 | message(STATUS "checking for thread-local storage - found") 127 | else() 128 | message(STATUS "checking for thread-local storage - not found") 129 | endif() 130 | endif() 131 | if(NOT HAVE_THREADLOCALSTORAGE) 132 | set(GK_COPTIONS "${GK_COPTIONS} -D__thread=") 133 | endif() 134 | endif() 135 | 136 | # Finally set the official C flags. 137 | set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${GK_COPTIONS} ${GK_COPTS}") 138 | 139 | -------------------------------------------------------------------------------- /defs.h: -------------------------------------------------------------------------------- 1 | /*! 2 | \file 3 | \brief This file contains various constant definitions 4 | \date Started 5/10/17 5 | \author George 6 | \version\verbatim $Id: defs.h 21907 2018-03-20 02:12:02Z karypis $ \endverbatim 7 | */ 8 | 9 | #ifndef __DEF_H__ 10 | #define __DEF_H__ 11 | 12 | #define MAXLINE 1024*128 13 | #define MAX_STRLEN 1024*128 14 | 15 | #ifdef __AVX512CD__ 16 | #define TC_VECOPT 1 17 | #endif 18 | 19 | /* command-line options */ 20 | #define CMD_IFTYPE 1 21 | #define CMD_NTHREADS 2 22 | #define CMD_HELP 200 23 | 24 | /* iftype */ 25 | #define IFTYPE_TSV 1 26 | #define IFTYPE_METIS 2 27 | 28 | 29 | /* The text labels for the different iftypes */ 30 | static char iftypenames[][10] = 31 | {"", "tsv", "metis", ""}; 32 | 33 | #endif 34 | 35 | -------------------------------------------------------------------------------- /io.c: -------------------------------------------------------------------------------- 1 | /*! 2 | \file 3 | \brief This file contains routines for reading in the data set 4 | \date Started 5/10/2017 5 | \author George 6 | \version\verbatim $Id: io.c 21013 2017-05-19 03:08:00Z karypis $ \endverbatim 7 | */ 8 | 9 | #include "tc.h" 10 | 11 | 12 | /**************************************************************************/ 13 | /*! Reads the input data */ 14 | /**************************************************************************/ 15 | vault_t *loadData(params_t *params) 16 | { 17 | vault_t *vault; 18 | 19 | vault = (vault_t *)gk_malloc(sizeof(vault_t), "loadData: vault"); 20 | memset(vault, 0, sizeof(vault_t)); 21 | 22 | /* read the graph */ 23 | printf("Reading graph %s...\n", params->infile); 24 | GKASSERT(gk_fexists(params->infile)); 25 | 26 | switch (params->iftype) { 27 | case IFTYPE_TSV: 28 | vault->graph = gk_graph_Read(params->infile, GK_GRAPH_FMT_IJV, 1, 1, 0, 0, 0); 29 | gk_free((void **)&(vault->graph->iadjwgt), LTERM); 30 | break; 31 | 32 | case IFTYPE_METIS: 33 | vault->graph = gk_graph_Read(params->infile, GK_GRAPH_FMT_METIS, -1, -1, 0, 0, 0); 34 | break; 35 | 36 | default: 37 | errexit("Unknown iftype of %d\n", params->iftype); 38 | } 39 | 40 | return vault; 41 | } 42 | 43 | -------------------------------------------------------------------------------- /main.c: -------------------------------------------------------------------------------- 1 | /*! 2 | \file 3 | \brief The entry point of the triangle counting code 4 | \date Started 5/10/2017 5 | \author George 6 | \version\verbatim $Id: cmdline.c 20946 2017-05-10 23:12:48Z karypis $ \endverbatim 7 | */ 8 | 9 | #include "tc.h" 10 | 11 | /************************************************************************* 12 | * The entry point 13 | **************************************************************************/ 14 | int main(int argc, char *argv[]) 15 | { 16 | int64_t ntriangles=0; 17 | params_t *params; 18 | vault_t *vault; 19 | 20 | params = getcmdline_params(argc, argv); 21 | 22 | vault = params->vault = loadData(params); 23 | vault->nprobes = 0; 24 | 25 | #if defined(_OPENMP) 26 | omp_set_num_threads(params->nthreads); 27 | omp_set_nested(0); 28 | omp_set_dynamic(0); 29 | #endif 30 | 31 | printf("\n-----------------\n"); 32 | printf(" infile: %s\n", params->infile); 33 | printf(" #nvtxs: %d\n", vault->graph->nvtxs); 34 | printf(" #nedges: %zd\n", vault->graph->xadj[vault->graph->nvtxs]); 35 | #if defined(_OPENMP) 36 | printf("nthreads: %d\n", omp_get_max_threads()); 37 | #else 38 | params->nthreads = 1; 39 | printf("nthreads: 1 (was not compiled with openmp support)\n"); 40 | #endif 41 | printf("\n"); 42 | 43 | 44 | gk_startwctimer(vault->timer_global); 45 | ntriangles = ptc_MapJIK(params, vault); 46 | gk_stopwctimer(vault->timer_global); 47 | 48 | printf("\nResults...\n"); 49 | printf(" #triangles: %12"PRId64"; #probes: %12"PRIu64"; rate: %10.2lf MP/sec\n", 50 | ntriangles, vault->nprobes, 51 | ((double)vault->nprobes)/((double)1e6*gk_getwctimer(vault->timer_tc))); 52 | 53 | printf("\nTimings...\n"); 54 | printf(" preprocessing: %9.3lfs\n", gk_getwctimer(vault->timer_pp)); 55 | printf(" triangle counting: %9.3lfs\n", gk_getwctimer(vault->timer_tc)); 56 | printf(" total (/x i/o): %9.3lfs\n", gk_getwctimer(vault->timer_global)); 57 | printf("-----------------\n"); 58 | 59 | } 60 | 61 | -------------------------------------------------------------------------------- /proto.h: -------------------------------------------------------------------------------- 1 | /*! 2 | \file 3 | \brief This file contains function prototypes 4 | \date Started 5/12/2017 5 | \author George 6 | \version\verbatim $Id: proto.h 21908 2018-03-20 02:14:37Z karypis $ \endverbatim 7 | */ 8 | 9 | #ifndef _PROTO_H_ 10 | #define _PROTO_H_ 11 | 12 | /* io.c */ 13 | vault_t *loadData(params_t *params); 14 | 15 | /* cmdline.c */ 16 | params_t *getcmdline_params(int argc, char *argv[]); 17 | 18 | /* ptc.c */ 19 | gk_graph_t *ptc_Preprocess(params_t *params, vault_t *vault); 20 | int64_t ptc_MapJIK(params_t *params, vault_t *vault); 21 | 22 | #endif 23 | -------------------------------------------------------------------------------- /ptc.c: -------------------------------------------------------------------------------- 1 | /*! 2 | \file 3 | \brief The OpenMP triangle counting routine 4 | \date Started 1/14/2018 5 | \author George 6 | \version\verbatim $Id: cmdline.c 20946 2017-05-10 23:12:48Z karypis $ \endverbatim 7 | */ 8 | 9 | #include "tc.h" 10 | 11 | #define SBSIZE 64 12 | #define DBSIZE 8 13 | 14 | 15 | /*************************************************************************/ 16 | /*! Reorders the vertices in the graph in inc degree order and returns 17 | the re-ordered graph in which the adjancency lists are sorted in 18 | increasing order. In addition, a diagonal entry is added to each 19 | row to make the code that follows tighter. 20 | */ 21 | /*************************************************************************/ 22 | gk_graph_t *ptc_Preprocess(params_t *params, vault_t *vault) 23 | { 24 | int32_t vi, nvtxs, nthreads, maxdegree=0, csrange=0; 25 | ssize_t *xadj, *nxadj, *psums; 26 | int32_t *adjncy, *nadjncy, *perm=NULL, *iperm=NULL, *chunkptr=NULL; 27 | int32_t *gcounts; 28 | gk_graph_t *graph; 29 | 30 | nthreads = params->nthreads; 31 | 32 | nvtxs = vault->graph->nvtxs; 33 | xadj = vault->graph->xadj; 34 | adjncy = vault->graph->adjncy; 35 | 36 | graph = gk_graph_Create(); 37 | graph->nvtxs = nvtxs; 38 | graph->xadj = nxadj = gk_zmalloc(nvtxs+1, "nxadj"); 39 | graph->adjncy = nadjncy = gk_i32malloc(nvtxs+xadj[nvtxs], "nadjncy"); 40 | 41 | perm = gk_i32malloc(nvtxs, "perm"); /* perm[old-vtx-num] => new-vtx-num */ 42 | iperm = gk_i32malloc(nvtxs, "iperm"); /* iperm[new-vtx-num] => old-vtx-num */ 43 | 44 | /* Determine maxdegree/csrange */ 45 | #pragma omp parallel for schedule(static,4096) default(none) \ 46 | shared(nvtxs, xadj) \ 47 | reduction(max: maxdegree) 48 | for (vi=0; vi=dstart; di--) { 105 | counts = gcounts + (nthreads-1)*csrange; 106 | for (ti=nthreads-1; ti>=0; ti--) { 107 | psum -= counts[di]; 108 | counts[di] = psum; 109 | counts -= csrange; 110 | } 111 | } 112 | #pragma omp barrier 113 | 114 | /* Create the perm/iperm arrays and the nxadj array of the re-ordered graph */ 115 | counts = gcounts + mytid*csrange; 116 | 117 | /* TODO: This can be optimized by pre-sorting the per-thread vertices according 118 | to their degree and processing them in increasing degree order */ 119 | for (vi=vistart; vi=vistart; vi--) { 145 | psum -= nxadj[vi]; 146 | nxadj[vi] = psum; 147 | } 148 | #pragma omp barrier 149 | 150 | /* Compute the chunk-based partitioning of the work for the reordered/sorted graph */ 151 | chunksize = 1+psums[nthreads-1]/(100*nthreads); 152 | for (nchunks=0, psum=0, vi=vistart; vi= chunksize) { 154 | nchunks++; 155 | psum = 0; 156 | } 157 | } 158 | psums[mytid] = nchunks+1; 159 | 160 | #pragma omp barrier 161 | #pragma omp single 162 | for (ti=1; ti=vistart; vi--) { 173 | if ((psum += nxadj[vi+1]-nxadj[vi]) >= chunksize) { 174 | chunkptr[--nchunks] = vi; 175 | psum = 0; 176 | } 177 | } 178 | if (mytid == 0) 179 | chunkptr[0] = 0; 180 | #pragma omp barrier 181 | 182 | nchunks = psums[nthreads-1]; /* this is the total # of chunks */ 183 | /* 184 | #pragma omp single 185 | { 186 | for (vi=0; vi=0; ci--) { 197 | for (vi=chunkptr[ci]; vi 1) 205 | gk_i32sorti(nedges, buffer); /* sort adjncy list */ 206 | } 207 | } 208 | 209 | } 210 | 211 | gk_free((void **)&perm, &iperm, &gcounts, &psums, &chunkptr, LTERM); 212 | 213 | return graph; 214 | } 215 | 216 | 217 | /*************************************************************************/ 218 | /*! The hash-map-based triangle-counting routine that uses the JIK 219 | triangle enumeration scheme. 220 | 221 | This version implements the following: 222 | - It does not store location information in L 223 | - Adds diagonal entries in U to make loops tighter 224 | - Reverts the order within U's adjancency lists to allow ++ traversal 225 | */ 226 | /*************************************************************************/ 227 | int64_t ptc_MapJIK(params_t *params, vault_t *vault) 228 | { 229 | int32_t vi, vj, nvtxs, startv; 230 | ssize_t ei, ej; 231 | int64_t ntriangles=0; 232 | ssize_t *xadj, *uxadj; 233 | int32_t *adjncy; 234 | int32_t l2, maxhmsize=0; 235 | gk_graph_t *graph; 236 | uint64_t nprobes=0; 237 | 238 | gk_startwctimer(vault->timer_pp); 239 | graph = ptc_Preprocess(params, vault); 240 | gk_stopwctimer(vault->timer_pp); 241 | 242 | nvtxs = graph->nvtxs; 243 | xadj = graph->xadj; 244 | adjncy = graph->adjncy; 245 | 246 | uxadj = gk_zmalloc(nvtxs, "uxadj"); /* the locations of the upper triangular part */ 247 | 248 | gk_startwctimer(vault->timer_tc); 249 | 250 | /* populate uxadj[] and determine the size of the hash-map */ 251 | startv = nvtxs; 252 | #pragma omp parallel for schedule(dynamic,1024) \ 253 | default(none) \ 254 | shared(nvtxs, xadj, adjncy, uxadj) \ 255 | private(vj, ei, ej) \ 256 | reduction(max: maxhmsize) \ 257 | reduction(min: startv) 258 | for (vi=nvtxs-1; vi>=0; vi--) { 259 | for (ei=xadj[vi+1]-1; adjncy[ei]>vi; ei--); 260 | uxadj[vi] = ei; 261 | maxhmsize = gk_max(maxhmsize, (int32_t)(xadj[vi+1]-uxadj[vi])); 262 | startv = (uxadj[vi] != xadj[vi] ? vi : startv); 263 | 264 | /* flip the order of Adj(vi)'s upper triangular adjacency list */ 265 | for (ej=xadj[vi+1]-1; ei(1< (1<(1< 0) { /* we had collisions */ 318 | for (ej=xadj[vj], ejend=uxadj[vj]; ejvj; ei++) { 321 | vk = adjncy[ei]; 322 | for (l=vk&hmsize; hmap[l]!=0 && hmap[l]!=vk; l=((l+1)&hmsize)); 323 | if (hmap[l] == vk) 324 | nlocal++; 325 | } 326 | nprobes += ei-uxadj[vi]; 327 | } 328 | 329 | /* reset hash */ 330 | for (ej=uxadj[vj], ejend=xadj[vj+1]-1; ejvj; eiend++); 341 | for (ei=uxadj[vi]; eivj; ei++) 344 | #endif 345 | { 346 | vk = adjncy[ei]; 347 | nlocal += (hmap[vk&hmsize] == vk); 348 | } 349 | nprobes += ei-uxadj[vi]; 350 | } 351 | 352 | /* reset hash */ 353 | for (ej=uxadj[vj], ejend=xadj[vj+1]-1; ej 0) 358 | ntriangles += nlocal; 359 | } 360 | 361 | 362 | /* Phase 2: Count triangles for the last hmsize vertices, which can be done 363 | faster by using hmap as a direct map array. */ 364 | hmap -= (nvtxs - maxhmsize); 365 | #pragma omp for schedule(dynamic,DBSIZE) nowait 366 | for (vj=nvtxs-1; vj>=nvtxs-maxhmsize; vj--) { 367 | if (xadj[vj+1]-uxadj[vj] == 1 || xadj[vj] == uxadj[vj]) 368 | continue; 369 | 370 | nlocal = 0; 371 | 372 | if (xadj[vj+1]-uxadj[vj] == nvtxs-vj) { /* complete row */ 373 | /* find intersections */ 374 | for (ej=xadj[vj], ejend=uxadj[vj]; ejvj; ei++); 377 | nlocal += ei-uxadj[vi]; 378 | nprobes += ei-uxadj[vi]; 379 | } 380 | } 381 | else { 382 | /* hash Adj(vj) */ 383 | for (ej=uxadj[vj], ejend=xadj[vj+1]-1; ejvj; eiend++); 391 | for (ei=uxadj[vi]; eivj; ei++) 394 | #endif 395 | nlocal += hmap[adjncy[ei]]; 396 | nprobes += ei-uxadj[vi]; 397 | } 398 | 399 | /* reset hash */ 400 | for (ej=uxadj[vj], ejend=xadj[vj+1]-1; ej 0) 405 | ntriangles += nlocal; 406 | } 407 | hmap += (nvtxs - maxhmsize); 408 | 409 | gk_free((void **)&hmap, LTERM); 410 | } 411 | 412 | gk_stopwctimer(vault->timer_tc); 413 | 414 | gk_graph_Free(&graph); 415 | gk_free((void **)&uxadj, LTERM); 416 | 417 | vault->nprobes = nprobes; 418 | 419 | return ntriangles; 420 | } 421 | 422 | -------------------------------------------------------------------------------- /struct.h: -------------------------------------------------------------------------------- 1 | /*! 2 | \file 3 | \brief Data structures used in the program 4 | \date Started 5/10/2017 5 | \author George 6 | \version\verbatim $Id: struct.h 21866 2018-03-04 22:27:13Z karypis $ \endverbatim 7 | */ 8 | 9 | #ifndef _STRUCT_TC_H_ 10 | #define _STRUCT_TC_H_ 11 | 12 | 13 | /************************************************************************* 14 | * the data vault 15 | **************************************************************************/ 16 | typedef struct { 17 | gk_graph_t *graph; /* the graph */ 18 | 19 | uint64_t nprobes; /* the number of intersections that are checked */ 20 | 21 | /* timers */ 22 | double timer_global; 23 | double timer_pp; 24 | double timer_tc; 25 | } vault_t; 26 | 27 | 28 | /************************************************************************* 29 | * run parameters 30 | **************************************************************************/ 31 | typedef struct { 32 | int iftype; /* The format of the input file */ 33 | int nthreads; /* The number of threads for the OpenMP variants */ 34 | 35 | char *infile; /* The file storing the input data */ 36 | 37 | vault_t *vault; 38 | } params_t; 39 | 40 | 41 | 42 | #endif 43 | -------------------------------------------------------------------------------- /tc.h: -------------------------------------------------------------------------------- 1 | /*! 2 | \file 3 | \brief This file contains the various header inclusions 4 | \date Started on 5/10/2017 5 | \author George 6 | \version\verbatim $Id: tc.h 21865 2018-03-04 21:41:35Z karypis $ \endverbatim 7 | */ 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | #if defined(__OPENMP__) 14 | #include 15 | #endif 16 | 17 | --------------------------------------------------------------------------------