[Haskell-cafe] [3/16] SBM: The Makefile

Peter Firefly Brodersen Lund firefly at vax64.dk
Sat Dec 22 04:16:59 EST 2007


This is the entire Makefile.  It perhaps ought to be sent as an attachment
but my hacky mailer script wouldn't like it.

A few of the lines are wider than 80 columns, unfortunately.

-Peter

# GHC benchmarks of parsing (bytestring, basic code generation, I/O).
#
# Copyright 2007 Peter Lund <firefly at vax64.dk>, licensed under GPLv2.
#
#
# You will need the following tools:
#  perl, strace, /usr/bin/time, bash, a gcc that uses shared libraries and libc
#  (not dietlibc, klibc, uclibc), objdump (usually found in the binutils
#  package).
#
# A benchmark run on a new platform can be split into two phases:
#
#  phase1: Compiles, dices, and slices the code in various ways.  If this
#          completes you can be pretty sure that everything works all right.
#          Performs non-timing sensitive measurements.
#
#  phase2: Performs timing sensitive measurements.  It is a good idea to run
#          this phase on an idle machine, preferably without using X.
#          For example, you can log out of X and run "telinit 1" to get to
#          single-user mode.  If you do so, please remember to set the correct
#          path to your ghc compiler.


# Make a few bits of the makefile less noisy.
Q:=@

#########################################################

# Ask ghc to optimize and warn
GHCFLAGS= -O2 -W

# Some newer versions of gcc prefer -Wextra -Wall
GCCWARNFLAGS=-W -Wall

# Default compilers
CC=gcc
GHC=ghc
GHCPKG=ghc-pkg

#########################################################

HSPROGS=hs/byte-bs----acc	\
	hs/byte-bs----foldlx	\
	hs/byte-bs----foldrx	\
	hs/byte-bsl---acc	\
	hs/byte-xxxxx-acc-1	\
	hs/byte-xxxxx-acc-2	\
	hs/byte-xxxxx-foldl	\
	\
	hs/space-bs-c8-acc-1	\
	hs/space-bs-c8-count	\
	hs/space-bs-c8-foldlx-1	\
	hs/space-bs-c8-foldlx-2	\
	hs/space-bs-c8-foldrx	\
	hs/space-bs-c8-lenfil	\
	hs/space-bslc8-acc-1	\
	hs/space-bslc8-acc-2	\
	hs/space-bslc8-acc-3	\
	hs/space-bslc8-chunk-1	\
	hs/space-bslc8-chunk-2	\
	hs/space-bslc8-chunk-3	\
	hs/space-bslc8-chunk-4	\
	hs/space-bslc8-count	\
	hs/space-bslc8-foldl	\
	hs/space-bslc8-foldlx-1	\
	hs/space-bslc8-foldlx-2	\
	hs/space-bslc8-foldr-1	\
	hs/space-bslc8-foldr-2	\
	hs/space-bslc8-lenfil-1	\
	hs/space-bslc8-lenfil-2	\
	hs/space-bsl---foldlx	\
	hs/space-xxxxx-acc-1	\
	hs/space-xxxxx-acc-2	\
	hs/space-xxxxx-foldl	\
	hs/space-xxxxx-lenfil


# RMPROGS keeps track of programs that are not always included in the tests.
# We do want 'make clean' to delete them even when they are not currently
# part of the build (they may be left over from a previous build).

# stack overflow with long4.
#HSPROGS:=$(HSPROGS) hs/byte-xxxxx-foldr-1
 RMPROGS:=$(RMPROGS) hs/byte-xxxxx-foldr-1

# stack overflow with long4.
#HSPROGS:=$(HSPROGS) hs/byte-xxxxx-foldr-2
 RMPROGS:=$(RMPROGS) hs/byte-xxxxx-foldr-2

# stack overflow with long4.
#HSPROGS:=$(HSPROGS) hs/space-xxxxx-foldr-1
 RMPROGS:=$(RMPROGS) hs/space-xxxxx-foldr-1

