profile-guided opt. w/ GCC

Started by Neil Conwayover 21 years ago5 messages
#1Neil Conway
neilc@samurai.com
1 attachment(s)

Profile-guided optimization is a relatively new GCC feature that
improves the quality of generated code by:

- compiling a copy of the source program with some profiling hooks
- running this copy of the program on some representative input data
- recompiling the program using the profiling data produced by the
previous stage; the profiling data lets GCC's optimizer generate more
efficient code

I think it would be cool to add support for PGO to PostgreSQL's build
system (for 8.1). There are a lot of situations where PostgreSQL is
compiled once, and then used for weeks or months (compilations for
inclusion in a distro being the extreme example). In that kind of
situation, trading some additional compile-time for even a small
improvement in run-time performance is worthwhile, IMHO.

I've attached a proof-of-concept patch that implements this. Caveats:

- you'll need to re-run autoconf
- the libgcov.a stuff is a temporary hack, you may need to adjust it for
where libgcov.a is on your system
- I've only bothered adding support for GCC 3.4 (IIRC profile-guided
optimization was introduced in GCC 3.3, but 3.4 adds a simpler interface
to using it). By the time 8.1 is out I think GCC 3.4+ will be pretty
prevalent anyway.
- the patch should remove the .gcda and .gcno files that are produced by
GCC; I haven't done that yet

The patch adds a new make target ("profile-opt") that does the PGO steps
outlined above -- the "representative input data" is the regression
tests running in serial mode. I haven't run any benchmarks yet (if
someone wants to try that, I'd be very curious to see the results).

Comments?

-Neil

Attachments:

pgo-support-8.patchtext/x-patch; charset=iso-8859-1; name=pgo-support-8.patchDownload
? autom4te.cache
Index: GNUmakefile.in
===================================================================
RCS file: /home/neilc/private-cvsroot/pgsql-server/GNUmakefile.in,v
retrieving revision 1.40
diff -c -r1.40 GNUmakefile.in
*** GNUmakefile.in	30 Jul 2004 12:26:39 -0000	1.40
--- GNUmakefile.in	30 Sep 2004 08:06:20 -0000
***************
*** 49,62 ****
  # Garbage from autoconf:
  	@rm -rf autom4te.cache/
  
! check: all
  
! check installcheck:
  	$(MAKE) -C src/test $@
  
  GNUmakefile: GNUmakefile.in $(top_builddir)/config.status
  	./config.status $@
  
  
  ##########################################################################
  
--- 49,79 ----
  # Garbage from autoconf:
  	@rm -rf autom4te.cache/
  
! singlecheck check: all
  
! singlecheck check installcheck:
  	$(MAKE) -C src/test $@
  
  GNUmakefile: GNUmakefile.in $(top_builddir)/config.status
  	./config.status $@
  
+ # XXX: do we need to run clean first?
+ profile-opt:
+ ifneq ($(profile_opt_support), yes)
+ 	@echo "Your C compiler does not contain support for profile-guided optimization."
+ 	@echo "PostgreSQL currently supports PGO when compiled with GCC 3.4 or later"
+ 	@false
+ else
+ 	@echo "Building PostgreSQL with support for profile generation"
+ 	$(MAKE) clean
+ 	$(MAKE) all CFLAGS="$(CFLAGS) -fprofile-generate"
+ 	@echo "Running regression tests to generate profile data"
+ 	$(MAKE) -C src/test/regress singlecheck
+ 	@echo "Rebuilding PostgreSQL to use profile data"
+ 	$(MAKE) clean
+ 	$(MAKE) all CFLAGS="$(CFLAGS) -fprofile-use"
+ 	@echo "Removing profile data"
+ endif
  
  ##########################################################################
  
Index: configure.in
===================================================================
RCS file: /home/neilc/private-cvsroot/pgsql-server/configure.in,v
retrieving revision 1.378
diff -c -r1.378 configure.in
*** configure.in	27 Sep 2004 02:17:14 -0000	1.378
--- configure.in	30 Sep 2004 08:06:20 -0000
***************
*** 253,258 ****
--- 253,261 ----
  # Need to specify -fno-strict-aliasing too in case it's gcc 3.3 or later.
  PGAC_PROG_CC_NO_STRICT_ALIASING
  
