1
0
Fork 0
mirror of https://github.com/ruby/ruby.git synced 2022-11-09 12:17:21 -05:00

File#rename releases GVL

rename(2) requires two pathname resolution operations which can
take considerable time on slow filesystems, release the GVL so
operations on other threads may proceed.

On fast, local filesystems, this change results in some slowdown
as shown by the new benchmark.  I consider the performance trade
off acceptable as cases are avoided.

benchmark results:
minimum results in each 3 measurements.
Execution time (sec)
name	trunk	built
file_rename	2.648	2.804

Speedup ratio: compare with the result of `trunk' (greater is better)
name	built
file_rename	0.944

* file.c (no_gvl_rename): new function
  (rb_file_s_rename): release GVL for renames
* benchmark/bm_file_rename.rb: new benchmark

git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@60088 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
This commit is contained in:
normal 2017-10-01 21:19:24 +00:00
parent f01846cdc8
commit e552afc30e
2 changed files with 29 additions and 4 deletions

View file

@ -0,0 +1,11 @@
# rename file
require 'tempfile'
max = 100_000
tmp = [ Tempfile.new('rename-a'), Tempfile.new('rename-b') ]
a, b = tmp.map { |x| x.path }
max.times do
File.rename(a, b)
File.rename(b, a)
end
tmp.each { |t| t.close! }

22
file.c
View file

@ -2873,6 +2873,19 @@ rb_file_s_unlink(int argc, VALUE *argv, VALUE klass)
return apply2files(unlink_internal, argc, argv, 0);
}
struct rename_args {
const char *src;
const char *dst;
};
static void *
no_gvl_rename(void *ptr)
{
struct rename_args *ra = ptr;
return (void *)(VALUE)rename(ra->src, ra->dst);
}
/*
* call-seq:
* File.rename(old_name, new_name) -> 0
@ -2886,19 +2899,20 @@ rb_file_s_unlink(int argc, VALUE *argv, VALUE klass)
static VALUE
rb_file_s_rename(VALUE klass, VALUE from, VALUE to)
{
const char *src, *dst;
struct rename_args ra;
VALUE f, t;
FilePathValue(from);
FilePathValue(to);
f = rb_str_encode_ospath(from);
t = rb_str_encode_ospath(to);
src = StringValueCStr(f);
dst = StringValueCStr(t);
ra.src = StringValueCStr(f);
ra.dst = StringValueCStr(t);
#if defined __CYGWIN__
errno = 0;
#endif
if (rename(src, dst) < 0) {
if ((int)(VALUE)rb_thread_call_without_gvl(no_gvl_rename, &ra,
RUBY_UBF_IO, 0) < 0) {
int e = errno;
#if defined DOSISH
switch (e) {