FW: [PATCH] Cilk Keywords (_Cilk_spawn and _Cilk_sync) for C

Tannenbaum, Barry M barry.m.tannenbaum@intel.com
Mon Sep 29 13:59:00 GMT 2014


In a nutshell, add the following code to main() before the call to f3():

    int status = __cilkrts_set_param("nworkers", "2");
    if (0 != status) {
        // Failed to set the number of Cilk workers
        return status;
    }

Here's the details:

There are three sources of information the Cilk runtime uses to set the number of workers.

1) By default the Cilk runtime will query the operating system for the number of cores and create a worker for each core. Note that the operating system counts each hyperthread as a core.

2) You can set your own default by defining the CILK_NWORKERS environment variable.  For example:

    export CILK_NWORKERS=2

3) You can override the default programmatically by calling __cilkrts_set_param() before the first spawning function is called. For example, place the following call in main():

    __cilkrts_set_param("nworkers", "2");

A "spawning function" is a function that contains a _Cilk_spawn. The Cilk runtime will be initialized the first time a spawning function is executed.

For this test to run correctly you must set the number of workers to at least 2, so the Cilk runtime will create at least one worker thread.

Please note that once the Cilk runtime has been initialized by the entry into the first spawning function, the number of workers cannot be changed (and the __cilkrts_set_param() call will return a non-zero value to indicate an error has occurred.) The first two values are evaluated lazily; the runtime will load its default values the first time one of the __cilkrts APIs defined in cilk_api.h is called [for example __cilkrts_get_nworkers()], or the first spawning function is entered. So for example, the following code will not do what you want:

    if (1 == __cilkrts_get_nworkers())
        setenv("CILK_NWORKERS=2");

The call to __cilkrts_get_nworkers() will cause the Cilk runtime to set its default. Setting the CILK_NWORKERS environment variable after the __cilkrts_get_nworkers() call will be too late.

It is possible to change the number of workers, but to do that you must return from all spawning functions and then call __cilkrts_end_cilk() to have the Cilk runtime shut down and then call __cilkrts_set_param(). When the Cilk runtime starts at the entry to the next spawning function, it will obey the new setting.

   - Barry

-----Original Message-----
From: Thomas Schwinge [mailto:thomas@codesourcery.com] 
Sent: Monday, September 29, 2014 6:54 AM
To: Tannenbaum, Barry M; Iyer, Balaji V; Zamyatin, Igor
Cc: gcc-patches@gcc.gnu.org
Subject: RE: FW: [PATCH] Cilk Keywords (_Cilk_spawn and _Cilk_sync) for C

Hi!

On Mon, 22 Sep 2014 19:21:33 +0000, "Tannenbaum, Barry M" <barry.m.tannenbaum@intel.com> wrote:
> That's exactly correct.
> 
> There are two ways to implement a work-stealing scheduler. We refer to 
> them as: [...]

Thanks for the explanation.

> Cilk implements Parent Stealing. Since you're running with 1 worker, there's no other worker to steal the continuation. So steal_flag will never be set to 1 and you'll never break out of the loop.

Remains the question about how to address that in the testsuite:

> -----Original Message-----
> From: Thomas Schwinge [mailto:thomas@codesourcery.com]
> Sent: Monday, September 22, 2014 9:56 AM
> To: Iyer, Balaji V
> Cc: gcc-patches@gcc.gnu.org
> Subject: Re: FW: [PATCH] Cilk Keywords (_Cilk_spawn and _Cilk_sync) 
> for C
> 
> Hi!
> 
> On Tue, 27 Aug 2013 21:30:49 +0000, "Iyer, Balaji V" <balaji.v.iyer@intel.com> wrote:
> > --- /dev/null
> > +++ gcc/testsuite/c-c++-common/cilk-plus/CK/spawning_arg.c
> > @@ -0,0 +1,37 @@
> > +/* { dg-do run  { target { i?86-*-* x86_64-*-* arm*-*-* } } } */
> > +/* { dg-options "-fcilkplus" } */
> > +/* { dg-options "-lcilkrts" { target { i?86-*-* x86_64-*-* arm*-*-* 
> > +} } } */
> > +
> > +void f0(volatile int *steal_flag)
> > +{
> > +  int i = 0;
> > +  /* Wait for steal_flag to be set */
> > +  while (!*steal_flag) 
> > +    ;
> > +}
> > +
> > +int f1()
> > +{
> > +
> > +  volatile int steal_flag = 0;
> > +  _Cilk_spawn f0(&steal_flag);
> > +  steal_flag = 1;  // Indicate stolen
> > +  _Cilk_sync;
> > +  return 0;
> > +}
> > +
> > +void f2(int q)
> > +{
> > +  q = 5;
> > +}
> > +
> > +void f3()
> > +{
> > +   _Cilk_spawn f2(f1());
> > +}
> > +
> > +int main()
> > +{
> > +  f3();
> > +  return 0;
> > +}
> 
> Is this really well-formed Cilk Plus code?  Running with CILK_NWORKERS=1, or -- the equivalent -- in a system with just one CPU (as per libcilkrts/runtime/os-unix.c:__cilkrts_hardware_cpu_count returning 1), I see this test busy-loop as follows:
> 
>     Breakpoint 1, __cilkrts_hardware_cpu_count () at ../../../source/libcilkrts/runtime/os-unix.c:358
>     358     {
>     (gdb) return 1
>     Make __cilkrts_hardware_cpu_count return now? (y or n) y
>     #0  cilkg_get_user_settable_values () at ../../../source/libcilkrts/runtime/global_state.cpp:385
>     385             CILK_ASSERT(hardware_cpu_count > 0);
>     (gdb) c
>     Continuing.
>     ^C
>     Program received signal SIGINT, Interrupt.
>     f0 (steal_flag=steal_flag@entry=0x7fffffffd03c) at [...]/source/gcc/testsuite/c-c++-common/cilk-plus/CK/spawning_arg.c:9
>     9         while (!*steal_flag) 
>     (gdb) info threads
>       Id   Target Id         Frame 
>     * 1    Thread 0x7ffff7fcd780 (LWP 30816) "spawning_arg.ex" f0 (steal_flag=steal_flag@entry=0x7fffffffd03c) at [...]/source/gcc/testsuite/c-c++-common/cilk-plus/CK/spawning_arg.c:9
>     (gdb) list
>     4
>     5       void f0(volatile int *steal_flag)
>     6       { 
>     7         int i = 0;
>     8         /* Wait for steal_flag to be set */
>     9         while (!*steal_flag) 
>     10          ;
>     11      }
>     12
>     13      int f1()
>     (gdb) bt
>     #0  f0 (steal_flag=steal_flag@entry=0x7fffffffd03c) at [...]/source/gcc/testsuite/c-c++-common/cilk-plus/CK/spawning_arg.c:9
>     #1  0x00000000004009c8 in _cilk_spn_0 () at [...]/source/gcc/testsuite/c-c++-common/cilk-plus/CK/spawning_arg.c:17
>     #2  0x0000000000400a4b in f1 () at [...]/source/gcc/testsuite/c-c++-common/cilk-plus/CK/spawning_arg.c:17
>     #3  0x0000000000400d0e in _cilk_spn_1 () at [...]/source/gcc/testsuite/c-c++-common/cilk-plus/CK/spawning_arg.c:30
>     #4  0x0000000000400d7a in f3 () at [...]/source/gcc/testsuite/c-c++-common/cilk-plus/CK/spawning_arg.c:30
>     #5  0x0000000000400e33 in main () at 
> [...]/source/gcc/testsuite/c-c++-common/cilk-plus/CK/spawning_arg.c:35
> 
> No additional thread has been spawned by libcilkrts, and the one initial thread is stuck in f0, without being able to make progress.  Should in f0's while loop, some function be called to "yield to libcilkrts scheduler", or should libcilkrts have spawned an additional thread, or is the test case just not valid Cilk Plus code?

Assuming the test cases are considered well-formed Cilk Plus code, I understand there is then a hard requirement to run them with more than one worker.  OK to fix as follows?

commit ee7138e451d1f3284d6fa0f61fe517c82db94060
Author: Thomas Schwinge <thomas@codesourcery.com>
Date:   Mon Sep 29 12:47:34 2014 +0200

    Audit Cilk Plus tests for CILK_NWORKERS=1.
    
    	gcc/testsuite/
    	* c-c++-common/cilk-plus/CK/spawning_arg.c (main): Call
    	__cilkrts_set_param to set two workers.
    	* c-c++-common/cilk-plus/CK/steal_check.c (main): Likewise.
    	* g++.dg/cilk-plus/CK/catch_exc.cc (main): Likewise.
---
 gcc/testsuite/c-c++-common/cilk-plus/CK/spawning_arg.c | 15 +++++++++++++++  gcc/testsuite/c-c++-common/cilk-plus/CK/steal_check.c  | 17 ++++++++++++++---
 gcc/testsuite/g++.dg/cilk-plus/CK/catch_exc.cc         | 14 ++++++++++++++
 3 files changed, 43 insertions(+), 3 deletions(-)

diff --git gcc/testsuite/c-c++-common/cilk-plus/CK/spawning_arg.c gcc/testsuite/c-c++-common/cilk-plus/CK/spawning_arg.c
index 95e6cab..138b82c 100644
--- gcc/testsuite/c-c++-common/cilk-plus/CK/spawning_arg.c
+++ gcc/testsuite/c-c++-common/cilk-plus/CK/spawning_arg.c
@@ -2,6 +2,17 @@
 /* { dg-options "-fcilkplus" } */
 /* { dg-additional-options "-lcilkrts" { target { i?86-*-* x86_64-*-* } } } */
 
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern int __cilkrts_set_param (const char *, const char *);
+
+#ifdef __cplusplus
+}
+#endif
+
+
 void f0(volatile int *steal_flag)
 { 
   int i = 0;
@@ -32,6 +43,10 @@ void f3()
 
 int main()
 {
+  /* Ensure more than one worker.  */
+  if (__cilkrts_set_param("nworkers", "2") != 0)
+    __builtin_abort();
+
   f3();
   return 0;
 }
diff --git gcc/testsuite/c-c++-common/cilk-plus/CK/steal_check.c gcc/testsuite/c-c++-common/cilk-plus/CK/steal_check.c
index 6e28765..6b41c7f 100644
--- gcc/testsuite/c-c++-common/cilk-plus/CK/steal_check.c
+++ gcc/testsuite/c-c++-common/cilk-plus/CK/steal_check.c
@@ -2,8 +2,16 @@
 /* { dg-options "-fcilkplus" } */
 /* { dg-additional-options "-lcilkrts" { target { i?86-*-* x86_64-*-* } } } */
 
-// #include <cilk/cilk_api.h>
-extern void __cilkrts_set_param (char *, char *);
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern int __cilkrts_set_param (const char *, const char *);
+
+#ifdef __cplusplus
+}
+#endif
+
 
 void foo(volatile int *);
 
