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 +8 -8
- data/bin/analyse_metadata +19 -0
- data/lib/thinp_xml/analysis.rb +212 -0
- data/lib/thinp_xml/version.rb +1 -1
- metadata +5 -2
checksums.yaml
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
---
|
2
2
|
!binary "U0hBMQ==":
|
3
3
|
metadata.gz: !binary |-
|
4
|
-
|
4
|
+
YjNhYTgyZDc5ZWJkNjAxZGFiNjA0Mzc4ZThlZGQ4MGYwYjdjNzQwMQ==
|
5
5
|
data.tar.gz: !binary |-
|
6
|
-
|
6
|
+
YTExY2E1NzM0M2ZlMTA0ZTgzODFmNTI1YzE1NTYwNGYwNGJjZmY5MQ==
|
7
7
|
!binary "U0hBNTEy":
|
8
8
|
metadata.gz: !binary |-
|
9
|
-
|
10
|
-
|
11
|
-
|
9
|
+
ZmQyOGZhODNkOWQ5NTNmNWE1OTRkMTQxYzQxZTBkNzIzM2RlZDY1ZjIwZGNh
|
10
|
+
MWFlOTczNzM2YjZiMjRkMjNkZTAyYmQ0MGM0NWMxZWZjZTBkODBjZGRhZWVl
|
11
|
+
NGVkZTFjOTMyNzAwZTkzOGE2ZmZlMjY0M2E1MjYyNzVjZmRjZmE=
|
12
12
|
data.tar.gz: !binary |-
|
13
|
-
|
14
|
-
|
15
|
-
|
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
|
+
#----------------------------------------------------------------
|
data/lib/thinp_xml/version.rb
CHANGED
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
|
+
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-
|
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
|