Note that with the current implementation, doing it “in one go” does a findall. It takes twice as long as going over the backtrackable predicate twice.
Here is how I tested it:
?- forall(between(1, 1 000 000, _), ( random(X), assertz(x(X)) )).
true.
?- time(( aggregate_all(sum(X), x(X), Sum), aggregate_all(count, x(_), N), Avg is Sum/N )).
% 4,000,008 inferences, 0.470 CPU in 0.472 seconds (100% CPU, 8509189 Lips)
Sum = 500477.74828369956,
N = 1000000,
Avg = 0.5004777482836995.
?- time(( aggregate_all(sum(X), x(X), Sum), aggregate_all(count, x(_), N), Avg is Sum/N )).
% 4,000,008 inferences, 0.431 CPU in 0.433 seconds (100% CPU, 9284337 Lips)
Sum = 500477.74828369956,
N = 1000000,
Avg = 0.5004777482836995.
?- time(( aggregate_all(sum(X), x(X), Sum), aggregate_all(count, x(_), N), Avg is Sum/N )).
% 4,000,008 inferences, 0.421 CPU in 0.422 seconds (100% CPU, 9506445 Lips)
Sum = 500477.74828369956,
N = 1000000,
Avg = 0.5004777482836995.
?- time( ( aggregate_all((sum(X), count), x(X), (Sum, N)), Avg is Sum/N )).
% 14,000,057 inferences, 0.962 CPU in 1.000 seconds (96% CPU, 14546829 Lips)
Sum = 500477.74828369956,
N = 1000000,
Avg = 0.5004777482836995.
?- time( ( aggregate_all((sum(X), count), x(X), (Sum, N)), Avg is Sum/N )).
% 14,000,057 inferences, 0.981 CPU in 1.051 seconds (93% CPU, 14265451 Lips)
Sum = 500477.74828369956,
N = 1000000,
Avg = 0.5004777482836995.
?- time( ( aggregate_all((sum(X), count), x(X), (Sum, N)), Avg is Sum/N )).
% 14,000,057 inferences, 0.979 CPU in 0.992 seconds (99% CPU, 14293196 Lips)
Sum = 500477.74828369956,
N = 1000000,
Avg = 0.5004777482836995.
It didn’t profile it (lazy again) but it is probably the list that findall creates that consumes memory and time.
EDIT: Now that I think about it, in the case of floats specifically there might also be an accumulating error if you do the average the obvious way. But I don’t really know enough to comment on that in detail.