News for hackers. Stuff that matters.
Re:Interviewing programmers: coding test example explained by loup-vaillant on Thursday June 03, 2010 @11:05AM (#1401485)
Here is the same code, without syntactic obfuscation:

  void condense_by_removing(char* s, char c)
  {
    char* d = s;
    while (*d = *s++)
      d += *d != c;
  }
I would have sworn I found a bug. There is none. Brilliant.

Now, I wonder if we could further optimize it. For instance by accessing memory several bytes at a time, in a fashion similar to strcmp().

Re:Interviewing programmers: coding test example explained by cperciva on Thursday June 03, 2010 @11:09AM (#1401499)
Optimizing for performance, sure. But I was trying to optimize for character count. :-)
Re:Interviewing programmers: coding test example explained by loup-vaillant on Thursday June 03, 2010 @11:55AM (#1401653)
Yes, but in doing so, you traded a memory write for a branch. And memory accesses are linear, so that's likely faster than the cannon. So surely we could go further? That's how I got the idea.
Re:Interviewing programmers: coding test example explained by Daniel_Newby on Thursday June 03, 2010 @02:13PM (#1402019)
Many compilers will implement

      d += *d != c;
with a branch, and depending on the data pattern the branch predictor will be bamboozled. The branchless version would involve a pair of subtractions and some bit banging.
Re:Interviewing programmers: coding test example explained by nkurz on Friday June 04, 2010 @05:31PM (#1405729)
I just tossed together a quick branchless version, and was a little surprised to see that it was significantly slower than the straightforward pointer version on random data: about 1.5x the cycles. Presumably this gap would narrow if checking for char_to_remove was less predictable.

Here's the code I tested. I think it works, but I barely checked it. I'm sure there are better ways to calculate 'increment'. Not sure if these would be enough to close the gap, though.

  void condense_by_removing_branchless(char *z_terminated,  char char_to_remove) { 
    char *condensed = z_terminated;
    while (*z_terminated) {
        *condensed = *z_terminated;
        int diff = *condensed - char_to_remove;
        int increment = -((diff | -diff) >> ((sizeof(int) * 8) - 1)); 
        condensed += increment;
        z_terminated++;
    }
    *condensed = 0;
  }
Re:Interviewing programmers: coding test example explained by loup-vaillant on Thursday June 03, 2010 @05:04PM (#1402546)
Gasp! I thought it would be easy to just transfer the compare flag to a general purpose register. I suppose x86 doesn't have such an instruction?
Re:Interviewing programmers: coding test example explained by Daniel_Newby on Thursday June 03, 2010 @06:00PM (#1402752)
I lied! A quick test shows that GCC uses the x86 SETE instruction (set register on condition equal). Cool.

However, code like this:

    if (x == 14) {
        y = 10;
    } else {
        y = 20;
    }
uses branches, although I think x86-64 has conditional load instructions.
Re:Interviewing programmers: coding test example explained by pbiggar on Wednesday June 09, 2010 @03:00AM (#1416399)
I believe there is a bug here. If the first character is an instance of c, then it will still be copied. For example, calling the function as condense_by_removing ("loup-vailiant", 'l'), will return "loup-vaiiant", where it should return "oup-vaiiant".

(I've not tested this though, I may be wrong.)

Re:Interviewing programmers: coding test example explained by gorrepati on Friday June 04, 2010 @02:58PM (#1405250)
I get access violation with VC, when trying to see how this works.

Works with gcc though.

Can somebody throw some light on how this works?

Re:Interviewing programmers: coding test example explained by RiderOfGiraffes on Saturday June 05, 2010 @09:35AM (#1406848)

  void condense_by_removing(char* s, char c)
  {
    char* d = s;
    while (*d = *s++)
      d += *d != c;
  }
It always does the copy, then evaluates *d!=c. If what it just copied is equal to the forbidden character this evaluates as false, which in C is 0, and hence d is left unchanged.

If what it copied is not equal then this evaluates as true, which is 1, and hence d is incremented.