CPSC 102 Computer Science II
(Advanced C Programming)

Assignment 4

Objectives:

To start working with Objective-C (and optionally OpenMP)

Description:

  1. Use a custom timer to time the code, e.g., the following timer_t interface can be used:
    //---- @interface section ----
    
    @interface timer_t: NSObject    // Class: Parent
    {
      double ts;
      double te;
      double tt;
    }
    
    // + class methods
    
    // - instance methods
      // constructors (overloaded)
    - (id) init;
    - (id) init: (double) _ts: (double) _te: (double) _tt;
    
      // accessor/mutator
    
      // operators
    
      // members
    - (void) start;
    - (void) stop;
    - (double) elapsed_us;
    - (double) elapsed_ms;
    - (double) elapsed_s;
    - (double) stamp_us;
    
    @end
    	
    along with its implementation:
    #import <AvailabilityMacros.h>
    #else
    #include <math.h>
    #ifndef NAN
    #define NAN FP_NAN
    #endif
    #endif
    
    #import <Foundation/Foundation.h>
    
    #import <stdio.h>
    #import <stdlib.h>
    #import <assert.h>
    #import <sys/time.h>
    
    #import <timer.h>
    
    //---- @implementation section ----
    
    @implementation timer_t
    
    - (id) init { return [self init: 0.0: 0.0: 0.0]; }
    - (id) init: (double) _ts: (double) _te: (double) _tt
    {
      if(!(self = [super init]))
        return nil;
      ts = _ts;
      te = _te;
      tt = _tt;
      return self;
    }
    
      // operators
    
      // members
    - (void) start        { ts = [self stamp_us]; }
    - (void) stop         { te = [self stamp_us]; tt = te - ts; }
    - (double) elapsed_us { return tt; }
    - (double) elapsed_ms { return tt/1000.0; }
    - (double) elapsed_s  { return tt/1000000.0; }
    - (double) stamp_us
    {
            double          s,us,tod;
            struct timeval  tp;
    
      // get time of day (tod), return in microseconds
      gettimeofday(&tp,NULL);
      s = (double)(tp.tv_sec);
      us = (double)(tp.tv_usec);
      tod = s*1000000.0 + us;
      return(tod);
    }
    
    @end
    	
  2. Write an Objective-C pixel_t object whose instance variable is a drgb_t drgb; double RGB array (very much like the vect_t object) and which provides the following methods:
    // - instance methods 
      // constructors (overloaded)
    - (id)      init;
    - (id)      init: (double) r;
    - (id)      init: (double) r: (double) g;
    - (id)      init: (double) r: (double) g: (double) b;
    
      // accessor/mutator
    - (bool)    nonzero;
    - (double)  get: (int) i;
    - (void)    set: (int) i: (double) d;
    - (void)    set: (double) r: (double) g: (double) b;
    
      // operators
    - (pixel_t *) add: (pixel_t *) p;
    - (pixel_t *) dif: (pixel_t *) p;
    - (pixel_t *) scale: (double) d;
    
      // friends
    - (void) read: (FILE *) _in;
    - (void) write: (FILE *) _out: (char *) _msg;
    	
  3. In the pixel.h file also include the definition of an unsigned char RGB array:
    typedef unsigned char irgb_t[3]; 
    	
  4. In main.m we want to do two things:
  5. The idea is to use the pixel_t object to store the predefined pixel colors:
            pixel_t *red = [(pixel_t *)[pixel_t alloc] init: 1.0: 0.0: 0.0];
            pixel_t *grn = [(pixel_t *)[pixel_t alloc] init: 0.0: 1.0: 0.0];
            pixel_t *blu = [(pixel_t *)[pixel_t alloc] init: 0.0: 0.0: 1.0];
            pixel_t *ylw = [(pixel_t *)[pixel_t alloc] init: 1.0: 1.0: 0.0];
            pixel_t *cyn = [(pixel_t *)[pixel_t alloc] init: 0.0: 1.0: 1.0];
            pixel_t *mgn = [(pixel_t *)[pixel_t alloc] init: 1.0: 0.0: 1.0];
            pixel_t *wht = [(pixel_t *)[pixel_t alloc] init: 1.0: 1.0: 1.0];
            pixel_t *blk = [(pixel_t *)[pixel_t alloc] init: 0.0: 0.0: 0.0];
    	
    and then to define an image that will store the scaled, unsigned char pixle representations, i.e.,
            irgb_t  *imgloc,*img=NULL;
    	
    where imgloc is a pointer to the image (like an index)
  6. Conversion of the double pixel value to an unsigned char value is one by scaling the former by 255.0 and then saving each of the R,G,B components to each of the R,G,B components of the latter:
          // where are we in the image (using old i*c + j)
          imgloc = img + y*w + x;
          for(i=0;i<3;i++) (*imgloc)[i] = [[pix scale: 255.0] get: i];
    	
    this code is within a doubly-nested loop
  7. Use a switch statement on a tid variable to select which pixel to write at each row:
          switch(tid) {
            case 0: pix = [red copy]; break;
            case 1: pix = [grn copy]; break;
            case 2: pix = [blu copy]; break;
            case 3: pix = [ylw copy]; break;
            case 4: pix = [cyn copy]; break;
            case 5: pix = [mgn copy]; break;
            case 6: pix = [wht copy]; break;
            case 7: pix = [blk copy]; break;
          }
    	
    where tid = y/(h/8); to simulate 8 CPU cores
  8. The image is written via another doubly-nested loop:
      // write out image, header first
      fprintf(stdout,"P6 %d %d 255\n",w,h);
      for(y=h-1;y>=0;y--) {
        for(x=0;x<w;x++) {
          imgloc = img + y*w + x;
          fwrite(imgloc,sizeof(irgb_t),1,stdout);
        }
      }
    	
  9. The above code can be parallelized on multi-core CPUs using OpenMP. The key to proper parallelization is setting up the #pragma compiler pre-processor directives. There are basically two relevant lines, the important one includes proper specification of shared and private variables. Functioning code using Apple's Objective-C (NeXTStep) compiler will be gone over in class. Unfortunately, our Linux Objective-C (gnustep) compiler is not thread-safe and so the code will not work as-is with our Objective-C code.
  10. Sample output (with 1 core, faking 8, 300.68 ms timed on a Mac):

  11. Sample output (with 8 cores, 165.35 ms timed on a Mac):

What to hand in:

A tar.gz archive of your asg4/ directory, including:
  1. A README file containing
    1. Course id--section no
    2. Name
    3. Assignment description
    4. Brief solution description (e.g., program design, description of algorithm, etc., however appropriate).
    5. Lessons learned, identified interesting features of your program
    6. Any special usage instructions
  2. Makefile
  3. source code (.h headers and .c source)
  4. object code (do a make clean before tar)

How to hand in:

See submit notes.