thinp_xml 0.0.4 → 0.0.5

Sign up to get free protection for your applications and to get access to all the features.
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