mirror of
https://github.com/Mercury-Language/mercury.git
synced 2025-12-06 07:49:02 +00:00
deep_profiler/var_use_analysis.m:
Pass never-varying and slower-varying input arguments first.
Consistently pass goal lists before the position of the first goal
in the overall list.
browser/declarative_analyser.m:
browser/declarative_edt.m:
deep_profiler/analysis_utils.m:
deep_profiler/autopar_reports.m:
deep_profiler/autopar_types.m:
deep_profiler/program_representation_utils.m:
deep_profiler/read_profile.m:
deep_profiler/recursion_patterns.m:
deep_profiler/timeout.m:
deep_profiler/top_procs.m:
deep_profiler/util.m:
Minor improvements in programming style.
462 lines
17 KiB
Mathematica
462 lines
17 KiB
Mathematica
%---------------------------------------------------------------------------%
|
|
% vim: ft=mercury ts=4 sw=4 et
|
|
%---------------------------------------------------------------------------%
|
|
% Copyright (C) 2011-2012 The University of Melbourne.
|
|
% Copyright (C) 2014-2015, 2017, 2021-2022, 2025 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: autopar_reports.m
|
|
% Author: pbone.
|
|
%
|
|
% This module contains code for creating reports for debugging.
|
|
%
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- module mdprof_fb.automatic_parallelism.autopar_reports.
|
|
:- interface.
|
|
|
|
:- import_module mdbcomp.
|
|
:- import_module mdbcomp.feedback.
|
|
:- import_module mdbcomp.feedback.automatic_parallelism.
|
|
:- import_module mdbcomp.feedback.feedback_info.
|
|
:- import_module mdbcomp.program_representation.
|
|
|
|
:- import_module cord.
|
|
:- import_module io.
|
|
|
|
:- pred print_feedback_report(io.text_output_stream::in, feedback_info::in,
|
|
io::di, io::uo) is det.
|
|
|
|
:- pred create_candidate_parallel_conj_report(var_name_table::in,
|
|
candidate_par_conjunction(pard_goal)::in, cord(string)::out) is det.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- implementation.
|
|
|
|
:- import_module mdbcomp.goal_path.
|
|
:- import_module measurement_units.
|
|
:- import_module message.
|
|
:- import_module program_representation_utils.
|
|
|
|
:- import_module assoc_list.
|
|
:- import_module float.
|
|
:- import_module int.
|
|
:- import_module list.
|
|
:- import_module maybe.
|
|
:- import_module pair.
|
|
:- import_module require.
|
|
:- import_module set.
|
|
:- import_module std_util.
|
|
:- import_module string.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
print_feedback_report(OutputStream, FeedbackInfo, !IO) :-
|
|
get_all_feedback_info(FeedbackInfo, ProfiledProgramName,
|
|
MaybeCandidateParConjs),
|
|
% This code is structured like this to make it easy to add
|
|
% new feedback components.
|
|
some [!RevReports] (
|
|
!:RevReports = [],
|
|
(
|
|
MaybeCandidateParConjs = no
|
|
;
|
|
MaybeCandidateParConjs = yes(CandidateParConjs),
|
|
create_feedback_autopar_report(CandidateParConjs,
|
|
CandidateParConjsReport),
|
|
!:RevReports = [CandidateParConjsReport | !.RevReports]
|
|
),
|
|
list.reverse(!.RevReports, Reports)
|
|
),
|
|
(
|
|
Reports = [],
|
|
Report = "no report available.\n"
|
|
;
|
|
Reports = [_ | _],
|
|
string.append_list(Reports, Report)
|
|
),
|
|
io.format(OutputStream, "Feedback report for %s:\n\n%s",
|
|
[s(ProfiledProgramName), s(Report)], !IO).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- pred create_feedback_autopar_report(
|
|
feedback_info_candidate_parallel_conjunctions::in, string::out) is det.
|
|
|
|
create_feedback_autopar_report(CandidateParConjs, Report) :-
|
|
CandidateParConjs =
|
|
feedback_info_candidate_parallel_conjunctions(Parameters, ProcConjs),
|
|
NumProcConjs = length(ProcConjs),
|
|
foldl(count_conjunctions_in_procs, ProcConjs, 0, NumConjs),
|
|
Parameters = candidate_par_conjunctions_params(DesiredParallelism,
|
|
IntermoduleVarUse, SparkingCost, SparkingDelay, BarrierCost,
|
|
SignalCost, WaitCost, ContextWakeupDelay, CliqueThreshold,
|
|
CallSiteThreshold, SpeedupThreshold,
|
|
ParalleliseDepConjs, AlgForFindingBestPar),
|
|
AlgForFindingBestParStr =
|
|
alg_for_finding_best_par_to_string(AlgForFindingBestPar),
|
|
string.format(
|
|
" Candidate parallel conjunctions:\n" ++
|
|
" Desired parallelism: %f\n" ++
|
|
" Intermodule var use: %s\n" ++
|
|
" Sparking cost: %d\n" ++
|
|
" Sparking delay: %d\n" ++
|
|
" Barrier cost: %d\n" ++
|
|
" Future signal cost: %d\n" ++
|
|
" Future wait cost: %d\n" ++
|
|
" Context wakeup delay: %d\n" ++
|
|
" Clique threshold: %d\n" ++
|
|
" Call site threshold: %d\n" ++
|
|
" Speedup threshold: %f\n" ++
|
|
" Dependent conjs: %s\n" ++
|
|
" BestParAlgorithm: %s\n" ++
|
|
" # of par procs with conjs: %d\n" ++
|
|
" # of par conjunctions: %d\n" ++
|
|
" Parallel conjunctions:\n\n",
|
|
[f(DesiredParallelism),
|
|
s(string(IntermoduleVarUse)),
|
|
i(SparkingCost),
|
|
i(SparkingDelay),
|
|
i(BarrierCost),
|
|
i(SignalCost),
|
|
i(WaitCost),
|
|
i(ContextWakeupDelay),
|
|
i(CliqueThreshold),
|
|
i(CallSiteThreshold),
|
|
f(SpeedupThreshold),
|
|
s(ParalleliseDepConjsStr),
|
|
s(AlgForFindingBestParStr),
|
|
i(NumProcConjs),
|
|
i(NumConjs)],
|
|
ReportHeader),
|
|
(
|
|
ParalleliseDepConjs = parallelise_dep_conjs(SpeedupAlg),
|
|
(
|
|
SpeedupAlg = estimate_speedup_naively,
|
|
ParalleliseDepConjsStr = "yes, pretend they're independent"
|
|
;
|
|
SpeedupAlg = estimate_speedup_by_overlap,
|
|
ParalleliseDepConjsStr = "yes, use overlap calculation"
|
|
)
|
|
;
|
|
ParalleliseDepConjs = do_not_parallelise_dep_conjs,
|
|
ParalleliseDepConjsStr = "no"
|
|
),
|
|
list.map(create_candidate_parallel_conj_proc_report,
|
|
ProcConjs, ReportConjCords),
|
|
string.append_list([ReportHeader | cord_list_to_list(ReportConjCords)],
|
|
Report).
|
|
|
|
:- pred count_conjunctions_in_procs(
|
|
pair(T, candidate_par_conjunctions_proc)::in, int::in, int::out) is det.
|
|
|
|
count_conjunctions_in_procs(_ - Cands, !NumConjs) :-
|
|
Cands = candidate_par_conjunctions_proc(_VarNameTable, _Pushes, Conjs),
|
|
!:NumConjs = !.NumConjs + length(Conjs).
|
|
|
|
:- func alg_for_finding_best_par_to_string(alg_for_finding_best_par) = string.
|
|
|
|
alg_for_finding_best_par_to_string(Alg) = Str :-
|
|
(
|
|
Alg = affbp_greedy,
|
|
Str = "greedy"
|
|
;
|
|
Alg = affbp_complete_branches(N),
|
|
Str = string.format("complete-branches(%d)", [i(N)])
|
|
;
|
|
Alg = affbp_complete_size(N),
|
|
Str = string.format("complete-size(%d)", [i(N)])
|
|
;
|
|
Alg = affbp_complete,
|
|
Str = "complete"
|
|
).
|
|
|
|
:- pred create_candidate_parallel_conj_proc_report(
|
|
pair(string_proc_label, candidate_par_conjunctions_proc)::in,
|
|
cord(string)::out) is det.
|
|
|
|
create_candidate_parallel_conj_proc_report(Proc - CandidateParConjunctionProc,
|
|
Report) :-
|
|
CandidateParConjunctionProc = candidate_par_conjunctions_proc(VarNameTable,
|
|
PushGoals, CandidateParConjunctions),
|
|
print_proc_label_to_string(Proc, ProcString),
|
|
list.map(create_push_goal_report, PushGoals, PushGoalReports),
|
|
list.map(create_candidate_parallel_conj_report(VarNameTable),
|
|
CandidateParConjunctions, CandidateParConjunctionReports),
|
|
Header = string.format(" %s\n", [s(ProcString)]),
|
|
Report = cord.singleton(Header) ++
|
|
cord_list_to_cord(PushGoalReports) ++
|
|
cord.singleton("\n") ++
|
|
cord_list_to_cord(CandidateParConjunctionReports).
|
|
|
|
:- pred create_push_goal_report(push_goal::in, cord(string)::out) is det.
|
|
|
|
create_push_goal_report(PushGoal, Report) :-
|
|
PushGoal = push_goal(PushGoalPathStr, Lo, Hi, PushedGoalPathStrs),
|
|
string.format("\n PushGoal: %s, lo %d, hi %d\n",
|
|
[s(PushGoalPathStr), i(Lo), i(Hi)], HeadPushGoalStr),
|
|
FormatPushedGoals = (
|
|
func(PushedGoalPathStr) =
|
|
string.format(" %s\n", [s(PushedGoalPathStr)])
|
|
),
|
|
TailPushGoalStrs = list.map(FormatPushedGoals, PushedGoalPathStrs),
|
|
Report = cord.from_list([HeadPushGoalStr | TailPushGoalStrs]).
|
|
|
|
create_candidate_parallel_conj_report(VarNameTable, CandidateParConjunction,
|
|
Report) :-
|
|
CandidateParConjunction = candidate_par_conjunction(GoalPathString,
|
|
MaybePushGoal, FirstConjNum, IsDependent, GoalsBefore, GoalsBeforeCost,
|
|
Conjs, GoalsAfter, GoalsAfterCost, ParExecMetrics),
|
|
ParExecMetrics = parallel_exec_metrics(NumCalls, SeqTime, ParTime,
|
|
SparkCost, BarrierCost, SignalsCost, WaitsCost, FirstConjDeadTime,
|
|
FutureDeadTime),
|
|
ParOverheads = parallel_exec_metrics_get_overheads(ParExecMetrics),
|
|
(
|
|
IsDependent = conjuncts_are_independent,
|
|
DependenceString = "no"
|
|
;
|
|
IsDependent = conjuncts_are_dependent(Vars),
|
|
map(lookup_var_name(VarNameTable), Vars, VarNames),
|
|
VarsString = join_list(", ", to_sorted_list(VarNames)),
|
|
DependenceString = format("on %s", [s(VarsString)])
|
|
),
|
|
Speedup = parallel_exec_metrics_get_speedup(ParExecMetrics),
|
|
TimeSaving = parallel_exec_metrics_get_time_saving(ParExecMetrics),
|
|
TotalDeadTime = FirstConjDeadTime + FutureDeadTime,
|
|
|
|
string.format(" Path: %s\n", [s(GoalPathString)], Header1Str),
|
|
Header1 = cord.singleton(Header1Str),
|
|
|
|
(
|
|
MaybePushGoal = no,
|
|
Header2 = cord.empty
|
|
;
|
|
MaybePushGoal = yes(PushGoal),
|
|
PushGoal = push_goal(PushGoalPathStr, Lo, Hi, PushedGoalPathStrs),
|
|
string.format(" PushGoal: %s, lo %d, hi %d\n",
|
|
[s(PushGoalPathStr), i(Lo), i(Hi)], HeadPushGoalStr),
|
|
FormatPushedGoals = (
|
|
func(PushedGoalPathStr) =
|
|
string.format(" %s\n", [s(PushedGoalPathStr)])
|
|
),
|
|
TailPushGoalStrs = list.map(FormatPushedGoals, PushedGoalPathStrs),
|
|
Header2 = cord.from_list([HeadPushGoalStr | TailPushGoalStrs])
|
|
),
|
|
|
|
string.format(
|
|
" Dependent: %s\n" ++
|
|
" NumCalls: %s\n" ++
|
|
" SeqTime: %s\n" ++
|
|
" ParTime: %s\n" ++
|
|
" SparkCost: %s\n" ++
|
|
" BarrierCost: %s\n" ++
|
|
" SignalsCost: %s\n" ++
|
|
" WaitsCost: %s\n" ++
|
|
" ParOverheads total: %s\n" ++
|
|
" Speedup: %s\n" ++
|
|
" Time saving: %s\n" ++
|
|
" First conj dead time: %s\n" ++
|
|
" Future dead time: %s\n" ++
|
|
" Total dead time: %s\n\n",
|
|
[s(DependenceString),
|
|
s(commas(NumCalls)),
|
|
s(two_decimal_fraction(SeqTime)),
|
|
s(two_decimal_fraction(ParTime)),
|
|
s(two_decimal_fraction(SparkCost)),
|
|
s(two_decimal_fraction(BarrierCost)),
|
|
s(two_decimal_fraction(SignalsCost)),
|
|
s(two_decimal_fraction(WaitsCost)),
|
|
s(two_decimal_fraction(ParOverheads)),
|
|
s(four_decimal_fraction(Speedup)),
|
|
s(two_decimal_fraction(TimeSaving)),
|
|
s(two_decimal_fraction(FirstConjDeadTime)),
|
|
s(two_decimal_fraction(FutureDeadTime)),
|
|
s(two_decimal_fraction(TotalDeadTime))],
|
|
Header2Str),
|
|
Header3 = cord.singleton(Header2Str),
|
|
|
|
( if rev_goal_path_from_string(GoalPathString, RevGoalPathPrime) then
|
|
RevGoalPath = RevGoalPathPrime
|
|
else
|
|
unexpected($pred, "couldn't parse goal path")
|
|
),
|
|
some [!ConjNum] (
|
|
!:ConjNum = FirstConjNum,
|
|
format_sequential_conjunction(VarNameTable, 4, RevGoalPath,
|
|
GoalsBefore, GoalsBeforeCost, !.ConjNum, ReportGoalsBefore0),
|
|
ReportGoalsBefore = indent(3) ++ singleton("Goals before:\n") ++
|
|
ReportGoalsBefore0,
|
|
|
|
!:ConjNum = !.ConjNum + length(GoalsBefore),
|
|
format_parallel_conjunction(VarNameTable, 4, RevGoalPath,
|
|
!.ConjNum, Conjs, ReportParConj0),
|
|
ReportParConj = indent(3) ++ singleton("Parallel conjunction:\n") ++
|
|
ReportParConj0,
|
|
|
|
!:ConjNum = !.ConjNum + 1,
|
|
format_sequential_conjunction(VarNameTable, 4, RevGoalPath,
|
|
GoalsAfter, GoalsAfterCost, !.ConjNum, ReportGoalsAfter0),
|
|
ReportGoalsAfter = indent(3) ++ singleton("Goals after:\n") ++
|
|
ReportGoalsAfter0
|
|
),
|
|
Report = Header1 ++ Header2 ++ Header3 ++ ReportGoalsBefore ++ nl
|
|
++ ReportParConj ++ nl ++ ReportGoalsAfter ++ nl.
|
|
|
|
:- pred format_parallel_conjunction(var_name_table::in, int::in,
|
|
reverse_goal_path::in, int::in,
|
|
list(seq_conj(pard_goal))::in, cord(string)::out) is det.
|
|
|
|
format_parallel_conjunction(VarNameTable, Indent, RevGoalPath, ConjNum, Conjs,
|
|
!:Report) :-
|
|
IndentStr = indent(Indent),
|
|
!:Report = IndentStr ++ singleton("(\n"),
|
|
format_parallel_conjuncts(VarNameTable, Indent,
|
|
rgp_cons(RevGoalPath, step_conj(ConjNum)), 1, Conjs, !Report).
|
|
|
|
:- pred format_parallel_conjuncts(var_name_table::in, int::in,
|
|
reverse_goal_path::in, int::in, list(seq_conj(pard_goal))::in,
|
|
cord(string)::in, cord(string)::out) is det.
|
|
|
|
format_parallel_conjuncts(_VarNameTable, Indent, _RevGoalPath, _ConjNum0,
|
|
[], !Report) :-
|
|
IndentStr = indent(Indent),
|
|
!:Report = snoc(!.Report ++ IndentStr, ")\n").
|
|
format_parallel_conjuncts(VarNameTable, Indent, RevGoalPath, ConjNum0,
|
|
[Conj | Conjs], !Report) :-
|
|
Conj = seq_conj(Goals),
|
|
(
|
|
Goals = [],
|
|
unexpected($pred, "empty conjunct in parallel conjunction")
|
|
;
|
|
Goals = [Goal | GoalsTail],
|
|
RevInnerGoalPath = rgp_cons(RevGoalPath, step_conj(ConjNum0)),
|
|
(
|
|
GoalsTail = [],
|
|
% A singleton conjunction gets printed as a single goal.
|
|
print_goal_to_strings(print_goal_info(id, VarNameTable),
|
|
Indent + 1, RevInnerGoalPath, Goal, ConjReport)
|
|
;
|
|
GoalsTail = [_ | _],
|
|
Cost = list.foldl(
|
|
(func(GoalI, Acc) =
|
|
Acc + GoalI ^ goal_annotation ^ pga_cost_percall),
|
|
Goals, 0.0),
|
|
format_sequential_conjunction(VarNameTable,
|
|
Indent + 1, RevInnerGoalPath, Goals, Cost, 1, ConjReport)
|
|
)
|
|
),
|
|
!:Report = !.Report ++ ConjReport,
|
|
(
|
|
Conjs = []
|
|
;
|
|
Conjs = [_ | _],
|
|
!:Report = cord.snoc(!.Report ++ indent(Indent), "&\n")
|
|
),
|
|
ConjNum = ConjNum0 + 1,
|
|
format_parallel_conjuncts(VarNameTable, Indent, RevGoalPath, ConjNum,
|
|
Conjs, !Report).
|
|
|
|
:- pred format_sequential_conjunction(var_name_table::in, int::in,
|
|
reverse_goal_path::in, list(pard_goal)::in, float::in, int::in,
|
|
cord(string)::out) is det.
|
|
|
|
format_sequential_conjunction(VarNameTable, Indent, RevGoalPath, Goals, Cost,
|
|
FirstConjNum, !:Report) :-
|
|
!:Report = empty,
|
|
( if FirstConjNum = 1 then
|
|
!:Report = !.Report ++
|
|
indent(Indent) ++
|
|
singleton(format("%% conjunction: %s",
|
|
[s(rev_goal_path_to_string(RevGoalPath))])) ++
|
|
nl_indent(Indent) ++
|
|
singleton(format("%% Cost: %s",
|
|
[s(two_decimal_fraction(Cost))])) ++
|
|
nl ++ nl
|
|
else
|
|
true
|
|
),
|
|
format_sequential_conjuncts(VarNameTable, Indent, RevGoalPath, Goals,
|
|
FirstConjNum, _, !Report).
|
|
|
|
:- pred format_sequential_conjuncts(var_name_table::in, int::in,
|
|
reverse_goal_path::in, list(pard_goal)::in, int::in, int::out,
|
|
cord(string)::in, cord(string)::out) is det.
|
|
|
|
format_sequential_conjuncts(_, _, _, [], !ConjNum, !Report).
|
|
format_sequential_conjuncts(VarNameTable, Indent, RevGoalPath, [Conj | Conjs],
|
|
!ConjNum, !Report) :-
|
|
print_goal_to_strings(print_goal_info(id, VarNameTable), Indent,
|
|
rgp_cons(RevGoalPath, step_conj(!.ConjNum)), Conj, ConjReport),
|
|
!:Report = !.Report ++ ConjReport,
|
|
!:ConjNum = !.ConjNum + 1,
|
|
(
|
|
Conjs = []
|
|
;
|
|
Conjs = [_ | _],
|
|
!:Report = !.Report ++ indent(Indent) ++ singleton(",\n"),
|
|
format_sequential_conjuncts(VarNameTable, Indent, RevGoalPath, Conjs,
|
|
!ConjNum, !Report)
|
|
).
|
|
|
|
:- instance goal_annotation(pard_goal_annotation) where [
|
|
pred(print_goal_annotation_to_strings/3) is format_pard_goal_annotation
|
|
].
|
|
|
|
:- pred format_pard_goal_annotation(var_name_table::in,
|
|
pard_goal_annotation::in, cord(cord(string))::out) is det.
|
|
|
|
format_pard_goal_annotation(VarNameTable, GoalAnnotation, Report) :-
|
|
GoalAnnotation = pard_goal_annotation(CostPercall, CostAboveThreshold,
|
|
Productions, Consumptions),
|
|
(
|
|
CostAboveThreshold = cost_above_par_threshold,
|
|
CostAboveThresholdStr = "above threshold"
|
|
;
|
|
CostAboveThreshold = cost_not_above_par_threshold,
|
|
CostAboveThresholdStr = "not above threshold"
|
|
),
|
|
CostLine = singleton(format("cost: %s (%s)",
|
|
[s(two_decimal_fraction(CostPercall)), s(CostAboveThresholdStr)])),
|
|
format_var_use_report(VarNameTable, productions, Productions,
|
|
ProductionsReport),
|
|
format_var_use_report(VarNameTable, consumptions, Consumptions,
|
|
ConsumptionsReport),
|
|
Report = singleton(CostLine) ++ ProductionsReport ++ ConsumptionsReport.
|
|
|
|
:- func productions = string.
|
|
|
|
productions = "Productions".
|
|
|
|
:- func consumptions = string.
|
|
|
|
consumptions = "Consumptions".
|
|
|
|
:- pred format_var_use_report(var_name_table::in, string::in,
|
|
assoc_list(var_rep, float)::in, cord(cord(string))::out) is det.
|
|
|
|
format_var_use_report(VarNameTable, Label, List, Report) :-
|
|
(
|
|
List = [_ | _],
|
|
list.map(format_var_use_line(VarNameTable), List, Lines),
|
|
Report = singleton(singleton(Label ++ ":")) ++ cord.from_list(Lines)
|
|
;
|
|
List = [],
|
|
Report = empty
|
|
).
|
|
|
|
:- pred format_var_use_line(var_name_table::in, pair(var_rep, float)::in,
|
|
cord(string)::out) is det.
|
|
|
|
format_var_use_line(VarNameTable, Var - Use, singleton(String)) :-
|
|
string.format(" %s: %s", [s(VarName), s(two_decimal_fraction(Use))],
|
|
String),
|
|
lookup_var_name(VarNameTable, Var, VarName).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
:- end_module autopar_reports.
|
|
%---------------------------------------------------------------------------%
|