From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on starla X-Spam-Level: X-Spam-Status: No, score=-1.1 required=3.0 tests=DKIM_SIGNED,DKIM_VALID, DKIM_VALID_AU,MAILING_LIST_MULTI,SPF_HELO_PASS,SPF_PASS, T_SCC_BODY_TEXT_LINE autolearn=ham autolearn_force=no version=3.4.6 Received: from nue.mailmanlists.eu (nue.mailmanlists.eu [IPv6:2a01:4f8:1c0c:6b10::1]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) by dcvr.yhbt.net (Postfix) with ESMTPS id C82E21F451 for ; Mon, 8 Jan 2024 20:02:42 +0000 (UTC) Authentication-Results: dcvr.yhbt.net; dkim=pass (1024-bit key; secure) header.d=ml.ruby-lang.org header.i=@ml.ruby-lang.org header.a=rsa-sha256 header.s=mail header.b=aJR8rHKe; dkim=fail reason="signature verification failed" (2048-bit key; unprotected) header.d=ruby-lang.org header.i=@ruby-lang.org header.a=rsa-sha256 header.s=s1 header.b=tuAMfOgn; dkim-atps=neutral Received: from nue.mailmanlists.eu (localhost [127.0.0.1]) by nue.mailmanlists.eu (Postfix) with ESMTP id D49F981CAE; Mon, 8 Jan 2024 20:02:33 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ml.ruby-lang.org; s=mail; t=1704744153; bh=LjjqAA/pqwvCfGFfN+Isqn6pLeio4Rf665UKP6SD6bw=; h=Date:References:To:Reply-To:Subject:List-Id:List-Archive: List-Help:List-Owner:List-Post:List-Subscribe:List-Unsubscribe: From:Cc:From; b=aJR8rHKeLe5bVRch9r437wJnO/RPMHPb+2BbVc9DXn8xTgkJEJvMk4XEM8kKfmfTF EDcXoy9lqdgPOT+2E7y5UfYHPyrxy1C8hxStEgeONiWMPmrK6KwDzs35XPFdxpA34I uXZfZPdykBVZkka2VagraPpPpztCOqgj/ufONHzU= Received: from csnrwnwx.outbound-mail.sendgrid.net (csnrwnwx.outbound-mail.sendgrid.net [198.37.146.154]) by nue.mailmanlists.eu (Postfix) with ESMTPS id 476E881CAA for ; Mon, 8 Jan 2024 20:02:30 +0000 (UTC) Authentication-Results: nue.mailmanlists.eu; dkim=pass (2048-bit key; unprotected) header.d=ruby-lang.org header.i=@ruby-lang.org header.a=rsa-sha256 header.s=s1 header.b=tuAMfOgn; dkim-atps=neutral DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=ruby-lang.org; h=from:references:subject:mime-version:content-type: content-transfer-encoding:list-id:to:cc:content-type:from:subject:to; s=s1; bh=OWcYx4yh/rFBubodelLLz1WwmkiDTrC83gxgCIDnO8Q=; b=tuAMfOgnCVeKa2LoKo4rE6R8ggvPspfQSdufI+yhJ1mjKw5g3cnsMX0xhIOaHRNjn3Q3 pWzVhEiv3A+1EeaZPHpcpAwkEyRRR+M9b0Ppl6gdrcl0w5J3NTY5sBSXrAcwqehxc2NG5J zwnk1RVUFgZfD05oFdcSGJhFRIHEL5Bn/v//N6PbhDUiRw8LcRYEe6exbi4DU5W1zs+rwY YQMQauHCB52kFBrr+uRITYELhw1bruuyP2Q/Pvv8j8t0kJu0PqWw1+QkV4UiWf4orliLAG 7xTeIlADFl/9gvfSsYIgIZroLIpriLJnu6cXvqec0ylfnuIAVUxV5NrJokE9bCuA== Received: by filterdrecv-656b5b4c75-gb89s with SMTP id filterdrecv-656b5b4c75-gb89s-1-659C54D4-3 2024-01-08 20:02:28.146337574 +0000 UTC m=+7176156.943087216 Received: from herokuapp.com (unknown) by geopod-ismtpd-16 (SG) with ESMTP id xa_K5EpBT2mq3PIofP_0qw for ; Mon, 08 Jan 2024 20:02:28.087 +0000 (UTC) Date: Mon, 08 Jan 2024 20:02:28 +0000 (UTC) Message-ID: References: Mime-Version: 1.0 X-Redmine-Project: ruby-master X-Redmine-Issue-Tracker: Feature X-Redmine-Issue-Id: 20066 X-Redmine-Issue-Author: jeremyevans0 X-Redmine-Sender: jeremyevans0 X-Mailer: Redmine X-Redmine-Host: bugs.ruby-lang.org X-Redmine-Site: Ruby Issue Tracking System X-Auto-Response-Suppress: All Auto-Submitted: auto-generated X-Redmine-MailingListIntegration-Message-Ids: 92643 X-SG-EID: =?us-ascii?Q?zy3UxWTRryXcrjCh7SV39ZkShJ1PHkEOmoUEekBgd8G8RAJk=2FsUOlbLwuWEoNU?= =?us-ascii?Q?BxG3BBFmcsr21OPpQwa7zNRSwswJm1szgMd841w?= =?us-ascii?Q?HPqc4P4VHBLPkLInWhhsLcOlhYSxW9QXIls2Nyu?= =?us-ascii?Q?6b26+3Jcxlz1XcmQ=2FNkkAu5EAvPkaiLLmgwMLnm?= =?us-ascii?Q?N2B4vYwLwD66aXYby39eCHDlifACIUF=2FWrRG0KJ?= =?us-ascii?Q?CsrOF2SmpNRRtvWNK6MKqn=2Fdv5gUBA3vSkir3TY?= =?us-ascii?Q?Htx3OxXnakUvjlvHRaxnQ=3D=3D?= To: ruby-core@ml.ruby-lang.org X-Entity-ID: b/2+PoftWZ6GuOu3b0IycA== Message-ID-Hash: TMIO54LB7ZBCZ64X5HZII77PCSDAC7BC X-Message-ID-Hash: TMIO54LB7ZBCZ64X5HZII77PCSDAC7BC X-MailFrom: bounces+313651-b711-ruby-core=ml.ruby-lang.org@em5188.ruby-lang.org X-Mailman-Rule-Misses: dmarc-mitigation; no-senders; approved; emergency; loop; banned-address; member-moderation; nonmember-moderation; administrivia; implicit-dest; max-recipients; max-size; news-moderation; no-subject; digests; suspicious-header X-Mailman-Version: 3.3.3 Precedence: list Reply-To: Ruby developers Subject: [ruby-core:116082] [Ruby master Feature#20066] Reduce Implicit Array/Hash Allocations For Method Calls Involving Splats List-Id: Ruby developers Archived-At: List-Archive: List-Help: List-Owner: List-Post: List-Subscribe: List-Unsubscribe: From: "jeremyevans0 (Jeremy Evans) via ruby-core" Cc: "jeremyevans0 (Jeremy Evans)" Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit Issue #20066 has been updated by jeremyevans0 (Jeremy Evans). Dan0042 (Daniel DeLorme) wrote in #note-1: > These are very nice optimizations, though they lead me to wonder: would it be possible to also optimize `def foo(**x) = bar(**x)` in the same way that `def foo(&x) = bar(&x)` is currently optimized? If a named block param can be optimized to avoid "materialization" in this case, I think it should be possible to do the same with a named splat param, and have it become equivalent to `def foo(**) = bar(**)`. This would have the nice benefit that existing forwarding code with named params would become faster, without having to convert to anonymous params. See the commit message for why you cannot do this for named variables (at least, without escape analysis): https://github.com/ruby/ruby/commit/7d14dc8983b4d279d22bbbb779e94e7a01a7e88f.patch ---------------------------------------- Feature #20066: Reduce Implicit Array/Hash Allocations For Method Calls Involving Splats https://bugs.ruby-lang.org/issues/20066#change-106077 * Author: jeremyevans0 (Jeremy Evans) * Status: Open * Priority: Normal ---------------------------------------- I have submitted a pull request (https://github.com/ruby/ruby/pull/9247) to reduce implicit array and hash allocations for method calls involving splats. The following optimizations are included: VM_CALL_ARGS_SPLAT_MUT callinfo flag This is similar to the VM_CALL_KW_SPLAT_MUT flag added in Ruby 3.0. This makes it so if the caller-side allocates an array for the method call, the flag is used to signal to the callee that it can reuse the allocated array and does not need to duplicate it. concattoarray VM instruction This instruction is similar to concatarray, but assumes the object being concatenated to is already a mutable array (such as those created by the splatarray VM instruction). This optimizes method calls with multiple splats such as `f(*a,*a,*a)` (which previously allocated 3 arrays), allocating a single array instead of an array per splatted array. pushtoarray VM instruction This is similar, but handles non-splat arguments after a splat. Previously, the VM would wrap those arguments in an array using newarray, and then call concatarray, such that `f(*a, a)` allocated 3 arrays caller-side. This instruction just appends to the mutable array, reducing the number of arrays allocated to 1. Allocationless Anonymous Splat Forwarding This allows `def f(*, **) end` to not allocate an array or hash callee side. This works because it is not possible to mutate the local variables, only pass them as splats to other methods. This can make the following call chain allocation less: ```ruby def f(a, b: 1) end def g(*, **) f(*, **) end ea a = [1] kw = {b: 2} g(*a, **kw) # No allocations in this call ``` Switch ... argument forwards to not use ruby2_keywords Using ruby2_keywords has probably been slower since Koichi's changes early in the Ruby 3.3 development cycle to not combine keyword splats into the positional splat array. This removes the FORWARD_ARGS_WITH_RUBY2_KEYWORDS define, so that `def f(...) end` operates similarly to `def f(*, **) end`, allowing allocationless splat forwarding Reduce array and hash allocations for nested argument forwarding calls This uses a combination of frame flags and callinfo flags to track mutability of anonymous splat variables. It can make it so the following call example only allocates a 1 array and 1 hash: ```ruby def m1(*args, **kw) end def m2(...) m1(...) end def m3(*, **) m2(*, **) end m3(1, a: 1) # 1 array and 1 hash allocated ``` In the above example, the call to `m3` allocates an array (`[1]`) and a hash (`{a: 1}`), but the call to `m2` passes them as mutable splats, `m2` treats them as mutable splats when calling `m1`, and `m1` reuses the array that `m3` allocated for `args` and the hash that `m3` allocated for `kw`. I created a benchmark for all of these changes. In the method calls optimized by these changes, it is significantly faster: ``` named_multi_arg_splat after: 5344097.6 i/s before: 3088134.0 i/s - 1.73x slower named_post_splat after: 5401882.3 i/s before: 2629321.8 i/s - 2.05x slower anon_arg_splat after: 12242780.9 i/s before: 6845413.2 i/s - 1.79x slower anon_arg_kw_splat after: 11277398.7 i/s before: 4329509.4 i/s - 2.60x slower anon_multi_arg_splat after: 5132699.5 i/s before: 3018103.7 i/s - 1.70x slower anon_post_splat after: 5602915.1 i/s before: 2645185.5 i/s - 2.12x slower anon_kw_splat after: 15403727.3 i/s before: 6249504.6 i/s - 2.46x slower anon_fw_to_named_splat after: 2985715.3 i/s before: 2049159.9 i/s - 1.46x slower anon_fw_to_named_no_splat after: 2941030.4 i/s before: 2100380.0 i/s - 1.40x slower fw_to_named_splat after: 2801008.7 i/s before: 2012416.4 i/s - 1.39x slower fw_to_named_no_splat after: 2742670.4 i/s before: 1957707.2 i/s - 1.40x slower fw_to_anon_to_named_splat after: 2309246.6 i/s before: 1375924.6 i/s - 1.68x slower fw_to_anon_to_named_no_splat after: 2193227.6 i/s before: 1351184.1 i/s - 1.62x slower ``` Only fallout from these changes: * Minor change to AST output for `...` not using `ruby2_keywords` * Prism and rbs need updating for `...` not using `ruby2_keywords` * typeprof need updating for new VM instructions (at least pushtoarray) VM_CALL_ARGS_SPLAT_MUT, concattoarray, and pushtoarray only affect uncommon callsites (multiple splats, argument after splat). Other commits only optimize calls to methods using anonymous splats or `...` argument forwarding. Previously, there was no performance reason to use anonymous splats or `...` argument forwarding, but with this change, using them can be faster, and can offer a new way for users to optimize their code. In my opinion, this is too late for consideration in Ruby 3.3, but it could be considered for Ruby 3.4. -- https://bugs.ruby-lang.org/ ______________________________________________ ruby-core mailing list -- ruby-core@ml.ruby-lang.org To unsubscribe send an email to ruby-core-leave@ml.ruby-lang.org ruby-core info -- https://ml.ruby-lang.org/mailman3/postorius/lists/ruby-core.ml.ruby-lang.org/