thinp_xml 0.0.4 → 0.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,15 +1,15 @@
1
1
  ---
2
2
  !binary "U0hBMQ==":
3
3
  metadata.gz: !binary |-
4
- NjEzY2RlNTQwYTJkM2U1YTU0ZmYxYzA4ZjgyMmI3OWM3ZTVlZGZhOA==
4
+ YjNhYTgyZDc5ZWJkNjAxZGFiNjA0Mzc4ZThlZGQ4MGYwYjdjNzQwMQ==
5
5
  data.tar.gz: !binary |-
6
- OGM5ZjgwY2FlN2FjYmUyZmJjMDE2MTdmMzQ0YWY4NGQzMDhkNjY5Yg==
6
+ YTExY2E1NzM0M2ZlMTA0ZTgzODFmNTI1YzE1NTYwNGYwNGJjZmY5MQ==
7
7
  !binary "U0hBNTEy":
8
8
  metadata.gz: !binary |-
9
- NDQ2YTM1ZDU3ZmU3MzllODJlYWI0OGMzNjI4ZWEyYThmMzUxMDI2MjAyZTAw
10
- ODIxZmJlZTNiMzE4NTc5N2RkNDY2MjdiODBmMjk5YzU1NGRjZTM2MDJhZDM4
11
- YTViODg4YzdlYmEyODZlOTdlMWQ0MjViNDk0NTBiMmIwNDRiYTk=
9
+ ZmQyOGZhODNkOWQ5NTNmNWE1OTRkMTQxYzQxZTBkNzIzM2RlZDY1ZjIwZGNh
10
+ MWFlOTczNzM2YjZiMjRkMjNkZTAyYmQ0MGM0NWMxZWZjZTBkODBjZGRhZWVl
11
+ NGVkZTFjOTMyNzAwZTkzOGE2ZmZlMjY0M2E1MjYyNzVjZmRjZmE=
12
12
  data.tar.gz: !binary |-
