This is the mail archive of the gcc-patches@gcc.gnu.org mailing list for the GCC project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]

[PATCH] tests and framework for profile-directed optimizations


This patch adds a framework for testing the functionality and
optionally, performance improvement, of profile-directed optimizations,
plus a small number of C tests that use it for profile-directed block
ordering.  Tests that use this framework will detect breakage in the
correctness of these optimizations.  Where the performance checks are
supported, tests can report cases where the optimizations cause slower
code to be generated.

I'm fairly new to TCL, expect, DejaGnu, and the organization of the GCC
test suite, so I would greatly appreciate feedback about how to do
things better.  I'm not sure if these work correctly for remote testing.
I'd like to set them up to handle XFAIL; is there documentation about a
preferred way of handling that for new tests?  The new tests here are
for C and are in gcc.misc-tests; is there a better place to put them?
I've got similar tests for C++ and Fortran; where should they go?

These first tests are very minimal, but they show how the framework can
be used for regression tests, or anything else that developers of these
optimizations think should be part of normal testing.

A list of sets of optimization and/or code-generation options are used
along with each pair of profile and feedback-directed optimization
options.  The test is compiled and run with the profile option and then
again with the feedback-directed optimization, using the profile data
just generated.

A test can also be set up to check for performance improvement when a
profile-directed optimization is used.  Such a test writes a repeatable
time value to a file.  The test script compiles such a test again with
the same optimization and code-generation options but without the
profile-directed optimization, and then runs it and compares the time
value with that of the first run.  If the profile-directed optimization
makes performance worse then the test fails.

I looked at the timing scheme in the GCC benchmark suite, but I wanted
something that could be used to time small bits of code rather than the
larger codes that are usually used in benchmarks, and that won't require
much execution time since I want it to be used in the normal testsuite.
I time using clock cycles, assuming that if a test is run enough times
and is small enough then even on a busy system it will get an
uninterrupted run often enough.  Currently it supports only i386 and
ia64, but adding support for other architectures is very simple.  If an
architecture isn't supported then the test quits timing after a single
run and the run with "normal" optimizations is skipped; this allows the
test to check functionality on all platforms, and to check performance
improvements on platforms that support it.

2001-08-31  Janis Johnson  <janis187@us.ibm.com>

	* lib/profopt.exp: New, to support profile-directed optimizations.
	* gcc.misc-tests/bprob.exp: New, to support branch profiling in C.
	* gcc.misc-tests/bprob-1.c: New test.
	* gcc.misc-tests/bprob-2.c: New test.

The following performance tests are included below but aren't ready to
commit; there here as an example of how the performance testing can be
used.

	* gcc.misc-tests/bprob-3.c: New test.
	* gcc.misc-tests/bprob-4.c: New test.
	* gcc.misc-tests/perftime.h: New, for timing branch profiling tests.


