@@ -973,28 +973,32 @@ pub struct IseqProfile {
973973 /// Only instructions that have actually been profiled have entries here.
974974 entries : Vec < ProfileEntry > ,
975975
976- /// Method entries for `super` calls (stored as VALUE to be GC-safe)
977- super_cme : HashMap < YarvInsnIdx , TypeDistribution > ,
976+ /// Method entries for `super` calls (stored as VALUE to be GC-safe).
977+ /// Boxed because only ISEQs containing `invokesuper` ever use it, and an
978+ /// inline `HashMap` costs every payload 48 bytes to hold nothing.
979+ super_cme : Option < Box < HashMap < YarvInsnIdx , TypeDistribution > > > ,
978980
979981 /// Method-name symbols observed as the first argument of `send`/`__send__` call sites
980982 /// (stored as VALUE to be GC-safe)
981- send_mid : HashMap < YarvInsnIdx , TypeDistribution > ,
983+ send_mid : Option < Box < HashMap < YarvInsnIdx , TypeDistribution > > > ,
982984
983- /// Observed lengths of caller splat arrays for call instructions.
984- splat_lengths : HashMap < YarvInsnIdx , SplatLengthDistribution > ,
985+ /// Observed lengths of caller splat arrays for call instructions. Boxed for
986+ /// the same reason as `super_cme`.
987+ splat_lengths : Option < Box < HashMap < YarvInsnIdx , SplatLengthDistribution > > > ,
988+
989+ /// Callinfos observed in the `...` local at `sendforward` sites. Unlike the tables above,
990+ /// this one holds no objects: see `profile_forwarded_callinfo`.
991+ forwarded_cis : Option < Box < HashMap < YarvInsnIdx , ForwardedCiDistribution > > > ,
985992
986993 /// Block handlers observed at `invokeblock` sites (stored as VALUE to be GC-safe).
987994 /// Kept out of `opnd_types` so that the entry's operand slots profile the yielded
988995 /// arguments instead: a `yield` to a Symbol block sends to the first argument, and
989996 /// that send only specializes if the argument's class was profiled.
990- block_handlers : HashMap < YarvInsnIdx , TypeDistribution > ,
991- /// Callinfos observed in the `...` local at `sendforward` sites. Unlike the tables above,
992- /// this one holds no objects: see `profile_forwarded_callinfo`.
993- forwarded_cis : HashMap < YarvInsnIdx , ForwardedCiDistribution > ,
997+ block_handlers : Option < Box < HashMap < YarvInsnIdx , TypeDistribution > > > ,
994998
995999 /// Live-traffic re-profiling windows for `invokeblock` sites whose compiled dispatch keeps
9961000 /// reaching `rb_vm_invokeblock()`.
997- block_fallbacks : HashMap < YarvInsnIdx , BlockFallbackEntry > ,
1001+ block_fallbacks : Option < Box < HashMap < YarvInsnIdx , BlockFallbackEntry > > > ,
9981002
9991003 /// Dense copy of every object the distributions above reference, which is what
10001004 /// GC marking actually walks. See [`IseqProfile::marked_objects`].
@@ -1017,12 +1021,12 @@ impl IseqProfile {
10171021 pub fn new ( ) -> Self {
10181022 Self {
10191023 entries : Vec :: new ( ) ,
1020- super_cme : HashMap :: new ( ) ,
1021- send_mid : HashMap :: new ( ) ,
1022- splat_lengths : HashMap :: new ( ) ,
1023- block_handlers : HashMap :: new ( ) ,
1024- forwarded_cis : HashMap :: new ( ) ,
1025- block_fallbacks : HashMap :: new ( ) ,
1024+ super_cme : None ,
1025+ send_mid : None ,
1026+ splat_lengths : None ,
1027+ forwarded_cis : None ,
1028+ block_handlers : None ,
1029+ block_fallbacks : None ,
10261030 marked_objects : Vec :: new ( ) ,
10271031 // Nothing recorded yet, so the (empty) dense copy is already accurate.
10281032 marked_objects_stale : false ,
@@ -1053,47 +1057,47 @@ impl IseqProfile {
10531057 }
10541058 }
10551059
1056- /// Mutable access to the forwarded callinfo distributions. Needs no marking invalidation
1057- /// because a packed callinfo is an immediate, not an object, and a heap one is recorded as
1058- /// `None`.
1059- fn forwarded_cis_mut ( & mut self ) -> & mut HashMap < YarvInsnIdx , ForwardedCiDistribution > {
1060- & mut self . forwarded_cis
1061- }
1062-
10631060 /// Mutable access to the `invokesuper` method-entry distributions. Goes through
10641061 /// here so that recording a CME cannot skip invalidating the dense copy GC
10651062 /// marking walks.
10661063 fn super_cme_mut ( & mut self ) -> & mut HashMap < YarvInsnIdx , TypeDistribution > {
10671064 self . marked_objects_stale = true ;
1068- & mut self . super_cme
1065+ self . super_cme . get_or_insert_with ( Default :: default )
10691066 }
10701067
10711068 /// Mutable access to the `send`/`__send__` method-name distributions. Same reason
10721069 /// as [`Self::super_cme_mut`].
10731070 fn send_mid_mut ( & mut self ) -> & mut HashMap < YarvInsnIdx , TypeDistribution > {
10741071 self . marked_objects_stale = true ;
1075- & mut self . send_mid
1072+ self . send_mid . get_or_insert_with ( Default :: default )
10761073 }
10771074
10781075 /// Mutable access to the splat length distributions. Unlike the two above this
10791076 /// needs no invalidation: a `SplatLengthDistribution` holds array lengths, not
10801077 /// objects, so GC marking never looks at it.
10811078 fn splat_lengths_mut ( & mut self ) -> & mut HashMap < YarvInsnIdx , SplatLengthDistribution > {
1082- & mut self . splat_lengths
1079+ self . splat_lengths . get_or_insert_with ( Default :: default)
1080+ }
1081+
1082+ /// Mutable access to the forwarded callinfo distributions. Needs no marking invalidation
1083+ /// for the same reason `splat_lengths_mut` does not: a packed callinfo is an immediate, not
1084+ /// an object, and a heap one is recorded as `None`.
1085+ fn forwarded_cis_mut ( & mut self ) -> & mut HashMap < YarvInsnIdx , ForwardedCiDistribution > {
1086+ self . forwarded_cis . get_or_insert_with ( Default :: default)
10831087 }
10841088
10851089 /// Mutable access to the `invokeblock` block-handler distributions. Same reason
10861090 /// as [`Self::super_cme_mut`].
10871091 fn block_handlers_mut ( & mut self ) -> & mut HashMap < YarvInsnIdx , TypeDistribution > {
10881092 self . marked_objects_stale = true ;
1089- & mut self . block_handlers
1093+ self . block_handlers . get_or_insert_with ( Default :: default )
10901094 }
10911095
10921096 /// Mutable access to the `invokeblock` re-profiling windows. Same reason as
10931097 /// [`Self::super_cme_mut`]: the windows hold block handlers, which are objects.
10941098 fn block_fallbacks_mut ( & mut self ) -> & mut HashMap < YarvInsnIdx , BlockFallbackEntry > {
10951099 self . marked_objects_stale = true ;
1096- & mut self . block_fallbacks
1100+ self . block_fallbacks . get_or_insert_with ( Default :: default )
10971101 }
10981102
10991103 /// Record one block handler that reached a compiled `yield`'s generic fallback, and say
@@ -1206,7 +1210,7 @@ impl IseqProfile {
12061210 }
12071211
12081212 pub fn get_splat_length_summary ( & self , insn_idx : YarvInsnIdx ) -> Option < SplatLengthDistributionSummary > {
1209- self . splat_lengths . get ( & insn_idx)
1213+ self . splat_lengths . as_ref ( ) ? . get ( & insn_idx)
12101214 . map ( SplatLengthDistributionSummary :: new)
12111215 }
12121216
@@ -1220,29 +1224,29 @@ impl IseqProfile {
12201224 }
12211225 }
12221226
1223- /// The distribution of block handlers observed at an `invokeblock` site.
1224- pub fn get_block_handlers ( & self , insn_idx : YarvInsnIdx ) -> Option < TypeDistributionSummary > {
1225- self . block_handlers . get ( & insn_idx) . map ( TypeDistributionSummary :: new)
1226- }
1227-
1228- /// The distribution of callinfos a `sendforward` site was seen forwarding. See
1229- /// [`profile_forwarded_callinfo`].
1230- pub fn get_forwarded_callinfos ( & self , insn_idx : YarvInsnIdx ) -> Option < ForwardedCiDistributionSummary > {
1231- self . forwarded_cis . get ( & insn_idx) . map ( ForwardedCiDistributionSummary :: new)
1232- }
1233-
12341227 /// The whole distribution of frame method entries seen at an `invokesuper` site. A site with
12351228 /// more than one is a `super` inside a method body that several classes run, most often a
12361229 /// module method reached through more than one includer; each such method entry resolves
12371230 /// `super` to a different target.
12381231 pub fn get_super_method_entries ( & self , insn_idx : YarvInsnIdx ) -> Option < TypeDistributionSummary > {
1239- let entry = self . super_cme . get ( & insn_idx) ?;
1232+ let entry = self . super_cme . as_ref ( ) ? . get ( & insn_idx) ?;
12401233 Some ( TypeDistributionSummary :: new ( entry) )
12411234 }
12421235
12431236 /// Get the distribution of method-name symbols seen at a `send`/`__send__` call site.
12441237 pub fn get_send_method_names ( & self , insn_idx : YarvInsnIdx ) -> Option < TypeDistributionSummary > {
1245- self . send_mid . get ( & insn_idx) . map ( TypeDistributionSummary :: new)
1238+ self . send_mid . as_ref ( ) ?. get ( & insn_idx) . map ( TypeDistributionSummary :: new)
1239+ }
1240+
1241+ /// The distribution of callinfos a `sendforward` site was seen forwarding. See
1242+ /// [`profile_forwarded_callinfo`].
1243+ pub fn get_forwarded_callinfos ( & self , insn_idx : YarvInsnIdx ) -> Option < ForwardedCiDistributionSummary > {
1244+ self . forwarded_cis . as_ref ( ) ?. get ( & insn_idx) . map ( ForwardedCiDistributionSummary :: new)
1245+ }
1246+
1247+ /// The distribution of block handlers observed at an `invokeblock` site.
1248+ pub fn get_block_handlers ( & self , insn_idx : YarvInsnIdx ) -> Option < TypeDistributionSummary > {
1249+ self . block_handlers . as_ref ( ) ?. get ( & insn_idx) . map ( TypeDistributionSummary :: new)
12461250 }
12471251
12481252 /// Bytes this profile owns on the Rust heap, excluding the `IseqProfile`
@@ -1265,20 +1269,38 @@ impl IseqProfile {
12651269 }
12661270 }
12671271 }
1268- out. bytes += hash_table_bytes :: < ( YarvInsnIdx , TypeDistribution ) > ( self . super_cme . capacity ( ) ) ;
1269- out. bytes += hash_table_bytes :: < ( YarvInsnIdx , TypeDistribution ) > ( self . send_mid . capacity ( ) ) ;
1270- out. bytes += hash_table_bytes :: < ( YarvInsnIdx , SplatLengthDistribution ) > ( self . splat_lengths . capacity ( ) ) ;
1271- out. bytes += hash_table_bytes :: < ( YarvInsnIdx , ForwardedCiDistribution ) > ( self . forwarded_cis . capacity ( ) ) ;
1272- out. bytes += hash_table_bytes :: < ( YarvInsnIdx , TypeDistribution ) > ( self . block_handlers . capacity ( ) ) ;
1273- out. bytes += hash_table_bytes :: < ( YarvInsnIdx , BlockFallbackEntry ) > ( self . block_fallbacks . capacity ( ) ) ;
1274- // Boxed distribution tails, wherever they live.
1275- out. bytes += self . super_cme . values ( ) . map ( TypeDistribution :: heap_size) . sum :: < usize > ( ) ;
1276- out. bytes += self . send_mid . values ( ) . map ( TypeDistribution :: heap_size) . sum :: < usize > ( ) ;
1277- out. bytes += self . splat_lengths . values ( ) . map ( SplatLengthDistribution :: heap_size) . sum :: < usize > ( ) ;
1278- out. bytes += self . forwarded_cis . values ( ) . map ( ForwardedCiDistribution :: heap_size) . sum :: < usize > ( ) ;
1279- out. bytes += self . block_handlers . values ( ) . map ( TypeDistribution :: heap_size) . sum :: < usize > ( ) ;
1280- out. bytes += self . block_fallbacks . values ( )
1281- . map ( |entry| entry. dist . heap_size ( ) + entry. symbol_recv . heap_size ( ) ) . sum :: < usize > ( ) ;
1272+ // The boxed side tables: the `HashMap` header itself once boxed, the bucket
1273+ // array, and each distribution's own boxed tail.
1274+ if let Some ( table) = self . super_cme . as_ref ( ) {
1275+ out. bytes += size_of :: < HashMap < YarvInsnIdx , TypeDistribution > > ( )
1276+ + hash_table_bytes :: < ( YarvInsnIdx , TypeDistribution ) > ( table. capacity ( ) )
1277+ + table. values ( ) . map ( TypeDistribution :: heap_size) . sum :: < usize > ( ) ;
1278+ }
1279+ if let Some ( table) = self . send_mid . as_ref ( ) {
1280+ out. bytes += size_of :: < HashMap < YarvInsnIdx , TypeDistribution > > ( )
1281+ + hash_table_bytes :: < ( YarvInsnIdx , TypeDistribution ) > ( table. capacity ( ) )
1282+ + table. values ( ) . map ( TypeDistribution :: heap_size) . sum :: < usize > ( ) ;
1283+ }
1284+ if let Some ( table) = self . splat_lengths . as_ref ( ) {
1285+ out. bytes += size_of :: < HashMap < YarvInsnIdx , SplatLengthDistribution > > ( )
1286+ + hash_table_bytes :: < ( YarvInsnIdx , SplatLengthDistribution ) > ( table. capacity ( ) )
1287+ + table. values ( ) . map ( SplatLengthDistribution :: heap_size) . sum :: < usize > ( ) ;
1288+ }
1289+ if let Some ( table) = self . forwarded_cis . as_ref ( ) {
1290+ out. bytes += size_of :: < HashMap < YarvInsnIdx , ForwardedCiDistribution > > ( )
1291+ + hash_table_bytes :: < ( YarvInsnIdx , ForwardedCiDistribution ) > ( table. capacity ( ) )
1292+ + table. values ( ) . map ( ForwardedCiDistribution :: heap_size) . sum :: < usize > ( ) ;
1293+ }
1294+ if let Some ( table) = self . block_handlers . as_ref ( ) {
1295+ out. bytes += size_of :: < HashMap < YarvInsnIdx , TypeDistribution > > ( )
1296+ + hash_table_bytes :: < ( YarvInsnIdx , TypeDistribution ) > ( table. capacity ( ) )
1297+ + table. values ( ) . map ( TypeDistribution :: heap_size) . sum :: < usize > ( ) ;
1298+ }
1299+ if let Some ( table) = self . block_fallbacks . as_ref ( ) {
1300+ out. bytes += size_of :: < HashMap < YarvInsnIdx , BlockFallbackEntry > > ( )
1301+ + hash_table_bytes :: < ( YarvInsnIdx , BlockFallbackEntry ) > ( table. capacity ( ) )
1302+ + table. values ( ) . map ( |entry| entry. dist . heap_size ( ) + entry. symbol_recv . heap_size ( ) ) . sum :: < usize > ( ) ;
1303+ }
12821304 out
12831305 }
12841306
@@ -1294,25 +1316,25 @@ impl IseqProfile {
12941316 }
12951317 }
12961318
1297- for super_cme_values in self . super_cme . values ( ) {
1319+ for super_cme_values in self . super_cme . iter ( ) . flat_map ( |map| map . values ( ) ) {
12981320 for profiled_type in super_cme_values. each_item ( ) {
12991321 callback ( profiled_type. class )
13001322 }
13011323 }
13021324
1303- for handler_values in self . block_handlers . values ( ) {
1304- for profiled_type in handler_values . each_item ( ) {
1325+ for send_mid_values in self . send_mid . iter ( ) . flat_map ( |map| map . values ( ) ) {
1326+ for profiled_type in send_mid_values . each_item ( ) {
13051327 callback ( profiled_type. class )
13061328 }
13071329 }
13081330
1309- for send_mid_values in self . send_mid . values ( ) {
1310- for profiled_type in send_mid_values . each_item ( ) {
1331+ for handler_values in self . block_handlers . iter ( ) . flat_map ( |map| map . values ( ) ) {
1332+ for profiled_type in handler_values . each_item ( ) {
13111333 callback ( profiled_type. class )
13121334 }
13131335 }
13141336
1315- for fallback in self . block_fallbacks . values ( ) {
1337+ for fallback in self . block_fallbacks . iter ( ) . flat_map ( |map| map . values ( ) ) {
13161338 for profiled_type in fallback. dist . each_item ( ) . chain ( fallback. symbol_recv . each_item ( ) ) {
13171339 callback ( profiled_type. class )
13181340 }
@@ -1383,25 +1405,25 @@ impl IseqProfile {
13831405 }
13841406
13851407 // Update CME references if they move during compaction.
1386- for super_cme_values in self . super_cme . values_mut ( ) {
1408+ for super_cme_values in self . super_cme . iter_mut ( ) . flat_map ( |map| map . values_mut ( ) ) {
13871409 for ref mut profiled_type in super_cme_values. each_item_mut ( ) {
13881410 callback ( & mut profiled_type. class )
13891411 }
13901412 }
13911413
1392- for handler_values in self . block_handlers . values_mut ( ) {
1393- for ref mut profiled_type in handler_values . each_item_mut ( ) {
1414+ for send_mid_values in self . send_mid . iter_mut ( ) . flat_map ( |map| map . values_mut ( ) ) {
1415+ for ref mut profiled_type in send_mid_values . each_item_mut ( ) {
13941416 callback ( & mut profiled_type. class )
13951417 }
13961418 }
13971419
1398- for send_mid_values in self . send_mid . values_mut ( ) {
1399- for ref mut profiled_type in send_mid_values . each_item_mut ( ) {
1420+ for handler_values in self . block_handlers . iter_mut ( ) . flat_map ( |map| map . values_mut ( ) ) {
1421+ for ref mut profiled_type in handler_values . each_item_mut ( ) {
14001422 callback ( & mut profiled_type. class )
14011423 }
14021424 }
14031425
1404- for fallback in self . block_fallbacks . values_mut ( ) {
1426+ for fallback in self . block_fallbacks . iter_mut ( ) . flat_map ( |map| map . values_mut ( ) ) {
14051427 for ref mut profiled_type in fallback. dist . each_item_mut ( ) {
14061428 callback ( & mut profiled_type. class )
14071429 }
0 commit comments