+ # Check whether the compiler supports profile-guided optimization
+ PGAC_PROG_CC_PROFILE_GUIDED_OPT
+ 
  # supply -g if --enable-debug
  if test "$enable_debug" = yes && test "$ac_cv_prog_cc_g" = yes; then
    CFLAGS="$CFLAGS -g"
Index: config/c-compiler.m4
===================================================================
RCS file: /home/neilc/private-cvsroot/pgsql-server/config/c-compiler.m4,v
retrieving revision 1.12
diff -c -r1.12 c-compiler.m4
*** config/c-compiler.m4	2 Feb 2004 04:07:18 -0000	1.12
--- config/c-compiler.m4	30 Sep 2004 08:06:21 -0000
***************
*** 150,155 ****
--- 150,177 ----
    CFLAGS="$CFLAGS $pgac_cv_prog_cc_no_strict_aliasing"
  fi])# PGAC_PROG_CC_NO_STRICT_ALIASING
  
+ # PGAC_PROG_CC_PROFILE_GUIDED_OPT
+ # -------------------------------
+ # Find out if the C compiler supports profile-guided optimization. For
+ # the moment, we only support GCC (and GCC 3.4+ at that).
+ AC_DEFUN([PGAC_PROG_CC_PROFILE_GUIDED_OPT],
+  [AC_CACHE_CHECK([if $CC supports profile-guided optimization],
+                  pgac_cv_prog_cc_pgo_support,
+ [pgac_save_CFLAGS=$CFLAGS
+ pgac_cv_prog_cc_pgo_support=no
+ if test "$GCC" = yes; then
+   CFLAGS="$pgac_save_CFLAGS -fprofile-use"
+   _AC_COMPILE_IFELSE([AC_LANG_PROGRAM()],
+                      [pgac_cv_prog_cc_pgo_support=yes
+ break])
+ fi
+ 
+ CFLAGS=$pgac_save_CFLAGS
+ ])
+ 
+ gcc_pgo_support=$pgac_cv_prog_cc_pgo_support
+ AC_SUBST(gcc_pgo_support)
+ ]) # PGAC_PROG_CC_PROFILE_GUIDED_OPT
  
  # The below backpatches the following Autoconf change:
  #
Index: src/Makefile.global.in
===================================================================
RCS file: /home/neilc/private-cvsroot/pgsql-server/src/Makefile.global.in,v
retrieving revision 1.195
diff -c -r1.195 Makefile.global.in
*** src/Makefile.global.in	18 Sep 2004 13:28:54 -0000	1.195
--- src/Makefile.global.in	30 Sep 2004 08:06:21 -0000
***************
*** 185,190 ****
--- 185,193 ----
    CFLAGS += -Wall -Wmissing-prototypes -Wmissing-declarations
  endif
  
+ # profile-guided optimization
+ profile_opt_support = @gcc_pgo_support@
+ 
  # Kind-of compilers
  
  YACC = @YACC@
Index: src/backend/Makefile
===================================================================
RCS file: /home/neilc/private-cvsroot/pgsql-server/src/backend/Makefile,v
retrieving revision 1.104
diff -c -r1.104 Makefile
*** src/backend/Makefile	1 Aug 2004 18:07:42 -0000	1.104
--- src/backend/Makefile	30 Sep 2004 08:06:21 -0000
***************
*** 35,41 ****
  ifneq ($(PORTNAME), win32)
  
  postgres: $(OBJS)
! 	$(CC) $(CFLAGS) $(LDFLAGS) $(export_dynamic) $^ $(LIBS) -o $@
  
  endif
  endif
--- 35,41 ----
  ifneq ($(PORTNAME), win32)
  
  postgres: $(OBJS)
! 	$(CC) $(CFLAGS) $(LDFLAGS) $(export_dynamic) $^ /usr/lib/gcc/i486-linux/3.4.1/libgcov.a $(LIBS) -o $@
  
  endif
  endif
