This is the mail archive of the gcc-patches@gcc.gnu.org mailing list for the GCC project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[gomp] RFC: GOMP_CPU_AFFINITY


Hi!

http://www.openmp.org/pipermail/omp/2006/000444.html
Apparently SunPRO has SUNW_MP_PROCBIND env variable so that it is possible
to bind OpenMP threads to user selected CPUs.
The following patch implements something similar, with GOMP_CPU_AFFINITY
env var.
Without the env var, nothing special happens, otherwise the variable should
contain space or comma separated list of CPUs (either CPU number (zero
based), or a range of CPUs (M-N) or a range with some stride (M-N:S)).
GOMP_CPU_AFFINITY="0 3 1-2 4-15:2"
will bind first thread (initial thread) to CPU 0, second to CPU 3, third
to CPU 1, 4th to CPU 2, 5th to CPU 4, 6th to CPU 6, 7th to CPU 8, ...
10th to CPU 14 and then starts assigning back from the beginning of the
list.
GOMP_CPU_AFFINITY=0
binds all threads to CPU 0.
The patch as is immediately binds the initial thread to the first CPU from
the list if the env var is set, an alternative perhaps could be to only do
that when the initial thread enters a parallel region and reset it to the
old affinity mask when it leaves it, what do you think?

2006-05-19  Jakub Jelinek  <jakub@redhat.com>

	* libgomp.h (gomp_cpu_affinity, gomp_cpu_affinity_len): New extern
	decls.
	(gomp_init_affinity, gomp_init_thread_affinity): New prototypes.
	* env.c (gomp_cpu_affinity, gomp_cpu_affinity_len): New variables.
	(parse_affinity): New function.
	(initialize_env): Call it and gomp_init_affinity.
	* team.c (gomp_team_start): If gomp_cpu_affinity != NULL,
	create new pthread_attr_t and call gomp_init_thread_affinity
	on it for each thread before passing the attribute to pthread_create.
	* config/linux/affinity.c: New file.
	* config/posix/affinity.c: New file.
	* configure.ac (HAVE_PTHREAD_AFFINITY_NP): New test.
	* configure: Rebuilt.
	* config.h.in: Rebuilt.
	* Makefile.am (libgomp_la_SOURCES): Add affinity.c.
	* Makefile.in: Rebuilt.

