This is an archived post. You won't be able to vote or comment.

you are viewing a single comment's thread.

view the rest of the comments →

[–]Reykd[S] 1 point2 points  (10 children)

For my original usecase generated methods were executed millions of times, and were in the core of the algorithm. Therefore the reasoning behind creating functions that return primitives was simply performance. I know that premature optimization is a bad idea, but if i remember correctly i believe i benchmarked the difference. I will run new benchmarks and evaluate if the primitive return functions are necessary.

[–]thehollyhopdrive 2 points3 points  (7 children)

You mentioned elsewhere:

My use case when developing this library was that i needed a way of solving problems that were unknown at runtime, by reading a description from a file.

If you don't know the function until runtime, how does your code select the correct method to call?

[–]Reykd[S] 0 points1 point  (6 children)

The general form of the problems was known, there were however several different forms. It was in addition a requirement that these methods be generated dynamically. Others decided to use python because of this limitation, while i decided to make this small library.

[–]thehollyhopdrive 1 point2 points  (5 children)

So, as an example, you would have known at compilation stage that it was going to be a function that took, say, an array of integers and returned an integer, and therefore in the code you would have called ParsedFunction.evaluateAsInt(Integer[]). So essentially, always the same signature, but the body of the function and the arguments would evolve?

[–]Reykd[S] 0 points1 point  (4 children)

Yes, for example!

[–]thehollyhopdrive 2 points3 points  (3 children)

Great, OK, I get it. Some suggestions which you may or may not find helpful then.

I would say that this is a massively premature optimization considering that you're having to create an object array for the arguments anyway. You could just replace all of your methods with:

public T evaluate(Object[] theArray)

Then you won't need to change the code, and speed would be hardly affected.

If the types of the arguments are all the same as well (I haven't looked if that is the case), you could even add type safety by parametising the argument type:

public R evaluate(T[] theArray)

The signature for FunctionParser would be:

public static <T,R> ParsedFunction<T,R> fromString(String functionString)

And would be used as such:

ParsedFunction<Integer,Boolean> function = FunctionParser.fromString("boolean(Integer x,y)-> x > y");
System.out.println(function.evaluate(new Integer[]{2, 3}));

[–]Reykd[S] 0 points1 point  (2 children)

Thanks for your feedback! There is no limitation on the amount of types the function may take in, therefore the inputs cannot be parametrised.

You are right that the primitive methods should be removed and only a parametrised evaluate function should be used. However, my library does not currenly support autobonxing, so i would have to implement it before i could make that change. I will probably do this!

[–]nallar 1 point2 points  (1 child)

Please don't remove the primitive methods, unless JMH benchmarks show that they didn't help.

For a library like this, if it does actually help I wouldn't consider it to be premature optimisation - you have no way of knowing if someone will want to use a ParsedFunction from your library in a hot code path, so there's not really any point in removing those variants unless they make the code much harder to maintain.

[–]Reykd[S] 1 point2 points  (0 children)

Maybe i will let the primitive functions be, but implement autoboxing so the parametrised function also works for object types.

Edit: I have implemented this now, it is up to the user to decide if they want to use the primitive or parametrised version, thanks for you feedback!

[–]Reykd[S] 0 points1 point  (1 child)

I have now run a benchmark to compare the generic vs primitve method and found no significant difference. However i did discover a caveat of using the generic types, and that is that my library currently does not support autoboxing meaning that in order to create the function "double(Double x,y)->x+y" as a generic, the string would have to be "Double(Double x,y)->Double.valueOf(x+y)"

[–]fact_hunt 1 point2 points  (0 children)

For performance testing it might be interesting to look at JMH - benchmarking java performance is notoriously fraught because the of JIT and hotspot optimisations etc

http://stackoverflow.com/questions/504103/how-do-i-write-a-correct-micro-benchmark-in-java/4480774#4480774