Index: src/bin/initdb/Makefile
===================================================================
RCS file: /home/neilc/private-cvsroot/pgsql-server/src/bin/initdb/Makefile,v
retrieving revision 1.44
diff -c -r1.44 Makefile
*** src/bin/initdb/Makefile	29 Aug 2004 04:13:01 -0000	1.44
--- src/bin/initdb/Makefile	30 Sep 2004 08:06:22 -0000
***************
*** 20,26 ****
  all: submake-libpq submake-libpgport initdb
  
  initdb: $(OBJS) $(libpq_builddir)/libpq.a
! 	$(CC) $(CFLAGS) $(OBJS) $(libpq) $(LDFLAGS) $(LIBS) -o $@$(X)
  
  dirmod.c: % : $(top_srcdir)/src/port/%
  	rm -f $@ && $(LN_S) $< .
--- 20,26 ----
  all: submake-libpq submake-libpgport initdb
  
  initdb: $(OBJS) $(libpq_builddir)/libpq.a
! 	$(CC) $(CFLAGS) $(OBJS) /usr/lib/gcc/i486-linux/3.4.1/libgcov.a $(libpq) $(LDFLAGS) $(LIBS) -o $@$(X)
  
  dirmod.c: % : $(top_srcdir)/src/port/%
  	rm -f $@ && $(LN_S) $< .
Index: src/bin/pg_config/Makefile
===================================================================
RCS file: /home/neilc/private-cvsroot/pgsql-server/src/bin/pg_config/Makefile,v
retrieving revision 1.8
diff -c -r1.8 Makefile
*** src/bin/pg_config/Makefile	1 Aug 2004 06:56:38 -0000	1.8
--- src/bin/pg_config/Makefile	30 Sep 2004 08:06:22 -0000
***************
*** 14,20 ****
  	rm -f $@ && $(LN_S) $< .
  
  pg_config: $(OBJS)
! 	$(CC) $(CFLAGS) $(OBJS) $(LDFLAGS) $(LIBS) -o $@$(X)
  
  install: all installdirs
  	$(INSTALL_SCRIPT) pg_config$(X) $(DESTDIR)$(bindir)/pg_config$(X)
--- 14,20 ----
  	rm -f $@ && $(LN_S) $< .
  
  pg_config: $(OBJS)
! 	$(CC) $(CFLAGS) $(OBJS) /usr/lib/gcc/i486-linux/3.4.1/libgcov.a $(LDFLAGS) $(LIBS) -o $@$(X)
  
  install: all installdirs
  	$(INSTALL_SCRIPT) pg_config$(X) $(DESTDIR)$(bindir)/pg_config$(X)
Index: src/bin/pg_ctl/Makefile
===================================================================
RCS file: /home/neilc/private-cvsroot/pgsql-server/src/bin/pg_ctl/Makefile,v
retrieving revision 1.15
diff -c -r1.15 Makefile
*** src/bin/pg_ctl/Makefile	29 Aug 2004 04:13:01 -0000	1.15
--- src/bin/pg_ctl/Makefile	30 Sep 2004 08:06:22 -0000
***************
*** 20,26 ****
  all: submake-libpq submake-libpgport pg_ctl
  
  pg_ctl: $(OBJS) $(libpq_builddir)/libpq.a
! 	$(CC) $(CFLAGS) $(OBJS) $(libpq) $(LDFLAGS) $(LIBS) -o $@$(X)
  
  exec.c: % : $(top_srcdir)/src/port/%
  	rm -f $@ && $(LN_S) $< .
--- 20,26 ----
  all: submake-libpq submake-libpgport pg_ctl
  
  pg_ctl: $(OBJS) $(libpq_builddir)/libpq.a
! 	$(CC) $(CFLAGS) $(OBJS) /usr/lib/gcc/i486-linux/3.4.1/libgcov.a $(libpq) $(LDFLAGS) $(LIBS) -o $@$(X)
  
  exec.c: % : $(top_srcdir)/src/port/%
  	rm -f $@ && $(LN_S) $< .
Index: src/bin/psql/Makefile
===================================================================
RCS file: /home/neilc/private-cvsroot/pgsql-server/src/bin/psql/Makefile,v
retrieving revision 1.47
diff -c -r1.47 Makefile
*** src/bin/psql/Makefile	24 May 2004 01:01:37 -0000	1.47
--- src/bin/psql/Makefile	30 Sep 2004 08:06:22 -0000
***************
*** 27,33 ****
  all: submake-libpq submake-libpgport psql
  
  psql: $(OBJS) $(libpq_builddir)/libpq.a
