diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000000000000000000000000000000000000..49524f944860b51ba9aa461b3299b70a68df46ae --- /dev/null +++ b/.clang-format @@ -0,0 +1,6 @@ +--- +Language: Cpp +BasedOnStyle: Google +KeepEmptyLinesAtTheStartOfBlocks: true +PenaltyBreakAssignment: 2 +... diff --git a/Makefile b/Makefile index 500e52e7c323cb9a54c31c5d13cfd43a224f1840..75a2e49df9978b6383cf26aa2ad88be12d501164 100644 --- a/Makefile +++ b/Makefile @@ -1,10 +1,9 @@ CFLAGS = -g -O0 -Wall - all: swiftmpistepsim swiftmpistepsim: swiftmpistepsim.c mpiuse.c mpiuse.h atomic.h cycle.h clocks.h clocks.c - $(CC) $(CFLAGS) -o swiftmpistepsim swiftmpistepsim.c mpiuse.c clocks.c -I/usr/include/mpi -lmpi -lpthread + mpicc $(CFLAGS) -o swiftmpistepsim swiftmpistepsim.c mpiuse.c clocks.c clean: - rm swiftmpistepsim + rm -f swiftmpistepsim diff --git a/format.sh b/format.sh new file mode 100755 index 0000000000000000000000000000000000000000..91346334c9b2eaf9fbb343aba44f8a02d866d1ef --- /dev/null +++ b/format.sh @@ -0,0 +1,80 @@ +#!/bin/bash + +# Clang format command, can be overridden using CLANG_FORMAT_CMD. +# We currrently use version 5.0 so any overrides should provide that. +clang=${CLANG_FORMAT_CMD:="clang-format-5.0"} + +# Formatting command +cmd="$clang -style=file $(git ls-files | grep '\.[ch]$')" + +# Test if `clang-format-5.0` works +command -v $clang > /dev/null +if [[ $? -ne 0 ]] +then + echo "ERROR: cannot find $clang" + exit 1 +fi + +# Print the help +function show_help { + echo -e "This script formats SWIFT according to Google style" + echo -e " -h, --help \t Show this help" + echo -e " -t, --test \t Test if SWIFT is well formatted" +} + +# Parse arguments (based on https://stackoverflow.com/questions/192249) +TEST=0 +while [[ $# -gt 0 ]] +do + key="$1" + + case $key in + # print the help and exit + -h|--help) + show_help + exit + ;; + # check if the code is well formatted + -t|--test) + TEST=1 + shift + ;; + # unknown option + *) + echo "Argument '$1' not implemented" + show_help + exit + ;; + esac +done + +# Run the required commands +if [[ $TEST -eq 1 ]] +then + # Note trapping the exit status from both commands in the pipe. Also note + # do not use -q in grep as that closes the pipe on first match and we get + # a SIGPIPE error. + echo "Testing if SWIFT is correctly formatted" + $cmd -output-replacements-xml | grep "<replacement " > /dev/null + status=("${PIPESTATUS[@]}") + + # Trap if first command failed. Note 141 is SIGPIPE, that happens when no + # output + if [[ ${status[0]} -ne 0 ]] + then + echo "ERROR: $clang command failed" + exit 1 + fi + + # Check formatting + if [[ ${status[1]} -eq 0 ]] + then + echo "ERROR: needs formatting" + exit 1 + else + echo "...is correctly formatted" + fi +else + echo "Formatting SWIFT" + $cmd -i +fi diff --git a/post-process.py b/post-process.py index 746db3503a9c1f6db7196595c7b4796a99100379..458fae25a26a1a2a6be033449dc3c7ef94e294fd 100755 --- a/post-process.py +++ b/post-process.py @@ -1,9 +1,12 @@ #!/usr/bin/env python """ Usage: - post-process.py [options] output-log + post-process.py [options] output-log matched-log -Match the sends and recvs across the ranks and output a unified log. +Match the sends and recvs across the ranks so that the timings for message +completion can be checked. This is written to the file matched_log. Also +produces simple report of some interesting values and produces plots comparing +various timings. This file is part of SWIFT. @@ -24,6 +27,11 @@ You should have received a copy of the GNU Lesser General Public License along with this program. If not, see <http://www.gnu.org/licenses/>. """ +import matplotlib +#matplotlib.use("Agg") +import matplotlib.pyplot as plt +import numpy +import pylab as pl import sys import argparse @@ -33,6 +41,9 @@ parser = argparse.ArgumentParser(description="Match MPI reports") parser.add_argument( "input", help="Output log from simulator") +parser.add_argument( + "output", + help="Matched entries from the input data") parser.add_argument( "-v", "--verbose", @@ -43,6 +54,7 @@ parser.add_argument( ) args = parser.parse_args() infile = args.input +outfile = args.output # Indices for words in a line. logticcol=0 @@ -63,8 +75,14 @@ mincol=14 maxcol=15 # Keyed lines. -sends = {} -recvs = {} +keysends = {} +keyrecvs = {} + +# Indexed lines. +sends = [] +recvs = [] +nsends = 0 +nrecvs = 0 # Gather keys from the input log. We created dicts with matchable keys # for when sends start and recvs end. Other pairings are possible... @@ -80,10 +98,12 @@ with open(infile, "r") as fp: words[isubtypecol] + "/" + \ words[tagcol] + "/" + \ words[sizecol] - if not key in sends: - sends[key] = [line[:-1]] + if not key in keysends: + keysends[key] = [nsends] else: - sends[key].append(line[:-1]) + keysends[key].append(nsends) + sends.append(words) + nsends = nsends + 1 elif words[itypecol] == "23": key = words[rankcol] + "/" + \ @@ -91,21 +111,68 @@ with open(infile, "r") as fp: words[isubtypecol] + "/" + \ words[tagcol] + "/" + \ words[sizecol] - if not key in recvs: - recvs[key] = [line[:-1]] + if not key in keyrecvs: + keyrecvs[key] = [nrecvs] else: - recvs[key].append(line[:-1]) + keyrecvs[key].append(nrecvs) + recvs.append(words) + nrecvs = nrecvs + 1 # Now output. Note we could have unmatched recv keys, we don't check for that. -print "# send_logticin send_logtic send_injtic send_endtic send_dtic send_step send_rank send_otherrank send_itype send_isubtype send_tag send_size send_nr_tests send_tsum send_tmin send_tmax recv_logticin recv_logtic recv_injtic recv_endtic recv_dtic recv_step recv_rank recv_otherrank recv_itype recv_isubtype recv_tag recv_size recv_nr_tests recv_tsum recv_tmin recv_tmax" -for key in sends: - if key in recvs: - if len(sends[key]) == 1 and len(recvs[key]) == 1: - print sends[key][0], recvs[key][0] +msends = [None] * nsends +with open(outfile, "w") as fp: + fp.write("# send_logticin send_logtic send_injtic send_endtic send_dtic send_step send_rank send_otherrank send_itype send_isubtype send_tag send_size send_nr_tests send_tsum send_tmin send_tmax recv_logticin recv_logtic recv_injtic recv_endtic recv_dtic recv_step recv_rank recv_otherrank recv_itype recv_isubtype recv_tag recv_size recv_nr_tests recv_tsum recv_tmin recv_tmax\n") + for key in keysends: + if key in keyrecvs: + if len(keysends[key]) == 1 and len(keyrecvs[key]) == 1: + isend = keysends[key][0] + irecv = keyrecvs[key][0] + msends[isend] = irecv + fp.write(" ".join(sends[isend]) + " " + " ".join(recvs[irecv]) + "\n") + else: + print "# ERROR: found ", len(keysends[key]), "/", len(keyrecvs[key]), " matches for key: ", key, " should be 1/1" else: - print "# ERROR: found ", len(sends[key]), "/", len(recvs[key]), " matches for key: ", key, " should be 1/1" - else: - print "# ERROR: missing recv key: ", key - + print "# ERROR: missing recv key: ", key + +print "# Matched sends and recvs written to file: ", outfile + +# Reorder recvs to same order as sends. +recvs = [recvs[i] for i in msends] + +# Do a plot. Display and saves the graphic, uncomment the "Agg" line above to just save. +def doplot(x, y, xlabel, ylabel, title, outpng): + axmax1 = max(x) + axmax2 = max(y) + axmax = max([axmax1, axmax2]) + fig,ax = plt.subplots() + ax.set_xlim(0,axmax) + ax.set_ylim(0,axmax) + ax.plot(x, y, ',') + ax.set_xlabel(xlabel) + ax.set_ylabel(ylabel) + ax.set_title("SWIFTmpistepsim plot: " + title) + fig.tight_layout() + plt.savefig(outpng, bbox_inches="tight") + print "# Saved plot to: ", outpng + plt.show() + +# Plot no. 1: sends injection time against time of local handoff. +send_injects = [float(send[injcol]) for send in [line for line in sends]] +send_ends = [float(send[endcol]) for send in [line for line in sends]] +doplot(send_injects, send_ends, "Message start time", + "Message local completion time", "local send completions", + "local_sends.png") + +# Plot no. 2: recv injection time against time of local handoff. +recv_injects = [float(recv[injcol]) for recv in [line for line in recvs]] +recv_ends = [float(recv[endcol]) for recv in [line for line in recvs]] +doplot(recv_injects, recv_ends, "Message start time", + "Message local completion time", "local recv completions", + "local_recvs.png") + +# Plot no. 3: send injection time against time of remote completion. +doplot(send_injects, recv_ends, "Message start time", + "Global message completion time", "message completion times", + "completions.png") sys.exit(0) diff --git a/swiftmpistepsim.c b/swiftmpistepsim.c index 6181b28f6101adfe1c73c05d68ad9ad7c8e866db..b6acea367938b98f42c8b2a83a9d0aeff345f67c 100644 --- a/swiftmpistepsim.c +++ b/swiftmpistepsim.c @@ -41,6 +41,9 @@ static int usetics = 1; /* The wait between injections, nanosecs. */ static long long waitns = 0; +/* Set a data pattern and check we get this back, slow... */ +static int datacheck = 0; + /* Integer types of send and recv tasks, must match log. */ static const int task_type_send = 22; static const int task_type_recv = 23; @@ -69,7 +72,37 @@ static int volatile todo_send = 0; // XXX need to store this in the data file. static double log_clocks_cpufreq = 2194844448.0; +/** + * @brief fill a data area with a pattern that can be checked for changes. + * + * @param size size of data in bytes. + * @param data the data to fill. + */ +static void datacheck_fill(size_t size, void *data) { + unsigned char *p = (unsigned char *)data; + for (size_t i = 0; i < size; i++) { + p[i] = 170; /* 10101010 in bits. */ + } +} + +/** + * @brief test a filled data area for our pattern. + * + * @param size size of data in bytes. + * @param data the data to fill. + * + * @result 1 on success, 0 otherwise. + */ +static int datacheck_test(size_t size, void *data) { + unsigned char *p = (unsigned char *)data; + for (size_t i = 0; i < size; i++) { + if (p[i] != 170) return 0; + } + return 1; +} + static void injection_runner(int qid) { + if (verbose) message("%d: injection thread starts", qid); ticks starttics = getticks(); struct mpiuse_log_entry **reqs = reqs_queue[qid]; @@ -130,6 +163,12 @@ static void injection_runner(int qid) { /* Differences to SWIFT: MPI_BYTE not the MPI_Type. */ int err = 0; if (log->type == task_type_send) { + log->data = calloc(log->size, 1); + + /* Fill data with pattern. */ + if (datacheck) datacheck_fill(log->size, log->data); + + /* And send. */ err = MPI_Isend(log->data, log->size, MPI_BYTE, log->otherrank, log->tag, subtypeMPI_comms[log->subtype], &log->req); @@ -139,6 +178,9 @@ static void injection_runner(int qid) { atomic_inc(&todo_send); } else { + + /* Ready to receive. */ + log->data = calloc(log->size, 1); err = MPI_Irecv(log->data, log->size, MPI_BYTE, log->otherrank, log->tag, subtypeMPI_comms[log->subtype], &log->req); @@ -253,6 +295,12 @@ static void queue_runner(struct mpiuse_log_entry **logs, int volatile *nr_logs, if (dt > lmaxt) lmaxt = dt; if (res) { + /* Check data sent data is unchanged and received data is as + * expected. */ + if (datacheck && !datacheck_test(log->size, log->data)) { + error("Data mismatch on completion"); + } + /* Done, clean up. */ log->done = 1; log->endtic = getticks(); @@ -349,11 +397,11 @@ static void pick_logs(void) { /* Duplicate of logs. */ struct mpiuse_log_entry **reqs = (struct mpiuse_log_entry **)malloc(sizeof(struct mpiuse_log_entry *) * nlogs); int nreqs = 0; - sends_queue = (struct mpiuse_log_entry **)malloc( - sizeof(struct mpiuse_log_entry *) * nlogs); + sends_queue = (struct mpiuse_log_entry **)calloc( + nlogs, sizeof(struct mpiuse_log_entry *)); nr_sends = 0; - recvs_queue = (struct mpiuse_log_entry **)malloc( - sizeof(struct mpiuse_log_entry *) * nlogs); + recvs_queue = (struct mpiuse_log_entry **)calloc( + nlogs, sizeof(struct mpiuse_log_entry *)); nr_recvs = 0; for (int k = 0; k < nlogs; k++) { @@ -361,13 +409,10 @@ static void pick_logs(void) { if (log->rank == myrank && log->activation) { if (log->type == task_type_send || log->type == task_type_recv) { - /* Allocate space for data. */ - log->data = calloc(log->size, 1); - /* And keep this log. */ + log->data = NULL; reqs[nreqs] = log; nreqs++; - } else { error("task type '%d' is not a known send or recv task", log->type); } @@ -436,8 +481,11 @@ int main(int argc, char *argv[]) { /* Handle the command-line, we expect a mpiuse data file to read and various * options. */ int opt; - while ((opt = getopt(argc, argv, "vfn:")) != -1) { + while ((opt = getopt(argc, argv, "vfdn:")) != -1) { switch (opt) { + case 'd': + datacheck = 1; + break; case 'f': usetics = 0; break; @@ -486,6 +534,10 @@ int main(int argc, char *argv[]) { message("Start of MPI tests"); message("=================="); if (waitns > 0) message("adding fixed waits of %lld ns", waitns); + if (verbose) { + if (!usetics) message("using fast untimed injections"); + if (datacheck) message("checking data pattern on send and recv completion"); + } } /* Make three threads, one for injecting tasks and two to check for