module RubyVM::RJIT
Constants
- CFP
- C_ARGS
SystemV x64 calling convention
- C_RET
- CantCompile
- Default
- EC
Callee-saved registers TODO: support using r12/r13 here
- EndBlock
- GC_REFS
- KeepCompiling
Compilation status
- MAX_LOCAL_TYPES
Maximum number of local variable types we keep track of
- MAX_TEMP_TYPES
Maximum number of temp value types we keep track of
- MAX_VERSIONS
Maximum number of versions per block 1 means always create generic versions
- MapToLocal
- MapToSelf
- MapToStack
Potential mapping of a value on the temporary stack to self, a local variable, or constant so that we can track its type
- Next0
Branch shapes
- Next1
- Qfalse
- Qnil
- Qtrue
Ruby constants
- Qundef
- QwordPtr
An Array is an ordered, integer-indexed collection of objects, called elements. Any object (even another array) may be an array element, and an array can contain objects of different types.
Array Indexes¶ ↑
Array indexing starts at 0, as in C or Java.
A positive index is an offset from the first element:
-
Index 0 indicates the first element.
-
Index 1 indicates the second element.
-
…
A negative index is an offset, backwards, from the end of the array:
-
Index -1 indicates the last element.
-
Index -2 indicates the next-to-last element.
-
…
A non-negative index is in range if and only if it is smaller than the size of the array. For a 3-element array:
-
Indexes 0 through 2 are in range.
-
Index 3 is out of range.
A negative index is in range if and only if its absolute value is not larger than the size of the array. For a 3-element array:
-
Indexes -1 through -3 are in range.
-
Index -4 is out of range.
Although the effective index into an array is always an integer, some methods (both within and outside of class Array) accept one or more non-integer arguments that are integer-convertible objects.
Creating Arrays¶ ↑
You can create an Array object explicitly with:
-
An array literal:
[1, 'one', :one, [2, 'two', :two]]
-
A array literal:
%w[foo bar baz] # => ["foo", "bar", "baz"] %w[1 % *] # => ["1", "%", "*"]
-
A array literal:
%i[foo bar baz] # => [:foo, :bar, :baz] %i[1 % *] # => [:"1", :%, :*]
-
Method
Kernel#Array
:Array(["a", "b"]) # => ["a", "b"] Array(1..5) # => [1, 2, 3, 4, 5] Array(key: :value) # => [[:key, :value]] Array(nil) # => [] Array(1) # => [1] Array({:a => "a", :b => "b"}) # => [[:a, "a"], [:b, "b"]]
-
Method
Array.new
:Array.new # => [] Array.new(3) # => [nil, nil, nil] Array.new(4) {Hash.new} # => [{}, {}, {}, {}] Array.new(3, true) # => [true, true, true]
Note that the last example above populates the array with references to the same object. This is recommended only in cases where that object is a natively immutable object such as a symbol, a numeric,
nil
,true
, orfalse
.Another way to create an array with various objects, using a block; this usage is safe for mutable objects such as hashes, strings or other arrays:
Array.new(4) {|i| i.to_s } # => ["0", "1", "2", "3"]
Here is a way to create a multi-dimensional array:
Array.new(3) {Array.new(3)} # => [[nil, nil, nil], [nil, nil, nil], [nil, nil, nil]]
A number of Ruby methods, both in the core and in the standard library, provide instance method
to_a
, which converts an object to an array.-
Gem::List#to_a
-
Racc::ISet#to_a
Example Usage¶ ↑
In addition to the methods it mixes in through the
Enumerable
module, the Array class has proprietary methods for accessing, searching and otherwise manipulating arrays.Some of the more common ones are illustrated below.
Accessing Elements¶ ↑
Elements in an array can be retrieved using the
Array#[]
method. It can take a single integer argument (a numeric index), a pair of arguments (start and length) or a range. Negative indices start counting from the end, with -1 being the last element.arr = [1, 2, 3, 4, 5, 6] arr[2] #=> 3 arr[100] #=> nil arr[-3] #=> 4 arr[2, 3] #=> [3, 4, 5] arr[1..4] #=> [2, 3, 4, 5] arr[1..-3] #=> [2, 3, 4]
Another way to access a particular array element is by using the at method
arr.at(0) #=> 1
The slice method works in an identical manner to
Array#[]
.To raise an error for indices outside of the array bounds or else to provide a default value when that happens, you can use fetch.
arr = ['a', 'b', 'c', 'd', 'e', 'f'] arr.fetch(100) #=> IndexError: index 100 outside of array bounds: -6...6 arr.fetch(100, "oops") #=> "oops"
The special methods first and last will return the first and last elements of an array, respectively.
arr.first #=> 1 arr.last #=> 6
To return the first
n
elements of an array, use takearr.take(3) #=> [1, 2, 3]
drop does the opposite of take, by returning the elements after
n
elements have been dropped:arr.drop(3) #=> [4, 5, 6]
Obtaining Information about an Array¶ ↑
Arrays keep track of their own length at all times. To query an array about the number of elements it contains, use length, count or size.
browsers = ['Chrome', 'Firefox', 'Safari', 'Opera', 'IE'] browsers.length #=> 5 browsers.count #=> 5
To check whether an array contains any elements at all
browsers.empty? #=> false
To check whether a particular item is included in the array
browsers.include?('Konqueror') #=> false
Adding Items to Arrays¶ ↑
Items can be added to the end of an array by using either push or <<
arr = [1, 2, 3, 4] arr.push(5) #=> [1, 2, 3, 4, 5] arr << 6 #=> [1, 2, 3, 4, 5, 6]
unshift will add a new item to the beginning of an array.
arr.unshift(0) #=> [0, 1, 2, 3, 4, 5, 6]
With insert you can add a new element to an array at any position.
arr.insert(3, 'apple') #=> [0, 1, 2, 'apple', 3, 4, 5, 6]
Using the insert method, you can also insert multiple values at once:
arr.insert(3, 'orange', 'pear', 'grapefruit') #=> [0, 1, 2, "orange", "pear", "grapefruit", "apple", 3, 4, 5, 6]
Removing Items from an Array¶ ↑
The method pop removes the last element in an array and returns it:
arr = [1, 2, 3, 4, 5, 6] arr.pop #=> 6 arr #=> [1, 2, 3, 4, 5]
To retrieve and at the same time remove the first item, use shift:
arr.shift #=> 1 arr #=> [2, 3, 4, 5]
To delete an element at a particular index:
arr.delete_at(2) #=> 4 arr #=> [2, 3, 5]
To delete a particular element anywhere in an array, use delete:
arr = [1, 2, 2, 3] arr.delete(2) #=> 2 arr #=> [1,3]
A useful method if you need to remove
nil
values from an array is compact:arr = ['foo', 0, nil, 'bar', 7, 'baz', nil] arr.compact #=> ['foo', 0, 'bar', 7, 'baz'] arr #=> ['foo', 0, nil, 'bar', 7, 'baz', nil] arr.compact! #=> ['foo', 0, 'bar', 7, 'baz'] arr #=> ['foo', 0, 'bar', 7, 'baz']
Another common need is to remove duplicate elements from an array.
It has the non-destructive uniq, and destructive method uniq!
arr = [2, 5, 6, 556, 6, 6, 8, 9, 0, 123, 556] arr.uniq #=> [2, 5, 6, 556, 8, 9, 0, 123]
Iterating over Arrays¶ ↑
Like all classes that include the
Enumerable
module, Array has an each method, which defines what elements should be iterated over and how. In case of Array’s each, all elements in the Array instance are yielded to the supplied block in sequence.Note that this operation leaves the array unchanged.
arr = [1, 2, 3, 4, 5] arr.each {|a| print a -= 10, " "} # prints: -9 -8 -7 -6 -5 #=> [1, 2, 3, 4, 5]
Another sometimes useful iterator is reverse_each which will iterate over the elements in the array in reverse order.
words = %w[first second third fourth fifth sixth] str = "" words.reverse_each {|word| str += "#{word} "} p str #=> "sixth fifth fourth third second first "
The map method can be used to create a new array based on the original array, but with the values modified by the supplied block:
arr.map {|a| 2*a} #=> [2, 4, 6, 8, 10] arr #=> [1, 2, 3, 4, 5] arr.map! {|a| a**2} #=> [1, 4, 9, 16, 25] arr #=> [1, 4, 9, 16, 25]
Selecting Items from an Array¶ ↑
Elements can be selected from an array according to criteria defined in a block. The selection can happen in a destructive or a non-destructive manner. While the destructive operations will modify the array they were called on, the non-destructive methods usually return a new array with the selected elements, but leave the original array unchanged.
Non-destructive Selection¶ ↑
arr = [1, 2, 3, 4, 5, 6] arr.select {|a| a > 3} #=> [4, 5, 6] arr.reject {|a| a < 3} #=> [3, 4, 5, 6] arr.drop_while {|a| a < 4} #=> [4, 5, 6] arr #=> [1, 2, 3, 4, 5, 6]
Destructive Selection¶ ↑
select! and reject! are the corresponding destructive methods to select and reject
Similar to select vs. reject, delete_if and keep_if have the exact opposite result when supplied with the same block:
arr.delete_if {|a| a < 4} #=> [4, 5, 6] arr #=> [4, 5, 6] arr = [1, 2, 3, 4, 5, 6] arr.keep_if {|a| a < 4} #=> [1, 2, 3] arr #=> [1, 2, 3]
What’s Here¶ ↑
First, what’s elsewhere. Class Array:
-
Inherits from class Object.
-
Includes module Enumerable, which provides dozens of additional methods.
Here, class Array provides methods that are useful for:
Methods for Creating an Array¶ ↑
-
::[]: Returns a new array populated with given objects.
-
::new: Returns a new array.
-
::try_convert: Returns a new array created from a given object.
Methods for Querying¶ ↑
-
length, size: Returns the count of elements.
-
include?: Returns whether any element
==
a given object. -
empty?: Returns whether there are no elements.
-
all?: Returns whether all elements meet a given criterion.
-
any?: Returns whether any element meets a given criterion.
-
none?: Returns whether no element
==
a given object. -
one?: Returns whether exactly one element
==
a given object. -
count: Returns the count of elements that meet a given criterion.
-
find_index, index: Returns the index of the first element that meets a given criterion.
-
rindex: Returns the index of the last element that meets a given criterion.
-
hash: Returns the integer hash code.
Methods for Comparing¶ ↑
-
<=>: Returns -1, 0, or 1 * as
self
is less than, equal to, or greater than a given object. -
==: Returns whether each element in
self
is==
to the corresponding element in a given object. -
eql?: Returns whether each element in
self
iseql?
to the corresponding element in a given object.
Methods for Fetching¶ ↑
These methods do not modify
self
.-
[]: Returns one or more elements.
-
fetch: Returns the element at a given offset.
-
first: Returns one or more leading elements.
-
last: Returns one or more trailing elements.
-
max: Returns one or more maximum-valued elements, as determined by
<=>
or a given block. -
min: Returns one or more minimum-valued elements, as determined by
<=>
or a given block. -
minmax: Returns the minimum-valued and maximum-valued elements, as determined by
<=>
or a given block. -
assoc: Returns the first element that is an array whose first element
==
a given object. -
rassoc: Returns the first element that is an array whose second element
==
a given object. -
at: Returns the element at a given offset.
-
values_at: Returns the elements at given offsets.
-
dig: Returns the object in nested objects that is specified by a given index and additional arguments.
-
drop: Returns trailing elements as determined by a given index.
-
take: Returns leading elements as determined by a given index.
-
drop_while: Returns trailing elements as determined by a given block.
-
take_while: Returns leading elements as determined by a given block.
-
slice: Returns consecutive elements as determined by a given argument.
-
sort: Returns all elements in an order determined by
<=>
or a given block. -
reverse: Returns all elements in reverse order.
-
compact: Returns an array containing all non-
nil
elements. -
select, filter: Returns an array containing elements selected by a given block.
-
uniq: Returns an array containing non-duplicate elements.
-
rotate: Returns all elements with some rotated from one end to the other.
-
bsearch: Returns an element selected via a binary search as determined by a given block.
-
bsearch_index: Returns the index of an element selected via a binary search as determined by a given block.
-
sample: Returns one or more random elements.
-
shuffle: Returns elements in a random order.
Methods for Assigning¶ ↑
These methods add, replace, or reorder elements in
self
.-
[]=: Assigns specified elements with a given object.
-
push, append, <<: Appends trailing elements.
-
unshift, prepend: Prepends leading elements.
-
insert: Inserts given objects at a given offset; does not replace elements.
-
concat: Appends all elements from given arrays.
-
fill: Replaces specified elements with specified objects.
-
replace: Replaces the content of
self
with the content of a given array. -
reverse!: Replaces
self
with its elements reversed. -
rotate!: Replaces
self
with its elements rotated. -
shuffle!: Replaces
self
with its elements in random order. -
sort!: Replaces
self
with its elements sorted, as determined by<=>
or a given block. -
sort_by!: Replaces
self
with its elements sorted, as determined by a given block.
Methods for Deleting¶ ↑
Each of these methods removes elements from
self
:-
pop: Removes and returns the last element.
-
shift: Removes and returns the first element.
-
compact!: Removes all
nil
elements. -
delete: Removes elements equal to a given object.
-
delete_at: Removes the element at a given offset.
-
delete_if: Removes elements specified by a given block.
-
keep_if: Removes elements not specified by a given block.
-
reject!: Removes elements specified by a given block.
-
select!, filter!: Removes elements not specified by a given block.
-
slice!: Removes and returns a sequence of elements.
-
uniq!: Removes duplicates.
Methods for Combining¶ ↑
-
&: Returns an array containing elements found both in
self
and a given array. -
intersection: Returns an array containing elements found both in
self
and in each given array. -
+: Returns an array containing all elements of
self
followed by all elements of a given array. -
-: Returns an array containing all elements of
self
that are not found in a given array. -
|: Returns an array containing all elements of
self
and all elements of a given array, duplicates removed. -
union: Returns an array containing all elements of
self
and all elements of given arrays, duplicates removed. -
difference: Returns an array containing all elements of
self
that are not found in any of the given arrays.. -
product: Returns or yields all combinations of elements from
self
and given arrays.
Methods for Iterating¶ ↑
-
each: Passes each element to a given block.
-
reverse_each: Passes each element, in reverse order, to a given block.
-
each_index: Passes each element index to a given block.
-
cycle: Calls a given block with each element, then does so again, for a specified number of times, or forever.
-
combination: Calls a given block with combinations of elements of
self
; a combination does not use the same element more than once. -
permutation: Calls a given block with permutations of elements of
self
; a permutation does not use the same element more than once. -
repeated_combination: Calls a given block with combinations of elements of
self
; a combination may use the same element more than once. -
repeated_permutation: Calls a given block with permutations of elements of
self
; a permutation may use the same element more than once.
Methods for Converting¶ ↑
-
map, collect: Returns an array containing the block return-value for each element.
-
map!, collect!: Replaces each element with a block return-value.
-
flatten: Returns an array that is a recursive flattening of
self
. -
flatten!: Replaces each nested array in
self
with the elements from that array. -
inspect, to_s: Returns a new
String
containing the elements. -
join: Returns a newsString containing the elements joined by the field separator.
-
to_a: Returns
self
or a new array containing all elements. -
to_ary: Returns
self
. -
to_h: Returns a new hash formed from the elements.
-
transpose: Transposes
self
, which must be an array of arrays. -
zip: Returns a new array of arrays containing
self
and given arrays; follow the link for details.
Other Methods¶ ↑
-
*: Returns one of the following:
-
With integer argument
n
, a new array that is the concatenation ofn
copies ofself
. -
With string argument
field_separator
, a new string that is equivalent tojoin(field_separator)
.
-
-
abbrev: Returns a hash of unambiguous abbreviations for elements.
-
pack: Packs the elements into a binary sequence.
-
sum: Returns a sum of elements according to either
+
or a given block.
-
- SP
- SelfOpnd
Operand to a YARV bytecode instruction
- StackOpnd
- Type
Represent the type of a value (local/stack/self) in
RJIT
Public Class Methods
Return true if RJIT
is enabled.
# File rjit.rb, line 3 def self.enabled? Primitive.cexpr! 'RBOOL(rb_rjit_enabled)' end
Start generating JITed code again after –rjit-pause.
# File rjit.rb, line 8 def self.resume Primitive.cstmt! %{ rb_rjit_call_p = true; return Qnil; } end
# File lib/ruby_vm/rjit/stats.rb, line 3 def self.runtime_stats stats = {} # Insn exits INSNS.each_value do |insn| exits = C.rjit_insn_exits[insn.bin] if exits > 0 stats[:"exit_#{insn.name}"] = exits end end # Runtime stats C.rb_rjit_runtime_counters.members.each do |member| stats[member] = C.rb_rjit_counters.public_send(member) end # Other stats are calculated here stats[:side_exit_count] = stats.select { |name, _count| name.start_with?('exit_') }.sum(&:last) if stats[:vm_insns_count] > 0 retired_in_rjit = stats[:rjit_insns_count] - stats[:side_exit_count] stats[:total_insns_count] = retired_in_rjit + stats[:vm_insns_count] stats[:ratio_in_rjit] = 100.0 * retired_in_rjit / stats[:total_insns_count] else stats.delete(:vm_insns_count) end stats end
Private Class Methods
–yjit-trace-exits at_exit
# File lib/ruby_vm/rjit/stats.rb, line 107 def dump_trace_exits filename = "#{Dir.pwd}/rjit_exit_locations.dump" File.binwrite(filename, Marshal.dump(exit_traces)) $stderr.puts("RJIT exit locations dumped to:\n#{filename}") end
Convert rb_rjit_raw_samples and rb_rjit_line_samples into a StackProf format.
# File lib/ruby_vm/rjit/stats.rb, line 114 def exit_traces results = C.rjit_exit_traces raw_samples = results[:raw].dup line_samples = results[:lines].dup frames = results[:frames].dup samples_count = 0 # Loop through the instructions and set the frame hash with the data. # We use nonexistent.def for the file name, otherwise insns.def will be displayed # and that information isn't useful in this context. RubyVM::INSTRUCTION_NAMES.each_with_index do |name, frame_id| frame_hash = { samples: 0, total_samples: 0, edges: {}, name: name, file: "nonexistent.def", line: nil, lines: {} } results[:frames][frame_id] = frame_hash frames[frame_id] = frame_hash end # Loop through the raw_samples and build the hashes for StackProf. # The loop is based off an example in the StackProf documentation and therefore # this functionality can only work with that library. # # Raw Samples: # [ length, frame1, frame2, frameN, ..., instruction, count # # Line Samples # [ length, line_1, line_2, line_n, ..., dummy value, count i = 0 while i < raw_samples.length stack_length = raw_samples[i] + 1 i += 1 # consume the stack length prev_frame_id = nil stack_length.times do |idx| idx += i frame_id = raw_samples[idx] if prev_frame_id prev_frame = frames[prev_frame_id] prev_frame[:edges][frame_id] ||= 0 prev_frame[:edges][frame_id] += 1 end frame_info = frames[frame_id] frame_info[:total_samples] += 1 frame_info[:lines][line_samples[idx]] ||= [0, 0] frame_info[:lines][line_samples[idx]][0] += 1 prev_frame_id = frame_id end i += stack_length # consume the stack top_frame_id = prev_frame_id top_frame_line = 1 sample_count = raw_samples[i] frames[top_frame_id][:samples] += sample_count frames[top_frame_id][:lines] ||= {} frames[top_frame_id][:lines][top_frame_line] ||= [0, 0] frames[top_frame_id][:lines][top_frame_line][1] += sample_count samples_count += sample_count i += 1 end results[:samples] = samples_count # Set missed_samples and gc_samples to 0 as their values # don't matter to us in this context. results[:missed_samples] = 0 results[:gc_samples] = 0 results end
Format large numbers with comma separators for readability
# File lib/ruby_vm/rjit/stats.rb, line 99 def format_number(pad, number) integer, decimal = number.to_s.split('.') d_groups = integer.chars.reverse.each_slice(3) with_commas = d_groups.map(&:join).join(',').reverse [with_commas, decimal].compact.join('.').rjust(pad, ' ') end
# File lib/ruby_vm/rjit/stats.rb, line 60 def print_counters(stats, prefix:, prompt:) $stderr.puts("#{prompt}: ") counters = stats.filter { |key, _| key.start_with?(prefix) } counters.filter! { |_, value| value != 0 } counters.transform_keys! { |key| key.to_s.delete_prefix(prefix) } if counters.empty? $stderr.puts(" (all relevant counters are zero)") return end counters = counters.to_a counters.sort_by! { |(_, counter_value)| counter_value } longest_name_length = counters.max_by { |(name, _)| name.length }.first.length total = counters.sum { |(_, counter_value)| counter_value } counters.reverse_each do |(name, value)| percentage = value.fdiv(total) * 100 $stderr.printf(" %*s %s (%4.1f%%)\n", longest_name_length, name, format_number(10, value), percentage) end end
# File lib/ruby_vm/rjit/stats.rb, line 82 def print_exit_counts(stats, how_many: 20, padding: 2) exits = stats.filter_map { |name, count| [name.to_s.delete_prefix('exit_'), count] if name.start_with?('exit_') }.to_h return if exits.empty? top_exits = exits.sort_by { |_name, count| -count }.first(how_many).to_h total_exits = exits.values.sum $stderr.puts "Top-#{top_exits.size} most frequent exit ops (#{format("%.1f", 100.0 * top_exits.values.sum / total_exits)}% of exits):" name_width = top_exits.map { |name, _count| name.length }.max + padding count_width = top_exits.map { |_name, count| format_number(10, count).length }.max + padding top_exits.each do |name, count| ratio = 100.0 * count / total_exits $stderr.puts "#{format("%#{name_width}s", name)}: #{format_number(count_width, count)} (#{format('%4.1f', ratio)}%)" end end
–yjit-stats at_exit
# File lib/ruby_vm/rjit/stats.rb, line 36 def print_stats stats = runtime_stats $stderr.puts("***RJIT: Printing RJIT statistics on exit***") print_counters(stats, prefix: 'send_', prompt: 'method call exit reasons') print_counters(stats, prefix: 'invokeblock_', prompt: 'invokeblock exit reasons') print_counters(stats, prefix: 'invokesuper_', prompt: 'invokesuper exit reasons') print_counters(stats, prefix: 'getblockpp_', prompt: 'getblockparamproxy exit reasons') print_counters(stats, prefix: 'getivar_', prompt: 'getinstancevariable exit reasons') print_counters(stats, prefix: 'setivar_', prompt: 'setinstancevariable exit reasons') print_counters(stats, prefix: 'optaref_', prompt: 'opt_aref exit reasons') print_counters(stats, prefix: 'optgetconst_', prompt: 'opt_getconstant_path exit reasons') print_counters(stats, prefix: 'expandarray_', prompt: 'expandarray exit reasons') $stderr.puts "compiled_block_count: #{format_number(13, stats[:compiled_block_count])}" $stderr.puts "side_exit_count: #{format_number(13, stats[:side_exit_count])}" $stderr.puts "total_insns_count: #{format_number(13, stats[:total_insns_count])}" if stats.key?(:total_insns_count) $stderr.puts "vm_insns_count: #{format_number(13, stats[:vm_insns_count])}" if stats.key?(:vm_insns_count) $stderr.puts "rjit_insns_count: #{format_number(13, stats[:rjit_insns_count])}" $stderr.puts "ratio_in_rjit: #{format('%12.1f', stats[:ratio_in_rjit])}%" if stats.key?(:ratio_in_rjit) print_exit_counts(stats) end