! 	$(CC) $(CFLAGS) $(OBJS) $(libpq) $(LDFLAGS) $(LIBS) -o $@$(X)
  
  exec.c: % : $(top_srcdir)/src/port/%
  	rm -f $@ && $(LN_S) $< .
--- 27,33 ----
  all: submake-libpq submake-libpgport psql
  
  psql: $(OBJS) $(libpq_builddir)/libpq.a
! 	$(CC) $(CFLAGS) $(OBJS) /usr/lib/gcc/i486-linux/3.4.1/libgcov.a $(libpq) $(LDFLAGS) $(LIBS) -o $@$(X)
  
  exec.c: % : $(top_srcdir)/src/port/%
  	rm -f $@ && $(LN_S) $< .
Index: src/bin/scripts/Makefile
===================================================================
RCS file: /home/neilc/private-cvsroot/pgsql-server/src/bin/scripts/Makefile,v
retrieving revision 1.28
diff -c -r1.28 Makefile
*** src/bin/scripts/Makefile	26 May 2004 17:24:05 -0000	1.28
--- src/bin/scripts/Makefile	30 Sep 2004 08:06:22 -0000
***************
*** 20,26 ****
  all: submake-libpq submake-backend $(PROGRAMS)
  
  %: %.o
! 	$(CC) $(CFLAGS) $^ $(libpq) $(LDFLAGS) $(LIBS) -o $@$(X)
  
  createdb: createdb.o common.o exec.o dumputils.o $(top_builddir)/src/backend/parser/keywords.o
  createlang: createlang.o common.o exec.o print.o mbprint.o
--- 20,26 ----
  all: submake-libpq submake-backend $(PROGRAMS)
  
  %: %.o
! 	$(CC) $(CFLAGS) $^ /usr/lib/gcc/i486-linux/3.4.1/libgcov.a $(libpq) $(LDFLAGS) $(LIBS) -o $@$(X)
  
  createdb: createdb.o common.o exec.o dumputils.o $(top_builddir)/src/backend/parser/keywords.o
  createlang: createlang.o common.o exec.o print.o mbprint.o
Index: src/test/regress/GNUmakefile
===================================================================
RCS file: /home/neilc/private-cvsroot/pgsql-server/src/test/regress/GNUmakefile,v
retrieving revision 1.47
diff -c -r1.47 GNUmakefile
*** src/test/regress/GNUmakefile	18 Jun 2004 06:14:25 -0000	1.47
--- src/test/regress/GNUmakefile	30 Sep 2004 08:06:22 -0000
***************
*** 127,142 ****
  ## Run tests
  ##
  
! check: all
  	-rm -rf ./testtablespace
  	mkdir ./testtablespace
  	$(SHELL) ./pg_regress --temp-install --top-builddir=$(top_builddir) --schedule=$(srcdir)/parallel_schedule --multibyte=$(MULTIBYTE) $(MAXCONNOPT)
  
! installcheck: all
! 	-rm -rf ./testtablespace
! 	mkdir ./testtablespace
  	$(SHELL) ./pg_regress --schedule=$(srcdir)/serial_schedule --multibyte=$(MULTIBYTE)
  
  
  # old interfaces follow...
  
--- 127,144 ----
  ## Run tests
  ##
  
! setup-tablespace:
  	-rm -rf ./testtablespace
  	mkdir ./testtablespace
+ 
+ check: all setup-tablespace
  	$(SHELL) ./pg_regress --temp-install --top-builddir=$(top_builddir) --schedule=$(srcdir)/parallel_schedule --multibyte=$(MULTIBYTE) $(MAXCONNOPT)
  
! installcheck: all setup-tablespace
  	$(SHELL) ./pg_regress --schedule=$(srcdir)/serial_schedule --multibyte=$(MULTIBYTE)
  
+ singlecheck: all setup-tablespace
+ 	$(SHELL) ./pg_regress --temp-install --top-builddir=$(top_builddir) --schedule=$(srcdir)/serial_schedule --multibyte=$(MULTIBYTE)
  
  # old interfaces follow...
  
