Hi,

Having realised the benefits of functional programming, I’ve been quite annoyed 
by the rumour of how expensive function calls are in Pike. I decided to look 
into f_map and could see how much seemingly unnecessary work it does when it 
calls apply_svalue once for each entry in the array – it should be possible to 
reuse the pike_frame if it’s about to be thrown away after the function call 
(if it has other refs on the other hand, it can’t be reused – it’s probably 
used as a scope in some other frame).

I’ve pushed my optimised variant in marty/optimised_map – it seems to work 
quite well and provides a major speedup. In fact, it’s a bit faster than the 
corresponding foreach variant. I haven’t verified correctness in various corner 
cases, and some input on whether it’s correct to do the things 
init_frame_reuse_context does only once before multiple function calls would be 
nice too. The *_reuse_context stuff in interpret.c should be applicable 
wherever the same svalue is applied repeatedly with the same number of 
arguments (I haven’t looked for it outside of f_map really).

What do you all think? Good idea or did I overlook something?

Without optimisation:
map: 1.660797
array index: 1.335115
array append: 1.17917

With optimisation:
map: 0.877659
array index: 1.351158
array append: 1.189812

Test program:

int main()
{
  array base = allocate(10000000, 1);

  float gmap = gauge {
      array res = map (base, lambda(int i) { return i + 2; });
    };

  float garrayindex = gauge {
      array res = allocate(sizeof(base));
      foreach (base; int idx; int i) {
        res[i] = i + 2;
      }
    };

  float garrayappend = gauge {
      array res = ({});
      foreach (base, int i) {
        res += ({ i + 2 });
      }
    };

  werror ("map: %O\n", gmap);
  werror ("array index: %O\n", garrayindex);
  werror ("array append: %O\n", garrayappend);
}

/Marty

Reply via email to