34
35:- module(prolog_table_utils,
36 [ table_statistics/0, 37 table_statistics/1, 38 table_statistics_by_predicate/0,
39 table_statistics_by_predicate/1, 40 table_statistics/2, 41 table_statistics/3, 42 tstat/2, 43 tstat/3, 44 tdump/0, 45 tdump/1, 46 tdump/2, 47 tidg/0,
48 tidg/1, 49 summarize_idg/0,
50 summarize_idg/1 51 ]). 52:- autoload(library(lists), [member/2]). 53:- autoload(library(aggregate), [aggregate_all/3]). 54:- autoload(library(ansi_term), [ansi_format/3]). 55:- autoload(library(apply), [exclude/3, maplist/2]). 56:- autoload(library(dif), [dif/2]). 57:- autoload(library(error), [domain_error/2]). 58:- autoload(library(option), [option/3, option/2]). 59:- autoload(library(prolog_code), [pi_head/2]). 60:- autoload(library(solution_sequences), [limit/2, order_by/2]). 61:- autoload(library(varnumbers), [numbervars/1]). 62
63:- meta_predicate
64 tdump(:),
65 tdump(:, +),
66 idg(:),
67 summarize_idg(:),
68 table_statistics(:),
69 table_statistics(:, ?, -),
70 tstat(:, ?, ?). 71
77
78summary_table_width(55).
79
115
116table_statistics(Stat, Value) :-
117 table_statistics(_:_, Stat, Value).
118
119table_statistics(Variant, Stat, Value) :-
120 ( var(Stat)
121 -> table_statistics_(Variant, Stat, Value)
122 ; table_statistics_(Variant, Stat, Value)
123 -> true
124 ).
125
126table_statistics_(Variant, tables, NTables) :-
127 aggregate_all(count, table(Variant, _), NTables).
128table_statistics_(Variant, Stat, Total) :-
129 variant_trie_stat(Stat, _What),
130 \+ hidden_stat(Stat, Variant),
131 ( avg(Stat)
132 -> aggregate_all(sum(Ratio)+count,
133 variant_stat(Stat, Variant, Ratio),
134 Sum+Count),
135 Count > 0,
136 Total is Sum/Count
137 ; aggregate_all(sum(Count), variant_stat(Stat, Variant, Count), Total)
138 ).
139
140hidden_stat(variables, _).
141hidden_stat(lookup, _).
142hidden_stat(invalidated, Variant) :-
143 callable(Variant),
144 \+ predicate_property(Variant, tabled(incremental)).
145hidden_stat(reevaluated, Variant) :-
146 callable(Variant),
147 \+ predicate_property(Variant, tabled(incremental)).
148
149avg(space_ratio).
150avg(duplicate_ratio).
151
157
158table_statistics :-
159 ( ( '$tbl_global_variant_table'(Table),
160 call_table_properties(shared, Table)
161 ; '$tbl_local_variant_table'(Table),
162 call_table_properties(private, Table)
163 ),
164 fail
165 ; true
166 ),
167 ansi_format([bold], 'Summary of answer trie statistics:', []),
168 nl,
169 table_statistics_(_:_, [tables(false)]).
170
175
176table_statistics(Variant) :-
177 table_statistics_(Variant, []).
178
179table_statistics_(Variant, Options) :-
180 table_statistics_dict(Variant, Dict),
181 print_table_statistics(Dict, Options).
182
183table_statistics_dict(Variant, Dict) :-
184 findall(Stat-Value, table_statistics(Variant, Stat, Value), Pairs),
185 dict_create(Dict, table_stat, [variant-Variant|Pairs]).
186
187print_table_statistics(Dict, Options) :-
188 summary_table_width(DefWidth),
189 option(width(Width), Options, DefWidth),
190 ( option(tables(false), Options)
191 -> dif(Stat, tables)
192 ; true
193 ),
194 ( option(header(true), Options)
195 -> print_table_predicate_header(Dict.variant, [width(Width)|Options])
196 ; true
197 ),
198 ( variant_trie_stat0(Stat, What),
199 Value = Dict.get(Stat),
200 ( integer(Value)
201 -> format(' ~w ~`.t ~D~*|~n', [What, Value, Width])
202 ; format(' ~w ~`.t ~2f~*|~n', [What, Value, Width])
203 ),
204 fail
205 ; true
206 ).
207
(Pred, Options) :-
209 option(width(Width), Options),
210 Pred = M:Head,
211 tflags(Pred, Flags),
212 functor(Head, Name, Arity),
213 format('~n~`\u2015t~*|~n', [Width]),
214 format('~t~p~t~w~*|~n', [M:Name/Arity, Flags, Width]),
215 format('~`\u2015t~*|~n', [Width]).
216
217tflags(Pred, Flags) :-
218 findall(F, tflag(Pred, F), List),
219 atomic_list_concat(List, Flags).
220
221tflag(Pred, Flag) :-
222 predicate_property(Pred, tabled(How)),
223 tflag_name(How, Flag).
224
225tflag_name(variant, 'V').
226tflag_name(subsumptive, 'S').
227tflag_name(shared, 'G').
228tflag_name(incremental, 'I').
229
230
231variant_trie_stat0(tables, "Answer tables").
232variant_trie_stat0(Stat, What) :-
233 dif(Stat, tables),
234 variant_trie_stat(Stat, What).
235
236call_table_properties(Which, Trie) :-
237 ansi_format([bold], 'Statistics for ~w call trie:', [Which]),
238 nl,
239 summary_table_width(Width),
240 ( call_trie_property_name(P, Label, Value),
241 atrie_prop(Trie, P),
242 ( integer(Value)
243 -> format(' ~w ~`.t ~D~*|~n', [Label, Value, Width])
244 ; format(' ~w ~`.t ~1f~*|~n', [Label, Value, Width])
245 ),
246 fail
247 ; true
248 ).
249
250call_trie_property_name(value_count(N), 'Number of tables', N).
251call_trie_property_name(size(N), 'Memory for call trie', N).
252call_trie_property_name(space_ratio(N), 'Space efficiency', N).
253
269
270table_statistics_by_predicate :-
271 Pred = _:_,
272 summary_table_width(Width),
273 ( tabled_predicate_with_tables(Pred),
274 print_table_predicate_header(Pred, [width(Width)]),
275 table_statistics(Pred),
276 fail
277 ; true
278 ).
279
280table_statistics_by_predicate(Options) :-
281 option(order_by(OrderBy), Options, tables),
282 option(top(Top), Options, infinite),
283 option(module(M), Options, _),
284 Pred = (M:_),
285 findall(Dict,
286 ( tabled_predicate_with_tables(Pred),
287 table_statistics_dict(Pred, Dict)
288 ),
289 Dicts),
290 exclude(has_no_key(OrderBy), Dicts, Dicts1),
291 ( integer(Top), Top < 0
292 -> Order = @=<,
293 TopN is -Top
294 ; Order = @>=,
295 TopN is Top
296 ),
297 sort(OrderBy, Order, Dicts1, Sorted),
298 forall(limit(TopN, member(Dict, Sorted)),
299 print_table_statistics(Dict, [header(true)|Options])).
300
301has_no_key(Key, Dict) :-
302 \+ _ = Dict.get(Key).
303
304tabled_predicate_with_tables(Pred) :-
305 Pred = _:_,
306 predicate_property(Pred, tabled),
307 \+ predicate_property(Pred, imported_from(_)),
308 \+ \+ table(Pred, _).
309
339
349
350tstat(Stat, Top) :-
351 tstat(_:_, Stat, Top).
352tstat(Variant, Stat, Top) :-
353 variant_trie_stat(Stat, What),
354 top(Top, Count, Limit, Dir, Order),
355 findall(Variant-Count,
356 limit(Limit, order_by([Order], variant_stat(Stat, Variant, Count))),
357 Pairs),
358 write_variant_table('~w ~w count per variant'-[Dir, What], Pairs).
359
360top(Top, Var, 10, "Top", desc(Var)) :-
361 var(Top), !.
362top(Top, Var, Top, "Top", desc(Var)) :-
363 Top >= 0, !.
364top(Top, Var, Limit, "Bottom", asc(Var)) :-
365 Limit is -Top.
366
367variant_stat(Stat, V, Count) :-
368 variant_trie_stat(Stat, _, Count, Property),
369 table(V, T),
370 atrie_prop(T, Property).
371
372atrie_prop(T, size(Bytes)) :-
373 '$trie_property'(T, size(Bytes)).
374atrie_prop(T, compiled_size(Bytes)) :-
375 '$trie_property'(T, compiled_size(Bytes)).
376atrie_prop(T, value_count(Count)) :-
377 '$trie_property'(T, value_count(Count)).
378atrie_prop(T, space_ratio(Values/Nodes)) :-
379 '$trie_property'(T, value_count(Values)),
380 Values > 0,
381 '$trie_property'(T, node_count(Nodes)).
382atrie_prop(T, lookup_count(Count)) :-
383 '$trie_property'(T, lookup_count(Count)).
384atrie_prop(T, duplicate_ratio(Ratio)) :-
385 '$trie_property'(T, value_count(Values)),
386 Values > 0,
387 '$trie_property'(T, lookup_count(Lookup)),
388 Ratio is (Lookup - Values)/Values.
389atrie_prop(T, gen_call_count(Count)) :-
390 '$trie_property'(T, gen_call_count(Count)).
391atrie_prop(T, invalidated(Count)) :-
392 '$trie_property'(T, invalidated(Count)).
393atrie_prop(T, reevaluated(Count)) :-
394 '$trie_property'(T, reevaluated(Count)).
395atrie_prop(T, deadlock(Count)) :-
396 '$trie_property'(T, deadlock(Count)),
397 Count > 0.
398atrie_prop(T, wait(Count)) :-
399 '$trie_property'(T, wait(Count)),
400 Count > 0.
401atrie_prop(T, variables(Count)) :-
402 '$tbl_table_status'(T, _Status, _Wrapper, Skeleton),
403 functor(Skeleton, ret, Count).
404
405variant_trie_stat(Stat, What) :-
406 ( variant_trie_stat(Stat, What, _, _)
407 *-> true
408 ; domain_error(tstat_key, Stat)
409 ).
410
411variant_trie_stat(answers, "Number of answers",
412 Count, value_count(Count)).
413variant_trie_stat(duplicate_ratio,"Duplicate answer ratio",
414 Ratio, duplicate_ratio(Ratio)).
415variant_trie_stat(space_ratio, "Space efficiency",
416 Ratio, space_ratio(Ratio)).
417variant_trie_stat(complete_call, "Calls to completed tables",
418 Count, gen_call_count(Count)).
419variant_trie_stat(invalidated, "Times the tables were invalidated",
420 Count, invalidated(Count)).
421variant_trie_stat(reevaluated, "Times the tables were reevaluated",
422 Count, reevaluated(Count)).
423variant_trie_stat(space, "Memory usage for answer tables",
424 Bytes, size(Bytes)).
425variant_trie_stat(compiled_space, "Memory usage for compiled answer tables",
426 Bytes, compiled_size(Bytes)).
427variant_trie_stat(variables, "Number of variables in answer skeletons",
428 Count, variables(Count)).
429variant_trie_stat(wait, "Times table was waited for",
430 Count, wait(Count)).
431variant_trie_stat(deadlock, "Times table was involved in a deadlock",
432 Count, deadlock(Count)).
433
435
436write_variant_table(Format-Args, Pairs) :-
437 format(string(Title), Format, Args),
438 tty_size(_, Cols),
439 W is Cols - 8,
440 format('~`\u2015t~*|~n', [W]),
441 format('~t~w~t~*|~n', [Title, W]),
442 format('~`\u2015t~*|~n', [W]),
443 maplist(write_variant_stat(W), Pairs).
444
445write_variant_stat(W, V-Stat) :-
446 \+ \+ ( numbervars(V, 0, _, [singletons(true)]),
447 ( integer(Stat)
448 -> format('~p ~`.t ~D~*|~n', [V, Stat, W])
449 ; format('~p ~`.t ~2f~*|~n', [V, Stat, W])
450 )
451 ).
452
453table(M:Variant, Trie) :-
454 '$tbl_variant_table'(VariantTrie),
455 trie_gen(VariantTrie, M:Variant, Trie).
456
457
458 461
475
476tdump :-
477 tdump(_:_).
478tdump(M:Goal) :-
479 tdump(M:Goal, []).
480
481tdump(M:Goal, Options) :-
482 option(scope(Scope), Options, _),
483 option(limit(Limit), Options, 100),
484 ( table(Scope, M:Goal, Trie),
485 '$tbl_table_status'(Trie, Status, M:Variant, Skeleton),
486 M:'$table_mode'(Head0, Variant, Moded),
487 Head = M:Head0,
488 ( option(reset(true), Options)
489 -> true
490 ; \+ (Scope == global, Status == fresh)
491 ),
492 ansi_format(comment, 'Trie for variant ', []),
493 pflags(Variant, Flags),
494 format('~s ', [Flags]),
495 print_variant(Head),
496 Answer = Head,
497 '$tbl_trienode'(Reserved),
498 ( Moded == Reserved
499 -> findall(Answer-Delay,
500 '$tbl_answer'(Trie, Skeleton, Delay), Pairs),
501 ExtraProp = ''
502 ; findall(Answer-Delay,
503 '$tbl_answer'(Trie, Skeleton, Moded, Delay), Pairs),
504 ExtraProp = 'moded, '
505 ),
506 sort(1, @<, Pairs, Sorted),
507 length(Sorted, Count),
508 status_color(Status, Color),
509 ansi_format(comment, ' (~p,', [Scope]),
510 ansi_format(Color, ' ~p', [Status]),
511 ansi_format(comment, ', ~w~D answers)~n', [ExtraProp, Count]),
512 ( Count == 0
513 -> ansi_format(warning, ' (empty)~n', [])
514 ; forall(limit(Limit, member(Ans, Sorted)),
515 dump_answer(M, Ans))
516 ),
517 fail
518 ; true
519 ).
520
521status_color(invalid, warning) :- !.
522status_color(_, comment).
523
524
525table(local, Variant, Trie) :-
526 '$tbl_local_variant_table'(VariantTrie),
527 trie_gen(VariantTrie, Variant0, Trie),
528 subsumes_term(Variant, Variant0),
529 Variant = Variant0.
530table(global, Variant, Trie) :-
531 '$tbl_global_variant_table'(VariantTrie),
532 trie_gen(VariantTrie, Variant0, Trie),
533 subsumes_term(Variant, Variant0),
534 Variant = Variant0.
535
536print_variant(Head) :-
537 term_attvars(Head, []),
538 !,
539 \+ \+ ( numbervars(Head, 0, _),
540 ansi_format(code, '~p', [Head])
541 ).
542print_variant(Head) :-
543 copy_term(Head, Copy, Constraints),
544 numbervars(Copy+Constraints, 0, _),
545 format('~p', [Copy]),
546 forall(member(C, Constraints),
547 ansi_format(fg(blue), ', ~p', [C])).
548
549dump_answer(M, Answer0-true) :-
550 !,
551 unqualify(Answer0, M, Answer),
552 \+ \+ print_answer(Answer).
553dump_answer(M, Answer0-Condition) :-
554 unqualify(Answer0, M, Answer),
555 unqualify(Condition, M, SimpleCondition),
556 \+ \+ ( numbervars(Answer+SimpleCondition, 0, _),
557 format(' ~p', [Answer]),
558 ansi_format(bold, ' :- ', []),
559 ansi_format(fg(cyan), '~p~n', [SimpleCondition])
560 ).
561
562print_answer(Answer) :-
563 term_attvars(Answer, []),
564 !,
565 numbervars(Answer, 0, _),
566 format(' ~p~n', [Answer]).
567print_answer(Answer) :-
568 copy_term(Answer, Copy, Constraints),
569 numbervars(Copy+Constraints, 0, _),
570 format(' ~p', [Copy]),
571 forall(member(C, Constraints),
572 ansi_format(fg(blue), ', ~p', [C])),
573 nl.
574
575unqualify(Var, _M, Var) :-
576 var(Var),
577 !.
578unqualify((A0,B0), M, (A,B)) :-
579 !,
580 unqualify(A0, M, A),
581 unqualify(B0, M, B).
582unqualify((A0;B0), M, (A;B)) :-
583 !,
584 unqualify(A0, M, A),
585 unqualify(B0, M, B).
586unqualify(tnot(A0), M, tnot(A)) :-
587 !,
588 unqualify(A0, M, A).
589unqualify((M1:Variant)/ModeArgs, M, Goal) :-
590 !,
591 M1:'$table_mode'(G0, Variant, ModeArgs),
592 unqualify(M1:G0, M, Goal).
593unqualify(M:G, M, G) :-
594 !.
595unqualify(G, _, G).
596
602
603tidg :-
604 ansi_format(comment,
605 '% Node1 [falsecount] (affects -->) Node1 [falsecount]~n', []),
606 forall(idg(t(_:From,FFC,FAC), affected, t(_:To,TFC,TAC)),
607 \+ \+ ( numbervars(From),
608 numbervars(To),
609 print_edge(From, FFC,FAC, To, TFC,TAC)
610 )).
611
612tidg(M:Node) :-
613 ansi_format(comment,
614 '% Node1 [falsecount] (affects -->) Node1 [falsecount]~n', []),
615 ansi_format([bold], 'Affected nodes~n', []),
616 forall(idg(t(M:Node,FFC,FAC), affected, t(_:To,TFC,TAC)),
617 \+ \+ ( numbervars(Node),
618 numbervars(To),
619 print_edge(Node, FFC,FAC, To, TFC,TAC)
620 )),
621 ansi_format([bold], 'Dependent nodes~n', []),
622 forall(idg(t(_:From,FFC,FAC), affected, t(M:Node,TFC,TAC)),
623 \+ \+ ( numbervars(From),
624 numbervars(Node),
625 print_edge(From, FFC,FAC, Node, TFC,TAC)
626 )).
627
628
629print_edge(From, FFC,FAC, To, TFC,TAC) :-
630 format(' '),
631 print_node(From, FFC,FAC),
632 format(' --> '),
633 print_node(To, TFC,TAC),
634 nl.
635
636print_node(Variant, Falsecount, AnswerCount) :-
637 pflags(Variant, Flags),
638 format('~s ', [Flags]),
639 ansi_format(code, '~p', [Variant]),
640 format(' '),
641 ( Falsecount == 0
642 -> ansi_format(comment, '[0]', [])
643 ; ansi_format([bg(red),fg(white)], '[~w]', [Falsecount])
644 ),
645 print_answer_count(AnswerCount).
646
647print_answer_count(answers(Count)) =>
648 format(' (~Da)', [Count]).
649print_answer_count(clauses(Count)) =>
650 format(' (~Dc)', [Count]).
651
652pflags(Variant, Flags) :-
653 findall(F, flag(Variant, F), Flags).
654
655flag(Variant, Flag) :-
656 ( pflag(Variant, dynamic, 'D', Flag)
657 ; pflag(Variant, incremental, 'I', Flag)
658 ; pflag(Variant, monotonic, 'M', Flag)
659 ).
660
661pflag(Variant, Property, Char, Flag) :-
662 ( predicate_property(Variant, Property)
663 -> Flag = Char
664 ; Flag = ' '
665 ).
666
667idg(t(FM:From,FFC,FAC), Dir, t(TM:To,TFC,TAC)) :-
668 '$tbl_variant_table'(VTrie),
669 trie_gen(VTrie, FM:FVariant, ATrie),
670 ( FM:'$table_mode'(From1, FVariant, _FModed)
671 -> true
672 ; From1 = FVariant 673 ),
674 subsumes_term(From, From1),
675 From = From1,
676 fc(ATrie, From, FFC,FAC),
677 '$idg_edge'(ATrie, Dir, DepTrie),
678 '$tbl_table_status'(DepTrie, _Status, TM:TVariant, _Return),
679 TM:'$table_mode'(To1, TVariant, _TModed),
680 subsumes_term(To, To1),
681 To = To1,
682 fc(DepTrie, To, TFC,TAC).
683
684fc(ATrie, Variant, FC, AC) :-
685 ( predicate_property(Variant, tabled)
686 -> trie_property(ATrie, value_count(C)),
687 AC = answers(C)
688 ; aggregate_all(count, clause(Variant,_), C),
689 AC = clauses(C)
690 ),
691 ( '$idg_falsecount'(ATrie, FC0)
692 -> ( '$idg_forced'(ATrie)
693 -> FC = FC0/'F'
694 ; FC = FC0
695 )
696 ; FC = 0
697 ).
698
703
704:- module_transparent
705 summarize_idg/0. 706
707summarize_idg :-
708 context_module(M),
709 summarize_idg(infinite, M).
710
711summarize_idg(M:Top) :-
712 summarize_idg(Top, M).
713
714summarize_idg(Top, M) :-
715 tty_width(Width),
716 header('Interior Nodes (Tabled Subgoals)', Width),
717 format('Predicate~t #idg nodes~*|~n', [Width]),
718 format('~`\u2015t~*|~n', [Width]),
719 forall(limit(Top,
720 order_by([desc(Count),asc(PI)],
721 interior_nodes(_:_, M, PI, Count))),
722 format('~q ~`.t ~D~*|~n', [PI, Count, Width])),
723 nl,
724 ColR is Width - 10,
725 header('Leaf Nodes (Calls to Dynamic Predicates)', Width),
726 format('Predicate~t #idg nodes~*|~t#facts~*|~n', [ColR, Width]),
727 format('~`\u2015t~*|~n', [Width]),
728 forall(limit(Top,
729 order_by([desc(Count),desc(Facts),asc(PI)],
730 leaf_nodes(_:_, M, PI, Count, Facts))),
731 format('~q ~`.t ~D~*| ~`.t ~D~*|~n',
732 [PI, Count, ColR, Facts, Width])).
733
734interior_nodes(Variant, M, PI, Count) :-
735 predicate_property(Variant, tabled(incremental)),
736 \+ predicate_property(Variant, imported_from(_)),
737 idg_node_count(Variant, Count),
738 pi_head(QPI, Variant),
739 unqualify_pi(QPI, M, PI).
740
741leaf_nodes(Variant, M, PI, Count, Facts) :-
742 predicate_property(Variant, dynamic),
743 predicate_property(Variant, incremental),
744 \+ predicate_property(Variant, imported_from(_)),
745 predicate_property(Variant, number_of_clauses(Facts)),
746 idg_node_count(Variant, Count),
747 pi_head(QPI, Variant),
748 unqualify_pi(QPI, M, PI).
749
750
751idg_node_count(Variant, Count) :-
752 aggregate_all(count,
753 ( '$tbl_variant_table'(VTrie),
754 trie_gen(VTrie, Variant, _ATrie)
755 ),
756 Count).
757
758unqualify_pi(M:PI, M, PI) :- !.
759unqualify_pi(PI, _, PI).
760
761tty_width(W) :-
762 catch(tty_size(_, TtyW), _, fail),
763 !,
764 W is max(60, TtyW - 8).
765tty_width(60).
766
(Title, Width) :-
768 format('~N~`\u2015t~*|~n', [Width]),
769 ansi_format([bold], '~t~w~t~*|', [Title,Width]),
770 nl