--- libgomp/config/linux/affinity.c.jj	2006-05-18 15:10:59.000000000 +0200
+++ libgomp/config/linux/affinity.c	2006-05-19 17:04:17.000000000 +0200
@@ -0,0 +1,106 @@
+/* Copyright (C) 2006 Free Software Foundation, Inc.
+   Contributed by Jakub Jelinek <jakub@redhat.com>.
+
+   This file is part of the GNU OpenMP Library (libgomp).
+
+   Libgomp is free software; you can redistribute it and/or modify it
+   under the terms of the GNU Lesser General Public License as published by
+   the Free Software Foundation; either version 2.1 of the License, or
+   (at your option) any later version.
+
+   Libgomp 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 Lesser General Public License for
+   more details.
+
+   You should have received a copy of the GNU Lesser General Public License 
+   along with libgomp; see the file COPYING.LIB.  If not, write to the
+   Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+   MA 02110-1301, USA.  */
+
+/* As a special exception, if you link this library with other files, some
+   of which are compiled with GCC, to produce an executable, this library
+   does not by itself cause the resulting executable to be covered by the
+   GNU General Public License.  This exception does not however invalidate
+   any other reasons why the executable file might be covered by the GNU
+   General Public License.  */
+
+/* This is a Linux specific implementation of a CPU affinity setting.  */
+
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE 1
+#endif
+#include "libgomp.h"
+#include <sched.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#ifdef HAVE_PTHREAD_AFFINITY_NP
+
+static unsigned int affinity_counter;
+#ifndef HAVE_SYNC_BUILTINS
+static gomp_mutex_t affinity_lock;
+#endif
+
+void
+gomp_init_affinity (void)
+{
+  cpu_set_t cpuset;
+  size_t idx, widx;
+
+  if (pthread_getaffinity_np (pthread_self (), sizeof (cpuset), &cpuset))
+    {
+      gomp_error ("could not get CPU affinity set");
+      free (gomp_cpu_affinity);
+      gomp_cpu_affinity = NULL;
+      gomp_cpu_affinity_len = 0;
+      return;
+    }
+
+  for (widx = idx = 0; idx < gomp_cpu_affinity_len; idx++)
+    if (CPU_ISSET (idx, &cpuset))
+      gomp_cpu_affinity[widx++] = gomp_cpu_affinity[idx];
+
+  if (widx == 0)
+    {
+      gomp_error ("no CPUs left for affinity setting");
+      free (gomp_cpu_affinity);
+      gomp_cpu_affinity = NULL;
+      gomp_cpu_affinity_len = 0;
+      return;
+    }
+
+  gomp_cpu_affinity_len = widx;
+  CPU_ZERO (&cpuset);
+  CPU_SET (gomp_cpu_affinity[0], &cpuset);
+  pthread_setaffinity_np (pthread_self (), sizeof (cpuset), &cpuset);
+  affinity_counter = 1;
+#ifndef HAVE_SYNC_BUILTINS
+  gomp_mutex_init (&affinity_lock);
+#endif
+}
+
+void
+gomp_init_thread_affinity (pthread_attr_t *attr)
+{
+  unsigned int cpu;
+  cpu_set_t cpuset;
+
+#ifdef HAVE_SYNC_BUILTINS
+  cpu = __sync_fetch_and_add (&affinity_counter, 1);
+#else
+  gomp_mutex_lock (&affinity_lock);
+  cpu = affinity_counter++;
+  gomp_mutex_unlock (&affinity_lock);
+#endif
+  cpu %= gomp_cpu_affinity_len;
+  CPU_ZERO (&cpuset);
+  CPU_SET (gomp_cpu_affinity[cpu], &cpuset);
+  pthread_attr_setaffinity_np (attr, sizeof (cpu_set_t), &cpuset);
+}
+
+#else
+
+#include "../posix/affinity.c"
+
+#endif
--- libgomp/team.c.jj	2006-01-27 08:31:16.000000000 +0100
+++ libgomp/team.c	2006-05-19 16:14:22.000000000 +0200
@@ -1,4 +1,4 @@
-/* Copyright (C) 2005 Free Software Foundation, Inc.
+/* Copyright (C) 2005, 2006 Free Software Foundation, Inc.
    Contributed by Richard Henderson <rth@redhat.com>.
 
    This file is part of the GNU OpenMP Library (libgomp).
@@ -183,6 +183,7 @@ gomp_team_start (void (*fn) (void *), vo
   struct gomp_team *team;
   bool nested;
   unsigned i, n, old_threads_used = 0;
+  pthread_attr_t thread_attr, *attr;
 
   thr = gomp_thread ();
   nested = thr->ts.team != NULL;
@@ -265,6 +266,14 @@ gomp_team_start (void (*fn) (void *), vo
 	}
     }
 
+  attr = &gomp_thread_attr;
+  if (gomp_cpu_affinity != NULL)
+    {
+      pthread_attr_init (&thread_attr);
+      pthread_attr_setdetachstate (&thread_attr, PTHREAD_CREATE_DETACHED);
+      attr = &thread_attr;
+    }
+
   start_data = gomp_alloca (sizeof (struct gomp_thread_start_data)
 			    * (nthreads-i));
 
@@ -283,12 +292,17 @@ gomp_team_start (void (*fn) (void *), vo
       start_data->fn_data = data;
       start_data->nested = nested;
 
-      err = pthread_create (&pt, &gomp_thread_attr,
-			    gomp_thread_start, start_data);
+      if (gomp_cpu_affinity != NULL)
+	gomp_init_thread_affinity (attr);
+
+      err = pthread_create (&pt, attr, gomp_thread_start, start_data);
       if (err != 0)
 	gomp_fatal ("Thread creation failed: %s", strerror (err));
     }
 
+  if (gomp_cpu_affinity != NULL)
+    pthread_attr_destroy (&thread_attr);
+
  do_release:
   gomp_barrier_wait (nested ? &team->barrier : &gomp_threads_dock);
 
--- libgomp/env.c.jj	2006-01-27 08:31:16.000000000 +0100
+++ libgomp/env.c	2006-05-19 16:42:02.000000000 +0200
@@ -1,4 +1,4 @@
-/* Copyright (C) 2005 Free Software Foundation, Inc.
+/* Copyright (C) 2005, 2006 Free Software Foundation, Inc.
    Contributed by Richard Henderson <rth@redhat.com>.
 
    This file is part of the GNU OpenMP Library (libgomp).
@@ -39,6 +39,8 @@ bool gomp_dyn_var = false;
 bool gomp_nest_var = false;
 enum gomp_schedule_type gomp_run_sched_var = GFS_DYNAMIC;
 unsigned gomp_run_sched_chunk = 1;
+unsigned short *gomp_cpu_affinity;
+size_t gomp_cpu_affinity_len;
 
 /* Parse the OMP_SCHEDULE environment variable.  */
 
