Reduce GNU Make latency 17% for cosmo

fgets() is now 4x faster which makes Make 2% faster. Landlock Make now
has a builtin $(uniq ...) function that uses critbit trees rather than
functional programming. Since uniq is the most important function this
optimization makes our cold start latency 15% faster.
This commit is contained in:
Justine Tunney 2022-08-19 15:29:24 -07:00
parent 8835b82a7c
commit d76dfadc7a
7 changed files with 96 additions and 11 deletions

2
.gitignore vendored
View file

@ -11,3 +11,5 @@ __pycache__
/TAGS /TAGS
/bx_enh_dbg.ini /bx_enh_dbg.ini
/tool/emacs/*.elc /tool/emacs/*.elc
/perf.data
/perf.data.old

View file

@ -12,5 +12,9 @@
tail = $(wordlist 2,$(words $1),$1) tail = $(wordlist 2,$(words $1),$1)
reverse = $(if $1,$(call reverse,$(call tail,$1)) $(firstword $1)) reverse = $(if $1,$(call reverse,$(call tail,$1)) $(firstword $1))
uniq = $(if $1,$(firstword $1) $(call uniq,$(filter-out $(firstword $1),$1)))
uniqr = $(if $1,$(call uniqr,$(filter-out $(firstword $1),$1)) $(firstword $1)) uniqr = $(if $1,$(call uniqr,$(filter-out $(firstword $1),$1)) $(firstword $1))
# polyfill uniq native (landlock make 1.4)
ifneq ($(call uniq,c b c a),c b a)
uniq = $(if $1,$(firstword $1) $(call uniq,$(filter-out $(firstword $1),$1)))
endif

View file

@ -16,8 +16,12 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE. PERFORMANCE OF THIS SOFTWARE.
*/ */
#include "libc/dce.h"
#include "libc/errno.h" #include "libc/errno.h"
#include "libc/intrin/kprintf.h"
#include "libc/macros.internal.h"
#include "libc/stdio/stdio.h" #include "libc/stdio/stdio.h"
#include "libc/str/str.h"
/** /**
* Reads line from stream. * Reads line from stream.
@ -33,11 +37,23 @@
* zero characters have been read * zero characters have been read
*/ */
char *fgets_unlocked(char *s, int size, FILE *f) { char *fgets_unlocked(char *s, int size, FILE *f) {
int c; int c, n;
char *p; char *p, *b, *t;
p = s; p = s;
if (size > 0) { if (size > 0) {
while (--size > 0) { while (--size > 0) {
if (!IsTiny() && f->beg < f->end) {
b = f->buf + f->beg;
n = MIN(f->end - f->beg, size);
if ((t = memchr(b, '\n', n))) {
n = t + 1 - b;
}
memcpy(p, b, n);
f->beg += n;
size -= n - 1;
p += n;
if (t) break;
} else {
if ((c = fgetc_unlocked(f)) == -1) { if ((c = fgetc_unlocked(f)) == -1) {
if (ferror_unlocked(f) == EINTR) { if (ferror_unlocked(f) == EINTR) {
continue; continue;
@ -48,6 +64,7 @@ char *fgets_unlocked(char *s, int size, FILE *f) {
*p++ = c & 255; *p++ = c & 255;
if (c == '\n') break; if (c == '\n') break;
} }
}
*p = '\0'; *p = '\0';
} }
return p > s ? s : NULL; return p > s ? s : NULL;

View file

@ -19,6 +19,7 @@
#include "libc/mem/mem.h" #include "libc/mem/mem.h"
#include "libc/runtime/gc.internal.h" #include "libc/runtime/gc.internal.h"
#include "libc/stdio/stdio.h" #include "libc/stdio/stdio.h"
#include "libc/testlib/ezbench.h"
#include "libc/testlib/hyperion.h" #include "libc/testlib/hyperion.h"
#include "libc/testlib/testlib.h" #include "libc/testlib/testlib.h"
@ -31,3 +32,19 @@ TEST(fgets, test) {
ASSERT_STREQ("John Keats\n", fgets(buf, sizeof(buf), f)); ASSERT_STREQ("John Keats\n", fgets(buf, sizeof(buf), f));
fclose(f); fclose(f);
} }
void Benchmark(void) {
FILE *f;
char *line;
char buf[512];
f = fmemopen(gc(strdup(kHyperion)), kHyperionSize, "r+");
for (;;) {
line = fgets(buf, sizeof(buf), f);
if (!line) break;
}
fclose(f);
}
BENCH(fgets, bench) {
EZBENCH2("fgets", donothing, Benchmark());
}

View file

@ -520,6 +520,7 @@ fatal_error_signal (int sig)
if (sig == SIGTERM || sig == SIGINT if (sig == SIGTERM || sig == SIGINT
|| sig == SIGHUP || sig == SIGHUP
|| sig == SIGQUIT || sig == SIGQUIT
|| sig == SIGPIPE
) )
{ {
struct child *c; struct child *c;

View file

@ -441,6 +441,7 @@ expand_argument (const char *str, const char *end)
r = allocated_variable_expand (tmp); r = allocated_variable_expand (tmp);
if (alloc)
free (alloc); free (alloc);
return r; return r;

View file

@ -23,6 +23,9 @@ this program. If not, see <http://www.gnu.org/licenses/>. */
#include "third_party/make/job.h" #include "third_party/make/job.h"
#include "third_party/make/os.h" #include "third_party/make/os.h"
#include "third_party/make/commands.h" #include "third_party/make/commands.h"
#include "libc/mem/critbit0.h"
#include "libc/log/rop.h"
#include "libc/runtime/runtime.h"
#include "third_party/make/debug.h" #include "third_party/make/debug.h"
struct function_table_entry struct function_table_entry
@ -1160,6 +1163,45 @@ func_sort (char *o, char **argv, const char *funcname UNUSED)
return o; return o;
} }
/*
chop argv[0] into words, and remove duplicates.
*/
static char *
func_uniq (char *o, char **argv, const char *funcname UNUSED)
{
char *p;
size_t len;
int mutated;
bool once = false;
const char *s = argv[0];
struct critbit0 t = {0};
while ((p = find_next_token (&s, &len)))
{
++s;
p[len] = 0;
RETURN_ON_ERROR ((mutated = critbit0_insert (&t, p)));
if (mutated)
{
if (once)
o = variable_buffer_output (o, " ", 1);
else
once = true;
o = variable_buffer_output (o, p, len);
}
}
critbit0_clear (&t);
return o;
OnError:
critbit0_clear (&t);
OSS (error, NILF, "%s: function failed: %s",
"uniq", strerror (errno));
exit (1);
}
/* /*
$(if condition,true-part[,false-part]) $(if condition,true-part[,false-part])
@ -2137,6 +2179,7 @@ static struct function_table_entry function_table_init[] =
FT_ENTRY ("value", 0, 1, 1, func_value), FT_ENTRY ("value", 0, 1, 1, func_value),
FT_ENTRY ("eval", 0, 1, 1, func_eval), FT_ENTRY ("eval", 0, 1, 1, func_eval),
FT_ENTRY ("file", 1, 2, 1, func_file), FT_ENTRY ("file", 1, 2, 1, func_file),
FT_ENTRY ("uniq", 0, 1, 1, func_uniq),
#ifdef EXPERIMENTAL #ifdef EXPERIMENTAL
FT_ENTRY ("eq", 2, 2, 1, func_eq), FT_ENTRY ("eq", 2, 2, 1, func_eq),
FT_ENTRY ("not", 0, 1, 1, func_not), FT_ENTRY ("not", 0, 1, 1, func_not),