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