@@ -124,6 +126,96 @@ parse_num_threads (void)
   return false;
 }
 
+/* Parse the GOMP_CPU_AFFINITY environment varible.  Return true if one was
+   present and it was successfully parsed.  */
+
+static bool
+parse_affinity (void)
+{
+  char *env, *end;
+  unsigned long cpu_beg, cpu_end, cpu_stride;
+  unsigned short *cpus = NULL;
+  size_t allocated = 0, used = 0, needed;
+
+  env = getenv ("GOMP_CPU_AFFINITY");
+  if (env == NULL)
+    return false;
+
+  do
+    {
+      while (*env == ' ' || *env == '\t')
+	env++;
+
+      cpu_beg = strtoul (env, &end, 0);
+      cpu_end = cpu_beg;
+      cpu_stride = 1;
+      if (env == end || cpu_beg >= 1024)
+	goto invalid;
+
+      env = end;
+      if (*env == '-')
+	{
+	  cpu_end = strtoul (++env, &end, 0);
+	  if (env == end || cpu_end >= 1024 || cpu_end < cpu_beg)
+	    goto invalid;
+
+	  env = end;
+	  if (*env == ':')
+	    {
+	      cpu_stride = strtoul (++env, &end, 0);
+	      if (env == end || cpu_stride == 0 || cpu_stride >= 1024)
+		goto invalid;
+
+	      env = end;
+	    }
+	}
+
+      needed = (cpu_end - cpu_beg) / cpu_stride + 1;
+      if (used + needed >= allocated)
+	{
+	  unsigned short *new_cpus;
+
+	  if (allocated < 64)
+	    allocated = 64;
+	  if (allocated > needed)
+	    allocated <<= 1;
+	  else
+	    allocated += 2 * needed;
+	  new_cpus = realloc (cpus, allocated * sizeof (unsigned short));
+	  if (new_cpus == NULL)
+	    {
+	      free (cpus);
+	      gomp_error ("not enough memory to store GOMP_CPU_AFFINITY list");
+	      return false;
+	    }
+
+	  cpus = new_cpus;
+	  while (needed--)
+	    {
+	      cpus[used++] = cpu_beg;
+	      cpu_beg += cpu_stride;
+	    }
+	}
+
+      while (*env == ' ' || *env == '\t')
+	env++;
+
+      if (*env == ',')
+	env++;
+      else if (*env == '\0')
+	break;
+    }
+  while (1);
+
+  gomp_cpu_affinity = cpus;
+  gomp_cpu_affinity_len = used;
+  return true;
+
+ invalid:
+  gomp_error ("Invalid value for enviroment variable GOMP_CPU_AFFINITY");
+  return false;
+}
+
 /* Parse a boolean value for environement variable NAME and store the 
    result in VALUE.  */
 
@@ -155,6 +247,8 @@ initialize_env (void)
   parse_boolean ("OMP_NESTED", &gomp_nest_var);
   if (!parse_num_threads ())
     gomp_init_num_threads ();
+  if (parse_affinity ())
+    gomp_init_affinity ();
 }
 
 