# stack overflow with long4.
#HSPROGS:=$(HSPROGS) hs/space-xxxxx-foldr-2
 RMPROGS:=$(RMPROGS) hs/space-xxxxx-foldr-2


HANDPROGS= hand/byte-bs----acc-a	\
	   hand/byte-bs----acc-b	\
	   hand/byte-bs----acc-c	\
	   hand/byte-bs----acc-d	\
	   \
	   hand/space-bs-c8-acc-1-a	\
	   hand/space-bs-c8-acc-1-b	\
	   hand/space-bs-c8-acc-1-c	\
	   hand/space-bs-c8-acc-1-d	\
	   hand/space-bs-c8-acc-1-e	\
	   hand/space-bs-c8-acc-1-f	\
	   hand/space-bs-c8-acc-1-g	\
	   hand/space-bs-c8-acc-1-h	\
	   hand/space-bs-c8-acc-1-i	\
	   hand/space-bs-c8-acc-1-j	\
	   hand/space-bs-c8-acc-1-k	\
	   hand/space-bs-c8-acc-1-l	\
	   hand/space-bs-c8-acc-1-m	\
	   hand/space-bs-c8-acc-1-n	\
	   hand/space-bs-c8-acc-1-o	\
	   hand/space-bs-c8-acc-1-p	\
	   hand/space-bs-c8-acc-1-q	\
	   hand/space-bs-c8-acc-1-r	\
	   hand/space-bs-c8-acc-1-s

RMPROGS:=$(RMPROGS) $(HANDPROGS)


ifeq ($(shell $(GHCPKG) list | grep bytestring),)
 # ghc 6.6.1 with an old version of bytestring in 'base' but without its own
 # module name
HSPROGS:=$(shell printf "%s\n" $(HSPROGS) | grep -v '.*-chunk-.*')
endif

HANDTEXT:=including hand-tweaked assembly
ifeq ($(shell $(GHC) --version | grep 6.9.20071119),)
HANDPROGS:=
HANDTEXT:=no hand-tweaked assembly
endif
ifneq ($(SUFFIX),)
HANDPROGS:=
HANDTEXT:=no hand-tweaked assembly
endif

CPROGS=	c/byte-getchar c/byte-getchar-u c/byte-4k		\
	\
	c/space-getchar c/space-getchar-u c/space-4k		\
	c/space-megabuf c/space-getwchar c/space-getwchar-u	\
	c/space-32k c/space-32k-8

#########################################################

# The benchmarks can be run in three modes.  The default can be overridden from
# command line:
#
#    make TESTKIND=SMOKETEST phase1
#
# just tests the test suite, as fast as possible
#TESTKINDDEFAULT=SMOKETEST
# small test
TESTKINDDEFAULT=NORMAL
# very thorough test
#TESTKINDDEFAULT=THOROUGH
TESTKIND=$(TESTKINDDEFAULT)

ifeq      ($(TESTKIND),THOROUGH)
TESTFILE=	testfiles/long4
TESTFILECACHE=	testfiles/long3
else
 ifeq ($(TESTKIND),NORMAL)
TESTFILE=	testfiles/long3
TESTFILECACHE=	testfiles/long2
 else
  ifeq ($(TESTKIND),SMOKETEST)
TESTFILE=	testfiles/long2
TESTFILECACHE=	testfiles/long1
  endif
 endif
endif


# Older versions of strace don't support the -E parameter which we use to 
# set LD_PRELOAD before running the straced command (so we can get by with
# a single run of each benchmark in phase 1 instead of 2).
#
# Override with STRACE=OLD on the command line if you need to work with an
# old strace.
STRACE=NEW

#########################################################

.PHONY: XXXXFIRST testfiles core stg cmm asm dis discut prog \
	time stat strace iotrace iosum mem cache doc phase1 phase2 redophase2

XXXXFIRST:	help

testfiles:	testfiles/long1 testfiles/long2 testfiles/long3 testfiles/long4

