On Monday, 18 July 2016 11:13:08 UTC+3, Egon wrote:
>
>
>
> On Monday, 18 July 2016 10:30:14 UTC+3, Egon wrote:
>>
>> On Monday, 18 July 2016 03:11:29 UTC+3, ondrej...@gmail.com wrote:
>>>
>>> Cheers, I tried replicating my endeavours (
>>> https://play.golang.org/p/Qxoo2ASac6), sorry if it's still too verbose. 
>>> It's essentially rewriting the inbuilt ast.Node into a simpler nested 
>>> struct and then walking it.
>>>
>>> In testing the performance, I started adding algebraic expressions, 
>>> which make my walking more expensive, but don't change the 'native' 
>>> expression evaluation (I guess due to constant folding).
>>>
>>> As to your suggestion three - I do the variable lookup in the parsing 
>>> stage, but I still need to retain the pointer, not the value itself, 
>>> because I'm accessing an element of that given variable (time series), and 
>>> this element (time period) changes at runtime.
>>>
>>
>> https://play.golang.org/p/dd4hTpMKrp
>>
>> Of course you can additionally add constant folding and similar... 
>> Additionally instead of working on a single float at a time, make each 
>> variable an array of 8 floats, that are computed in parallel.
>>
>>
> Just realized that it's trivial to write basic constant folding: 
> https://play.golang.org/p/iqWX5_Mweb
>

Although it probably will be easier to maintain and extend as a separate 
pass: https://play.golang.org/p/xcz5mXoaOG
 

>
> This brings the result to:
>
> interpreter: 17.001ms
> native: 7.0004ms
>
> Which is approximately the best I would expect from an interpreter without 
> JIT (and not computing multiple time-points at a time).
>
> + Egon
>
> One performance gain I can think of is to implement some pruning through 
>>> the abovementioned constant folding and other optimisations, but I'd rather 
>>> leave that as the last resort. Another thing that comes to mind is that I 
>>> could return nested closures in some way - meaning that '1+3*x' would be, 
>>> in go-like pseudocode, add(func() { return one }, func mul(func() { return 
>>> three}, func() {return model[x]} )), where the one/tree are values passed 
>>> to the closure when parsing the equation; but that's just now off the top 
>>> of my head.
>>>
>>> I attached a pprof result in the header.
>>>
>>> Thanks again.
>>>
>>> On Friday, 8 July 2016 15:46:32 UTC+1, Egon wrote:
>>>>
>>>> On Friday, 8 July 2016 16:25:40 UTC+3, Ondrej wrote:
>>>>>
>>>>> Hi all,
>>>>> I have a model with variables, let's call them a, b, c, ..., z. These 
>>>>> are numerical values (time series loaded from a database) and I let the 
>>>>> user specify their relationships in a JSON, say 'z = 12; x = a + 2/3 + 
>>>>> 3*c; 
>>>>> y = log(12*f) + exp(g)' etc. The syntax is trivial - it's basically just 
>>>>> algebraic relationships + a few functions (log, log2, log10, exp, 
>>>>> trigonometrics, ...; all 1:1 mappings to their math package equivalents).
>>>>>
>>>>
>>>> *Tip: include a working piece of code that you want to make faster, it 
>>>> makes it easier for people to see the problems and common issues.*
>>>>
>>>>
>>>>> Now, I get these relationships in a JSON and I parse them using 
>>>>> go/parser. Then I walk the tree once and process it a bit - replacing 
>>>>> keywords by pointers to my variable stores, replacing all the log/exp/sin 
>>>>> with function pointers, leaving literals be literals etc. Each node is 
>>>>> then 
>>>>> a struct with a type and the actual contents (sadly a generic interface, 
>>>>> because the value can be almost anything). The prep stage is now over.
>>>>>
>>>>> When actually running the model, I loop through years and within each 
>>>>> year I solve each variable - I walk the tree and evaluate it where 
>>>>> needed. 
>>>>> The only non-trivial action is when I get to a model variable, I need to 
>>>>> do 
>>>>> a bit of lookup (it's a time series, so I need to look up the correct 
>>>>> time 
>>>>> period and other bits). Otherwise it's just literals, operators and 
>>>>> function calls, all of which is fairly straightforward.
>>>>>
>>>>> This is all well and good. One of the issues is that it's rather slow. 
>>>>> I thought it would be the recursive nature (and interface assertions), 
>>>>> but 
>>>>> converting all this into a shunting yard system didn't improve the 
>>>>> performance dramatically. I've profiled the thing and removed a few 
>>>>> hotspots, my question is not about profiling. I'm after a bit more 
>>>>> general 
>>>>> advice on how to handle these runtime evaluations and if there are better 
>>>>> ways of doing so. Essentially some sort of a JIT (but Go does not have 
>>>>> runtime assembly, right?), or maybe convert each expression into a 
>>>>> closure 
>>>>> or maybe a whole different algorithm or...?
>>>>>
>>>>
>>>> Reduce the amount of code and indirection that you need to do, few 
>>>> basic ideas:
>>>> 1. implement a VM https://play.golang.org/p/dlmZ2lGPY7
>>>> 2. operate on vectors of variables instead of single values 
>>>> https://play.golang.org/p/25MIjIXs0D
>>>> 3. try to do the lookup of all necessary variables before starting to 
>>>> compute with them; if possible
>>>>
>>>> Obviously pprof is your friend. (
>>>> https://blog.golang.org/profiling-go-programs)
>>>>
>>>> + Egon
>>>>
>>>

-- 
You received this message because you are subscribed to the Google Groups 
"golang-nuts" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to golang-nuts+unsubscr...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Reply via email to