dumping ground for random patches and texts
 help / color / mirror / Atom feed
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).