Implement a mechanism to generate the information required to determine the

Estimated hours taken: weeks
Branches: main

Implement a mechanism to generate the information required to determine the
algorithmic complexity of selected procedures.

The basis of the mechanism is a program transformation that wraps up the body
of each selected procedure in code that detects top-level (non-recursive)
calls, and for each top-level call, records the sizes of the input arguments
and information about the cost of the call. For now, the cost information
consists only of the number of cells and words allocated during the call,
but there is provision for later adding information from a real-time clock.

compiler/complexity.m:
	A new module containing the new transformation.

compiler/transform_hlds.m:
	Add complexity.m to the list of submodules.

compiler/mercury_compile.m:
	Invoke the new module.

compiler/notes/compiler_design.html:
	Mention the new module.

compiler/options.m:
	Add an option, --experimental-complexity. Its argument is a filename
	that specifies the list of procedures to transform.

	Add an option, --no-allow-inlining, to disallow all inlining.
	This is simpler to use than specifying several options to turn off
	each potential reason to inline procedures.

doc/user_guide.texi:
	Document the new options. The documentation present now is only a
	shell; it will be expanded later.

compiler/table_gen.m:
compiler/goal_util.m:
	Move the predicate for creating renamings from table_gen.m to
	goal_util.m, since complexity.m also needs it now. In the process,
	make it more general by allowing outputs to have more complex modes
	than simply `out'.

compiler/goal_util.m:
	Fix a bug exposed by the new transformation: when renaming goals
	(e.g. for quantification), rename the variables holding information
	about term sizes.

compiler/handle_options.m:
	Disable inlining if experimental complexity analysis is enabled.

compiler/compile_target_code.m:
	Pass the --experimental-complexity option on to the linker.

library/term_size_prof_builtin.m:
	Add the Mercury predicates that serve as interfaces to the primitives
	needed by the experimental complexity transformation.

runtime/mercury_term_size.[ch]:
	Add the implementations of the primitives needed by the experimental
	complexity transformation.

runtime/mercury_wrapper.[ch]:
	Add global variables holding counters of the numbers of words and cells
	allocated so far.

runtime/mercury_heap.h:
	Update these global variables when allocating memory.

runtime/mercury_complexity.h:
	New file that contains the definition of the data structures holding
	the data collected by the experimental complexity transformation.
	This is separate from mercury_term_size.h, because it needs to be
	#included in mercury_init.h, the header file of the mkinit-generated
	<program>_init.c files.

runtime/mercury_init.h:
runtime/mercury_imp.h:
	#include mercury_complexity.h.

util/mkinit.c:
	Define and initialize the data structures holding complexity
	information when given the -X option (mkinit doesn't have long
	options).

	Fix some deviations from our coding style.

scripts/parse_ml_options.sh-subr.in:
	Accept the --experiment-complexity option.

scripts/c2init.in:
	Pass the --experiment-complexity option on to mkinit.c.

tools/bootcheck:
	Preserve the files containing the results of complexity analysis,
	if they exist.

tools/makebatch:
	Allow the specification of EXTRA_MLFLAGS in the generated
	Mmake.stage.params files.
This commit is contained in:
Zoltan Somogyi
2005-02-15 05:22:40 +00:00
parent 2613f3edcc
commit 86ac840326
30 changed files with 2183 additions and 316 deletions

View File

@@ -14,9 +14,23 @@
*/
#include "mercury_imp.h"
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#ifdef MR_RECORD_TERM_SIZES
MR_ComplexityCounter MR_complexity_word_counter = 0;
MR_ComplexityCounter MR_complexity_cell_counter = 0;
MR_ComplexityCounter MR_complexity_tick_counter = 0;
static void MR_write_complexity_proc(MR_ComplexityProc *proc);
static void MR_complexity_output_args_desc(FILE *fp,
MR_ComplexityProc *proc);
MR_Unsigned
MR_term_size(MR_TypeInfo type_info, MR_Word term)
{
@@ -312,4 +326,378 @@ try_again:
MR_fatal_error("MR_term_size: unexpected fallthrough");
}
void
MR_write_complexity_procs(void)
{
int proc_num;
MR_ComplexityProc *proc;
for (proc_num = 0; proc_num < MR_num_complexity_procs; proc_num++) {
proc = &MR_complexity_procs[proc_num];
if (proc->MR_clp_num_profiled_args >= 0) {
MR_write_complexity_proc(proc);
}
}
}
#define MR_COMPLEXITY_ARGS_DIR "ComplexityArgs"
#define MR_COMPLEXITY_DATA_DIR "ComplexityData"
static MR_bool MR_have_created_complexity_dirs = MR_FALSE;
static MR_bool MR_have_printed_complexity_dirs_error = MR_FALSE;
static void
MR_write_complexity_proc(MR_ComplexityProc *proc)
{
char *full_proc_name;
int full_proc_name_len;
FILE *fp;
char *args_filename;
char *data_filename;
const char *data_filemode;
struct stat statbuf;
char *slash;
MR_ComplexityMetrics *metrics;
int num_profiled_args;
int *sizes;
int num_slots;
MR_ComplexityPastSlots *past_slots;
char *cmd_buf;
full_proc_name_len = strlen(proc->MR_clp_full_proc_name);
full_proc_name = MR_malloc(100 + full_proc_name_len);
strcpy(full_proc_name, proc->MR_clp_full_proc_name);
/*
** We can't have slash characters in the filenames we construct from
** full_proc_name.
*/
while ((slash = strchr(full_proc_name, '/')) != NULL) {
*slash = ':';
}
cmd_buf = MR_malloc(100 + 2 * full_proc_name_len);
/* will be big enough */
if (! MR_have_created_complexity_dirs) {
sprintf(cmd_buf, "mkdir -p %s %s",
MR_COMPLEXITY_ARGS_DIR, MR_COMPLEXITY_DATA_DIR);
if (system(cmd_buf) != 0) {
if (! MR_have_printed_complexity_dirs_error) {
fprintf(stderr, "%s: cannot create %s and %s: %s\n",
MR_progname, MR_COMPLEXITY_ARGS_DIR,
MR_COMPLEXITY_DATA_DIR, strerror(errno));
/* there is no point in aborting */
MR_have_printed_complexity_dirs_error = MR_TRUE;
return;
}
}
MR_have_created_complexity_dirs = MR_TRUE;
}
args_filename = MR_malloc(100 + full_proc_name_len);
/* will be big enough */
sprintf(args_filename, "%s/%s", MR_COMPLEXITY_ARGS_DIR, full_proc_name);
if (stat(args_filename, &statbuf) != 0) {
/* args_filename does not exist */
fp = fopen(args_filename, "w");
if (fp == NULL) {
fprintf(stderr, "%s: cannot open %s: %s\n",
MR_progname, args_filename, strerror(errno));
/* there is no point in aborting */
return;
}
MR_complexity_output_args_desc(fp, proc);
fclose(fp);
data_filemode = "w";
} else {
/* args_filename does exist */
char *tmp_filename;
tmp_filename = MR_malloc(100 + full_proc_name_len);
sprintf(tmp_filename, "%s/%s.tmp",
MR_COMPLEXITY_ARGS_DIR, full_proc_name);
fp = fopen(tmp_filename, "w");
if (fp == NULL) {
fprintf(stderr, "%s: cannot open %s: %s\n",
MR_progname, tmp_filename, strerror(errno));
/* there is no point in aborting */
return;
}
MR_complexity_output_args_desc(fp, proc);
fclose(fp);
sprintf(cmd_buf, "cmp -s %s %s", args_filename, tmp_filename);
if (system(cmd_buf) == 0) {
/* the files are identical */
(void) unlink(tmp_filename);
data_filemode = "a";
} else {
/* the files are different */
rename(tmp_filename, args_filename);
data_filemode = "w";
}
}
data_filename = MR_malloc(100 + full_proc_name_len);
sprintf(data_filename, "%s/%s", MR_COMPLEXITY_DATA_DIR, full_proc_name);
fp = fopen(data_filename, data_filemode);
if (fp == NULL) {
fprintf(stderr, "%s: cannot open %s: %s\n",
MR_progname, data_filename, strerror(errno));
/* there is no point in aborting */
return;
}
num_profiled_args = proc->MR_clp_num_profiled_args;
metrics = proc->MR_clp_metrics;
sizes = proc->MR_clp_sizes;
num_slots = proc->MR_clp_next_slot_num;
past_slots = proc->MR_clp_past_slots;
do {
int slot, arg;
for (slot = num_slots - 1; slot >= 0; slot--) {
fprintf(fp, "%d %d %d",
metrics[slot].MR_clpm_num_words,
metrics[slot].MR_clpm_num_cells,
metrics[slot].MR_clpm_num_ticks);
for (arg = 0; arg < num_profiled_args; arg++) {
fprintf(fp, " %d", sizes[slot * num_profiled_args + arg]);
}
fprintf(fp, "\n");
}
if (past_slots == NULL) {
break;
}
metrics = past_slots->MR_clpps_metrics;
sizes = past_slots->MR_clpps_sizes;
num_slots = MR_COMPLEXITY_SLOTS_PER_CHUNK;
past_slots = past_slots->MR_clpps_previous;
} while (MR_TRUE);
(void) fclose(fp);
}
static void
MR_complexity_output_args_desc(FILE *fp, MR_ComplexityProc *proc)
{
int arg;
int num_args;
MR_ComplexityArgInfo *arg_infos;
arg_infos = proc->MR_clp_arg_infos;
num_args = proc->MR_clp_num_args;
for (arg = 0; arg < num_args; arg++) {
if (arg_infos[arg].MR_clpai_maybe_name != NULL) {
fprintf(fp, "%s ", arg_infos[arg].MR_clpai_maybe_name);
} else {
fprintf(fp, "_ ");
}
switch (arg_infos[arg].MR_clpai_kind) {
case MR_COMPLEXITY_INPUT_VAR_SIZE:
fprintf(fp, "profiled_input\n");
break;
case MR_COMPLEXITY_INPUT_FIX_SIZE:
fprintf(fp, "unprofiled_input\n");
break;
case MR_COMPLEXITY_OUTPUT:
fprintf(fp, "output\n");
break;
default:
fprintf(fp, "unknown\n");
break;
}
}
}
void
MR_init_complexity_proc(int proc_num, const char *fullname,
int num_profiled_args, int num_args, MR_ComplexityArgInfo *arg_infos)
{
MR_ComplexityProc *proc;
if (MR_complexity_procs == NULL) {
fprintf(stderr, "%s: executable wasn't fully prepared "
"for complexity experiment\n", MR_progname);
exit(1);
}
proc = &MR_complexity_procs[proc_num];
if (! MR_streq(fullname, proc->MR_clp_full_proc_name)) {
fprintf(stderr, "%s: proc_num %d is %s: expected %s\n",
MR_progname, proc_num, proc->MR_clp_full_proc_name, fullname);
exit(1);
}
if (proc->MR_clp_num_profiled_args >= 0) {
fprintf(stderr, "%s: proc_num %d: duplicate initialization\n",
MR_progname, proc_num);
exit(1);
}
if (num_profiled_args < 0) {
fprintf(stderr, "%s: proc_num %d: bad num_profiled_args\n",
MR_progname, proc_num);
exit(1);
}
proc->MR_clp_num_profiled_args = num_profiled_args;
proc->MR_clp_num_args = num_args;
proc->MR_clp_arg_infos = arg_infos;
proc->MR_clp_metrics = MR_NEW_ARRAY(MR_ComplexityMetrics,
MR_COMPLEXITY_SLOTS_PER_CHUNK);
proc->MR_clp_sizes = MR_NEW_ARRAY(int,
MR_COMPLEXITY_SLOTS_PER_CHUNK * num_profiled_args);
}
void
MR_check_complexity_init(void)
{
int proc_num;
MR_bool printed_heading;
MR_ComplexityProc *proc;
printed_heading = MR_FALSE;
for (proc_num = 0; proc_num < MR_num_complexity_procs; proc_num++) {
proc = &MR_complexity_procs[proc_num];
if (proc->MR_clp_num_profiled_args < 0) {
if (! printed_heading) {
fprintf(stderr, "%s: the following procedures are "
"not available for complexity experiment:\n",
MR_progname);
printed_heading = MR_TRUE;
}
fprintf(stderr, "%s\n", proc->MR_clp_full_proc_name);
}
}
if (printed_heading) {
exit(1);
}
}
MR_ComplexityIsActive
MR_complexity_is_active_func(int num_procs, int proc_num, const char *name,
int num_profiled_inputs)
{
MR_ComplexityProc *proc;
if (num_procs != MR_num_complexity_procs || MR_complexity_procs == NULL) {
fprintf(stderr, "%s: executable wasn't fully prepared "
"for complexity experiment\n", MR_progname);
exit(1);
}
if (proc_num >= num_procs) {
fprintf(stderr, "%s: proc_num %d >= num_procs %d\n",
MR_progname, proc_num, num_procs);
exit(1);
}
proc = &MR_complexity_procs[proc_num];
if (! MR_streq(name, proc->MR_clp_full_proc_name)) {
fprintf(stderr, "%s: proc_num %d is %s: expected %s\n",
MR_progname, proc_num, proc->MR_clp_full_proc_name, name);
exit(1);
}
if (proc->MR_clp_num_profiled_args != num_profiled_inputs) {
fprintf(stderr, "%s: proc_num %d: bad num_profiled_inputs\n",
MR_progname, proc_num);
exit(1);
}
return proc->MR_clp_is_active;
}
int
MR_complexity_call_func(int procnum)
{
MR_ComplexityProc *proc;
MR_ComplexityMetrics *metrics;
int slot;
proc = &MR_complexity_procs[procnum];
slot = proc->MR_clp_next_slot_num;
if (slot < MR_COMPLEXITY_SLOTS_PER_CHUNK) {
proc->MR_clp_next_slot_num++;
} else {
MR_ComplexityPastSlots *past_slots;
past_slots = MR_NEW(MR_ComplexityPastSlots);
past_slots->MR_clpps_metrics = proc->MR_clp_metrics;
past_slots->MR_clpps_sizes = proc->MR_clp_sizes;
past_slots->MR_clpps_previous = proc->MR_clp_past_slots;
proc->MR_clp_past_slots = past_slots;
proc->MR_clp_metrics = MR_NEW_ARRAY(MR_ComplexityMetrics,
MR_COMPLEXITY_SLOTS_PER_CHUNK);
proc->MR_clp_sizes = MR_NEW_ARRAY(int,
MR_COMPLEXITY_SLOTS_PER_CHUNK * proc->MR_clp_num_profiled_args);
proc->MR_clp_next_slot_num = 1;
slot = 0;
}
metrics = &proc->MR_clp_metrics[slot];
metrics->MR_clpm_num_words -= MR_complexity_word_counter;
metrics->MR_clpm_num_cells -= MR_complexity_cell_counter;
metrics->MR_clpm_num_ticks -= MR_complexity_tick_counter;
proc->MR_clp_is_active = MR_COMPLEXITY_IS_ACTIVE;
return slot;
}
void
MR_complexity_fill_size_slot(MR_ComplexityProc *proc, int slot,
int num_profiled_args, int argnum, int size)
{
MR_ComplexityCounter *sizes;
sizes = proc->MR_clp_sizes;
sizes[(slot * proc->MR_clp_num_profiled_args) + argnum] = size;
}
void
MR_complexity_leave_func(int procnum, int slot)
{
MR_ComplexityProc *proc;
MR_ComplexityMetrics *metrics;
proc = &MR_complexity_procs[procnum];
metrics = &proc->MR_clp_metrics[slot];
metrics->MR_clpm_num_words += MR_complexity_word_counter;
metrics->MR_clpm_num_cells += MR_complexity_cell_counter;
metrics->MR_clpm_num_ticks += MR_complexity_tick_counter;
proc->MR_clp_is_active = MR_COMPLEXITY_IS_INACTIVE;
}
void
MR_complexity_redo_func(int procnum, int slot)
{
MR_ComplexityProc *proc;
MR_ComplexityMetrics *metrics;
proc = &MR_complexity_procs[procnum];
metrics = &proc->MR_clp_metrics[slot];
metrics->MR_clpm_num_words -= MR_complexity_word_counter;
metrics->MR_clpm_num_cells -= MR_complexity_cell_counter;
metrics->MR_clpm_num_ticks -= MR_complexity_tick_counter;
proc->MR_clp_is_active = MR_COMPLEXITY_IS_ACTIVE;
}
#endif /* MR_RECORD_TERM_SIZES */