@@ -29,6 +29,8 @@ static VALUE cYjitBlock;
29
29
static int64_t exit_op_count [VM_INSTRUCTION_SIZE ] = { 0 };
30
30
struct rb_yjit_runtime_counters yjit_runtime_counters = { 0 };
31
31
static VALUE cYjitCodeComment ;
32
+
33
+ extern const int rb_vm_max_insn_name_size ;
32
34
#endif
33
35
34
36
// Machine code blocks (executable memory)
@@ -707,15 +709,26 @@ comments_for(rb_execution_context_t *ec, VALUE self, VALUE start_address, VALUE
707
709
return comment_array ;
708
710
}
709
711
710
- // Primitive called in yjit.rb. Export all runtime counters as a Ruby hash.
712
+ // Primitive called in yjit.rb. Export all YJIT statistics as a Ruby hash.
711
713
static VALUE
712
- get_stat_counters (rb_execution_context_t * ec , VALUE self )
714
+ get_yjit_stats (rb_execution_context_t * ec , VALUE self )
713
715
{
714
716
#if RUBY_DEBUG
715
717
if (!rb_yjit_opts .gen_stats ) return Qnil ;
716
718
717
719
VALUE hash = rb_hash_new ();
718
720
RB_VM_LOCK_ENTER ();
721
+
722
+ {
723
+ VALUE key = ID2SYM (rb_intern ("inline_code_size" ));
724
+ VALUE value = LL2NUM ((long long )cb -> write_pos );
725
+ rb_hash_aset (hash , key , value );
726
+
727
+ key = ID2SYM (rb_intern ("outlined_code_size" ));
728
+ value = LL2NUM ((long long )ocb -> write_pos );
729
+ rb_hash_aset (hash , key , value );
730
+ }
731
+
719
732
{
720
733
int64_t * counter_reader = (int64_t * )& yjit_runtime_counters ;
721
734
int64_t * counter_reader_end = & yjit_runtime_counters .last_member ;
@@ -747,6 +760,22 @@ get_stat_counters(rb_execution_context_t *ec, VALUE self)
747
760
name_reader = name_end ;
748
761
}
749
762
}
763
+
764
+ {
765
+ // For each entry in exit_op_count, add a stats entry with key "exit_INSTRUCTION_NAME",
766
+ // where the value is the count of side exits for that instruction.
767
+
768
+ char key_string [rb_vm_max_insn_name_size + 6 ]; // Leave room for "exit_" and a final NUL
769
+ for (int i = 0 ; i < VM_INSTRUCTION_SIZE ; i ++ ) {
770
+ const char * i_name = insn_name (i ); // Look up Ruby's NUL-terminated insn name string
771
+ snprintf (key_string , rb_vm_max_insn_name_size + 6 , "%s%s" , "exit_" , i_name );
772
+
773
+ VALUE key = ID2SYM (rb_intern (key_string ));
774
+ VALUE value = LL2NUM ((long long )exit_op_count [i ]);
775
+ rb_hash_aset (hash , key , value );
776
+ }
777
+ }
778
+
750
779
RB_VM_LOCK_LEAVE ();
751
780
return hash ;
752
781
#else
@@ -768,8 +797,6 @@ reset_stats_bang(rb_execution_context_t *ec, VALUE self)
768
797
#include "yjit.rbinc"
769
798
770
799
#if RUBY_DEBUG
771
- // implementation for --yjit-stats
772
-
773
800
void
774
801
rb_yjit_collect_vm_usage_insn (int insn )
775
802
{
@@ -795,128 +822,7 @@ rb_yjit_count_side_exit_op(const VALUE *exit_pc)
795
822
exit_op_count [insn ]++ ;
796
823
return exit_pc ; // This function must return exit_pc!
797
824
}
798
-
799
- struct insn_count {
800
- int64_t insn ;
801
- int64_t count ;
802
- };
803
-
804
- static int
805
- insn_count_sort_comp (const void * a , const void * b )
806
- {
807
- const struct insn_count * count_a = a ;
808
- const struct insn_count * count_b = b ;
809
- if (count_a -> count > count_b -> count ) {
810
- return -1 ;
811
- }
812
- else if (count_a -> count < count_b -> count ) {
813
- return 1 ;
814
- }
815
- return 0 ;
816
- }
817
-
818
- static struct insn_count insn_sorting_buffer [VM_INSTRUCTION_SIZE ];
819
- static const struct insn_count *
820
- sort_insn_count_array (int64_t * array )
821
- {
822
- for (int i = 0 ; i < VM_INSTRUCTION_SIZE ; i ++ ) {
823
- insn_sorting_buffer [i ] = (struct insn_count ) { i , array [i ] };
824
- }
825
- qsort (insn_sorting_buffer , VM_INSTRUCTION_SIZE , sizeof (insn_sorting_buffer [0 ]), & insn_count_sort_comp );
826
- return insn_sorting_buffer ;
827
- }
828
-
829
- // Compute the total interpreter exit count
830
- static int64_t
831
- calc_total_exit_count ()
832
- {
833
- size_t total_exit_count = 0 ;
834
- for (int i = 0 ; i < VM_INSTRUCTION_SIZE ; i ++ ) {
835
- total_exit_count += exit_op_count [i ];
836
- }
837
-
838
- return total_exit_count ;
839
- }
840
-
841
- static void
842
- print_insn_count_buffer (int how_many , int left_pad )
843
- {
844
- size_t total_exit_count = calc_total_exit_count ();
845
-
846
- // Sort the exit ops by decreasing frequency
847
- const struct insn_count * sorted_exit_ops = sort_insn_count_array (exit_op_count );
848
-
849
- // Compute the longest instruction name and top10_exit_count
850
- size_t longest_insn_len = 0 ;
851
- size_t top10_exit_count = 0 ;
852
- for (int i = 0 ; i < how_many ; i ++ ) {
853
- const char * instruction_name = insn_name (sorted_exit_ops [i ].insn );
854
- size_t len = strlen (instruction_name );
855
- if (len > longest_insn_len ) {
856
- longest_insn_len = len ;
857
- }
858
- top10_exit_count += sorted_exit_ops [i ].count ;
859
- }
860
-
861
- double top10_exit_percent = 100.0 * top10_exit_count / total_exit_count ;
862
-
863
- fprintf (stderr , "top-%d most frequent exit ops (%.1f%% of exits):\n" , how_many , top10_exit_percent );
864
-
865
- // Print the top-N most frequent exit counts
866
- for (int i = 0 ; i < how_many ; i ++ ) {
867
- const char * instruction_name = insn_name (sorted_exit_ops [i ].insn );
868
- size_t padding = left_pad + longest_insn_len - strlen (instruction_name );
869
- for (size_t j = 0 ; j < padding ; j ++ ) {
870
- fputc (' ' , stderr );
871
- }
872
- double percent = 100 * sorted_exit_ops [i ].count / (double )total_exit_count ;
873
- fprintf (stderr , "%s: %10" PRId64 " (%.1f%%)\n" , instruction_name , sorted_exit_ops [i ].count , percent );
874
- }
875
- }
876
-
877
- __attribute__((destructor ))
878
- static void
879
- print_yjit_stats (void )
880
- {
881
- if (!rb_yjit_opts .gen_stats ) {
882
- return ;
883
- }
884
-
885
- // Warn if the executable code block is out of the relative
886
- // 32-bit jump range away from compiled C code
887
- ptrdiff_t start_diff = (cb -> mem_block + cb -> mem_size ) - (uint8_t * )& print_yjit_stats ;
888
- if (start_diff < INT32_MIN || start_diff > INT32_MAX ) {
889
- fprintf (stderr , "WARNING: end of code block past rel32 offset range from C code\n" );
890
- }
891
-
892
- // Compute the total exit count
893
- int64_t total_exit_count = calc_total_exit_count ();
894
-
895
- // Number of instructions that finish executing in YJIT. See :count-placement:.
896
- int64_t retired_in_yjit = yjit_runtime_counters .exec_instruction - total_exit_count ;
897
-
898
- // Average length of instruction sequences executed by YJIT
899
- double avg_len_in_yjit = (double )retired_in_yjit / total_exit_count ;
900
-
901
- // Proportion of instructions that retire in YJIT
902
- int64_t total_insns_count = retired_in_yjit + yjit_runtime_counters .vm_insns_count ;
903
- double ratio = retired_in_yjit / (double )total_insns_count ;
904
-
905
- fprintf (stderr , "compiled_iseq_count: %10" PRId64 "\n" , yjit_runtime_counters .compiled_iseq_count );
906
- fprintf (stderr , "inline_code_size: %10d\n" , cb -> write_pos );
907
- fprintf (stderr , "outlined_code_size: %10d\n" , ocb -> write_pos );
908
-
909
- fprintf (stderr , "total_exit_count: %10" PRId64 "\n" , total_exit_count );
910
- fprintf (stderr , "total_insns_count: %10" PRId64 "\n" , total_insns_count );
911
- fprintf (stderr , "vm_insns_count: %10" PRId64 "\n" , yjit_runtime_counters .vm_insns_count );
912
- fprintf (stderr , "yjit_insns_count: %10" PRId64 "\n" , yjit_runtime_counters .exec_instruction );
913
- fprintf (stderr , "ratio_in_yjit: %9.1f%%\n" , ratio * 100 );
914
- fprintf (stderr , "avg_len_in_yjit: %10.1f\n" , avg_len_in_yjit );
915
-
916
- // Print the top-N most frequent exit ops
917
- print_insn_count_buffer (20 , 4 );
918
- }
919
- #endif // if RUBY_DEBUG
825
+ #endif
920
826
921
827
void
922
828
rb_yjit_iseq_mark (const struct rb_iseq_constant_body * body )
0 commit comments