Skip to content

Commit e04ecfd

Browse files
committed
Makefile: native-tuned default build (clang + -march=native) for plain 'make'
For the fully-default invocation only (no CC=, no CFLAGS=, and no runtime-dispatch build requested), prefer clang when available and add -march=native to the default CFLAGS. On an AVX-512 host this lifts the official benchmark (xxhsum -b, 100KB, pinned, best of 5) from 36.7 GB/s to 58.8 GB/s for XXH3_64b (+60%) and from 36.5 GB/s to 58.5 GB/s for XXH128 (+60%); XXH32/XXH64 are unchanged. Hash values are bit-identical in every configuration. Any explicit choice restores exact upstream behavior: CC=... and CFLAGS=... are never modified, and supplying either turns the tuning off. DISPATCH=1 (or the 'dispatch' target, or LIBXXH_DISPATCH=1) keeps the portable runtime-dispatch build and never receives -march=native; when native tuning is off, DISPATCH still defaults to 1 on x86/x64 exactly like today. Guards: NATIVE=0 opt-out (NATIVE=1 force-request); -march=native is skipped for cross-compilation (-target/--target/-arch/-m32/-mx32/-m16 anywhere in CC/CFLAGS/CPPFLAGS/MOREFLAGS, or CC -dumpmachine differing from uname -m), for compilers that reject the flag, and for multi-word or shell-unsafe CC values (probes only ever execute a plain single-word CC). Also fix gcc-og-test to pin CC=gcc so it keeps testing gcc when the default compiler is substituted. make check passes for default, CC=gcc, NATIVE=0, DISPATCH=1, and explicit-CFLAGS configurations.
1 parent c0b5ea9 commit e04ecfd

1 file changed

Lines changed: 170 additions & 14 deletions

File tree

Makefile

Lines changed: 170 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,175 @@ LIBVER_PATCH := $(shell echo $(LIBVER_PATCH_SCRIPT))
3737
LIBVER := $(LIBVER_MAJOR).$(LIBVER_MINOR).$(LIBVER_PATCH)
3838

