ruby-core@ruby-lang.org archive (unofficial mirror)
 help / color / mirror / Atom feed
From: tenderlove@ruby-lang.org
To: ruby-core@ruby-lang.org
Subject: [ruby-core:102418] [Ruby master Feature#17613] Eliminate useless catch tables and nops from lambdas
Date: Mon, 08 Feb 2021 19:37:59 +0000 (UTC)	[thread overview]
Message-ID: <redmine.issue-17613.20210208193758.73@ruby-lang.org> (raw)
In-Reply-To: redmine.issue-17613.20210208193758.73@ruby-lang.org

Issue #17613 has been reported by tenderlovemaking (Aaron Patterson).

----------------------------------------
Feature #17613: Eliminate useless catch tables and nops from lambdas
https://bugs.ruby-lang.org/issues/17613

* Author: tenderlovemaking (Aaron Patterson)
* Status: Open
* Priority: Normal
----------------------------------------
This patch frees catch tables on iseqs that don't use the catch tables.  It also eliminates `nop` instructions from lambdas that don't need them.

Before this patch, lambdas have a "prelude nop" that is used for catch table entries:

```
$ ruby --dump=insn -e '1.times { |x| puts x }'
== disasm: #<ISeq:<main>@-e:1 (1,0)-(1,22)> (catch: FALSE)
== catch table
| catch type: break  st: 0000 ed: 0004 sp: 0000 cont: 0004
| == disasm: #<ISeq:block in <main>@-e:1 (1,8)-(1,22)> (catch: FALSE)
| == catch table
| | catch type: redo   st: 0001 ed: 0006 sp: 0000 cont: 0001
| | catch type: next   st: 0001 ed: 0006 sp: 0000 cont: 0006
| |------------------------------------------------------------------------
| local table (size: 1, argc: 1 [opts: 0, rest: -1, post: 0, block: -1, kw: -1@-1, kwrest: -1])
| [ 1] x@0<Arg>
| 0000 nop                                                              (   1)[Bc]
| 0001 putself                                [Li]
| 0002 getlocal_WC_0                          x@0
| 0004 opt_send_without_block                 <calldata!mid:puts, argc:1, FCALL|ARGS_SIMPLE>
| 0006 leave                                  [Br]
|------------------------------------------------------------------------
0000 putobject_INT2FIX_1_                                             (   1)[Li]
0001 send                                   <calldata!mid:times, argc:0>, block in <main>
0004 leave
```

But since this particular lambda doesn't use the catch tables, there is no reason to keep the catch table or the `nop` instruction.  This patch eliminates the `nop` instructions as well as the unused catch tables:

```
> ruby --dump=insn -e '1.times { |x| puts x }'
== disasm: #<ISeq:<main>@-e:1 (1,0)-(1,22)> (catch: FALSE)
0000 putobject_INT2FIX_1_                                             (   1)[Li]
0001 send                                   <calldata!mid:times, argc:0>, block in <main>
0004 leave

== disasm: #<ISeq:block in <main>@-e:1 (1,8)-(1,22)> (catch: FALSE)
local table (size: 1, argc: 1 [opts: 0, rest: -1, post: 0, block: -1, kw: -1@-1, kwrest: -1])
[ 1] x@0<Arg>
0000 putself                                                          (   1)[LiBc]
0001 getlocal_WC_0                          x@0
0003 opt_send_without_block                 <calldata!mid:puts, argc:1, FCALL|ARGS_SIMPLE>
0005 leave
```

It's not huge, but this frees about 600kb of catch tables on RailsBench.  Here is a histogram of the catch tables and sizes freed for RailsBench:

![Freed Catch Tables](https://user-images.githubusercontent.com/3124/107269241-2d723080-69fe-11eb-9bf7-64f102251df7.png)

The X axis is the catch table size, so the actually malloc'd size for 2 would be approximately `2 * sizeof(struct iseq_catch_table_entry)`.  So if we have 5 tables of size 2, that would be about `5 * 2 * sizeof(struct iseq_catch_table_entry)`.

The size of iseq_catch_table_entry is 32:

```
(lldb) p sizeof(struct iseq_catch_table_entry)
(unsigned long) $0 = 32
```

The total catch tables freed in RailsBench is 18275, so this frees about `18275 * 32` bytes, or about 584kb:

```
> sum(freed_table_sizes$V1)
[1] 18275
> sum(freed_table_sizes$V1) * 32
[1] 584800
```

Instruction Sequence size is also reduced due to `nop` elimination, but I didn't measure it.

Finally, this patch reduces `nop` calls on RailsBench from 6868813 ( 2.1%) to 2467772 ( 0.8%).

`nop` instructions on the `master` branch (`265c002239`):

```
[RUBY_INSNS_COUNTER]	nop                                  6868813 ( 2.1%)
```

`nop` instructions with this patch applied:

```
[RUBY_INSNS_COUNTER]	nop                                  2467772 ( 0.8%)
```

Pull request is [here](https://github.com/ruby/ruby/pull/4125)

---Files--------------------------------
0001-Eliminate-useless-catch-tables-and-nops-from-lambdas.patch (4.02 KB)


-- 
https://bugs.ruby-lang.org/

           reply	other threads:[~2021-02-08 19:38 UTC|newest]

Thread overview: expand[flat|nested]  mbox.gz  Atom feed
 [parent not found: <redmine.issue-17613.20210208193758.73@ruby-lang.org>]

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-list from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

  List information: https://www.ruby-lang.org/en/community/mailing-lists/

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=redmine.issue-17613.20210208193758.73@ruby-lang.org \
    --to=ruby-core@ruby-lang.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).