I’ll do a PR shortly after a tidy up and some investigation into compiler flags for the crmath library. You should definitely review it because my expertise in this whole area is pretty limited.
Longer term we’ll see if the CORE-MATH performance issue can be sorted. If the standard C libraries catch up (I’m not holding my breath), this whole thing (including the status quo explicit outward rounding) can just be disabled using the O-ROUND_UP_DOWN flag.
That would be interesting; the source is below. I’ve commented out the interval width tests as that’s probably not of interest. But while doing this I saw something odd while running it on an Apple M3 laptop/SWIP 10.1.11. When initially loaded, the net times are very small, even negative. If a spy point is set and I skip/leap through the test, at some point it seems to “fix itself”, and that persists after turning debug off. An example below:
% first run - negative times for all functions !?
102 ?- f_statistics.
Net time for base is -0.026874999999999996 sec.
Net time for sin is -0.002577999999999983 sec.
Net time for cos is -0.0028840000000000116 sec.
Net time for tan is -0.0021999999999999936 sec.
Net time for exp is -0.004882000000000011 sec.
Net time for log is 0.002085000000000045 sec.
true.
% set spy point
103 ?- spy(f_timeit).
% New spy point on elemf_test:f_timeit/2
true.
% leap/skip through test. Notice times fixed on log test, but when this occurs varies
[debug] 104 ?- f_statistics.
* Call: (13) elemf_test:f_timeit(base, _56262) ? skip
* Exit: (13) elemf_test:f_timeit(base, 0.21563300000000007) ? leap
* Call: (13) elemf_test:f_timeit(base, _58302) ? skip
* Exit: (13) elemf_test:f_timeit(base, 0.21135199999999998) ? leap
Net time for base is -0.00428100000000009 sec.
* Call: (13) elemf_test:f_timeit(sin, _60304) ? skip
* Exit: (13) elemf_test:f_timeit(sin, 0.23986099999999988) ? leap
Net time for sin is 0.024227999999999805 sec.
* Call: (13) elemf_test:f_timeit(cos, _62306) ? skip
* Exit: (13) elemf_test:f_timeit(cos, 0.23684499999999997) ? leap
Net time for cos is 0.021211999999999898 sec.
* Call: (13) elemf_test:f_timeit(tan, _64308) ? skip
* Exit: (13) elemf_test:f_timeit(tan, 0.23745899999999986) ? leap
Net time for tan is 0.02182599999999979 sec.
* Call: (13) elemf_test:f_timeit(exp, _66310) ? skip
* Exit: (13) elemf_test:f_timeit(exp, 0.2342249999999999) ? leap
Net time for exp is 0.01859199999999983 sec.
* Call: (13) elemf_test:f_timeit(log, _68312) ? skip
* Exit: (13) elemf_test:f_timeit(log, 1.334591) ? leap
Net time for log is 1.1189580000000001 sec.
true.
[debug] 105 ?- nodebug.
true.
% after turning debug off all times look plausible
106 ?- f_statistics.
Net time for base is 0.030053999999999803 sec.
Net time for sin is 0.1210249999999995 sec.
Net time for cos is 0.11901799999999962 sec.
Net time for tan is 0.12725599999999915 sec.
Net time for exp is 0.09614999999999974 sec.
Net time for log is 0.15450799999999898 sec.
true.
I never observed this behaviour on my X86 Mac desktop. I profiled it to check port counts, but all looked normal. This is very repeatable on the M3 laptop, but hard to imagine what might be causing it.
Performance testing code
:- module(elemf_test,
[
f_timeit/2,
f_precision/2,
f_statistics/0
]).
:- set_prolog_flag(optimise, true).
f_statistics :-
f_timeit(base,Tbase),
member(F,[base,sin,cos,tan,exp,log]),
f_timeit(F,T), DT is T-Tbase, format("Net time for ~w is ~w sec.\n",[F,DT]),
fail.
/*f_statistics :-
member(F,[base,sin,cos,tan,exp,log]),
f_precision(F,Average), format("Average interval width for ~w is ~w ULP's.\n",[F,Average]),
fail.
*/
f_statistics.
f_time(base) :- between(1,1000000,_), random_input(X), _ is X, fail.
f_time(sin) :- between(1,1000000,_), random_input(X), _ is sin(X), fail.
f_time(cos) :- between(1,1000000,_), random_input(X), _ is cos(X), fail.
f_time(tan) :- between(1,1000000,_), random_input(X), _ is tan(X), fail.
f_time(exp) :- between(1,1000000,_), random_input(X), _ is exp(X), fail.
f_time(log) :- between(1,1000000,_), random_input(X), _ is log(abs(X)), fail.
f_time(_).
f_timeit(F,T) :-
statistics(cputime,T0),
f_time(F),
statistics(cputime,T1),
T is T1-T0.
f_precision(F,Average) :-
f_precision_(0,1000000,F,0.0,Average).
f_precision_(Count,Count,_F,Acc,Average) :- !,
Average is Acc/Count.
f_precision_(InCount,Count,F,Acc,Average) :-
f_bounds_(F,L,H),
delta_(L,H,0,D),
NxtAcc is Acc+D,
NxtCount is InCount+1,
f_precision_(NxtCount,Count,F,NxtAcc,Average).
f_bounds_(sin,L,H) :-
random_input(X), L is roundtoward(sin(X),to_negative), H is roundtoward(sin(X),to_positive).
f_bounds_(cos,L,H) :-
random_input(X), L is roundtoward(cos(X),to_negative), H is roundtoward(cos(X),to_positive).
f_bounds_(tan,L,H) :-
random_input(X), L is roundtoward(tan(X),to_negative), H is roundtoward(tan(X),to_positive).
f_bounds_(exp,L,H) :-
random_input(X), L is roundtoward(exp(X),to_negative), H is roundtoward(exp(X),to_positive).
f_bounds_(log,L,H) :-
random_input(X), L is roundtoward(log(abs(X)),to_negative), H is roundtoward(log(abs(X)),to_positive).
delta_(B,B,D,D) :- !.
delta_(L,H,N,D) :-
NxtL is nexttoward(L,1.0Inf),
NxtN is N+1,
delta_(NxtL,H,NxtN,D).
random_input(X) :- X is 2*pi*random_float - pi.