[ECOS] nc_test_slave.c fix

Grant Edwards grante@visi.com
Fri Apr 7 09:29:00 GMT 2000


On Thu, Apr 06, 2000 at 05:48:53PM -0600, Gary Thomas wrote:
> 
> On 06-Apr-00 Grant Edwards wrote:
> > 
> > In the calibrate_load() function in nc_test_slave.c, the while(1)
> > loop will never terminate if percent_load >= desired_load && delta == 1.
> > 
> > The code at the end of the loop needs to be changed from 
> > 
> >             load_thread_level -= (delta / 2);
> > to        
> >             load_thread_level -= (delta / 2) ? (delta / 2) : 1;
> 
> Yes, I've already encountered this (has a lot to do with testing on
> machines of vastly different speeds).  Here's how I changed it:
> 
>         if (abs(desired_load-percent_load) <= 2) break;
>         delta = load_thread_level - prev_load_level;
>         prev_load_level = load_thread_level;
>         if (percent_load < desired_load) {
>             load_thread_level *= 2;
>         } else {            
>             if (delta < 0) {
>                 // Trying to decrease load, but haven't gone far enough yet
>                 if (abs(delta) < 2) delta = -2;
>                 delta = abs(delta);
>             }
>             load_thread_level -= delta / 2;
>         }
> 
> 
> Does this work for you as well?

Yes, but I think there's still a problem:  when delta==1,
load_thread_level won't change.

A new option would be:

#define max(a,b) ((a)>(b)?(a):(b))

[...]

         if (percent_load < desired_load) {
             load_thread_level *= 2;
         } else {            
             load_thread_level -= max(1,abs(delta/2));
         }

Unless I've botched my test program these are the four
algorithms (the number shown in the right hand column is the
value by which load_thread_level is decremented):

delta orig mine yours new

 -10   -5   -5    5    5
  -9   -4   -4    4    4
  -8   -4   -4    4    4
  -7   -3   -3    3    3
  -6   -3   -3    3    3
  -5   -2   -2    2    2
  -4   -2   -2    2    2
  -3   -1   -1    1    1
  -2   -1   -1    1    1
  -1    0    1    1    1
   0    0    1    0    1
   1    0    1    0    1
   2    1    1    1    1
   3    1    1    1    1
   4    2    2    2    2
   5    2    2    2    2
   6    3    3    3    3
   7    3    3    3    3
   8    4    4    4    4
   9    4    4    4    4
  10    5    5    5    5

owever, [there's always a "however"], the other potential
problem is that the loop never terminates because the actual
load is never within +/- 2 of the desired load.  On my target,
changing load_thread_level by 1 changes the CPU load by about
8%.  Luckily, a load_thread_level of 3 comes out pretty close
to 20%, so the loop terminates.

If the desired value is 25% instead of 20%, the loop doesn't
terminate.

What I would probably do is use a binary search that terminates
when we've gotten as close as we can, independant of the amount
of error in the result:

static void
calibrate_load(int desired_load)
{
  long long no_load_idle, load_idle;
  int percent_load;
  int high=20,low=0;
  
  test_printf("calibrate_load(%d)\n",desired_load);
  
  // Compute the "no load" idle value
  idle_thread_count = 0;
  cyg_semaphore_post(&idle_thread_sem);  // Start idle thread
  cyg_thread_delay(1*100);               // Pause for one second
  cyg_semaphore_wait(&idle_thread_sem);  // Stop idle thread
  no_load_idle = idle_thread_count;
  test_printf("No load = %d\n", (int)idle_thread_count);
  high=20;
  low=0;
  while (true) 
    {
      load_thread_level = (high+low)/2;
      
      start_load(desired_load);              // Start up a given load
      idle_thread_count = 0;
      cyg_semaphore_post(&idle_thread_sem);  // Start idle thread
      cyg_thread_delay(1*100);               // Pause for one second
      cyg_semaphore_wait(&idle_thread_sem);  // Stop idle thread
      load_idle = idle_thread_count;
      start_load(0);                         // Shut down background load
      percent_load = 100 - ((load_idle * 100) / no_load_idle);
      test_printf("high=%d, low=%d\n",high,low);
      test_printf("Load[%d] = %d => %d%%\n", load_thread_level, (int)idle_thread_count, percent_load);
      
      if ((high-low) <= 1)
        break;

      if (percent_load < desired_load)
        low = load_thread_level;
      else
        high = load_thread_level;
    }
    test_printf("calibrate_load ** finished\n");
}

When the loop terminates, you could add code to see if the
"high" value or the "low" value is closer to the desired result
(if you want to minimize the magnitude of the error). 

Or if you care more about the sign of the error than the
magnitude, you just always return one (e.g. if you always want
the actual load to be less than or equal to the desired, use
the "low" value).

-- 
Grant Edwards
grante@visi.com


More information about the Ecos-discuss mailing list