Ah, very interesting. Now that you mention it, I recall seeing something about 
this on the list but apparently I failed to recall it before implementing 
something on my own… ;) Your refactoring looks much more appealing though, so 
I’ll definitely vote for your branch. What’s left to be sorted out?

Another idea I had, which I don’t know whether it’s feasible or not, was to 
populate a frame cache in the parent frame when a function is likely to be 
called repeatedly (e.g. inside foreach) – but let’s sort out the basic stuff 
first...

(I have very little spare time because of kids, but every once in a while I’ll 
make a “frustration hack” ;) )

/Marty

> On 07 Apr 2016, at 10:49 , Arne Goedeke <e...@laramies.com> wrote:
> 
> I have looked into this exact problem a while ago. One thing I noticed,
> which might be relevant for your code (which I have not looked at too
> closely, yet), is that in case of tail recursion frames can be replaced.
> This might be something that your code needs to handle (for instance
> when calling a recursive function with map).
> 
> When I attempted to improve the performance of function calls (and in
> particular map/automap/filter/...) I started by refactoring the API for
> handling frames and doing function calls. I came up with something like
> frame_prepare(), frame_execute(), frame_return(), frame_pop(), etc.
> 
> I kept all the apply variants as fallbacks, which simply call the right
> combination of these new functions. I then implemented the map
> optimization similar to the one that you did. At some point I mentioned
> this work on the list (or maybe also just in person) but I never pushed
> it anywhere. I also never got around to finish this, but it is something
> I would still like to find some spare time for. I will push my branch
> now, maybe it is of interest.
> 
> Arne
> 
> On 04/06/16 00:12, Martin Karlgren wrote:
>> 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