--- lib/profopt.exp.orig	Fri Aug 31 10:16:35 2001
+++ lib/profopt.exp	Fri Aug 31 09:42:56 2001
@@ -0,0 +1,284 @@
+#   Copyright (C) 2001 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+# 
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+# 
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  
+#
+# This script was written by Janis Johnson <janis187@us.ibm.com>.
+
+# Test the functionality and optionally, performance improvement, of
+# programs compiled with profile-directed optimizations.  Compile and
+# run a test with profile options, compile it with options using the
+# profile feedback, and then run the test again.  Optionally compile
+# and run a third time without the profile-directed optimization and
+# compare timing results of the program with normal optimization and
+# with the profile-directed optimization. Each test is run using
+# multiple sets of optimization and/or code generation options in
+# addition to the profiling and feedback options.
+
+# If perf_ext is defined and the performance value for the
+# profile-directed test run is non-zero then the performance check will
+# be done.
+
+global PROFOPT_OPTIONS perf_delta
+
+# The including .exp file must define these.
+global tool profile_option feedback_option prof_ext
+if ![info exists tool] {
+    error "Tools is not specified."
+}
+if ![info exists profile_option] {
+    error "No profile option specified for first compile."
+}
+if ![info exists feedback_option] {
+    error "No feedback option specified for second compile."
+}
+if ![info exists prof_ext] {
+    error "No profile data file extension specified."
+}
+
+# The maximum perforance degradation can be defined in the including file.
+if ![info exists perf_delta] {
+    set perf_delta 4
+}
+
+# The default option list can be overridden by
+# PROFOPT_OPTIONS="{ { list1 } ... { list2 } }"
+
+if ![info exists PROFOPT_OPTIONS] {
+    set PROFOPT_OPTIONS [list \
+	{ -g } \
+	{ -O0 } \
+	{ -O1 } \
+	{ -O2 } \
+	{ -O3 } \
+	{ -O3 -g } \
+	{ -Os } ]
+}
+
+set option_list $PROFOPT_OPTIONS
+
+#
+# profopt-cleanup -- remove profiling or performance results files.
+#
+# EXT is the extension of files to remove
+#
+proc profopt-cleanup { ext } {
+    set files [glob -nocomplain *.$ext]
+    if { $files != "" } {
+	eval "remote_file build delete $files"
+    }
+}
+
+#
+# profopt-perf-value -- get performance value for a test
+#
+# TESTCASE is the name of the test
+# PERF_EXT is the extension of the performance result file
+# OPTSTR is the string of compiler options
+#
+proc profopt-perf-value { testcase perf_ext optstr } {
+    set basename [file tail $testcase]
+    set base [file rootname $basename]
+    set files [glob -nocomplain $base.$perf_ext]
+    # The file doesn't exist; let the caller decide if that's a problem.
+    if { $files == "" } {
+        return -2 
+    }
+    remote_upload host $base.$perf_ext $base.$perf_ext
+    set fd [open $base.$perf_ext r]
+    gets $fd line
+    set val -2
+    if [regexp "TIME" $line] {
+        if [regexp "TIME -1" $line] {
+	    fail "$testcase perf check: no consistent time available, $optstr"
+	    set val -1
+	} elseif ![regexp "(\[0-9\]+)" "$line" val] {
+	    set val -2
+	}
+    }
+    # Report problems with an existing file.
+    if { $val == -2 } {
+	fail "$testcase perf check: file $base.$perf_ext has wrong format, $optstr"
+    }
+    close $fd
+    profopt-cleanup $perf_ext
+    return $val
+}
+
+#
+# c-prof-execute -- compile for profiling and then feedback, then normal
+#
+# SRC is the full pathname of the testcase.
+#
+proc profopt-execute { src } {
+    global srcdir tmpdir
+    global option_list
+    global tool profile_option feedback_option prof_ext perf_ext perf_delta
+    global verbose
+
+    regsub "^$srcdir/?" $src "" testcase
+    # If we couldn't rip $srcdir out of `src' then just do the best we can.
+    # The point is to reduce the unnecessary noise in the logs.  Don't strip
+    # out too much because different testcases with the same name can confuse
+    # `test-tool'.
+    if [string match "/*" $testcase] {
+	set testcase "[file tail [file dirname $src]]/[file tail $src]"
+    }
+
+    set executable $tmpdir/[file tail [file rootname $src].x]
+
+    set count 0
+    foreach option $option_list {
+	set execname1 "${executable}${count}1"
+	set execname2 "${executable}${count}2"
+	set execname3 "${executable}${count}3"
+	incr count
+
+	remote_file build delete $execname1
+	remote_file build delete $execname2
+	remote_file build delete $execname3
+	verbose "Testing $testcase, $option" 1
+
+	# Compile for profiling.
+
+	set options ""
+	lappend options "additional_flags=$option $profile_option"
+	set optstr "$option $profile_option"
+	set comp_output [${tool}_target_compile "$src" "$execname1" executable $options];
+	if ![${tool}_check_compile "$testcase compilation" $optstr $execname1 $comp_output] {
+ 	    unresolved "$testcase execution,   $optstr"
+ 	    unresolved "$testcase compilation, $option $feedback_option"
+ 	    unresolved "$testcase execution,   $option $feedback_option"
+	    continue
+	}
+
+	# Run the profiled test.
+
+	set result [${tool}_load $execname1 "" ""]
+	set status [lindex $result 0]
+	# Make sure the profile data was generated, and fail if not.
+	if { $status == "pass" } {
+	    set basename [file tail $testcase]
+	    set base [file rootname $basename]
+	    set files [glob -nocomplain $base.$prof_ext]
+	    if { $files == "" } {
+		set status "fail"
+		fail "$testcase execution: file $base.$prof_ext does not exist, $option $profile_option"
+	    } else {
+	        $status "$testcase execution,   $optstr"
+	    }
+	} else {
+	    $status "$testcase execution,   $optstr"
+	}
+	# Quit for this round if it failed
+	if { $status != "pass" } {
+ 	    unresolved "$testcase compilation, $option $feedback_option"
+ 	    unresolved "$testcase execution,   $option $feedback_option"
+	    continue
+	}
+	remote_file build delete $execname1
+
+	# Compile with feedback-directed optimizations.
+
+	set options ""
+	lappend options "additional_flags=$option $feedback_option"
+	set optstr "$option $feedback_option"
+	set comp_output [${tool}_target_compile "$src" "$execname2" "executable" $options];
+	if ![${tool}_check_compile "$testcase compilation" $optstr $execname2 $comp_output] {
+ 	    unresolved "$testcase execution,   $optstr"
+	    continue
+	}
+
+	# Run the profile-directed optimized test.
+
+	set result [${tool}_load "$execname2" "" ""]
+	set status [lindex $result 0]
+	$status "$testcase execution,   $optstr"
+	if { $status != "pass" } {
+	    continue
+	}
+
+	# Remove the profiling data files.
+	profopt-cleanup $prof_ext
+
+	# If the test is not expected to produce performance data then
+	# we're done now.
+	if ![info exists perf_ext] {
+	    remote_file build delete $execname2
+	    continue
+	}
+
+	# Get the performance data from the test built with
+	# profile-directed optimization.  If the file doesn't exist or if
+	# the value is zero, skip the performance comparison.
+	set val2 [profopt-perf-value $testcase $perf_ext $optstr]
+	if { $val2 <= 0 } {
+	    remote_file build delete $execname2
+	    continue
+	}
+
+	# Compile with normal optimizations.
+
+	set options ""
+	lappend options "additional_flags=$option"
+	set optstr "$option"
+	set comp_output [${tool}_target_compile "$src" "$execname3" "executable" $options];
+	if ![${tool}_check_compile "$testcase compilation" $optstr $execname3 $comp_output] {
+ 	    unresolved "$testcase execution,   $optstr"
+	    unresolved "$testcase perf check,  $optstr"
+	    continue
+	}
+
+	# Run the test with normal optimizations.
+
+	set result [${tool}_load "$execname3" "" ""]
+	set status [lindex $result 0]
+	$status "$testcase execution,   $optstr"
+	if { $status != "pass" } {
+	    unresolved "$testcase perf check, $optstr"
+	    continue
+	}
+
+	# Get the performance data from the test built with normal
+	# optimization.
+	set val1 [profopt-perf-value $testcase $perf_ext $optstr]
+	if { $val1 < 0 } {
+	    if { $val1 == -2 } {
+		# The data file existed with the profile-directed
+		# optimization so this one should, too.
+		fail "$testcase perf check: file $base.$perf_ext does not exist, $optstr"
+	    }
+	    continue
+	}
+
+	# Compare results of the two runs and fail if the time with the
+	# profile-directed optimization is significantly more than the time
+	# without it.
+	set status "pass"
+	if { $val2 > $val1 } {
+	    # Check for a performance degration outside of allowable limits.
+	    if { [expr $val2 - $val1] > [expr [expr $val1 * $perf_delta] / 100] } {
+		set status "fail"
+	    }
+	}
+	if { $status == "fail" } {
+	    fail "$testcase perf check: orig: $val1  new: $val2, $optstr"
+	} else {
+	    $status "$testcase perf check,  $optstr"
+	    verbose "$testcase orig: $val1  new: $val2, $optstr" 2
+	    remote_file build delete $execname2
+	    remote_file build delete $execname3
+	}
+    }
+}
--- gcc.misc-tests/bprob.exp.orig	Fri Aug 31 10:16:35 2001
+++ gcc.misc-tests/bprob.exp	Fri Aug 31 09:43:49 2001
@@ -0,0 +1,55 @@
+#   Copyright (C) 2001 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+# 
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+# 
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  
+
+# Test the functionality of programs compiled with profile-directed block
+# ordering using -fprofile-arcs followed by -fbranch-probabilities.
+
+# The procedures in profopt.exp need these parameters.
+set tool gcc
+set profile_option -fprofile-arcs
+set feedback_option -fbranch-probabilities
+set prof_ext da
+set perf_ext tim
+
+# Override the list defined in profopt.exp.
+set PROFOPT_OPTIONS [list \
+	{ -g } \
+	{ -O0 } \
+	{ -O1 } \
+	{ -O2 -DPERFTIME } \
+	{ -O3 -DPERFTIME } \
+	{ -O3 -g -DPERFTIME } \
+	{ -Os } ]
+
+if $tracelevel then {
+    strace $tracelevel
+}
+
+# Load support procs.
+load_lib profopt.exp
+
+# Clean up existing .da and .tim files.
+profopt-cleanup da
+profopt-cleanup tim
+
+foreach src [lsort [glob -nocomplain $srcdir/$subdir/bprob-*.c]] {
+    # If we're only testing specific files and this isn't one of them, skip it.
+    if ![runtest_file_p $runtests $src] then {
+	continue
+    }
+
+    profopt-execute $src
+}
--- gcc.misc-tests/bprob-1.c.orig	Fri Aug 31 10:16:35 2001
+++ gcc.misc-tests/bprob-1.c	Fri Aug 31 09:18:15 2001
@@ -0,0 +1,269 @@
+/* Check that various C constructs are handled correctly by profile-directed
+   block ordering.
+  
+   This test is the same as gcov-4.c.  The "count" comments are left in to
+   make comparisons easier; they are ignored for this test. */
+
+int do_something (int i)
+{
+  return i;
+}
+
+/* Check static inline functions. */
+
+int unref_val;
+
+static inline int
+unreferenced (int i, int j)
+{
+  return i - j;
+}
+
+static inline int
+uncalled (int i, int j)
+{
+  return i * j;
+}
+
+static inline int
+called (int i, int j)
+{
+    return i + j;			/* count(1) */
+}
+
+void
+call_unref ()
+{
+  if (unref_val)			/* count(1) */
+    unref_val = uncalled (1, 2);
+  unref_val = called (unref_val, 4);	/* count(1) */
+}
+
+
+/* Check for loops. */
+
+int for_val1;
+int for_val2;
+int for_temp;
+
+int
+test_for1 (int n)
+{
+  int i;
+  for_temp = 1;				/* count(3) */
+  for (i = 0; i < n; i++)
+    for_temp++;				/* count(9) */
+  return for_temp;			/* count(3) */
+}
+
+int
+test_for2 (int m, int n, int o)
+{
+  int i, j, k;
+  for_temp = 1;				/* count(6) */
+  for (i = 0; i < n; i++)
+    for (j = 0; j < m; j++)
+      for (k = 0; k < o; k++)
+	for_temp++;			/* count(81) */
+  return for_temp;			/* count(6) */
+}
+
+int
+call_for ()
+{
+  for_val1 += test_for1 (0);
+  for_val1 += test_for1 (2);
+  for_val1 += test_for1 (7);
+
+  for_val2 += test_for2 (0, 0, 0);
+  for_val2 += test_for2 (1, 0, 0);
+  for_val2 += test_for2 (1, 3, 0);
+  for_val2 += test_for2 (1, 3, 1);
+  for_val2 += test_for2 (3, 1, 5);
+  for_val2 += test_for2 (3, 7, 3);
+}
+
+/* Check the use of goto. */
+
+int goto_val;
+
+int
+test_goto1 (int f)
+{
+  if (f)				/* count(2) */
+    goto lab1;				/* count(1) */
+  return 1;				/* count(1) */
+lab1:
+  return 2;				/* count(1) */
+}
+
+int
+test_goto2 (int f)
+{
+  int i;
+  for (i = 0; i < 10; i++)		/* count(15) */
+    if (i == f) goto lab2;		/* count(14) */
+  return 4;				/* count(1) */
+lab2:
+  return 8;				/* count(1) */
+}
+
+void
+call_goto ()
+{
+  goto_val += test_goto1 (0);
+  goto_val += test_goto1 (1);
+  goto_val += test_goto2 (3);
+  goto_val += test_goto2 (30);
+}
+
+/* Check nested if-then-else statements. */
+
+int ifelse_val1;
+int ifelse_val2;
+int ifelse_val3;
+
+int
+test_ifelse1 (int i, int j)
+{
+  int result = 0;
+  if (i)				/* count(5) */
+    if (j)				/* count(3) */
+      result = do_something (4);	/* count(3) */
+    else
+      result = do_something (1024);
+  else
+    if (j)				/* count(2) */
+      result = do_something (1);	/* count(1) */
+    else
+      result = do_something (2);	/* count(1) */
+  if (i > j)				/* count(5) */
+    result = do_something (result*2);	/* count(1) */
+  if (i > 10)				/* count(5) */
+    if (j > 10)				/* count(1) */
+      result = do_something (result*4);	/* count(1) */
+  return result;			/* count(5) */
+}
+
+int
+test_ifelse2 (int i)
+{
+  int result = 0;
+  if (!i)				/* count(6) */
+    result = do_something (1);		/* count(1) */
+  if (i == 1)				/* count(6) */
+    result = do_something (1024);
+  if (i == 2)				/* count(6) */
+    result = do_something (2);		/* count(3) */
+  if (i == 3)				/* count(6) */
+    return do_something (8);		/* count(2) */
+  if (i == 4)				/* count(4) */
+    return do_something (2048);
+  return result;			/* count(4) */
+}
+
+int
+test_ifelse3 (int i, int j)
+{
+  int result = 1;
+  if (i > 10 && j > i && j < 20)	/* count(11) */
+    result = do_something (16);		/* count(1) */
+  if (i > 20)				/* count(11) */
+    if (j > i)				/* count(5) */
+      if (j < 30)			/* count(2) */
+	result = do_something (32);	/* count(1) */
+  if (i == 3 || j == 47 || i == j)	/* count(11) */
+    result = do_something (64);		/* count(3) */
+  return result;			/* count(11) */
+}
+
+void
+call_ifelse ()
+{
+  ifelse_val1 += test_ifelse1 (0, 2);
+  ifelse_val1 += test_ifelse1 (0, 0);
+  ifelse_val1 += test_ifelse1 (1, 2);
+  ifelse_val1 += test_ifelse1 (10, 2);
+  ifelse_val1 += test_ifelse1 (11, 11);
+
+  ifelse_val2 += test_ifelse2 (0);
+  ifelse_val2 += test_ifelse2 (2);
+  ifelse_val2 += test_ifelse2 (2);
+  ifelse_val2 += test_ifelse2 (2);
+  ifelse_val2 += test_ifelse2 (3);
+  ifelse_val2 += test_ifelse2 (3);
+
+  ifelse_val3 += test_ifelse3 (11, 19);
+  ifelse_val3 += test_ifelse3 (25, 27);
+  ifelse_val3 += test_ifelse3 (11, 22);
+  ifelse_val3 += test_ifelse3 (11, 10);
+  ifelse_val3 += test_ifelse3 (21, 32);
+  ifelse_val3 += test_ifelse3 (21, 20);
+  ifelse_val3 += test_ifelse3 (1, 2);
+  ifelse_val3 += test_ifelse3 (32, 31);
+  ifelse_val3 += test_ifelse3 (3, 0);
+  ifelse_val3 += test_ifelse3 (0, 47);
+  ifelse_val3 += test_ifelse3 (65, 65);
+}
+
+/* Check switch statements. */
+
+int switch_val, switch_m;
+
+int
+test_switch (int i, int j)
+{
+  int result = 0;			/* count(5) */
+
+  switch (i)				/* count(5) */
+    {
+      case 1:
+        result = do_something (2);	/* count(1) */
+        break;
+      case 2:
+        result = do_something (1024);
+        break;
+      case 3:
+      case 4:
+        if (j == 2)			/* count(3) */
+          return do_something (4);	/* count(1) */
+        result = do_something (8);	/* count(2) */
+        break;
+      default:
+	result = do_something (32);	/* count(1) */
+	switch_m++;			/* count(1) */
+        break;
+    }
+  return result;			/* count(4) */
+}
+
+void
+call_switch ()
+{
+  switch_val += test_switch (1, 0);
+  switch_val += test_switch (3, 0);
+  switch_val += test_switch (3, 2);
+  switch_val += test_switch (4, 0);
+  switch_val += test_switch (16, 0);	
+  switch_val += switch_m;
+}
+
+int
+main()
+{
+  call_for ();
+  call_goto ();
+  call_ifelse ();
+  call_switch ();
+  call_unref ();
+  if ((for_val1 != 12)
+      || (for_val2 != 87)
+      || (goto_val != 15)
+      || (ifelse_val1 != 31)
+      || (ifelse_val2 != 23)
+      || (ifelse_val3 != 246)
+      || (switch_val != 55)
+      || (unref_val != 4))
+    abort ();
+  return 0;
+}
--- gcc.misc-tests/bprob-2.c.orig	Fri Aug 31 10:16:35 2001
+++ gcc.misc-tests/bprob-2.c	Fri Aug 31 09:18:15 2001
@@ -0,0 +1,42 @@
+/* Test profile-directed block ordering with computed gotos.
+ *
+   This is the same as test gcc.c-torture/execute/980526-1.c and
+   gcc.misc-tests/gcov-3.c */
+
+int expect_do1 = 1, expect_do2 = 2;
+ 
+static int doit(int x){
+  __label__ lbl1;
+  __label__ lbl2;
+  static int jtab_init = 0;
+  static void *jtab[2];
+ 
+  if(!jtab_init) {
+    jtab[0] = &&lbl1;
+    jtab[1] = &&lbl2;
+    jtab_init = 1;
+  }
+  goto *jtab[x];
+lbl1:
+  return 1;
+lbl2:
+  return 2;
+}
+ 
+static void do1(void) {
+  if (doit(0) != expect_do1)
+    abort ();
+}
+ 
+static void do2(void){
+  if (doit(1) != expect_do2)
+    abort ();
+}
+ 
+int main(void){
+#ifndef NO_LABEL_VALUES
+  do1();
+  do2();
+#endif
+  exit(0);
+}
--- gcc.misc-tests/bprob-3.c.orig	Fri Aug 31 10:16:35 2001
+++ gcc.misc-tests/bprob-3.c	Fri Aug 31 09:18:15 2001
@@ -0,0 +1,38 @@
+/* Test profile-directed branch ordering on a loop from the Queens benchmark. */
+
+#define TESTNAME "bprob-3"
+
+#include "perftime.h"
+
+int junk1 (int a, int b, int c, int d) {}
+int junk2 (int a, int b, int c, int d) {}
+int a = 0, b = 0, c = 0, d = 0, e = 0;
+int t0 = 0;
+int t1[16] = { 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1 };
+int t2[16] = { 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0 };
+int t3[16] = { 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+
+time_type doit ()
+{
+  time_type start, stop;
+  extern int a, b, c, d, e;
+  extern int t0;
+  extern int t1[16], t2[16], t3[16];
+  int i;
+
+  start = get_itc ();
+  if (t0)
+    e = junk1 (a, b, c, d);
+  else
+    for (i = 0; i < 20; i++)
+      if (t1[i] && t2[i] && t3[i])
+        e = junk2 (a, b, c, d);
+  stop = get_itc ();
+  return stop - start - OVERHEAD;
+}
+
+int main ()
+{
+  time_test ();
+  return 0;
+}
--- gcc.misc-tests/bprob-4.c.orig	Fri Aug 31 10:16:35 2001
+++ gcc.misc-tests/bprob-4.c	Fri Aug 31 09:18:15 2001
@@ -0,0 +1,67 @@
+/* Test profile-directed branch ordering on the Hanoi benchmark.  This
+   version of the benchmark is the one used in the GCC benchark suite,
+   but hard-coded to use long int values. */
+
+#define TESTNAME "bprob-4"
+#define EXPECTED 127
+
+#include "perftime.h"
+#define CYLINDERS 7
+
+#define TYPE type
+typedef long type;
+
+static type ncylinders[3];
+static type cylinders[3][CYLINDERS];
+static type
+move (type n, TYPE source, TYPE destination)
+{
+  type sum = 0;
+  type free = 0;
+  if (free == source)
+    free++;
+  if (free == destination)
+    free++;
+  if (free == source)
+    free++;
+  if (n)
+      sum += move (n - 1, source, free);
+  cylinders[destination][ncylinders[destination]] =
+    cylinders[source][ncylinders[source] - 1];
+  ncylinders[destination]++;
+  ncylinders[source]--;
+  sum++;
+  if (n)
+      sum += move (n - 1, free, destination);
+  return sum;
+}
+
+/* Run the code to be timed and return the number of cycles it took. */
+
+time_type
+doit ()
+{
+  int i;
+  type result;
+  time_type start, stop;
+  for (i = 0; i < CYLINDERS; i++)
+    cylinders[0][i] = CYLINDERS - i;
+  ncylinders[0] = CYLINDERS;
+  ncylinders[1] = 0;
+  ncylinders[2] = 0;
+  start = get_itc ();
+  result = move (CYLINDERS - 1, 0, 1);      /* Run the timed code. */
+  stop = get_itc ();
+  if (result != EXPECTED) { /* Check that the result is correct. */
+    /* printf("result = %ld\n", result); */
+    abort ();
+  }
+  return stop - start - OVERHEAD;
+}
+
+int
+main ()
+{
+  time_test ();
+  return 0;
+}
--- gcc.misc-tests/perftime.h.orig	Fri Aug 31 10:16:35 2001
+++ gcc.misc-tests/perftime.h	Fri Aug 31 09:43:49 2001
@@ -0,0 +1,136 @@
+#include <stdio.h>
+
+#ifndef PERF_EXT
+#define PERF_EXT ".tim"
+#endif
+
+/* The timed code is run TIMECNT times.  There must exist a set of MINREPEAT
+   times whose greatest difference is less than DELTA_PERCENTAGE percent of
+   the larger of those times. */
+#ifndef TIMECNT
+#define TIMECNT 20
+#endif
+#ifndef MINREPEAT
+#define MINREPEAT 3
+#endif
+#ifndef DELTA_PERCENTAGE
+#define DELTA_PERCENTAGE 1
+#endif
+
+/* For supported architectures and for the default case (no timing is done),
+   define the type that holds the cycle count, the printf format for that
+   type, and function get_itc() to return a cycle counter. */
+#if defined(__ia64) && defined(PERFTIME)
+typedef unsigned long time_type;
+#define TIME_TYPE_FORMAT "lu"
+#define OVERHEAD 4
+static inline time_type get_itc (void)
+{
+  time_type ret;
+  __asm__ __volatile__ ("mov %0=ar.itc" : "=r"(ret) :: "memory");
+  return ret;
+}
+#elif defined(__i386) && defined(PERFTIME)
+typedef unsigned long long time_type;
+#define TIME_TYPE_FORMAT "llu"
+#define OVERHEAD 35    /* with -O2 */
+extern time_type get_itc (void);
+asm(" .align 16; .type get_itc,@function; get_itc: rdtsc; ret");
+#else
+typedef unsigned long time_type;
+#define TIME_TYPE_FORMAT "lu"
+#define OVERHEAD 0
+static inline time_type get_itc (void)
+{
+  return 0;
+}
+#endif
+
+time_type doit ();
+
+/* Sort time values in descending order. */
+
+void
+isort (time_type *t, int n)
+{
+  time_type tmp;
+  int i, j;
+
+  for (i = 1; i < n; i++)
+    {
+      for (j = i; j > 0 && t[j-1] < t[j]; j--)
+        {
+          tmp = t[j-1];
+          t[j-1] = t[j];
+          t[j] = tmp;
+        }
+    }
+}
+
+/* Find and return the minimum time that is repeated at least
+   MINREPEAT times.  If one isn't found, return 0 and set the
+   time value to 0. */
+int
+gettime (time_type *retval, time_type *t)
+{
+  int i, j;
+
+  /* Sort the times into descending order to make them easier to process. */
+  isort (t, TIMECNT);
+
+  /* Starting at the lowest times, find one that is repeated (within a
+     small delta) at least MINREPEAT times and return it. */
+  for (i = TIMECNT-1; i >= MINREPEAT; i--)
+    {
+      if ((t[i-MINREPEAT+1] - t[i]) <=
+	  ((t[i-MINREPEAT+1] * DELTA_PERCENTAGE)/100))
+        {
+          *retval = t[i];
+          return 1;
+        }
+      /* skip past equal values */
+      while (t[i] == t[i-1] && i > 0)
+        i--;
+    }
+
+  /* Didn't find anything to meet the criteria. */
+  *retval = 0;
+  return 0; 
+}
+
+/* Run the function containing the code to time multiple times and write
+   a time to the performance data file.  If there is not a consistent
+   time to report then write an invalid value followed by a line with
+   the entire list of times. */
+
+void time_test ()
+{
+  FILE *fd;
+  time_type val, t[TIMECNT];
+  int i;
+
+  fd = fopen (TESTNAME PERF_EXT, "w");
+  t[0] = doit ();
+
+  /* A value of zero means we're not timing for this platform or for
+     this test run. */
+  if (t[0] == 0)
+    fprintf (fd, "TIME 0\n");
+  else
+    {
+      for (i = 1; i < TIMECNT; i++)
+        t[i] = doit ();
+
+      if (gettime (&val, t) == 1)
+        /* Write out a good value. */
+        fprintf (fd, "TIME %" TIME_TYPE_FORMAT "\n", val);
+      else
+        {
+	  /* Write out an invalid value followed by the list of times. */
+	  fprintf (fd, "TIME -1\n");
+	  for (i = 1; i < TIMECNT; i++)
+	    fprintf (fd, "%" TIME_TYPE_FORMAT "\n", t[i]);
+        }
+    }
+  fclose (fd);
+}


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]