-5

My current function is:

void Path(Point j){
   if(j.y > 0 && j.y <= a){
      char moveMsg[1] = {'j'};
      write(fd1, moveMsg, 1);
   }
   else if(j.y > a && j.y <= b){
      char moveMsg[1] = {'i'};
      write(fd1, moveMsg, 1);
   }
   else if(j.y > b && j.y <= c){
      char moveMsg[1] = {'h'};
      write(fd1, moveMsg, 1);
   }
   else if(j.y > c && j.y <= d){
      char moveMsg[1] = {'g'};
      write(fd1, moveMsg, 1);
   }
   else if(j.y > d && j.y <= e){
      char moveMsg[1] = {'f'};
      write(fd1, moveMsg, 1);
   }
   else if(j.y > e && j.y <= f){
      char moveMsg[1] = {'e'};
      write(fd1, moveMsg, 1);
   }
   else if(j.y > f && j.y <= g){
      char moveMsg[1] = {'d'};
      write(fd1, moveMsg, 1);
   }
   else if(j.y > g && j.y <= h){
      char moveMsg[1] = {'c'};
      write(fd1, moveMsg, 1);
   }
   else if(j.y > h && j.y <= i){
      char moveMsg[1] = {'b'};
      write(fd1, moveMsg, 1);
   }
   else if(j.y > i && j.y <= r){
      char moveMsg[1] = {'a'};
      write(fd1, moveMsg, 1);
   }
}

The intervals are predefined ints, I tried initializing char moveMsg outside of the function, and then redefining it inside, it gave me a ton of errors. I tested several different methods for serial communication, and they all gave me similar times, but now I just need to shave off a few milliseconds, so if anyone knows a faster way, that would be great.

Thanks!

Miki
  • 40,887
  • 13
  • 123
  • 202
tydcghk
  • 1
  • 3
  • 1
    When you make your function call to `write`, why not just put the `char` in there, instead of defining a variable? I.e., `write(fd1, 'd', 1);`? I think this might be faster since you're not defining a char every time. – Javia1492 Jun 23 '17 at 20:50
  • Also, your `if` conditions are kind of weird. For example, `j.y > b && j.y <=c` is the same as `j.y == c`. I dont know if this makes has any improvements on speed, but it makes it more readable in my opinion. – Javia1492 Jun 23 '17 at 20:52
  • 1
    What is the relationship of the variables a, b, c, ... i, r? If they're monotonic increasing, then the **if** conditions can be greatly simplified. I see a lot of other code optimizations possible, but a good optimizing compiler should be able to do the same. Such optimizations would save code space more than time, and time savings is probably not in the order of millisec, unless you have a really slow processor. Also, it's not clear how you are measuring this response time, i.e. you're probably not showing enough code. – sawdust Jun 23 '17 at 22:24
  • How can rearramging this code make much if any difference to the serial data transfer? You don't specify the Baud, if it's 115200 then a byte takes 100us to transfer. Fiddling with these lines of code won't change that by more than a few ns for a 1ghz cpu. – DisappointedByUnaccountableMod Jun 24 '17 at 20:56
  • The variables a, b, c, ... are currently increments of 24 because of the resolution of the Mat. They are subject to change, so I don't know if there would be an easier way to increment that. And I am using 115200 Baud, but the receiving code is optimized, while running through this segment is taking around 600 clock cycles, and I feel that it could be cut down from that. – tydcghk Jun 24 '17 at 21:12
  • I'm voting to close this question as off-topic because this code is working. If you ask for a review, you must post your question on https://codereview.stackexchange.com/. – Stargateur Jun 26 '17 at 02:09

1 Answers1

0
  • If the integer values of variables a, b, c, ... i, and r are monotonic increasing, then the if conditionals can be optimized.
  • The repeated access of a structure element and an array element can be optimized to local scaler variables.
  • Repeated allocation & deallocation of a local variable can be eliminated for time optimization.
  • The repetition of the write() syscall can be eliminated for space optimization and code maintainability.

  • A binary comparison algorithm can improve upon a sequential comparison.
    Instead of a maximum of executing ten if statements, the maximum could be four (plus a boolean test for the syscall).
    The original code has two integer comparisons and a logical operation (which are not that expensive) in each if, but that can be optimized to just one comparison in each if.

The following (is tested and) may be optimal for space and time.

