From 7d7912d17db277c2c3e246c07be16ea78b751ba1 Mon Sep 17 00:00:00 2001 From: Hiroshi Shirosaki Date: Mon, 11 Sep 2017 23:11:21 +0900 Subject: [PATCH] dir.c: performance fix with braces using cache Cache real basename result to avoid slow system call. --- dir.c | 78 +++++++++++++++++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 65 insertions(+), 13 deletions(-) diff --git a/dir.c b/dir.c index b7afaec4e0..e026ccbcc0 100644 --- a/dir.c +++ b/dir.c @@ -1776,6 +1776,36 @@ replace_real_basename(char *path, long base, rb_encoding *enc, int norm_p, int f # error not implemented #endif +#if defined HAVE_GETATTRLIST || defined _WIN32 +struct real_path { + char *path; + rb_pathtype_t type; +}; + +static char * +replace_real_basename_with_cache(char *path, long base, rb_encoding *enc, int norm_p, int flags, + rb_pathtype_t *type, st_table *cached_paths) +{ + struct real_path *real_path; + st_data_t key = (st_data_t)path; + + if (cached_paths && st_lookup(cached_paths, key, (st_data_t *)&real_path)) { + path = ruby_strdup(real_path->path); + *type = real_path->type; + } + else { + path = replace_real_basename(path, base, enc, norm_p, flags, type); + if (cached_paths) { + real_path = GLOB_ALLOC(struct real_path); + real_path->path = ruby_strdup(path); + real_path->type = *type; + st_insert(cached_paths, key, (st_data_t)real_path); + } + } + return path; +} +#endif + #ifndef S_ISDIR # define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR) #endif @@ -1888,6 +1918,7 @@ glob_helper( int flags, const ruby_glob_funcs_t *funcs, VALUE arg, + st_table *cached_paths, rb_encoding *enc) { struct stat st; @@ -2093,8 +2124,8 @@ glob_helper( } status = glob_helper(fd, buf, baselen, name - buf - baselen + namlen, 1, - new_pathtype, new_beg, new_end, - flags, funcs, arg, enc); + new_pathtype, new_beg, new_end, flags, funcs, + arg, cached_paths, enc); GLOB_FREE(buf); GLOB_FREE(new_beg); if (status) break; @@ -2152,15 +2183,17 @@ glob_helper( #if USE_NAME_ON_FS == USE_NAME_ON_FS_REAL_BASENAME if ((*cur)->type == ALPHA) { long base = pathlen + (dirsep != 0); - buf = replace_real_basename(buf, base, enc, IF_NORMALIZE_UTF8PATH(1)+0, - flags, &new_pathtype); + buf = replace_real_basename_with_cache(buf, base, enc, + IF_NORMALIZE_UTF8PATH(1)+0, + flags, &new_pathtype, + cached_paths); if (!buf) break; } #endif status = glob_helper(fd, buf, baselen, namelen + strlen(buf + pathlen), 1, new_pathtype, new_beg, new_end, - flags, funcs, arg, enc); + flags, funcs, arg, cached_paths, enc); GLOB_FREE(buf); GLOB_FREE(new_beg); if (status) break; @@ -2176,7 +2209,7 @@ glob_helper( static int ruby_glob0(const char *path, int fd, const char *base, int flags, const ruby_glob_funcs_t *funcs, VALUE arg, - rb_encoding *enc) + st_table *cached_paths, rb_encoding *enc) { struct glob_pattern *list; const char *root, *start; @@ -2211,7 +2244,7 @@ ruby_glob0(const char *path, int fd, const char *base, int flags, } status = glob_helper(fd, buf, baselen, n-baselen, dirsep, path_unknown, &list, &list + 1, - flags, funcs, arg, enc); + flags, funcs, arg, cached_paths, enc); glob_free_pattern(list); GLOB_FREE(buf); @@ -2225,7 +2258,7 @@ ruby_glob(const char *path, int flags, ruby_glob_func *func, VALUE arg) funcs.match = func; funcs.error = NULL; return ruby_glob0(path, AT_FDCWD, 0, flags & ~GLOB_VERBOSE, - &funcs, arg, rb_ascii8bit_encoding()); + &funcs, arg, NULL, rb_ascii8bit_encoding()); } static int @@ -2254,7 +2287,7 @@ rb_glob(const char *path, void (*func)(const char *, VALUE, void *), VALUE arg) args.enc = rb_ascii8bit_encoding(); status = ruby_glob0(path, AT_FDCWD, 0, GLOB_VERBOSE, &rb_glob_funcs, - (VALUE)&args, args.enc); + (VALUE)&args, NULL, args.enc); if (status) GLOB_JUMP_TAG(status); } @@ -2343,7 +2376,7 @@ glob_brace(const char *path, VALUE val, void *enc) { struct brace_args *arg = (struct brace_args *)val; - return ruby_glob0(path, AT_FDCWD, 0, arg->flags, &arg->funcs, arg->value, enc); + return ruby_glob0(path, AT_FDCWD, 0, arg->flags, &arg->funcs, arg->value, NULL, enc); } int @@ -2369,6 +2402,7 @@ struct push_glob_args { struct glob_args glob; int flags; int fd; + st_table *cached_paths; }; static int @@ -2377,14 +2411,24 @@ push_caller(const char *path, VALUE val, void *enc) struct push_glob_args *arg = (struct push_glob_args *)val; return ruby_glob0(path, arg->fd, arg->glob.base, arg->flags, &rb_glob_funcs, - (VALUE)&arg->glob, enc); + (VALUE)&arg->glob, arg->cached_paths, enc); } +#if defined HAVE_GETATTRLIST || defined _WIN32 +static int +free_cached_paths(st_data_t key, st_data_t val, st_data_t arg) +{ + GLOB_FREE((struct real_path *)val); + return ST_DELETE; +} +#endif + static int push_glob(VALUE ary, VALUE str, VALUE base, int flags) { struct push_glob_args args; rb_encoding *enc = rb_enc_get(str); + int status; #if defined _WIN32 || defined __APPLE__ str = rb_str_encode_ospath(str); @@ -2416,8 +2460,16 @@ push_glob(VALUE ary, VALUE str, VALUE base, int flags) enc = rb_utf8_encoding(); #endif - return ruby_brace_expand(RSTRING_PTR(str), flags, - push_caller, (VALUE)&args, enc, str); +#if defined HAVE_GETATTRLIST || defined _WIN32 + args.cached_paths = st_init_strtable(); +#endif + status = ruby_brace_expand(RSTRING_PTR(str), flags, + push_caller, (VALUE)&args, enc, str); +#if defined HAVE_GETATTRLIST || defined _WIN32 + st_foreach(args.cached_paths, free_cached_paths, 0); + st_free_table(args.cached_paths); +#endif + return status; } static VALUE -- 2.14.1.windows.1