diff options
Diffstat (limited to 'glpk-5.0/examples/threads')
-rw-r--r-- | glpk-5.0/examples/threads/Build_Multiseed.bat | 12 | ||||
-rw-r--r-- | glpk-5.0/examples/threads/Makefile | 5 | ||||
-rw-r--r-- | glpk-5.0/examples/threads/Makefile_VC | 26 | ||||
-rw-r--r-- | glpk-5.0/examples/threads/README | 66 | ||||
-rw-r--r-- | glpk-5.0/examples/threads/clustering.mod | 109 | ||||
-rw-r--r-- | glpk-5.0/examples/threads/multiseed.c | 255 | ||||
-rw-r--r-- | glpk-5.0/examples/threads/thread.h | 50 |
7 files changed, 523 insertions, 0 deletions
diff --git a/glpk-5.0/examples/threads/Build_Multiseed.bat b/glpk-5.0/examples/threads/Build_Multiseed.bat new file mode 100644 index 0000000..34dd95f --- /dev/null +++ b/glpk-5.0/examples/threads/Build_Multiseed.bat @@ -0,0 +1,12 @@ +rem Build GLPK DLL with Microsoft Visual Studio Community 2015 + +rem NOTE: Make sure that HOME variable specifies correct path +set HOME="C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC" + +call %HOME%\vcvarsall.bat x64 +copy config_VC config.h +%HOME%\bin\nmake.exe /f Makefile_VC +set PATH=..\..\w64\ +%HOME%\bin\nmake.exe /f Makefile_VC check + +pause diff --git a/glpk-5.0/examples/threads/Makefile b/glpk-5.0/examples/threads/Makefile new file mode 100644 index 0000000..61063d3 --- /dev/null +++ b/glpk-5.0/examples/threads/Makefile @@ -0,0 +1,5 @@ +all: + gcc multiseed.c -I. -lglpk -pthread -o multiseed + +check: + ./multiseed clustering.mod 20 diff --git a/glpk-5.0/examples/threads/Makefile_VC b/glpk-5.0/examples/threads/Makefile_VC new file mode 100644 index 0000000..5bb8179 --- /dev/null +++ b/glpk-5.0/examples/threads/Makefile_VC @@ -0,0 +1,26 @@ +## Build multiseed example with Microsoft Visual Studio Express ## + +CFLAGS = \ +/I. \ +/I..\..\src \ +/I..\..\w64 \ +/DHAVE_CONFIG_H=1 \ +/D_CRT_SECURE_NO_WARNINGS=1 \ +/nologo \ +/W3 \ +/O2 \ +/Zi + +.c.obj: + cl.exe $(CFLAGS) /Fo$*.obj /c $*.c + +all: multiseed.exe + +multiseed.exe: multiseed.obj ..\..\w64\glpk_4_61.dll + cl.exe $(CFLAGS) /Fmultiseed.exe \ + multiseed.obj ..\..\w64\glpk_4_61.lib + +check: multiseed.exe + .\multiseed.exe clustering.mod 20 + +## eof ## diff --git a/glpk-5.0/examples/threads/README b/glpk-5.0/examples/threads/README new file mode 100644 index 0000000..cb547f0 --- /dev/null +++ b/glpk-5.0/examples/threads/README @@ -0,0 +1,66 @@ +Thread local memory example +=========================== + +The GLPK library, when compiled with default options, uses a separate environment +for each thread that is executed. So each thread is isolated. The only exeption +is error handling. An error in any of the library functions will not only +terminate the active thread but the complete process. + +This can be circumvented by defining an error handling routine with +glp_error_hook(). This directory contains an example demonstrating running a +multithreaded application with error handling. + + +The example code +---------------- + +The program multiseed solves a MathProg model multiple times in separate parallel +threads. Each threads uses a different seed for the MathProg pseudo random number +generator. + +The MathProg model clustering.mod generates 50 randomly distributed "towns". Out +of the towns it selects 3 to be cluster centers and assign the other towns to the +clusters such that the sum of the population weighted euclidian distances between +towns and centers is minimized. + +The solution is written to a Scalable Vector File which can be viewed with a web +browser. + +For demonstration purposes at the end of every third thread the error handling +routine is triggered by calling glp_error(). This results in output like + + 18-00086 Model has been successfully processed + 18-00087 Voluntarily throwing an error in multiseed.c at line 147 + 18-00088 Error detected in file multiseed.c at line 146 + 18-00089 Error caught + +Terminal output is preceeded by numbers indicating the thread index and the +output line. You can pipe it through sort to get a better overiew, e.g. + + multiseed clustering.mod 20 | sort + + +Building and running the example code +------------------------------------- + +On Linux multiseed can be compiled with gcc by calling + + make + +The code can be executed with + + make check + +For compiling the example on 64bit Windows with Microsoft Visual Studio C++ run + + Build_Multiseed.bat + +You may have to adust the variable HOME in the batch file depending on the +installation path of Visual Studio. + +You can run multiseed with + + multiseed <filename> <count> + +Where filename is the path to the model file and count is the number of parallel +threads. diff --git a/glpk-5.0/examples/threads/clustering.mod b/glpk-5.0/examples/threads/clustering.mod new file mode 100644 index 0000000..cd8cd37 --- /dev/null +++ b/glpk-5.0/examples/threads/clustering.mod @@ -0,0 +1,109 @@ +/* + * Author: Heinrich Schuchardt <xypron.glpk@gmx.de> + * + * This model solves a clustering problem: + * + * Out of 50 towns select 3 to be cluster centers and assign the other + * towns to the clusters such that the sum of the population weighted + * euclidian distances between towns and centers is minimized. + * + * The solution is saved as a scalable vector graphic file with a + * pseudo-random file name. + */ + +# Output file +param fn, symbolic := "00000" & 100000 * Uniform01(); +param f, symbolic := "ct" & substr(fn, length(fn) - 4) & ".svg"; + +# Centers +param nc := 3; +set C := {1 .. nc}; + +# Towns +param nt := 50; +set T := {1 .. nt}; +param xt{T} := Uniform01(); +param yt{T} := Uniform01(); +param pt{T} := ceil(1000 * Uniform01()); + +# Image size +param scale := 1000; + +# Colors +# saturation [0, 255] +param sat := 192; +param hue{c in C} := 6 * (c - 1) / nc; +param red{c in C} := + if hue[c] <= 1 or hue[c] >= 5 then 255 + else (if hue[c] >=2 and hue[c] <= 4 then 255 - sat + else (if hue[c] <=2 then 255 - sat + sat * (2-hue[c]) + else 255 - sat + sat * (hue[c]-4) )); +param green{c in C} := + if hue[c] >= 1 and hue[c] <= 3 then 255 + else (if hue[c] >= 4 then 255 - sat + else (if hue[c] <=1 then 255 - sat + sat * hue[c] + else 255 - sat + sat * (4-hue[c]) )); +param blue{c in C} := + if hue[c] >= 3 and hue[c] <= 5 then 255 + else (if hue[c] <=2 then 255 - sat + else (if hue[c] <=3 then 255 - sat + sat * (hue[c]-2) + else 255 - sat + sat * (6-hue[c]) )); + +var x{T}; +var y{T,T}, binary; + +minimize obj : sum{c in T, t in T} y[c,t] * pt[t] + * sqrt((xt[c] - xt[t])^2 + (yt[c] - yt[t])^2); + +s.t. sumx : sum{c in T} x[c] = nc; +s.t. cxy{c in T, t in T} : y[c,t] <= x[c]; +s.t. sumy{t in T} : sum{c in T} y[c,t] = 1; + +solve; + +for {c in T : x[c] > .5} { + printf "Center %5.4f %5.4f\n", xt[c], yt[c]; + for {t in T : y[c,t] > .5} { + printf " Town %5.4f %5.4f (%5.0f)\n", xt[t], yt[t], pt[t]; + } +} + +# Output the solution as scalable vector graphic + +# header +printf "<?xml version=""1.0"" standalone=""no""?>\n" > f; +printf "<!DOCTYPE svg PUBLIC ""-//W3C//DTD SVG 1.1//EN"" \n" >> f; +printf """http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"">\n" >> f; +printf "<svg width=""%d"" height=""%d"" version=""1.0"" \n", + 1.2 * scale, 1.2 * scale >> f; +printf "xmlns=""http://www.w3.org/2000/svg"">\n" >> f; + +# background +printf "<rect x=""0"" y=""0"" width=""%d"" height=""%d""" & + " stroke=""none"" fill=""white""/>\n", + 1.2 * scale, 1.2 * scale>> f; + +# border +printf "<rect x=""%d"" y=""%d"" width=""%d"" height=""%d""" & + " stroke=""black"" stroke-width="".5"" fill=""white""/>\n", + .1 * scale, .1 * scale, scale, scale >> f; + +# circles for towns +for {t in T} + printf {s in T, c in C : y[s,t] > .5 + && c = floor( .5 + sum{u in T : u <= s} x[u])} + "<circle cx=""%f"" cy=""%f"" r=""%f"" stroke=""black"" " & + "stroke-width=""1"" fill=""rgb(%d,%d,%d)""/>\n", + (.1 + xt[t]) * scale, (.1 + yt[t]) * scale, .001 * sqrt(pt[t]) * scale, + red[c], green[c] , blue[c] >> f; + +# lines from towns to assigned centers +for {t in T, c in T : y[c,t] > .5} + printf "<line x1=""%f"" y1=""%f"" x2=""%f"" y2=""%f""" & + " style=""stroke:black;stroke-width:.5""/>\n", + (.1 + xt[c]) * scale, (.1 + yt[c]) * scale, + (.1 + xt[t]) * scale, (.1 + yt[t]) * scale >> f; + +printf "</svg>\n" >> f; + +end; diff --git a/glpk-5.0/examples/threads/multiseed.c b/glpk-5.0/examples/threads/multiseed.c new file mode 100644 index 0000000..eb5d215 --- /dev/null +++ b/glpk-5.0/examples/threads/multiseed.c @@ -0,0 +1,255 @@ +/* multiseed.c (multithreading demo) */ + +/*********************************************************************** +* This code is part of GLPK (GNU Linear Programming Kit). +* Copyright (C) 2017 Free Software Foundation, Inc. +* Written by Heinrich Schuchardt <xypron.glpk@gmx.de>. +* +* GLPK is free software: you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* GLPK is distributed in the hope that it will be useful, but WITHOUT +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +* License for more details. +* +* You should have received a copy of the GNU General Public License +* along with GLPK. If not, see <http://www.gnu.org/licenses/>. +***********************************************************************/ + +/* + * This program demonstrates running the GLPK library with multiple threads. + * + * When called the program requires two arguments: + * + * filename - the name of the MathProg model to be solved + * threads - the count of parallel threads to be run. + * + * Each thread is run with a different seed for the random number generator + * provided by the GLPK library. + */ + +#include <glpk.h> +#include <malloc.h> +#include <setjmp.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "thread.h" + +#define BUFLEN 256 + +/* Task descriptor */ +struct task { + pthread_t tid; + char *filename; + int seed; + size_t pos; + char buf[BUFLEN + 1]; + int line; + jmp_buf jmp; +}; + +/* Mutex for console output */ +pthread_mutex_t mutex; + +/* Console output handler */ +int term_hook(void *info, const char *text) +{ + struct task *task = (struct task *) info; + size_t len = strlen(text); + + /* Lock mutex so this is the only task creating console output. */ + pthread_mutex_lock(&mutex); + + /* Append the new text to the buffer. */ + if (task->pos + len > BUFLEN) { + printf("%02d-%05d %s%s", task->seed, ++task->line, task->buf, text); + task->pos = 0; + task->buf[0] = 0; + } else { + strcpy(task->buf + task->pos, text); + task->pos += len; + } + + /* If a complete line is available, send it to the console. */ + if (strchr(task->buf, '\n')) { + printf("%02d-%05d %s", task->seed, ++task->line, task->buf); + task->pos = 0; + task->buf[0] = 0; + } + + /* Unlock the mutex. */ + pthread_mutex_unlock(&mutex); + + /* Disable default output. */ + return -1; +} + +/* Error handler */ +void error_hook(void *info) +{ + struct task *task = (struct task *) info; + + term_hook(task, "Error caught\n"); + glp_free_env(); + longjmp(task->jmp, 1); +} + +void worker(void *arg) +{ + struct task *task = (struct task *) arg; + int ret; + glp_prob *lp; + glp_tran *tran; + glp_iocp iocp; + + if (setjmp(task->jmp)) { + /* If an error is caught leave the function. */ + return; + } + + /* Set the error handler. */ + glp_error_hook(error_hook, task); + + /* Set the console output handler. */ + glp_term_hook(term_hook, arg); + + glp_printf("Seed %02d\n", task->seed); + + /* Create the problem object. */ + lp = glp_create_prob(); + if (!lp) { + glp_error("Out of memory\n"); + } + + /* Create the MathProg translator workspace. */ + tran = glp_mpl_alloc_wksp(); + if (!lp) { + glp_error("Out of memory\n"); + } + + /* Set the pseudo random number generator seed. */ + glp_mpl_init_rand(tran, task->seed); + + /* Read the model file. */ + ret = glp_mpl_read_model (tran, task->filename, GLP_OFF); + if (ret != 0) { + glp_error("Model %s is not valid\n", task->filename); + } + + /* Generate the model. */ + ret = glp_mpl_generate(tran, NULL); + if (ret != 0) { + glp_error("Cannot generate model %s\n", task->filename); + } + + /* Build the problem. */ + glp_mpl_build_prob(tran, lp); + + /* Solve the problem. */ + glp_init_iocp(&iocp); + iocp.presolve = GLP_ON; + ret = glp_intopt(lp, &iocp); + if (ret == 0) { + /* Execute the post solve part of the model. */ + glp_mpl_postsolve(tran, lp, GLP_MIP); + } + + /* Release the memory. */ + glp_mpl_free_wksp (tran); + glp_delete_prob(lp); + + if (0 == task->seed % 3) { + glp_error("Voluntarily throwing an error in %s at line %d\n", + __FILE__, __LINE__); + } + + glp_term_hook(NULL, NULL); + + glp_error_hook(NULL, NULL); + + glp_free_env(); +} + +#ifdef __WOE__ +DWORD run(void *arg) +{ +#else +void *run(void *arg) +{ +#endif + worker(arg); + pthread_exit(NULL); +} + +int main(int argc, char *argv[]) +{ + int i, n, rc; + struct task *tasks; + + /* Make sure thread local memory is used by the GLPK library. */ + if (!glp_config("TLS")) { + printf("The loaded GLPK library does not support thread local memory.\n" + "You need a version of the library configured with " + "--enable-reentrant=yes to run this program.\n"); + exit(EXIT_FAILURE); + } + + /* Check program arguments. */ + if (argc != 3) { + printf("Usage %s filename threads\n" + " filename - MathProg model file\n" + " threads - number of threads\n", + argv[0]); + exit(EXIT_FAILURE); + } + + /* Parse the arguments. */ + n = atoi(argv[2]); + if (n > 50) { + printf("Number of threads is to high (> 50).\n"); + exit(EXIT_FAILURE); + } + if (n <= 1) { + printf("Need positive number of threads\n"); + exit(EXIT_FAILURE); + } + + /* Allocate memory for the task descriptors. */ + tasks = calloc(n, sizeof(struct task)); + if (!tasks) { + printf("Out of memory"); + exit(EXIT_FAILURE); + } + + /* Create a mutex for console output. */ + pthread_mutex_init(&mutex, NULL); + + /* Create the threads. */ + for (i = 0; i < n; ++i) { + tasks[i].filename = argv[1]; + tasks[i].seed = i + 1; + tasks[i].pos = 0; + tasks[i].buf[0] = 0; + tasks[i].line = 0; + rc = pthread_create(&tasks[i].tid, NULL, run, &tasks[i]); + if (rc) { + printf("ERROR; return code from pthread_create() is %d\n", rc); + exit(EXIT_FAILURE); + } + } + + /* Wait for all threads to complete. */ + for (i = 0; i < n; ++i) { + pthread_join(tasks[i].tid, NULL); + } + + /* Destroy the mutex. */ + pthread_mutex_destroy(&mutex); + + return EXIT_SUCCESS; +} diff --git a/glpk-5.0/examples/threads/thread.h b/glpk-5.0/examples/threads/thread.h new file mode 100644 index 0000000..79c3dde --- /dev/null +++ b/glpk-5.0/examples/threads/thread.h @@ -0,0 +1,50 @@ +/* thread.h (pthread emulation for Windows) */ + +/*********************************************************************** +* This code is part of GLPK (GNU Linear Programming Kit). +* Copyright (C) 2011-2017 Free Software Foundation, Inc. +* Written by Heinrich Schuchardt <xypron.glpk@gmx.de>. +* +* GLPK is free software: you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* GLPK is distributed in the hope that it will be useful, but WITHOUT +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +* License for more details. +* +* You should have received a copy of the GNU General Public License +* along with GLPK. If not, see <http://www.gnu.org/licenses/>. +***********************************************************************/ + +#ifndef THREAD_H + +#define THREAD_H 1 + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif // HAVE_CONFIG_H + +#ifdef __WOE__ +#include <windows.h> +typedef CRITICAL_SECTION pthread_mutex_t; +typedef HANDLE pthread_t; +// @todo The return type of routine C is "DWORD" for Windows and +// "void *" for Posix. +#define pthread_create(A,B,C,D) \ + (int)((*A = CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)&C,D,0,NULL))==NULL) +#define pthread_exit(A) ExitThread(0) +#define pthread_mutex_destroy(A) DeleteCriticalSection(A) +#define pthread_mutex_init(A,B) (InitializeCriticalSection(A),0) +#define pthread_mutex_lock(A) (EnterCriticalSection(A),0) +#define pthread_mutex_unlock(A) (LeaveCriticalSection(A),0) +#define pthread_self() GetCurrentThreadId() +#define pthread_join(A, B) \ + (WaitForSingleObject(A, INFINITE),CloseHandle(A),0) +#else +#include <pthread.h> +#endif + +#endif // THREAD_H |