--- libgomp/Makefile.am.jj	2006-02-16 08:17:18.000000000 +0100
+++ libgomp/Makefile.am	2006-05-19 16:16:46.000000000 +0200
@@ -32,7 +32,7 @@ libgomp_la_LDFLAGS = $(libgomp_version_i
 
 libgomp_la_SOURCES = alloc.c barrier.c critical.c env.c error.c iter.c \
 	loop.c ordered.c parallel.c sections.c single.c team.c work.c \
-	lock.c mutex.c proc.c sem.c bar.c time.c fortran.c
+	lock.c mutex.c proc.c sem.c bar.c time.c fortran.c affinity.c
 
 nodist_noinst_HEADERS = libgomp_f.h
 nodist_include_HEADERS = omp.h
--- libgomp/config.h.in.jj	2006-03-01 14:45:38.000000000 +0100
+++ libgomp/config.h.in	2006-05-19 16:56:54.000000000 +0200
@@ -24,6 +24,9 @@
 /* Define to 1 if you have the <memory.h> header file. */
 #undef HAVE_MEMORY_H
 
+/* Define if pthread_{,attr_}{g,s}etaffinity_np is supported. */
+#undef HAVE_PTHREAD_AFFINITY_NP
+
 /* Define to 1 if you have the <semaphore.h> header file. */
 #undef HAVE_SEMAPHORE_H
 
--- libgomp/Makefile.in.jj	2006-04-26 10:37:59.000000000 +0200
+++ libgomp/Makefile.in	2006-05-19 16:17:16.000000000 +0200
@@ -77,7 +77,7 @@ libgomp_la_LIBADD =
 am_libgomp_la_OBJECTS = alloc.lo barrier.lo critical.lo env.lo \
 	error.lo iter.lo loop.lo ordered.lo parallel.lo sections.lo \
 	single.lo team.lo work.lo lock.lo mutex.lo proc.lo sem.lo \
-	bar.lo time.lo fortran.lo
+	bar.lo time.lo fortran.lo affinity.lo
 libgomp_la_OBJECTS = $(am_libgomp_la_OBJECTS)
 DEFAULT_INCLUDES = -I. -I$(srcdir) -I.
 depcomp = $(SHELL) $(top_srcdir)/../depcomp
@@ -260,7 +260,7 @@ libgomp_version_info = -version-info $(l
 libgomp_la_LDFLAGS = $(libgomp_version_info) $(libgomp_version_script)
 libgomp_la_SOURCES = alloc.c barrier.c critical.c env.c error.c iter.c \
 	loop.c ordered.c parallel.c sections.c single.c team.c work.c \
-	lock.c mutex.c proc.c sem.c bar.c time.c fortran.c
+	lock.c mutex.c proc.c sem.c bar.c time.c fortran.c affinity.c
 
 nodist_noinst_HEADERS = libgomp_f.h
 nodist_include_HEADERS = omp.h
@@ -366,6 +366,7 @@ mostlyclean-compile:
 distclean-compile:
 	-rm -f *.tab.c
 
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/affinity.Plo@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/alloc.Plo@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/bar.Plo@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/barrier.Plo@am__quote@
--- libgomp/libgomp.h.jj	2006-01-27 08:31:16.000000000 +0100
+++ libgomp/libgomp.h	2006-05-19 16:16:16.000000000 +0200
@@ -243,8 +243,18 @@ extern bool gomp_nest_var;
 extern enum gomp_schedule_type gomp_run_sched_var;
 extern unsigned gomp_run_sched_chunk;
 
+/* Other variables.  */
+
+extern unsigned short *gomp_cpu_affinity;
+extern size_t gomp_cpu_affinity_len;
+
 /* Function prototypes.  */
 
+/* affinity.c */
+
+extern void gomp_init_affinity (void);
+extern void gomp_init_thread_affinity (pthread_attr_t *);
+
 /* alloc.c */
 
 extern void *gomp_malloc (size_t) __attribute__((malloc));
--- libgomp/configure.jj	2006-04-26 10:37:59.000000000 +0200
+++ libgomp/configure	2006-05-19 17:01:02.000000000 +0200
@@ -8511,6 +8511,68 @@ _ACEOF
     ;;
 esac
 
+# Check for pthread_{,attr_}[sg]etaffinity_np.
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#define _GNU_SOURCE
+   #include <pthread.h>
+int
+main ()
+{
+cpu_set_t cpuset;
+   pthread_attr_t attr;
+   pthread_getaffinity_np (pthread_self (), sizeof (cpu_set_t), &cpuset);
+   if (CPU_ISSET (0, &cpuset))
+     CPU_SET (1, &cpuset);
+   else
+     CPU_ZERO (&cpuset);
+   pthread_setaffinity_np (pthread_self (), sizeof (cpu_set_t), &cpuset);
+   pthread_attr_init (&attr);
+   pthread_attr_getaffinity_np (&attr, sizeof (cpu_set_t), &cpuset);
+   pthread_attr_setaffinity_np (&attr, sizeof (cpu_set_t), &cpuset);
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+  (eval $ac_link) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+	 { ac_try='test -z "$ac_c_werror_flag"
+			 || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+	 { ac_try='test -s conftest$ac_exeext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+
+cat >>confdefs.h <<\_ACEOF
+#define HAVE_PTHREAD_AFFINITY_NP 1
+_ACEOF
+
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+fi
+rm -f conftest.err conftest.$ac_objext \
+      conftest$ac_exeext conftest.$ac_ext
+
 # At least for glibc, clock_gettime is in librt.  But don't pull that
 # in if it still doesn't give us the function we want.
 if test $ac_cv_func_clock_gettime = no; then
--- libgomp/configure.ac.jj	2006-04-26 10:37:59.000000000 +0200
+++ libgomp/configure.ac	2006-05-19 17:00:57.000000000 +0200
@@ -173,6 +173,25 @@ case "$host" in
     ;;
 esac
 
+# Check for pthread_{,attr_}[sg]etaffinity_np.
+AC_LINK_IFELSE(
+ [AC_LANG_PROGRAM(
+  [#define _GNU_SOURCE
+   #include <pthread.h>],
+  [cpu_set_t cpuset;
+   pthread_attr_t attr;
+   pthread_getaffinity_np (pthread_self (), sizeof (cpu_set_t), &cpuset);
+   if (CPU_ISSET (0, &cpuset))
+     CPU_SET (1, &cpuset);
+   else
+     CPU_ZERO (&cpuset);
+   pthread_setaffinity_np (pthread_self (), sizeof (cpu_set_t), &cpuset);
+   pthread_attr_init (&attr);
+   pthread_attr_getaffinity_np (&attr, sizeof (cpu_set_t), &cpuset);
+   pthread_attr_setaffinity_np (&attr, sizeof (cpu_set_t), &cpuset);])],
+  AC_DEFINE(HAVE_PTHREAD_AFFINITY_NP, 1,
+[	Define if pthread_{,attr_}{g,s}etaffinity_np is supported.]))
+
 # At least for glibc, clock_gettime is in librt.  But don't pull that
 # in if it still doesn't give us the function we want.
 if test $ac_cv_func_clock_gettime = no; then
--- libgomp/config/posix/affinity.c.jj	2006-05-18 15:10:59.000000000 +0200
+++ libgomp/config/posix/affinity.c	2006-05-19 17:05:13.000000000 +0200
@@ -0,0 +1,41 @@
+/* Copyright (C) 2006 Free Software Foundation, Inc.
+   Contributed by Jakub Jelinek <jakub@redhat.com>.
+
+   This file is part of the GNU OpenMP Library (libgomp).
+
+   Libgomp is free software; you can redistribute it and/or modify it
+   under the terms of the GNU Lesser General Public License as published by
+   the Free Software Foundation; either version 2.1 of the License, or
+   (at your option) any later version.
+
+   Libgomp 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 Lesser General Public License for
+   more details.
+
+   You should have received a copy of the GNU Lesser General Public License 
+   along with libgomp; see the file COPYING.LIB.  If not, write to the
+   Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+   MA 02110-1301, USA.  */
+
+/* As a special exception, if you link this library with other files, some
+   of which are compiled with GCC, to produce an executable, this library
+   does not by itself cause the resulting executable to be covered by the
+   GNU General Public License.  This exception does not however invalidate
+   any other reasons why the executable file might be covered by the GNU
+   General Public License.  */
+
+/* This is a generic stub implementation of a CPU affinity setting.  */
+
+#include "libgomp.h"
+
+void
+gomp_init_affinity (void)
+{
+}
+
+void
+gomp_init_thread_affinity (pthread_attr_t *attr)
+{
+  (void) attr;
+}

	Jakub


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]