@@ -11,7 +19,10 @@ void main2(void);
 
 int main(void)
 {
- //  __cilkrts_set_param ((char *)"nworkers", (char *)"2");
+  /* Ensure more than one worker.  */
+  if (__cilkrts_set_param("nworkers", "2") != 0)
+    __builtin_abort();
+
   main2();
   return 0;
 }
diff --git gcc/testsuite/g++.dg/cilk-plus/CK/catch_exc.cc gcc/testsuite/g++.dg/cilk-plus/CK/catch_exc.cc
index 0633d19..09ddf8b 100644
--- gcc/testsuite/g++.dg/cilk-plus/CK/catch_exc.cc
+++ gcc/testsuite/g++.dg/cilk-plus/CK/catch_exc.cc
@@ -10,6 +10,16 @@
 #endif
 #include <cstdlib>
 
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern int __cilkrts_set_param (const char *, const char *);
+
+#ifdef __cplusplus
+}
+#endif
+
 
 void func(int volatile* steal_me)
 {
@@ -59,6 +69,10 @@ void my_test()
 
 int main()
 {
+  /* Ensure more than one worker.  */
+  if (__cilkrts_set_param("nworkers", "2") != 0)
+    __builtin_abort();
+
   my_test();
 #if HAVE_IO
   printf("PASSED\n");


Grüße,
 Thomas


More information about the Gcc-patches mailing list