core:	$(addsuffix .core   ,$(HSPROGS)                       )

stg:	$(addsuffix .stg    ,$(HSPROGS)                       )

cmm:	$(addsuffix .cmm    ,$(HSPROGS)                       )

asm:	$(addsuffix .s      ,$(HSPROGS)              $(CPROGS))

dis:	$(addsuffix .dis    ,$(HSPROGS) $(HANDPROGS) $(CPROGS))

discut:	$(addsuffix .discut ,$(HSPROGS) $(HANDPROGS) $(CPROGS))

prog:	                     $(HSPROGS) $(HANDPROGS) $(CPROGS)

time:	prog		\
	testfiles	\
	$(addsuffix .time   ,$(HSPROGS) $(HANDPROGS) $(CPROGS))

stat:	$(addsuffix .stat   ,$(HSPROGS) $(HANDPROGS) $(CPROGS))

strace:	$(addsuffix .strace ,$(HSPROGS) $(HANDPROGS) $(CPROGS))

iotrace:$(addsuffix .iotrace,$(HSPROGS) $(HANDPROGS) $(CPROGS))

iosum:  $(addsuffix .iosum  ,$(HSPROGS) $(HANDPROGS) $(CPROGS))

mem:	$(addsuffix .mem    ,$(HSPROGS) $(HANDPROGS) $(CPROGS))

cache:	$(addsuffix .cache  ,$(HSPROGS) $(HANDPROGS) $(CPROGS))

doc:	$(addsuffix .doc    ,$(HSPROGS) $(HANDPROGS) $(CPROGS))

###

phase1: testfiles tools/eatmem prog iosum mem
	printf "Done!\07"	# beep

phase2:	time report
	printf "Done!\07"	# beep

redophase2:	cleartime
	rm -f */*.srctimespace report.txt
	$(MAKE) phase2

#########################################################

testfiles/long1:
	mkdir -p testfiles
	tools/genfiles.pl    10000 > "$@"

testfiles/long2:
	mkdir -p testfiles
	tools/genfiles.pl   100000 > "$@"

testfiles/long3:
	mkdir -p testfiles
	tools/genfiles.pl  1000000 > "$@"

testfiles/long4:
	mkdir -p testfiles
	tools/genfiles.pl 10000000 > "$@"

#########################################################

tools/eatmem:	tools/eatmem.c
	$(CC) $(GCCWARNFLAGS) -O2 "$<" -o "$@"

tools/pause-at-end.so:	tools/pause-at-end.c
	$(CC) $(GCCWARNFLAGS) -shared -ldl "$<" -o "$@"

#########################################################

docs:	core stg cmm asm discut time iotrace doc sysinfo
	rm -f docs
	cat */*.doc sysinfo >  docs


hs/%.doc:	hs/%.core hs/%.stg hs/%.cmm hs/%.s hs/%.discut hs/%.time
	(export F="$(basename $@)"					; \
	printf "\n"							; \
	printf "*********************************************\n"	; \
	printf "****\n"							; \
	printf "**** %s:\n" "$$F"					; \
	printf "****\n"							; \
	printf "*********************************************\n\n"	; \
	printf "Haskell code:\n\n"					; \
	cat "$$F.hs"							; \
	printf -- "-------------------------------\n"			; \
	printf "%s:\n" "$$F.core"					; \
	cat "$$F.core"							; \
	printf -- "-------------------------------\n"			; \
	printf "%s:\n" "$$F.stg"					; \
	cat "$$F.stg"							; \
	printf -- "-------------------------------\n"			; \
	printf "%s:\n" "$$F.cmm"					; \
	cat "$$F.cmm"							; \
	printf -- "-------------------------------\n"			; \
	printf "%s:\n" "$$F.s"						; \
	cat "$$F.s"							; \
	printf -- "-------------------------------\n"			; \
	printf "%s:\n" "$$F.discut"					; \
	cat "$$F.discut"						; \
	printf -- "-------------------------------\n"			; \
	printf "%s:\n" "$$F.time"					; \
	cat "$$F.time"							; \
	printf -- "-------------------------------\n"			; \
	printf "\014"							; \
	) >> "$@"