void Path(Point j)
{
   int val = j.y;
   char moveMsg = 0;

   /* ASSERT(0 < a < b < c < d < e < f < g < h < i < r) */

   if (val <= e) {
      if (val <= b) {
         if (val <= a) {
            if (val > 0)   /* && val <= a */
               moveMsg = 'j';
         } else    /* val <= b && val > a */
            moveMsg = 'i';
      } else {  
         if (val <= c)      /* && val > b */
            moveMsg = 'h';
         else if (val <= d) /* && val > c */
            moveMsg = 'g';
         else      /* val <= e && val > d */
            moveMsg = 'f';
      }
   } else {
      if (val <= h) {
         if (val <= f)      /* && val > e */
            moveMsg = 'e';
         else if (val <= g) /* && val > f */
            moveMsg = 'd';
         else      /* val <= h && val > g */
            moveMsg = 'c';    
      } else {
         if (val <= i)      /* && val > h */
            moveMsg = 'b';
         else if (val <= r) /* && val > i */
            moveMsg = 'a';
      }
   }

   if (moveMsg)
      write(fd1, &moveMsg, 1);
}

The savings in time is highly dependent on the distribution of the input values. If the data skews low then the savings is small, whereas if it skews to large values or uniform distribution then the savings is better.
The variation in execution time is also no longer a function of the input value; of course for integer comparisons this variation isn't large.


... the receiving code is optimized, while running through this segment is taking around 600 clock cycles, and I feel that it could be cut down from that.

Except for the binary algorithm, these are all well-known optimizations that a good optimizing compiler can perform for you.
Since hand optimization is becoming a useless/lost skill, I'm inclined to be dubious of your claim that "the receiving code is optimized".
The best optimizations are the proper algorithm and proper use of syscalls (e.g. the syscall to write only one byte will consume the bulk of the execution time of this routine).


Addendum

Could you explain why you did the if statements like that? Why would gong through more if statements help make it faster?

The optimized code has 11 if statements to find the range; that's only one "more" than the original code.
Moreover each if statement is just one simple integer comparison, versus the original's compound logical expression of two comparisons.
In terms of the number of actual ALU operations (rather than source code complexity), the original code is clearly more expensive to execute than the optimized code.

The optimized code could typically execute fewer (not more) if statements and perform fewer comparisons than the original code.
The apparent simplicity of that original code has a well-known inefficiency, since it employs a linear or sequential (i.e. one after the other) search.

The worst case scenario with the original code occurs when the input value is equal to the maximum value r.
That is when the original code has to execute all 10 if statements and perform 20 integer comparisons.
The optimized code uses a binary search that executes 4 if statements and performs only 4 integer comparisons for that same input.
The worst case scenario with the optimized code is execution of 4 if statements and perform 4 integer comparisons.

The best case scenario with the original code occurs when the input value is equal to the minimum value a.
The original code has to execute just 1 if statement, but that does involve 2 integer comparisons.
The optimized code would execute 4 if statements and performs 4 integer comparisons for that same input.
That's only 2 more comparisons, and it's the number of comparisons that influence execution time, not the number of if statements in the source code.
The penalty of two more comparisons for this best case is offset by the significant advantage of 16 fewer comparisons for the worst case.

The average case favors the optimized code using a binary search.
The original's 5 if statements performing 10 integer comparisons is slower than than the optimized 4 if statements performing only 4 integer comparisons.
That's an advantage of 6 integer comparisons.

In fact unless the input value is consistently not more than a (i.e. the first interval), then the optimized code will execute the same number or fewer integer comparisons than the original code, despite the appearance of having more if statements.

That seems counterintuitive to me.

With search (and sorting) algorithms, simple or straightforward usually means slow.

sawdust
  • 16,103
  • 3
  • 40
  • 50
  • The receiving code is on an Arduino, Arduino code is pretty simple to optimize because there's really not much there. It's basically just a read command, then setting some pins high/low. Could you explain why you did the if statements like that? Why would gong through more if statements help make it faster? That seems counterintuitive to me. – tydcghk Jun 26 '17 at 16:48
  • @tydcghk -- See addendum. I'm still curious as to the bigger picture. You still haven't clarified what or how you're measuring performance. – sawdust Jun 26 '17 at 19:50
  • Thanks for the clarification. I have been measuring performance times with to see which parts of the code are taking time, and work on the parts taking the most time, this part is the only part that's giving me trouble. When I take the char definitions out of the if statements, it gives me errors saying that the char is mismatched with the const void*... Uh, this is a portion of my code to make a basket catch balls, so I guess the measure of performance is whether or not it catches them. I am currently close enough that the basket bumps the ball away. – tydcghk Jun 27 '17 at 20:15
  • Does that mere token of thanks mean you're not going to bother to use or measure the improvement? FYI, this optimized code has been compiled and tested for correctness. *"When I take the char definitions out of the if statements, it gives me errors..."* -- If you mean the `char` declarations with the initialization, then those are statements that require proper editing to be reused as assignment statements. We can't be expected to correct code that isn't posted. – sawdust Jun 27 '17 at 22:47
  • Your repeated mentioning of attempts to define/declare `moveMsg` seems to indicate a wrong kind optimizing (that probably won't save any space or time anyway). From an object oriented programming standpoint, the conversion function (of matching input value to a range) should be separate from the output (syscall) step. The original code that mashes these two steps together is not ideal. – sawdust Jun 27 '17 at 23:30