13
- ZjEwMDE2ZWE0YTNlNmUyZGUyYTI2YTg4NDA3MGI5ZTJiMDA5ODA1Zjc1ZTQz
14
- ZGQ1NjNiMjRlMGNmZDg0MjhlNmJmMzM4NWJlYjc1Yzg2Yjc5YjM1ZTQzNGE2
15
- NGUxNTdiNGNhYThlNWE5NzRmMDBmNmFhYzgyYTkyYTE1YWEwMGI=
13
+ OTkxYjhlZmZjOWI3YTUyMTRjYTUyOTQ0YjgyNTNiNDNiOTBjZTg0NjEyZDEx
14
+ YTNjNjllYThmNjUyYjlkMDhlOWQ4MDRmZjAzYmYwMGFhNjBjZTI5M2JjMDg2
15
+ NjUwOTE5Nzk1Mzk1NGJmNzEwNWFjYWZmNTViNzQ4YWQ5MDExYWQ=
@@ -0,0 +1,19 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'thinp_xml'
4
+ require 'thinp_xml/analysis'
5
+
6
+ #----------------------------------------------------------------
7
+
8
+ include ThinpXML
9
+
10
+ ARGV.each do |path|
11
+ STDERR.puts "analysing #{path}"
12
+ File.open(path, 'r') do |file|
13
+ md = read_xml(file)
14
+ analysis = MetadataAnalysis.new(md)
15
+ analysis.fragmentations
16
+ end
17
+ end
18
+
19
+ #----------------------------------------------------------------
@@ -0,0 +1,212 @@
1
+ require 'thinp_xml'
2
+
3
+ #----------------------------------------------------------------
4
+
5
+ # In sectors, not blocks as in the mapping structs
6
+ LogicalSegment = Struct.new(:length, :physical_segments)
7
+ PhysicalSegment = Struct.new(:origin_begin, :data_begin, :length)
8
+
9
+ class MetadataAnalysis
10
+ include ThinpXML
11
+
12
+ def initialize(md)
13
+ @md = md
14
+ end
15
+
16
+ def block_length_histograms
17
+ @md.devices.each do |dev|
18
+ puts "device #{dev.dev_id}"
19
+ block_length_histogram(dev)
20
+ puts ""
21
+ end
22
+ end
23
+
24
+ def fragmentations
25
+ @md.devices.each do |dev|
26
+ puts "device #{dev.dev_id}"
27
+ printf(" %10s %10s %10s\n",
28
+ 'io size', 'seeks/io', 'distance/seek')
29
+ puts " --------------------------------------"
30
+
31
+ power = 0
32
+ loop do
33
+ io_size = 8 * 4 ** power
34
+
35
+ r = fragmentation(dev, @md.superblock.data_block_size, io_size)
36
+ break if r.nil?
37
+ seeks, dist = r
38
+
39
+ printf(" %10s\t%.3f\t%s\n",
40
+ segments_to_human(io_size),
41
+ seeks, segments_to_human(dist.to_i))
42
+
43
+ power += 1
44
+ end
45
+ end
46
+ end
47
+
48
+ private
49
+
50
+ GRANULARITY = 2 * 4
51
+ Unit = Struct.new(:factor, :abbrev)
52
+
53
+ def segments_to_human(size)
54
+ units = [Unit.new(2048 * 1024, 'g'),
55
+ Unit.new(2048, 'm'),
56
+ Unit.new(2, 'k')]
57
+
58
+ units.each do |u|
59
+ if size >= u.factor
60
+ return "#{size / u.factor}#{u.abbrev}"
61
+ end
62
+ end
63
+
64
+ "#{size * 512}b"
65
+ end
66
+
67
+ def round_down(n, d)
68
+ (n / d) * d
69
+ end
70
+
71
+ def rand_io(max, io_size)
72
+ round_down(rand(max - io_size), 8)
73
+ end
74
+
75
+ def mappings_to_phys_segments(mappings, block_size)
76
+ mappings.map do |m|
77
+ PhysicalSegment.new(m.origin_begin * block_size,
78
+ m.data_begin * block_size,
79
+ m.length * block_size)
80
+ end
81
+ end
82
+
83
+ # FIXME: test this
84
+ def to_logical_segments(p_segs)
85
+ l_segs = Array.new
86
+
87
+ if p_segs.empty?
88
+ return l_segs
89
+ end
90
+
91
+ while !p_segs.empty?
92
+ a = Array.new
93
+
94
+ p = p_segs.shift
95
+ a << p
96
+
97
+ while !p_segs.empty? && (p_segs[0].origin_begin == p.origin_begin + p.length)
98
+ p = p_segs.shift
99
+ a << p
100
+ end
101
+
102
+ l_segs << LogicalSegment.new(a.inject(0) {|tot, ps| tot + ps.length}, a)
103
+ end
104
+
105
+ l_segs
106
+ end
107
+
108
+ def unique_ios_per_lseg(lseg, io_size, granularity)
109
+ (lseg.length < io_size) ? 0 : ((lseg.length - io_size) / granularity) + 1
110
+ end
111
+
112
+ # |pre| and |post| are the length in the lseg before and after the junction
113
+ # FIXME: check this
114
+ def ios_that_overlap_junction(pre, post, io_size, granularity)
115
+ pre /= granularity
116
+ post /= granularity
117
+ io_size /= granularity
118
+
119
+ intersections = io_size - 1
120
+ r = intersections
121
+
122
+ if pre < io_size
123
+ r -= intersections - pre
124
+ end
125
+
126
+ if post < io_size
127
+ r -= intersections - post
128
+ end
129
+
130
+ r
131
+ end
132
+
133
+ #--------------------------------------------------------------
134
+ # Fragmentation is dependent on the IO pattern; if you're doing
135
+ # random IO of 4k blocks then your device is never fragmented.
136
+ #
137
+ # For a random IO, to a mapped region, of size X what is the
138
+ # expected nr and distance of seeks experienced? (not including the
139
+ # initial seek to the start of the data)
140
+ #
141
+ # We only generate IOs to mapped areas.
142
+ #--------------------------------------------------------------
143
+ def fragmentation(dev, block_size, io_size)
144
+ # Break the mappings up into logical segments. Each segment will
145
+ # have one or more physical segments.
146
+ logical_segments = to_logical_segments(mappings_to_phys_segments(dev.mappings, block_size))
147
+
148
+ total_ios = 0.0
149
+ total_seeks = 0.0
150
+ total_seek_distance = 0.0
151
+
152
+ logical_segments.each do |lseg|
153
+ # calculate the nr of different ios of |io_size| can fit into
154
+ nr_ios = unique_ios_per_lseg(lseg, io_size, GRANULARITY)
155
+ next if nr_ios == 0
156
+
157
+ total_ios += nr_ios
158
+
159
+ # now we look at the junctions between physical segments
160
+ pre = 0
161
+ post = lseg.length
162
+ psegs = lseg.physical_segments
163
+ if psegs.length > 1
164
+ 0.upto(psegs.length - 2) do |i|
165
+ l = psegs[i].length
166
+ pre += l
167
+ post -= l
168
+ nr_seeks = ios_that_overlap_junction(pre, post, io_size, GRANULARITY)
169
+
170
+ total_seeks += nr_seeks
171
+
172
+ # FIXME: check we can handle these large numbers
173
+ total_seek_distance += nr_seeks * ((psegs[i].data_begin + psegs[i].length) - psegs[i + 1].data_begin).abs
174
+ end
175
+ end
176
+ end
177
+
178
+ (total_ios == 0) ? nil :
179
+ [total_seeks / total_ios, total_seeks > 0 ? total_seek_distance / total_seeks : 0]
180
+ end
181
+
182
+ # assumes the pairs are sorted
183
+ def format_histogram(pairs)
184
+ width = 80
185
+
186
+ m = pairs.inject(0) {|m, p| m = [m, p[1]].max}
187
+
188
+ pairs.each do |bin, count|
189
+ printf("%-8d: %s\n", 2 ** bin, '*' * (width * count / m))
190
+ end
191
+ end
192
+
193
+ def block_length_histogram(dev)
194
+ histogram = Hash.new {|hash, key| hash[key] = 0}
195
+
196
+ dev.mappings.each do |m|
197
+ histogram[nearest_power(m.length)] += m.length
198
+ end
199
+
200
+ format_histogram(histogram.sort)
201
+ end
202
+
203
+ def nearest_power(n)
204
+ 1.upto(32) do |p|
205
+ if 2 ** p > n
206
+ return p - 1
207
+ end
208
+ end
209
+ end
210
+ end
211
+
212
+ #----------------------------------------------------------------
@@ -1,3 +1,3 @@
1
1
  module ThinpXml
2
- VERSION = "0.0.4"
2
+ VERSION = "0.0.5"
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: thinp_xml
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.4
4
+ version: 0.0.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Joe Thornber
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2013-06-26 00:00:00.000000000 Z
11
+ date: 2013-08-01 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: ejt_command_line
@@ -113,6 +113,7 @@ description: Ruby library for parsing and generating the Linux thin-provisioning
113
113
  email:
114
114
  - ejt@redhat.com
115
115
  executables:
116
+ - analyse_metadata
116
117
  - thinp_xml
117
118
  extensions: []
118
119
  extra_rdoc_files: []
@@ -122,12 +123,14 @@ files:
122
123
  - LICENSE.txt
123
124
  - README.md
124
125
  - Rakefile
126
+ - bin/analyse_metadata
125
127
  - bin/thinp_xml
126
128
  - features/create.feature
127
129
  - features/step_definitions/thinp_xml.rb
128
130
  - features/support/env.rb
129
131
  - features/usage.feature
130
132
  - lib/thinp_xml.rb
133
+ - lib/thinp_xml/analysis.rb
131
134
  - lib/thinp_xml/builder.rb
132
135
  - lib/thinp_xml/distribution.rb
133
136
  - lib/thinp_xml/emit.rb