hand/%.doc:	hand/%.discut hand/%.time
	(export F="$(basename $@)"					; \
	printf "\n"							; \
	printf "*********************************************\n"	; \
	printf "****\n"							; \
	printf "**** %s:\n" "$$F"					; \
	printf "****\n"							; \
	printf "*********************************************\n\n"	; \
	printf "Haskell code:\n\n"					; \
	export X=`echo $$F | sed -e 's/[a-z]$$//'`			; \
	cat "$$X.hs"							; \
	printf -- "-------------------------------\n"			; \
	printf "%s (hand tweaked):\n" "$$F.s"				; \
	cat "$$F.s"							; \
	printf -- "-------------------------------\n"			; \
	printf "%s:\n" "$$F.discut"					; \
	cat "$$F.discut"						; \
	printf -- "-------------------------------\n"			; \
	printf "%s:\n" "$$F.time"					; \
	cat "$$F.time"							; \
	printf -- "-------------------------------\n"			; \
	printf "\014"							; \
	) >> "$@"

c/%.doc:	c/%.s c/%.discut c/%.time
	(export F="$(basename $@)"					; \
	printf "\n"							; \
	printf "*********************************************\n"	; \
	printf "****\n"							; \
	printf "**** %s:\n" "$$F"					; \
	printf "****\n"							; \
	printf "*********************************************\n\n"	; \
	printf "C code:\n\n"						; \
	cat "$$F.c"							; \
	printf -- "-------------------------------\n"			; \
	printf "%s:\n" "$$F.s"						; \
	cat "$$F.s"							; \
	printf -- "-------------------------------\n"			; \
	printf "%s:\n" "$$F.discut"					; \
	cat "$$F.discut"						; \
	printf -- "-------------------------------\n"			; \
	printf "%s:\n" "$$F.time"					; \
	cat "$$F.time"							; \
	printf -- "-------------------------------\n"			; \
	printf "\014"							; \
	) >> "$@"


#########################################################

.PHONY:	report.txt lastreport

report: report.txt
	$(Q)cat report.txt

report.txt	\
 $(addsuffix .srctimespace,$(HSPROGS) $(HANDPROGS) $(CPROGS)): \
 iosum mem time stat platforminfo
	$(Q)tools/genreport.pl $(HSPROGS) $(HANDPROGS) $(CPROGS) > report.txt

lastreport:
	$(Q)cat report.txt

#########################################################

# Probably all or most of the targets (left-hand sides) in these rules should
# be mentioned in a .SECONDARY rule so make won't delete them behind our backs,
# in its infinite wisdom.  This is sometimes necessary when using pattern
# rules (i.e. rules with '%' wildcards in them).
#
# For some reason, it doesn't seem to be all that necessary, although I had to
# insert a couple of those .SECONDARY things earlier to make make behave.  For
# example, I had to insert this rule at some point but now things keep working
# even when it's commented out:
#
#   .SECONDARY: $(HSPROGS) $(HANDPROGS) $(CPROGS)
#

hs/%:	hs/%.hs
	$(GHC) $(GHCFLAGS) --make -fforce-recomp "$<" -o "$@"

hand/%:	hand/%.s
	$(GHC) -no-hs-main "$<" -o "$@" -package bytestring

c/%:	c/%.c
	$(CC) $(GCCWARNFLAGS) -O2 "$<" -o "$@"

###

%.dis:	%
	@# Limit the disassembly for speed reasons (10x+ difference) and
	@# file size reasons (20x-30x difference).
	@# The stuff we are interested in comes early in the .text segment so
	@# there's no reason to disassemble the entire runtime system, which
	@# comes afterwards in case of hand/ and hs/ binaries.
	objdump -M intel -D --stop-address=0x08060000 "$<" > "$@"

