[前][次][番号順一覧][スレッド一覧]

ruby-changes:47997

From: normal <ko1@a...>
Date: Wed, 4 Oct 2017 09:04:56 +0900 (JST)
Subject: [ruby-changes:47997] normal:r60111 (trunk): Dir.empty? releases GVL

normal	2017-10-04 09:04:51 +0900 (Wed, 04 Oct 2017)

  New Revision: 60111

  https://svn.ruby-lang.org/cgi-bin/viewvc.cgi?view=revision&revision=60111

  Log:
    Dir.empty? releases GVL
    
    This converts all slow syscalls in the Dir.empty? implementation
    to release GVL.  We avoid unnecessarily GVL release and
    reacquire for each slow call (opendir, readdir, closedir) and
    instead only release and acquire the GVL once in the common
    case.
    
    Benchmark results show a small degradation in single-threaded
    performance:
    Execution time (sec)
    name	trunk	built
    dir_empty_p	0.689	0.758
    
    Speedup ratio: compare with the result of `trunk' (greater is better)
    name	built
    dir_empty_p	0.909
    
    * dir.c (rb_gc_for_fd_with_gvl): new function
      (nogvl_dir_empty_p): ditto
      (dir_s_empty_p): use new functions to release GVL
    * benchmark/bm_dir_empty_p.rb: new benchmark
      [ruby-core:83071] [Feature #13958]

  Added files:
    trunk/benchmark/bm_dir_empty_p.rb
  Modified files:
    trunk/dir.c
Index: benchmark/bm_dir_empty_p.rb
===================================================================
--- benchmark/bm_dir_empty_p.rb	(nonexistent)
+++ benchmark/bm_dir_empty_p.rb	(revision 60111)
@@ -0,0 +1,5 @@ https://github.com/ruby/ruby/blob/trunk/benchmark/bm_dir_empty_p.rb#L1
+require 'tmpdir'
+max = 100_000
+Dir.mktmpdir('bm_dir_empty_p') do |dir|
+  max.times { Dir.empty?(dir) }
+end
Index: dir.c
===================================================================
--- dir.c	(revision 60110)
+++ dir.c	(revision 60111)
@@ -13,6 +13,7 @@ https://github.com/ruby/ruby/blob/trunk/dir.c#L13
 
 #include "internal.h"
 #include "encindex.h"
+#include "ruby/thread.h"
 
 #include <sys/types.h>
 #include <sys/stat.h>
@@ -722,6 +723,8 @@ fundamental_encoding_p(rb_encoding *enc) https://github.com/ruby/ruby/blob/trunk/dir.c#L723
 #else
 # define READDIR(dir, enc) readdir((dir))
 #endif
+
+/* safe to use without GVL */
 static int
 to_be_skipped(const struct dirent *dp)
 {
@@ -2982,6 +2985,46 @@ rb_dir_exists_p(VALUE obj, VALUE fname) https://github.com/ruby/ruby/blob/trunk/dir.c#L2985
     return rb_file_directory_p(obj, fname);
 }
 
+static void *
+gc_for_fd_with_gvl(void *ptr)
+{
+    int *e = ptr;
+
+    return (void *)(rb_gc_for_fd(*e) ? Qtrue : Qfalse);
+}
+
+static void *
+nogvl_dir_empty_p(void *ptr)
+{
+    const char *path = ptr;
+    DIR *dir = opendir(path);
+    struct dirent *dp;
+    VALUE result = Qtrue;
+
+    if (!dir) {
+	int e = errno;
+	switch ((int)(VALUE)rb_thread_call_with_gvl(gc_for_fd_with_gvl, &e)) {
+	  default:
+	    dir = opendir(path);
+	    if (dir) break;
+	    e = errno;
+	    /* fall through */
+	  case 0:
+	    if (e == ENOTDIR) return (void *)Qfalse;
+	    errno = e; /* for rb_sys_fail_path */
+	    return (void *)Qundef;
+	}
+    }
+    while ((dp = READDIR(dir, NULL)) != NULL) {
+	if (!to_be_skipped(dp)) {
+	    result = Qfalse;
+	    break;
+	}
+    }
+    closedir(dir);
+    return (void *)result;
+}
+
 /*
  * call-seq:
  *   Dir.empty?(path_name)  ->  true or false
@@ -2992,9 +3035,7 @@ rb_dir_exists_p(VALUE obj, VALUE fname) https://github.com/ruby/ruby/blob/trunk/dir.c#L3035
 static VALUE
 rb_dir_s_empty_p(VALUE obj, VALUE dirname)
 {
-    DIR *dir;
-    struct dirent *dp;
-    VALUE result = Qtrue, orig;
+    VALUE result, orig;
     const char *path;
     enum {false_on_notdir = 1};
 
@@ -3023,28 +3064,11 @@ rb_dir_s_empty_p(VALUE obj, VALUE dirnam https://github.com/ruby/ruby/blob/trunk/dir.c#L3064
     }
 #endif
 
-    dir = opendir(path);
-    if (!dir) {
-	int e = errno;
-	switch (rb_gc_for_fd(e)) {
-	  default:
-	    dir = opendir(path);
-	    if (dir) break;
-	    e = errno;
-	    /* fall through */
-	  case 0:
-	    if (false_on_notdir && e == ENOTDIR) return Qfalse;
-	    rb_syserr_fail_path(e, orig);
-	}
-    }
-    errno = 0;
-    while ((dp = READDIR(dir, NULL)) != NULL) {
-	if (!to_be_skipped(dp)) {
-	    result = Qfalse;
-	    break;
-	}
+    result = (VALUE)rb_thread_call_without_gvl(nogvl_dir_empty_p, (void *)path,
+					    RUBY_UBF_IO, 0);
+    if (result == Qundef) {
+	rb_sys_fail_path(orig);
     }
-    closedir(dir);
     return result;
 }
 

--
ML: ruby-changes@q...
Info: http://www.atdot.net/~ko1/quickml/

[前][次][番号順一覧][スレッド一覧]