Files
mercury/compiler/pd_cost.m
Julien Fischer dc2c470787 Update and fix copyright notices.
*/Mercury.options:
compiler/*.m:
extras/references/c_references.h:
extras/references/samples/max_test.m:
scripts/mprof_merge_runs:
     As above.
2024-12-30 20:17:22 +11:00

198 lines
6.4 KiB
Mathematica

%---------------------------------------------------------------------------%
% vim: ft=mercury ts=4 sw=4 et
%---------------------------------------------------------------------------%
% Copyright (C) 1998-2008, 2010-2012 The University of Melbourne.
% Copyright (C) 2015-2018, 2021 The Mercury team.
% This file may only be copied under the terms of the GNU General
% Public License - see the file COPYING in the Mercury distribution.
%---------------------------------------------------------------------------%
%
% File: pd_cost.m.
% Main author: stayl.
%
% goal_cost gives a very rough guess as to how much work a given goal
% will cause at runtime. This only counts the local cost not including
% the time taken by called predicates.
%
%---------------------------------------------------------------------------%
:- module transform_hlds.pd_cost.
:- interface.
:- import_module hlds.
:- import_module hlds.hlds_goal.
%---------------------------------------------------------------------------%
:- pred goal_cost(hlds_goal::in, int::out) is det.
:- func cost_of_reg_assign = int.
:- func cost_of_heap_assign = int.
:- func cost_of_simple_test = int.
:- func cost_of_heap_incr = int.
:- func cost_of_stack_flush = int.
:- func cost_of_builtin_call = int.
:- func cost_of_call = int.
:- func cost_of_higher_order_call = int.
:- func cost_of_eliminate_switch = int.
:- func cost_of_fold = int.
:- func cost_of_recursive_fold = int.
%---------------------------------------------------------------------------%
%---------------------------------------------------------------------------%
:- implementation.
:- import_module parse_tree.
:- import_module parse_tree.prog_data.
:- import_module parse_tree.prog_data_foreign.
:- import_module parse_tree.set_of_var.
:- import_module int.
:- import_module list.
:- import_module require.
%---------------------------------------------------------------------------%
goal_cost(Goal, Cost) :-
Goal = hlds_goal(GoalExpr, GoalInfo),
goal_expr_cost(GoalExpr, GoalInfo, Cost).
:- pred goal_expr_cost(hlds_goal_expr::in, hlds_goal_info::in, int::out)
is det.
goal_expr_cost(GoalExpr, GoalInfo, Cost) :-
(
GoalExpr = conj(_, Goals),
goal_costs(Goals, 0, Cost)
;
GoalExpr = disj(Goals),
goal_costs(Goals, 0, Cost0),
Cost = Cost0 + cost_of_stack_flush
;
GoalExpr = switch(_, _, Cases),
cases_cost(Cases, cost_of_simple_test, Cost)
;
GoalExpr = if_then_else(_, Cond, Then, Else),
goal_cost(Cond, Cost1),
goal_cost(Then, Cost2),
goal_cost(Else, Cost3),
Cost = Cost1 + Cost2 + Cost3
;
GoalExpr = plain_call(_, _, Args, BuiltinState, _, _),
(
BuiltinState = inline_builtin,
Cost = cost_of_builtin_call
;
BuiltinState = not_builtin,
list.length(Args, Arity),
InputArgs = Arity // 2, % rough
Cost = cost_of_stack_flush + cost_of_call
+ cost_of_reg_assign * InputArgs
)
;
GoalExpr = negation(Goal),
goal_cost(Goal, Cost)
;
GoalExpr = scope(Reason, Goal),
( if Reason = from_ground_term(_, from_ground_term_construct) then
Cost = cost_of_reg_assign
else
goal_cost(Goal, Cost)
)
;
GoalExpr = generic_call(_, Args, _, _, _),
list.length(Args, Arity),
Cost0 = cost_of_reg_assign * Arity // 2,
Cost = Cost0 + cost_of_stack_flush + cost_of_higher_order_call
;
GoalExpr = unify(_, _, _, Unification, _),
NonLocals = goal_info_get_nonlocals(GoalInfo),
unify_cost(NonLocals, Unification, Cost)
;
GoalExpr = call_foreign_proc(Attributes, _, _, Args, _, _, _),
( if get_may_call_mercury(Attributes) = proc_will_not_call_mercury then
Cost1 = 0
else
Cost1 = cost_of_stack_flush
),
% XXX This is *too* rough.
list.length(Args, Arity),
InputArgs = Arity // 2, % rough
Cost = Cost1 + cost_of_call + cost_of_reg_assign * InputArgs
;
GoalExpr = shorthand(_),
% these should have been expanded out by now
unexpected($pred, "shorthand")
).
:- pred unify_cost(set_of_progvar::in, unification::in, int::out) is det.
unify_cost(NonLocals, Unification, Cost) :-
(
Unification = assign(_, _),
Cost = 0
;
Unification = complicated_unify(_, _, _),
Cost = cost_of_stack_flush
;
Unification = simple_test(_, _),
Cost = cost_of_simple_test
;
Unification = construct(Var, _, Args, _, _, _, _),
( if set_of_var.member(NonLocals, Var) then
list.length(Args, Arity),
Cost = cost_of_heap_incr + Arity * cost_of_heap_assign
else
Cost = 0
)
;
Unification = deconstruct(_, _, Args, _, CanFail, _),
(
CanFail = can_fail,
Cost0 = cost_of_simple_test
;
CanFail = cannot_fail,
Cost0 = 0
),
list.filter(set_of_var.contains(NonLocals), Args, NonLocalArgs),
list.length(NonLocalArgs, NumAssigns),
Cost = Cost0 + cost_of_heap_incr + NumAssigns * cost_of_heap_assign
).
:- pred goal_costs(list(hlds_goal)::in, int::in, int::out) is det.
goal_costs([], Cost, Cost).
goal_costs([Goal | Goals], Cost0, Cost) :-
goal_cost(Goal, Cost1),
Cost2 = Cost0 + Cost1,
goal_costs(Goals, Cost2, Cost).
:- pred cases_cost(list(case)::in, int::in, int::out) is det.
cases_cost([], Cost, Cost).
cases_cost([case(_, _, Goal) | Cases], Cost0, Cost) :-
goal_cost(Goal, Cost1),
Cost2 = Cost0 + Cost1,
cases_cost(Cases, Cost2, Cost).
%---------------------------------------------------------------------------%
cost_of_reg_assign = 1.
cost_of_heap_assign = 2.
cost_of_simple_test = 3.
cost_of_heap_incr = 3. % Depends on GC, want to penalise mem usage.
cost_of_stack_flush = 5.
cost_of_builtin_call = 3. % very rough - int:'+' == float:'/' !.
cost_of_call = 3.
cost_of_higher_order_call = 8.
cost_of_eliminate_switch = 5.
cost_of_fold = 15. % reward folding
cost_of_recursive_fold = 25. % reward recursive folding more
%---------------------------------------------------------------------------%
:- end_module transform_hlds.pd_cost.
%---------------------------------------------------------------------------%