Diagonal Tutorial
Table of Contents
1 Introduction
This documentation is a tutorial of diagonal. You will find a basic idea about what you can do by diagonal.
2 Getting started
To build and install Diagonal from its source code is easy on every supported platform. We are now using Git1 for its revision control, so you can get its source code with Git by the following command:
$ git clone git://git.code.sf.net/p/diagonal/code diagonal
Prerequisites for building Diagonal are CMake2 and a modern C compiler, such as clang3 or gcc4, that conforms to C995. For FreeBSD / Linux / Mac OS X / NetBSD, a typical installation process of Diagonal is as follows:
$ mkdir build $ cd build $ cmake -DCMAKE_INSTALL_PREFIX=$PREFIX ../diagonal $ make all # make install
where $PREFIX
is where you would like to install it.
For Windows, currently you will need MinGW-w646 as well.
$ mkdir build $ cd build $ cmake -DCMAKE_INSTALL_PREFIX=$PREFIX -DCMAKE_TOOLCHAIN_FILE=../diagonal/Toolchain-$HOST.cmake ../diagonal $ make all # make install
where $HOST is x86_64-w64-mingw32
if you are going to build it for 64-bit Windows, or i686-w64-mingw32
for 32-bin Windows.
3 Implementing Bubble sort easier with diag-fix
If you have learnt programming, you understand Bubble sort7 is too naive to be practical. So you do not want to bother thinking it again, right? What if we can implement a bubble sort even easier with diag-fix :)
#include <stdio.h> #include <stdlib.h> int main(void) { int prev, next; int r = fscanf(stdin, "%d", &prev); if (r == EOF) return EXIT_SUCCESS; if (r == 0) { fprintf(stderr, "read error\n"); return EXIT_FAILURE; } for (;;) { r = fscanf(stdin, "%d", &next); if (r == EOF) break; if (r == 0) { fprintf(stderr, "read error\n"); return EXIT_FAILURE; } if (prev < next) { printf("%d ", prev); prev = next; } else { printf("%d ", next); } } printf("%d\n", prev); return EXIT_SUCCESS; }
Let's compile the above source code and call it bubble
.
It can execute a single run of swapping input elements as follows:
$ cat input 2 3 4 10 -2 2 -3 1 5 10 9 8 7 -7 $ ./bubble < input 2 3 4 -2 2 -3 1 5 10 9 8 7 -7 10 $ ./bubble < input | ./bubble 2 3 -2 2 -3 1 4 5 9 8 7 -7 10 10
Then diag-fix
does the rest of all for sorting:
$ diag fix -i input ./bubble -7 -3 -2 1 2 2 3 4 5 7 8 9 10 10
4 Simple report of elapsed time by diag-rep and diag-medi
Repeated observation is important for a scientific experiment.
Let's see how diag-rep
and diag-medi
helps us to produce a reasonable guess of a process's elapsed time from repeated trials.
When you would like to tell how long dd(1)
takes to copy /dev/zero
to /dev/null
, time(1)
is your friend:
$ /usr/bin/time dd if=/dev/zero of=/dev/null count=1m 1048576+0 records in 1048576+0 records out 536870912 bytes (537 MB) copied, 0.274461 s, 2.0 GB/s 0.08user 0.18system 0:00.27elapsed 98%CPU (0avgtext+0avgdata 804maxresident)k 0inputs+0outputs (0major+257minor)pagefaults 0swaps
From now on, suppose Linux's time(1)
for its useful options other time(1)
implementations may lack.
More specifically, you can use options -f, -o
and redirection in order to focus on elapsed wall clock time:
$ /usr/bin/time -f "%e" -o /dev/stdout dd if=/dev/zero of=/dev/null count=1m 2> /dev/null 0.26
Of course the resulting time (in seconds) can vary every time you run the command.
A procedure of repeated runs is needed for making your number as reliable as possible.
And that's what diag-rep
is for.
$ diag rep -n 15 /usr/bin/time -f "%e" -o /dev/stdout dd if=/dev/zero of=/dev/null count=1m 2> /dev/null 0.27 0.28 0.26 0.26 0.27 0.27 0.27 0.27 0.27 0.26 0.28 0.26 0.27 0.27 0.27
where diag-rep
drives fifteen trials. In addition, you can choose a reasonable one in the candidate numbers with diag-medi
:
$ diag rep -n 15 /usr/bin/time -f "%e" -o /dev/stdout dd if=/dev/zero of=/dev/null count=1m 2> /dev/null | diag-medi 0.27
5 Easy process pooling by diag-pool
BSD's SO_REUSEPORT, which becomes available as of Linux 3.98, is a neat option for TCP/UDP server socket if you would like to serve clients with process pooling.
diag-pool
makes it even easier as you see below.
Let's describe a simple program to wait for a TCP client on a port, then reply the process ID, and exit:
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> static void usage(void) { fprintf(stderr, "usage: mypid port\n"); } int main(int argc, char *argv[]) { if (argc < 2) { usage(); return EXIT_FAILURE; } int port = atoi(argv[1]); if (port == 0) { usage(); return EXIT_FAILURE; } int s = socket(PF_INET, SOCK_STREAM, 0); if (s == -1) { fprintf(stderr, "failed to create an endpoint\n"); return EXIT_FAILURE; } int val = 1; setsockopt(s, SOL_SOCKET, SO_REUSEPORT, &val, sizeof(val)); struct sockaddr_in sa; memset(&sa, 0, sizeof(sa)); sa.sin_family = AF_INET; sa.sin_port = htons(port); sa.sin_addr.s_addr = htonl(INADDR_ANY); if (bind(s, (struct sockaddr *)&sa, sizeof(sa)) != 0) { perror(argv[0]); return EXIT_FAILURE; } if (listen(s, 1) != 0) { perror(argv[0]); return EXIT_FAILURE; } socklen_t socklen = sizeof(sa); int d = accept(s, (struct sockaddr *)&sa, &socklen); if (d < 0) { perror(argv[0]); return EXIT_FAILURE; } FILE *fp = fdopen(d, "w"); if (!fp) { perror(argv[0]); return EXIT_FAILURE; } fprintf(fp, "pid %d\n", (int)getpid()); fclose(fp); close(d); close(s); return EXIT_SUCCESS; }
We suppose you save the above source code in a file named mypid.c
, then
$ gcc -o mypid mypid.c
will give you an executable mypid
.
OK, here is all you have to do for process pooling:
$ diag pool ./mypid 43210
Please keep it running and switch another terminal. Now you will see all five processes of mypid
listening the same port 43210
:
$ pgrep mypid 5657 5656 5655 5654 5653 $ netstat -al | head Active Internet connections (including servers) Proto Recv-Q Send-Q Local Address Foreign Address (state) tcp4 0 0 *.43210 *.* LISTEN tcp4 0 0 *.43210 *.* LISTEN tcp4 0 0 *.43210 *.* LISTEN tcp4 0 0 *.43210 *.* LISTEN tcp4 0 0 *.43210 *.* LISTEN ...
In order to access the port you can use nc(1)
as follows:
$ nc localhost 43210 pid 5653
Note that retrying the access many times is a typical use case of diag-rep
:
$ diag rep -n 10 nc localhost 43210 pid 5654 pid 5655 pid 5656 pid 5657 pid 5741 pid 5746 pid 5748 pid 5750 pid 5752 pid 5754
Footnotes:
HTML generated by org-mode 6.33x in emacs 23