3939
MAKEFLAGS += --no-print-directory
40-
CFLAGS ?= -O3
40+
41+
# ------------------------------------------------------------------
42+
# Performance tuning for the default build:
43+
#
44+
# For the fully-default invocation only (plain 'make': no CC and no
45+
# CFLAGS supplied on the command line or in the environment, and no
46+
# runtime-dispatch build requested), this Makefile:
47+
# 1. prefers clang over the built-in 'cc' default (clang generates a
48+
# measurably faster XXH3/XXH128 AVX-512 kernel than GCC on this
49+
# class of CPU), and
50+
# 2. adds -march=native to the *default* CFLAGS (the AVX-512 XXH3
51+
# kernel is ~60% faster than the portable SSE2 + runtime-dispatch
52+
# configuration).
53+
# Hash values are unaffected (bit-identical on every architecture).
54+
#
55+
# As soon as the user chooses anything (CC=... or CFLAGS=... on the
56+
# command line or in the environment), behavior is exactly upstream:
57+
# no compiler substitution, no -march=native, and the usual runtime
58+
# vector-dispatch default on x86/x64 (DISPATCH=1).
59+
#
60+
# Knobs:
61+
# NATIVE=0 never add -march=native. Use this (or supply explicit
62+
# CFLAGS=...) before 'make install' when the produced
63+
# binaries/libraries will be copied to other (possibly
64+
# older) machines - native binaries can raise SIGILL on
65+
# CPUs lacking the build host's ISA.
66+
# NATIVE=1 request -march=native even together with an explicit
67+
# CC=... . The flag is still skipped automatically when
68+
# the build cannot or must not use it: cross-compilation
69+
# (a --target/-target/-arch/-m32 style option anywhere in
70+
# CC/CFLAGS/CPPFLAGS/MOREFLAGS, or '$(CC) -dumpmachine'
71+
# reporting a machine different from 'uname -m'), a
72+
# compiler that rejects -march=native, a multi-word or
73+
# shell-unsafe CC value, or any runtime-dispatch build.
74+
# DISPATCH=1 runtime x86 vector-dispatch build. The Makefile then
75+
# never adds -march=native itself, so the baseline code
76+
# paths stay portable (the AVX2/AVX512 kernels are still
77+
# compiled with their own target attributes and selected
78+
# at runtime); explicit user CFLAGS/MOREFLAGS are, as
79+
# always, left untouched. The same no-native rule applies
80+
# to the 'dispatch' target and to LIBXXH_DISPATCH=1.
81+
# When native tuning is off, DISPATCH defaults to 1 on
82+
# x86/x64 exactly like upstream; when native tuning is
83+
# active it defaults to 0 (native codegen already covers
84+
# the host ISA, and this is the configuration the
85+
# documented performance numbers, ~59 GB/s XXH3_64b, were
86+
# measured with).
87+
# CFLAGS=... explicit CFLAGS keep working exactly as before and are
88+
# never modified; supplying them also restores the
89+
# upstream DISPATCH default.
90+
# MOREFLAGS=... appended after CFLAGS as before; unaffected.
91+
92+
# Remember whether the user chose a compiler, before any substitution.
93+
CC_USER_CHOSEN := $(if $(filter default,$(origin CC)),,1)
94+
ifeq ($(CC_USER_CHOSEN),)
95+
# 'command -v' only ever performs a PATH lookup (no user input is
96+
# re-evaluated by the shell), and the resolved absolute path is a
97+
# single safe token. The assignment is exported so that recursive
98+
# makes (tests/, tests/bench, tests/collisions, ...) build with the
99+
# same compiler as the top-level artifacts.
100+
CLANG_BIN := $(shell command -v clang 2> /dev/null || command -v clang-18 2> /dev/null)
101+
ifneq ($(CLANG_BIN),)
102+
export CC := $(CLANG_BIN)
103+
endif
104+
endif
105+
106+
# CC_SAFE=1 iff CC is a single word containing none of the characters a
107+
# POSIX shell treats specially (; | & $ ` ( ) < > ' " \ or whitespace)
108+
# and no pathname-expansion metacharacter (* ? [ ]). Computed with pure
109+
# make string functions: dangerous characters are removed one by one and
110+
# the result compared to the original - any difference means a
111+
# metacharacter was present. No shell is involved. The awkward
112+
# definitions below exist because some characters cannot appear
113+
# literally in a make function call argument.
114+
EMPTY :=
115+
SPACE := $(EMPTY) $(EMPTY)
116+
DOLLAR := $$
117+
LPAREN := (
118+
RPAREN := )
119+
CC_STRIPPED := $(CC)
120+
CC_STRIPPED := $(subst ;,,$(CC_STRIPPED))
121+
CC_STRIPPED := $(subst |,,$(CC_STRIPPED))
122+
CC_STRIPPED := $(subst &,,$(CC_STRIPPED))
123+
CC_STRIPPED := $(subst `,,$(CC_STRIPPED))
124+
CC_STRIPPED := $(subst ',,$(CC_STRIPPED))
125+
CC_STRIPPED := $(subst ",,$(CC_STRIPPED))
126+
CC_STRIPPED := $(subst \,,$(CC_STRIPPED))
127+
CC_STRIPPED := $(subst <,,$(CC_STRIPPED))
128+
CC_STRIPPED := $(subst >,,$(CC_STRIPPED))
129+
CC_STRIPPED := $(subst *,,$(CC_STRIPPED))
130+
CC_STRIPPED := $(subst ?,,$(CC_STRIPPED))
131+
CC_STRIPPED := $(subst [,,$(CC_STRIPPED))
132+
CC_STRIPPED := $(subst ],,$(CC_STRIPPED))
133+
CC_STRIPPED := $(subst $(DOLLAR),,$(CC_STRIPPED))
134+
CC_STRIPPED := $(subst $(SPACE),,$(CC_STRIPPED))
135+
CC_STRIPPED := $(subst $(LPAREN),,$(CC_STRIPPED))
136+
CC_STRIPPED := $(subst $(RPAREN),,$(CC_STRIPPED))
137+
CC_SAFE := $(and $(filter 1,$(words $(CC))),$(if $(filter x$(CC),x$(CC_STRIPPED)),1))
138+
139+
# NATIVE: 1 = add -march=native to the default CFLAGS (when usable),
140+
# 0 = never. Default 'auto' resolves to 1 only for the fully-default
141+
# invocation (CC not chosen by the user, CFLAGS not supplied).
142+
# NATIVE_ON is the internal result: non-empty iff -march=native will be
143+
# added. It is a separate variable because a command-line 'NATIVE=1'
144+
# would override any later file-level reassignment of NATIVE itself.
145+
NATIVE ?= auto
146+
NATIVE_ON :=
147+
ifeq ($(NATIVE),1)
148+
NATIVE_ON := 1
149+
else ifeq ($(NATIVE),auto)
150+
ifeq ($(CC_USER_CHOSEN)$(origin CFLAGS),undefined)
151+
NATIVE_ON := 1
152+
endif
153+
endif
154+
# A runtime-dispatch build must stay portable: never add the native flag
155+
# (this intentionally overrides even an explicit NATIVE=1).
156+
ifneq (,$(filter dispatch,$(MAKECMDGOALS))$(filter 1,$(DISPATCH) $(LIBXXH_DISPATCH)))
157+
NATIVE_ON :=
158+
endif
159+
# Cross-compilation guard #1: a target-selection option anywhere in the
160+
# compile line disables native tuning (covers --target=/-target, Apple
161+
# -arch, and -m32/-mx32/-m16 sub-architecture builds).
162+
ifneq (,$(findstring -target,$(CC) $(CFLAGS) $(CPPFLAGS) $(MOREFLAGS))$(filter -arch -m32 -mx32 -m16,$(CC) $(CFLAGS) $(CPPFLAGS) $(MOREFLAGS)))
163+
NATIVE_ON :=
164+
endif
165+
# Remaining guards need to run the compiler; $(CC) is only ever executed
166+
# here when CC_SAFE=1, i.e. a plain single word free of shell
167+
# metacharacters, so the probes cannot execute anything but the named
168+
# compiler. Probe output is filtered by 'tr' so only [A-Za-z0-9._-]
169+
# survive before make compares it.
170+
ifeq ($(NATIVE_ON),1)
171+
ifneq ($(CC_SAFE),1)
172+
NATIVE_ON := # non-plain CC -> stay portable, skip probes
173+
else ifeq ($(findstring $(shell uname -m),$(shell $(CC) -dumpmachine 2> /dev/null | tr -cd 'A-Za-z0-9._-')),)
174+
NATIVE_ON := # cross toolchain, or target undeterminable
175+
else ifneq ($(shell $(CC) -march=native -E -x c /dev/null > /dev/null 2>&1 && echo 1),1)
176+
NATIVE_ON := # compiler does not accept -march=native
177+
endif
178+
endif
179+
180+
# Runtime vector dispatch: upstream defaults DISPATCH to 1 on x86/x64
181+
# targets. That default is preserved in every configuration except the
182+
# fully-default native-tuned build, where it becomes 0 (the whole binary
183+
# is compiled for the host ISA anyway).
184+
detect_x86_arch = $(shell $(CC) -dumpmachine | grep -E 'i[3-6]86|x86_64')
185+
ifneq ($(strip $(call detect_x86_arch)),)
186+
#note: can be overridden at compile time, by setting DISPATCH=0/1
187+
ifeq ($(NATIVE_ON),1)
188+
DISPATCH ?= 0
189+
else
190+
DISPATCH ?= 1
191+
endif
192+
else
193+
ifeq ($(DISPATCH),1)
194+
$(info "Note: DISPATCH=1 is only supported on x86/x64 targets")
195+
endif
196+
override DISPATCH := 0
197+
endif
198+
199+
# Default CFLAGS. Explicit CFLAGS=... on the command line or in the
200+
# environment is never modified (the '?=' below only fills in a
201+
# make-default CFLAGS; when the user supplied CFLAGS, NATIVE_ON is off).
202+
ifeq ($(NATIVE_ON),1)
203+
CFLAGS ?= -O3 -march=native
204+
else
205+
CFLAGS ?= -O3
206+
endif
207+
# ------------------------------------------------------------------
208+
41209
DEBUGFLAGS+=-Wall -Wextra -Wconversion -Wcast-qual -Wcast-align -Wshadow \
42210
-Wstrict-aliasing=1 -Wswitch-enum -Wdeclaration-after-statement \
43211
-Wstrict-prototypes -Wundef -Wpointer-arith -Wformat-security \
@@ -54,18 +222,6 @@ else
54222
EXT =
55223
endif
56224

57-
# automatically enable runtime vector dispatch on x86/64 targets
58-
detect_x86_arch = $(shell $(CC) -dumpmachine | grep -E 'i[3-6]86|x86_64')
59-
ifneq ($(strip $(call detect_x86_arch)),)
60-
#note: can be overridden at compile time, by setting DISPATCH=0
61-
DISPATCH ?= 1
62-
else
63-
ifeq ($(DISPATCH),1)
64-
$(info "Note: DISPATCH=1 is only supported on x86/x64 targets")
65-
endif
66-
override DISPATCH := 0
67-
endif
68-
69225
ifeq ($(NODE_JS),1)
70226
# Link in unrestricted filesystem support
71227
LDFLAGS += -sNODERAWFS
@@ -372,7 +528,7 @@ clangtest:
372528
.PHONY: gcc-og-test
373529
gcc-og-test:
374530
@echo ---- test gcc -Og compilation ----
375-
CFLAGS="-Og -Wall -Wextra -Wundef -Wshadow -Wcast-align -Werror -fPIC" CPPFLAGS="-DXXH_NO_INLINE_HINTS" MOREFLAGS="-Werror" $(MAKE) all
531+
CFLAGS="-Og -Wall -Wextra -Wundef -Wshadow -Wcast-align -Werror -fPIC" CPPFLAGS="-DXXH_NO_INLINE_HINTS" MOREFLAGS="-Werror" $(MAKE) all CC=gcc
376532

377533
.PHONY: cxxtest
378534
cxxtest:

0 commit comments

Comments
 (0)