Merge branch 'develop' of github.com:xianyi/OpenBLAS into develop

This commit is contained in:
Zhang Xianyi 2014-02-21 07:15:35 +08:00
commit b161ac29e3
8 changed files with 181 additions and 31 deletions

View File

@ -74,6 +74,21 @@ USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <sys/resource.h>
#endif
#ifndef likely
#ifdef __GNUC__
#define likely(x) __builtin_expect(!!(x), 1)
#else
#define likely(x) (x)
#endif
#endif
#ifndef unlikely
#ifdef __GNUC__
#define unlikely(x) __builtin_expect(!!(x), 0)
#else
#define unlikely(x) (x)
#endif
#endif
#ifdef SMP_SERVER
#undef MONITOR
@ -83,8 +98,6 @@ USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#define ATTRIBUTE_SIZE 128
extern void openblas_warning(int verbose, const char * msg);
/* This is a thread server model implementation. The threads are */
/* spawned at first access to blas library, and still remains until */
/* destruction routine is called. The number of threads are */
@ -586,6 +599,10 @@ static BLASULONG exec_queue_lock = 0;
int exec_blas_async(BLASLONG pos, blas_queue_t *queue){
#ifdef SMP_SERVER
// Handle lazy re-init of the thread-pool after a POSIX fork
if (unlikely(blas_server_avail == 0)) blas_thread_init();
#endif
BLASLONG i = 0;
blas_queue_t *current = queue;
#if defined(OS_LINUX) && !defined(NO_AFFINITY) && !defined(PARAMTEST)
@ -710,7 +727,11 @@ int exec_blas_async_wait(BLASLONG num, blas_queue_t *queue){
/* Execute Threads */
int exec_blas(BLASLONG num, blas_queue_t *queue){
int (*routine)(blas_arg_t *, void *, void *, double *, double *, BLASLONG);
#ifdef SMP_SERVER
// Handle lazy re-init of the thread-pool after a POSIX fork
if (unlikely(blas_server_avail == 0)) blas_thread_init();
#endif
int (*routine)(blas_arg_t *, void *, void *, double *, double *, BLASLONG);
#ifdef TIMING_DEBUG
BLASULONG start, stop;
@ -923,17 +944,5 @@ int BLASFUNC(blas_thread_shutdown)(void){
return 0;
}
/*
https://github.com/xianyi/OpenBLAS/issues/294
Use pthread_atfork to close blas_thread_server before fork.
Then, re-init blas_thread_server after fork at child and parent.
*/
void openblas_fork_handler()
{
int err;
err = pthread_atfork (BLASFUNC(blas_thread_shutdown), blas_thread_init, blas_thread_init);
if(err != 0)
openblas_warning(0, "OpenBLAS cannot install fork handler. You may meet hang after fork.\n");
}
#endif

View File

@ -315,9 +315,4 @@ int exec_blas(BLASLONG num, blas_queue_t *queue){
return 0;
}
void openblas_fork_handler()
{
}
#endif

View File

@ -498,8 +498,3 @@ void openblas_set_num_threads(int num)
{
goto_set_num_threads(num);
}
void openblas_fork_handler()
{
}

View File

@ -143,6 +143,8 @@ USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
gotoblas_t *gotoblas = NULL;
#endif
extern void openblas_warning(int verbose, const char * msg);
#ifndef SMP
#define blas_cpu_number 1
@ -253,6 +255,23 @@ int goto_get_num_procs (void) {
return blas_cpu_number;
}
void openblas_fork_handler()
{
// This handler shuts down the OpenBLAS-managed PTHREAD pool when OpenBLAS is
// built with "make USE_OPENMP=0".
// Hanging can still happen when OpenBLAS is built against the libgomp
// implementation of OpenMP. The problem is tracked at:
// http://gcc.gnu.org/bugzilla/show_bug.cgi?id=60035
// In the mean time build with USE_OPENMP=0 or link against another
// implementation of OpenMP.
#ifndef OS_WINDOWS
int err;
err = pthread_atfork (BLASFUNC(blas_thread_shutdown), NULL, NULL);
if(err != 0)
openblas_warning(0, "OpenBLAS Warning ... cannot install fork handler. You may meet hang after fork.\n");
#endif
}
int blas_get_cpu_number(void){
char *p;
#if defined(OS_LINUX) || defined(OS_WINDOWS) || defined(OS_FREEBSD) || defined(OS_DARWIN)
@ -1268,6 +1287,9 @@ void CONSTRUCTOR gotoblas_init(void) {
if (gotoblas_initialized) return;
#ifdef SMP
openblas_fork_handler();
#endif
#ifdef PROFILE
moncontrol (0);
@ -1288,11 +1310,7 @@ void CONSTRUCTOR gotoblas_init(void) {
#ifdef SMP
if (blas_cpu_number == 0) blas_get_cpu_number();
#ifdef SMP_SERVER
if (blas_server_avail == 0) {
blas_thread_init();
//deal with pthread and fork.
openblas_fork_handler();
}
if (blas_server_avail == 0) blas_thread_init();
#endif
#endif

View File

@ -11,7 +11,7 @@ CUNIT_LIB=$(CUNIT_DIR)/lib/libcunit.a
CFLAGS+=-I$(CUNIT_DIR)/include
OBJS=main.o test_rot.o test_swap.o test_axpy.o test_dotu.o test_rotmg.o test_dsdot.o test_amax.o
OBJS=main.o test_rot.o test_swap.o test_axpy.o test_dotu.o test_rotmg.o test_dsdot.o test_amax.o test_fork.o
all : run_test

View File

@ -63,4 +63,6 @@ void test_dsdot_n_1(void);
void test_samax(void);
void test_fork_safety(void);
#endif

View File

@ -60,6 +60,14 @@ CU_TestInfo test_level1[]={
{"Testing dsdot with n == 1",test_dsdot_n_1},
{"Testing samax", test_samax},
#if !defined(USE_OPENMP) && !defined(OS_WINDOWS)
// The GNU OpenMP implementation libgomp is not fork-safe (as of 4.8.2):
// http://gcc.gnu.org/bugzilla/show_bug.cgi?id=60035
// Hence skip this test when OpenBLAS is built with OpenMP.
{"Testing fork safety", test_fork_safety},
#endif
CU_TEST_INFO_NULL,
};

123
utest/test_fork.c Normal file
View File

@ -0,0 +1,123 @@
/*****************************************************************************
Copyright (c) 2014, Lab of Parallel Software and Computational Science,ICSAS
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in
the documentation and/or other materials provided with the
distribution.
3. Neither the name of the ISCAS nor the names of its contributors may
be used to endorse or promote products derived from this software
without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
**********************************************************************************/
#ifndef OS_WINDOWS
#include "common_utest.h"
#include <sys/wait.h>
#include <cblas.h>
void* xmalloc(size_t n)
{
void* tmp;
tmp = malloc(n);
if (tmp == NULL) {
fprintf(stderr, "You are about to die\n");
exit(1);
} else {
return tmp;
}
}
void check_dgemm(double *a, double *b, double *result, double *expected, int n)
{
int i;
cblas_dgemm(CblasRowMajor, CblasNoTrans, CblasNoTrans, n, n, n,
1.0, a, n, b, n, 0.0, result, n);
for(i = 0; i < n * n; ++i) {
CU_ASSERT_DOUBLE_EQUAL(expected[i], result[i], CHECK_EPS);
}
}
void test_fork_safety(void)
{
int n = 1000;
int i;
double *a, *b, *c, *d;
size_t n_bytes;
pid_t fork_pid;
pid_t fork_pid_nested;
n_bytes = sizeof(*a) * n * n;
a = xmalloc(n_bytes);
b = xmalloc(n_bytes);
c = xmalloc(n_bytes);
d = xmalloc(n_bytes);
// Put ones in a and b
for(i = 0; i < n * n; ++i) {
a[i] = 1;
b[i] = 1;
}
// Compute a DGEMM product in the parent process prior to forking to
// ensure that the OpenBLAS thread pool is initialized.
cblas_dgemm(CblasRowMajor, CblasNoTrans, CblasNoTrans, n, n, n,
1.0, a, n, b, n, 0.0, c, n);
fork_pid = fork();
if (fork_pid == -1) {
CU_FAIL("Failed to fork process.");
} else if (fork_pid == 0) {
// Compute a DGEMM product in the child process to check that the
// thread pool as been properly been reinitialized after the fork.
check_dgemm(a, b, d, c, n);
// Nested fork to check that the pthread_atfork protection can work
// recursively
fork_pid_nested = fork();
if (fork_pid_nested == -1) {
CU_FAIL("Failed to fork process.");
exit(1);
} else if (fork_pid_nested == 0) {
check_dgemm(a, b, d, c, n);
exit(0);
} else {
check_dgemm(a, b, d, c, n);
int child_status = 0;
pid_t wait_pid = wait(&child_status);
CU_ASSERT(wait_pid == fork_pid_nested);
CU_ASSERT(WEXITSTATUS (child_status) == 0);
exit(0);
}
} else {
check_dgemm(a, b, d, c, n);
// Wait for the child to finish and check the exit code.
int child_status = 0;
pid_t wait_pid = wait(&child_status);
CU_ASSERT(wait_pid == fork_pid);
CU_ASSERT(WEXITSTATUS (child_status) == 0);
}
}
#endif