From: Eric Wong <e@80x24.org>
To: spew@80x24.org
Subject: [PATCH] io.c (io_fwrite): temporarily freeze string when writing
Date: Sat, 31 Dec 2016 00:58:20 +0000 [thread overview]
Message-ID: <20161231005820.13258-1-e@80x24.org> (raw)
This avoids garbage from IO#write for [Bug #13085].
Memory usage from benchmark/bm_io_copy_stream_write.rb
is reduced greatly:
target 0: a (ruby 2.5.0dev (2016-12-30 trunk 57236) [x86_64-linux])
target 1: b (ruby 2.5.0dev (2016-12-30) [x86_64-linux])
Memory usage (last size) (B)
name a b
io_copy_stream_write 82235392.000 6651904.000
Memory consuming ratio (size) with the result of `a' (greater is better)
name b
io_copy_stream_write 12.363
There is also a speedup in execution time:
Execution time (sec)
name a b
io_copy_stream_write 0.380 0.143
Speedup ratio: compare with the result of `a' (greater is better)
name b
io_copy_stream_write 2.651
Caveat, there is one potential race condition:
If another thread calls String#freeze on the string we are
currently writing; we will blindly unfreeze it during
fwrite_unfreeze from ensure. However, I do not expect this to
be a real-world case.
Ideally, Ruby should have a way of detecting threads which
are not visible to other threads.
---
io.c | 43 +++++++++++++++++++++++++++++++++++++++++--
1 file changed, 41 insertions(+), 2 deletions(-)
diff --git a/io.c b/io.c
index 1074689..77bcbac 100644
--- a/io.c
+++ b/io.c
@@ -1419,6 +1419,37 @@ do_writeconv(VALUE str, rb_io_t *fptr, int *converted)
return str;
}
+struct fwrite_tmp_freeze {
+ VALUE str;
+ union {
+ rb_io_t *fptr;
+ long written;
+ } as;
+ int nosync;
+};
+
+static VALUE
+fwrite_freeze(VALUE p)
+{
+ struct fwrite_tmp_freeze *x = (struct fwrite_tmp_freeze *)p;
+ const char *ptr;
+ long len;
+
+ OBJ_FREEZE_RAW(x->str);
+ RSTRING_GETMEM(x->str, ptr, len);
+ x->as.written = io_binwrite(x->str, ptr, len, x->as.fptr, x->nosync);
+
+ return Qfalse;
+}
+
+static VALUE
+fwrite_unfreeze(VALUE str)
+{
+ FL_UNSET_RAW(str, FL_FREEZE);
+
+ return Qfalse;
+}
+
static long
io_fwrite(VALUE str, rb_io_t *fptr, int nosync)
{
@@ -1432,8 +1463,16 @@ io_fwrite(VALUE str, rb_io_t *fptr, int nosync)
str = do_writeconv(str, fptr, &converted);
if (converted)
OBJ_FREEZE(str);
- else
- str = rb_str_new_frozen(str);
+ else if (!OBJ_FROZEN_RAW(str)) {
+ struct fwrite_tmp_freeze x;
+
+ x.str = str;
+ x.as.fptr = fptr;
+ x.nosync = nosync;
+ rb_ensure(fwrite_freeze, (VALUE)&x, fwrite_unfreeze, str);
+
+ return x.as.written;
+ }
return io_binwrite(str, RSTRING_PTR(str), RSTRING_LEN(str),
fptr, nosync);
--
EW
reply other threads:[~2016-12-31 0:58 UTC|newest]
Thread overview: [no followups] expand[flat|nested] mbox.gz Atom feed
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20161231005820.13258-1-e@80x24.org \
--to=e@80x24.org \
--cc=spew@80x24.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).