←to practical programming

Exercise "multiprocessing"

Tasks:

  1. Manual multithreding

    1. Prepare the subroutine to be run in a thread,

      public class data { public int a,b; public double sum;}
      public static void harm(object obj){
      	var arg = (data)obj;
      	arg.sum=0;
      	for(int i=arg.a;i<arg.b;i++)arg.sum+=1.0/i;
      	}
      

    2. Create a Main function that reads—from the command-line—two parameters, the number of threads to be created and the number of terms in the harmonic sum to calculate,

      int nthreads = 1, nterms = (int)1e8; /* default values */
      foreach(var arg in args) {
         var words = arg.Split(':');
         if(words[0]=="-threads") nthreads=int.Parse(words[1]);
         if(words[0]=="-terms"  ) nterms  =(int)float.Parse(words[1]);
         }
      

    3. Prepare data-objects to be used locally in separate threads,

      data[] params = new data[nthreads];
      for(int i=0;i<nthreads;i++) {
         params[i] = new data();
         params[i].a = 1 + nterms/nthreads*i;
         params[i].b = 1 + nterms/nthreads*(i+1);
         }
      params[params.Length-1].b=nterms+1; /* the enpoint might need adjustment */
      

    4. Prepare the threads and run them, hopefully in parallel (if there is enough processors in your box),

      var threads = new System.Threading.Thread[nthreads];
      for(int i=0;i<nthreads;i++) {
         threads[i] = new System.Threading.Thread(harm); /* create a thread */
         threads[i].Start(params[i]); /* run it with params[i] as argument to "harm" */
         }
      

    5. Join the threads,

      foreach(var thread in threads) thread.Join();
      

    6. Calculate the total sum,

      double total=0; foreach(var p in params) total+=p.sum;
      

    7. Using the POSIX "time"-utility measure the processor times running your Main with different number of threads. Something like

      N=1e8
      TIME = time --portability --append --output $@
      Out.txt : main.exe Makefile
              >$@
              $(TIME) mono $< -terms:$N -threads:1 >>$@
              $(TIME) mono $< -terms:$N -threads:2 >>$@
              $(TIME) mono $< -terms:$N -threads:3 >>$@
              $(TIME) mono $< -terms:$N -threads:4 >>$@
      

  2. Pitfals in multiprocessing

    1. Now calculate the harmonic sum using "Parallel.For",
      double sum=0;
      System.Threading.Tasks.Parallel.For( 1, N+1, (int i) => sum+=1.0/i );
      
      Time it and explain why it runs slower than the serial for-loop (even with one thread) and why it returns the wrong (and somewhat random) result.
    2. Now try this one,
      using System.Linq;
      ...
      var sum = new System.Threading.ThreadLocal<double>( ()=>0, trackAllValues:true);
      System.Threading.Tasks.Parallel.For( 1, N+1, (int i)=>sum.Value+=1.0/i );
      double totalsum=sum.Values.Sum();
      
      Does it return the correct result? Is it still slower? Why?