#2Peter Eisentraut
peter_e@gmx.net
In reply to: Neil Conway (#1)
Re: profile-guided opt. w/ GCC

Neil Conway wrote:

The patch adds a new make target ("profile-opt") that does the PGO
steps outlined above -- the "representative input data" is the
regression tests running in serial mode. I haven't run any benchmarks
yet (if someone wants to try that, I'd be very curious to see the
results).

I doubt that the regression tests are anywhere near representative input
data. They run a proportion of borderline and error cases that is much
higher than I would expect in normal use.

--
Peter Eisentraut
http://developer.postgresql.org/~petere/

#3Neil Conway
neilc@samurai.com
In reply to: Peter Eisentraut (#2)
Re: profile-guided opt. w/ GCC

On Thu, 2004-09-30 at 19:49, Peter Eisentraut wrote:

I doubt that the regression tests are anywhere near representative input
data. They run a proportion of borderline and error cases that is much
higher than I would expect in normal use.

That's definitely true. At first glance, the regression tests don't seem
to be *too* badly skewed:

[src/test/regress/expected]% grep ERROR *.out | wc -l
867
[src/test/regress/expected]% grep -i "^SELECT" *.out | wc -l
2924
[src/test/regress/expected]% grep -i "^INSERT" *.out | wc -l
2714
[src/test/regress/expected]% grep -i "^UPDATE" *.out | wc -l
122
[/src/test/regress/expected]% grep -i "^DELETE" *.out | wc -l
110
[src/test/regress/expected]% grep -i "^CREATE" *.out | wc -l
848
[src/test/regress/expected]% grep -i "^COPY" *.out | wc -l
46

I guess it depends on how closely the test data needs to match "normal"
input data for the gcc optimizer to be able to make valid decisions. My
intuition is that the regression tests are sufficiently close to normal
input that it won't be an issue, but I'm not sure.

-Neil

In reply to: Neil Conway (#1)
Re: profile-guided opt. w/ GCC

On Thu, Sep 30, 2004 at 07:07:27PM +1000, Neil Conway wrote:

I think it would be cool to add support for PGO to PostgreSQL's build
system (for 8.1). There are a lot of situations where PostgreSQL is
compiled once, and then used for weeks or months (compilations for
inclusion in a distro being the extreme example). In that kind of
situation, trading some additional compile-time for even a small
improvement in run-time performance is worthwhile, IMHO.

It's some time ago now, but a group at the Universitat Politecnica de
Catalunya (including my thesis advisor Alex Ramirez and our databases
specialist Josep Larriba-Pey) has done a case study on something similar,
using PostgreSQL as their case study.

What they researched was a code reordering algorithm that minimized both
taken branches and I-cache clashes. The scheme was quite aggressive, even
going so far as to coallocate code in some functions with code in their
most frequent callers. The study also includes a characterization of the
I-miss and execution intensities of the backend, in a neat matrix with
the major functions on one axis and the stage from which they're invoked
on the other.

The paper may be enlightening. Just a moment while I google for it...

...Got it. Here's the paper:

http://research.ac.upc.es/CAP/hpc/Papers/1999/aramirez1999aC.pdf

And here's the Citeseer entry:

http://citeseer.ist.psu.edu/context/163268/0

Jeroen

#5Tom Lane
tgl@sss.pgh.pa.us
In reply to: Peter Eisentraut (#2)
Re: profile-guided opt. w/ GCC

Peter Eisentraut <peter_e@gmx.net> writes:

Neil Conway wrote:

The patch adds a new make target ("profile-opt") that does the PGO
steps outlined above -- the "representative input data" is the
regression tests running in serial mode. I haven't run any benchmarks
yet (if someone wants to try that, I'd be very curious to see the
results).

I doubt that the regression tests are anywhere near representative input
data. They run a proportion of borderline and error cases that is much
higher than I would expect in normal use.

Also, the serial regression tests provide absolutely 0 exercise for any
of the code paths associated with concurrent behavior. At minimum I'd
suggest using the parallel tests instead.

It might be interesting to compare the results from PGO using the
regression tests to PGO using pgbench. pgbench probably goes overboard
in the other direction of not exercising enough stuff, but it would give
us some kind of data point about the consequences of different profiling
loads.

regards, tom lane