###

%.discut:	%.dis
	tools/cut.pl < "$<" > "$@"

###

%.core:	%.hs
	$(GHC) $(GHCFLAGS) -c -ddump-simpl "$<" > "$@"

###

%.stg:	%.hs
	$(GHC) $(GHCFLAGS) -c -ddump-stg "$<" > "$@"

###

%.cmm:	%.hs
	$(GHC) $(GHCFLAGS) -c -ddump-cmm "$<" > "$@"

###

%.s:	%.hs
	$(GHC) $(GHCFLAGS) -c -fforce-recomp -keep-s-files "$<"

.SECONDARY: $(addsuffix .s,$(CPROGS))
%.s:	%.c
	$(CC) $(GCCWARNFLAGS) -O2 -S $< -o $@

#########################################################

# The first run is sacrificial, except when smoketesting where there only is
# one run.

ifeq      ($(TESTKIND),THOROUGH)
TIME=	bash -c "time $<" < $(TESTFILE); \
	bash -c "time $<" < $(TESTFILE); \
	bash -c "time $<" < $(TESTFILE); \
	bash -c "time $<" < $(TESTFILE); \
	bash -c "time $<" < $(TESTFILE); \
	bash -c "time $<" < $(TESTFILE)
NOSKIP:=
else
 ifeq ($(TESTKIND),NORMAL)
TIME=	bash -c "time $<" < $(TESTFILE); \
	bash -c "time $<" < $(TESTFILE); \
	bash -c "time $<" < $(TESTFILE); \
	bash -c "time $<" < $(TESTFILE)
NOSKIP:=
 else
  ifeq ($(TESTKIND),SMOKETEST)
TIME=	bash -c "time $<" < $(TESTFILE)
NOSKIP:= NOSKIP=1
  endif
 endif
endif


# In order to reduce the risk of swapping during the time test, we try to make
# sure there's twice the test file size free (and a bit).
# NEEDFREE is measured in kilobytes.
NEEDFREE=$(shell expr 22 '*' `ls -s $(TESTFILE) | cut -f1 -d' '` / 10)

%.time:	% tools/eatmem $(TESTFILE)
	printf "%s\n" "$<" > "$@"
	tools/eatmem $(NEEDFREE)
	dd if=$(TESTFILE) of=/dev/null
	dd if="$<" of=/dev/null
	($(TIME)) >>"$@" 2>&1
	printf "%s\n\n" "-----" >> "$@"

%.stat: %.time
	$(NOSKIP) tools/stat.pl < "$<" > "$@"

%.mem %.strace:	% tools/pause-at-end.so $(TESTFILE)
ifeq ($(STRACE),OLD)
	strace -o tmp.strace -f \
		/usr/bin/time "$<" +RTS -sstderr < $(TESTFILE) > $(basename $@).mem 2>&1
	LD_PRELOAD=tools/pause-at-end.so \
		"$<" +RTS -sstderr < $(TESTFILE) >> $(basename $@).mem 2>&1
else
	strace -o tmp.strace -ELD_PRELOAD=tools/pause-at-end.so -f \
		/usr/bin/time "$<" +RTS -sstderr < $(TESTFILE) > $(basename $@).mem 2>&1
endif
	tools/cutmem.pl < $(basename $@).mem > tmp
	mv tmp $(basename $@).mem
	tools/cutpid.pl < tmp.strace > $(basename $@).strace
	rm -f tmp.strace

#%.strace: %.mem
#	@echo > /dev/null

%.iotrace: %.strace
	grep '^\(read\|write\|select\)' "$<" > "$@"

%.iosum: %.iotrace
	tools/iosummary.pl < "$<" > "$@"

%.cache: % $(TESTFILECACHE)
	valgrind --tool=cachegrind "$<" < $(TESTFILECACHE) 2> "$@"

#########################################################

.PHONY:	zipdata help cleartime clean distclean

