I noticed fixdep uses ~2% of cpu time in kernel build, in function use_config() fixdep spends a lot of cpu cycles in linear searches in its internal string array. With about 400 stored strings per dep file, this begins to be noticeable. Convert fixdep to use a hash table. kbuild results on my x86_64 allmodconfig Before patch : real 10m30.414s user 61m51.456s sys 8m28.200s real 10m12.334s user 61m50.236s sys 8m30.448s real 10m42.947s user 61m50.028s sys 8m32.380s After: real 10m8.180s user 61m22.506s sys 8m32.384s real 10m35.039s user 61m21.654s sys 8m32.212s real 10m14.487s user 61m23.498s sys 8m32.312s
Showing the changes to scripts/basic (1 of 1 file). Show all
Unified · Split
| @@ -138,38 +138,36 @@ static void print_cmdline(void) · 137 unmodified lines | ||
| 138 | 138 | printf("cmd_%s := %s\n\n", target, cmdline); |
| 139 | 139 | } |
| 140 | 140 | |
| 141 | −char * str_config = NULL; | |
| 142 | −int size_config = 0; | |
| 143 | −int len_config = 0; | |
| 141 | +struct item { | |
| 142 | + struct item *next; | |
| 143 | + unsigned int len; | |
| 144 | + unsigned int hash; | |
| 145 | + char name[0]; | |
| 146 | +}; | |
| 144 | 147 | |
| 145 | −/* | |
| 146 | − * Grow the configuration string to a desired length. | |
| 147 | − * Usually the first growth is plenty. | |
| 148 | − */ | |
| 149 | −static void grow_config(int len) | |
| 150 | −{ | |
| 151 | − while (len_config + len > size_config) { | |
| 152 | − if (size_config == 0) | |
| 153 | − size_config = 2048; | |
| 154 | − str_config = realloc(str_config, size_config *= 2); | |
| 155 | − if (str_config == NULL) | |
| 156 | − { perror("fixdep:malloc"); exit(1); } | |
| 157 | − } | |
| 158 | −} | |
| 148 | +#define HASHSZ 256 | |
| 149 | +static struct item *hashtab[HASHSZ]; | |
| 159 | 150 | |
| 151 | +static unsigned int strhash(const char *str, unsigned int sz) | |
| 152 | +{ | |
| 153 | + /* fnv32 hash */ | |
| 154 | + unsigned int i, hash = 2166136261U; | |
| 160 | 155 | |
| 156 | + for (i = 0; i < sz; i++) | |
| 157 | + hash = (hash ^ str[i]) * 0x01000193; | |
| 158 | + return hash; | |
| 159 | +} | |
| 161 | 160 | |
| 162 | 161 | /* |
| 163 | 162 | * Lookup a value in the configuration string. |
| 164 | 163 | */ |
| 165 | −static int is_defined_config(const char * name, int len) | |
| 164 | +static int is_defined_config(const char *name, int len, unsigned int hash) | |
| 166 | 165 | { |
| 167 | − const char * pconfig; | |
| 168 | − const char * plast = str_config + len_config - len; | |
| 169 | − for ( pconfig = str_config + 1; pconfig < plast; pconfig++ ) { | |
| 170 | − if (pconfig[ -1] == '\n' | |
| 171 | − && pconfig[len] == '\n' | |
| 172 | − && !memcmp(pconfig, name, len)) | |
| 166 | + struct item *aux; | |
| 167 | + | |
| 168 | + for (aux = hashtab[hash % HASHSZ]; aux; aux = aux->next) { | |
| 169 | + if (aux->hash == hash && aux->len == len && | |
| 170 | + memcmp(aux->name, name, len) == 0) | |
| 173 | 171 | return 1; |
| 174 | 172 | } |
| 175 | 173 | return 0; |
| @@ -178,13 +176,19 @@ static int is_defined_config(const char * name, int len) · 2 unmodified lines | ||
| 178 | 176 | /* |
| 179 | 177 | * Add a new value to the configuration string. |
| 180 | 178 | */ |
| 181 | −static void define_config(const char * name, int len) | |
| 179 | +static void define_config(const char *name, int len, unsigned int hash) | |
| 182 | 180 | { |
| 183 | − grow_config(len + 1); | |
| 181 | + struct item *aux = malloc(sizeof(*aux) + len); | |
| 184 | 182 | |
| 185 | − memcpy(str_config+len_config, name, len); | |
| 186 | − len_config += len; | |
| 187 | − str_config[len_config++] = '\n'; | |
| 183 | + if (!aux) { | |
| 184 | + perror("fixdep:malloc"); | |
| 185 | + exit(1); | |
| 186 | + } | |
| 187 | + memcpy(aux->name, name, len); | |
| 188 | + aux->len = len; | |
| 189 | + aux->hash = hash; | |
| 190 | + aux->next = hashtab[hash % HASHSZ]; | |
| 191 | + hashtab[hash % HASHSZ] = aux; | |
| 188 | 192 | } |
| 189 | 193 | |
| 190 | 194 | /* |
| @@ -192,40 +196,49 @@ static void define_config(const char * name, int len) · 1 unmodified line | ||
| 192 | 196 | */ |
| 193 | 197 | static void clear_config(void) |
| 194 | 198 | { |
| 195 | − len_config = 0; | |
| 196 | − define_config("", 0); | |
| 199 | + struct item *aux, *next; | |
| 200 | + unsigned int i; | |
| 201 | + | |
| 202 | + for (i = 0; i < HASHSZ; i++) { | |
| 203 | + for (aux = hashtab[i]; aux; aux = next) { | |
| 204 | + next = aux->next; | |
| 205 | + free(aux); | |
| 206 | + } | |
| 207 | + hashtab[i] = NULL; | |
| 208 | + } | |
| 197 | 209 | } |
| 198 | 210 | |
| 199 | 211 | /* |
| 200 | 212 | * Record the use of a CONFIG_* word. |
| 201 | 213 | */ |
| 202 | −static void use_config(char *m, int slen) | |
| 214 | +static void use_config(const char *m, int slen) | |
| 203 | 215 | { |
| 204 | − char s[PATH_MAX]; | |
| 205 | − char *p; | |
| 216 | + unsigned int hash = strhash(m, slen); | |
| 217 | + int c, i; | |
| 206 | 218 | |
| 207 | − if (is_defined_config(m, slen)) | |
| 219 | + if (is_defined_config(m, slen, hash)) | |
| 208 | 220 | return; |
| 209 | 221 | |
| 210 | − define_config(m, slen); | |
| 211 | − | |
| 212 | − memcpy(s, m, slen); s[slen] = 0; | |
| 222 | + define_config(m, slen, hash); | |
| 213 | 223 | |
| 214 | − for (p = s; p < s + slen; p++) { | |
| 215 | − if (*p == '_') | |
| 216 | − *p = '/'; | |
| 224 | + printf(" $(wildcard include/config/"); | |
| 225 | + for (i = 0; i < slen; i++) { | |
| 226 | + c = m[i]; | |
| 227 | + if (c == '_') | |
| 228 | + c = '/'; | |
| 217 | 229 | else |
| 218 | − *p = tolower((int)*p); | |
| 230 | + c = tolower(c); | |
| 231 | + putchar(c); | |
| 219 | 232 | } |
| 220 | − printf(" $(wildcard include/config/%s.h) \\\n", s); | |
| 233 | + printf(".h) \\\n"); | |
| 221 | 234 | } |
| 222 | 235 | |
| 223 | −static void parse_config_file(char *map, size_t len) | |
| 236 | +static void parse_config_file(const char *map, size_t len) | |
| 224 | 237 | { |
| 225 | − int *end = (int *) (map + len); | |
| 238 | + const int *end = (const int *) (map + len); | |
| 226 | 239 | /* start at +1, so that p can never be < map */ |
| 227 | − int *m = (int *) map + 1; | |
| 228 | − char *p, *q; | |
| 240 | + const int *m = (const int *) map + 1; | |
| 241 | + const char *p, *q; | |
| 229 | 242 | |
| 230 | 243 | for (; m < end; m++) { |
| 231 | 244 | if (*m == INT_CONF) { p = (char *) m ; goto conf; } |
| @@ -265,7 +278,7 @@ static int strrcmp(char *s, char *sub) · 33 unmodified lines | ||
| 265 | 278 | return memcmp(s + slen - sublen, sub, sublen); |
| 266 | 279 | } |
| 267 | 280 | |
| 268 | −static void do_config_file(char *filename) | |
| 281 | +static void do_config_file(const char *filename) | |
| 269 | 282 | { |
| 270 | 283 | struct stat st; |
| 271 | 284 | int fd; |