sysinfo:
	hostname		>  sysinfo
	cat /etc/*release	>> sysinfo
	@echo			>> sysinfo
	uname -a		>> sysinfo
	@echo			>> sysinfo
	cat /proc/cpuinfo	>> sysinfo
	$(GHC) --version	>> sysinfo
	echo			>> sysinfo
	$(CC) --version		>> sysinfo

# This variable makes testing with weird /proc/cpuinfo files easier
CPUINFO=/proc/cpuinfo

platforminfo:
	hostname		>  platforminfo
	(printf 'ghc '; ($(GHC) --version  | sed -ne 's/^The.*version //p')) >> platforminfo
	cat $(CPUINFO) | sed -ne '/model name.*:/ { s/model name.*: //p; q}' >> platforminfo
	printf "%s MHz\n" `cat $(CPUINFO) | sed -ne '/cpu MHz.*:/ { s/cpu MHz.*: //p; q}'` >> platforminfo
	printf "TESTKIND=$(TESTKIND)\n" >> platforminfo
	printf "SUFFIX=$(SUFFIX)\n"     >> platforminfo

zipdata: time stat mem strace iotrace iosum sysinfo report.txt
	rm -f ghc-measurements.tar.gz
	rm -rf ghc-measurements
	mkdir -p ghc-measurements
	cp --parents \
		$(addprefix */*, .time .stat .mem .iosum) sysinfo report.txt platforminfo \
		ghc-measurements
	printf "%s " "$(HSPROGS)" "$(HANDPROGS)" "$(CPROGS)" > ghc-measurements/progs
	tar -zcf ghc-measurements.tar.gz ghc-measurements
	rm -rf ghc-measurements

help:
	@echo 'Measurements of very simple string I/O and parsing.'
	@printf '  (%d benchmarks, %s)\n' `echo $(HSPROGS) $(HANDPROGS) $(CPROGS) | wc -w` "$(HANDTEXT)"
	@echo ''
	@echo '  phase1 -- preparation + measurements that can run in background'
	@echo '  phase2 -- measurements that should run on unloaded machine'
	@echo '  redophase2 -- rerun phase2'
	@echo ''
	@echo '  doc, [ASCII=1] report, lastreport - reports'
	@echo '  zipdata -- zip up measurements (to ghc-measurements.tar.gz)'
	@echo ''
	@echo '  prog,core,stg,cmm,asm,dis,discut'
	@echo '     -- compile, compile to core/stg/cmm/asm, disassemble, cut out main loop'
	@echo '  time,stat,mem,strace,iotrace,iosum,cache'
	@echo '     -- measure run-time, GHC heap + OS mem, syscalls, I/O patterns, cache'
	@echo ''
	@echo '  cleartime, clean, distclean -- delete measurements etc'
	@echo ''
	@echo '  TESTKIND=(SMOKETEST,NORMAL,THOROUGH), defaults to $(TESTKINDDEFAULT)'
	@echo '  STRACE=OLD, defaults to NEW'

cleartime:
	rm -f */*.time

clean:
	# keep and hand/*.s !
	rm -rf	*/*.hi */*.o *.o					 \
		*/*.core */*.stg */*.cmm hs/*.s c/*.s */*.dis */*.discut \
		*/*.hcr							 \
		*/*.time */*.stat					 \
		*/*.real						 \
		*/*.strace */*.iotrace */*.iosum			 \
		*/*.mem */*.cache cachegrind.out.*		 	 \
		*/*.doc	*/*.srctimespace				 \
		tmp.strace tmp						 \
		tools/eatmem tools/pause-at-end.so			 \
		$(HSPROGS) $(CPROGS) $(HANDPROGS) $(RMPROGS) a.out	 \
		testfiles/						 \
		ghc-measurements/					 \
		sysinfo platforminfo docs xx.ps

distclean:	clean
	rm -f *~ */*~ report.txt ghc-measurements.tar.gz



More information about the Haskell-Cafe mailing list