rbsim 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (71) hide show
  1. checksums.yaml +7 -0
  2. data/.hgignore +6 -0
  3. data/.rspec +2 -0
  4. data/Gemfile +12 -0
  5. data/Gemfile.lock +66 -0
  6. data/LICENSE.txt +674 -0
  7. data/README.md +960 -0
  8. data/TODO +28 -0
  9. data/basic_sim.rb +62 -0
  10. data/fast-tcpn.rb +3 -0
  11. data/lib/rbsim.rb +14 -0
  12. data/lib/rbsim/dsl.rb +30 -0
  13. data/lib/rbsim/dsl/infrastructure.rb +48 -0
  14. data/lib/rbsim/dsl/mapping.rb +32 -0
  15. data/lib/rbsim/dsl/process.rb +129 -0
  16. data/lib/rbsim/dsl/program.rb +10 -0
  17. data/lib/rbsim/experiment.rb +110 -0
  18. data/lib/rbsim/hlmodel.rb +25 -0
  19. data/lib/rbsim/hlmodel/infrastructure.rb +116 -0
  20. data/lib/rbsim/hlmodel/mapping.rb +5 -0
  21. data/lib/rbsim/hlmodel/process.rb +152 -0
  22. data/lib/rbsim/numeric_units.rb +107 -0
  23. data/lib/rbsim/simulator.rb +184 -0
  24. data/lib/rbsim/statistics.rb +77 -0
  25. data/lib/rbsim/tokens.rb +146 -0
  26. data/lib/rbsim/version.rb +3 -0
  27. data/new_process.rb +49 -0
  28. data/rbsim.gemspec +42 -0
  29. data/show_readme.rb +15 -0
  30. data/sim.rb +142 -0
  31. data/sim_bamboo.rb +251 -0
  32. data/sim_process.rb +83 -0
  33. data/sim_process_dsl.rb +58 -0
  34. data/spec/dsl/infrastructure_nets_spec.rb +39 -0
  35. data/spec/dsl/infrastructure_nodes_spec.rb +72 -0
  36. data/spec/dsl/infrastructure_routes_spec.rb +44 -0
  37. data/spec/dsl/mapping_spec.rb +70 -0
  38. data/spec/dsl/process_spec.rb +56 -0
  39. data/spec/dsl/program_spec.rb +36 -0
  40. data/spec/dsl_and_hlmodel/new_process_spec.rb +235 -0
  41. data/spec/hlmodel/net_spec.rb +112 -0
  42. data/spec/hlmodel/process_spec.rb +242 -0
  43. data/spec/hlmodel/route_spec.rb +47 -0
  44. data/spec/hlmodel/routes_spec.rb +44 -0
  45. data/spec/integration/basic_simulation_spec.rb +104 -0
  46. data/spec/integration/net_spec.rb +44 -0
  47. data/spec/integration/process_spec.rb +117 -0
  48. data/spec/integration/rbsim_spec.rb +40 -0
  49. data/spec/simulator/logger_spec.rb +35 -0
  50. data/spec/simulator/stats_spec.rb +93 -0
  51. data/spec/spec_helper.rb +26 -0
  52. data/spec/statistics_spec.rb +300 -0
  53. data/spec/tcpn/add_route_spec.rb +55 -0
  54. data/spec/tcpn/cpu_spec.rb +53 -0
  55. data/spec/tcpn/map_data_spec.rb +37 -0
  56. data/spec/tcpn/network_spec.rb +163 -0
  57. data/spec/tcpn/register_event_spec.rb +48 -0
  58. data/spec/tcpn/route_to_self_spec.rb +53 -0
  59. data/spec/tcpn/stats_spec.rb +77 -0
  60. data/spec/tokens/data_queue_obsolete.rb +121 -0
  61. data/spec/tokens/data_queue_spec.rb +111 -0
  62. data/spec/units_spec.rb +48 -0
  63. data/tcpn/model.rb +6 -0
  64. data/tcpn/model/add_route.rb +78 -0
  65. data/tcpn/model/application.rb +250 -0
  66. data/tcpn/model/cpu.rb +75 -0
  67. data/tcpn/model/map_data.rb +42 -0
  68. data/tcpn/model/network.rb +108 -0
  69. data/tcpn/model/register_event.rb +89 -0
  70. data/tcpn/model/stats.rb +46 -0
  71. metadata +221 -0
@@ -0,0 +1,25 @@
1
+ require 'rbsim/hlmodel/infrastructure.rb'
2
+ require 'rbsim/hlmodel/process.rb'
3
+ require 'rbsim/hlmodel/mapping.rb'
4
+
5
+ module RBSim
6
+ module HLModel
7
+
8
+ Model = Struct.new :nodes, :nets, :routes, :programs, :processes, :mapping, :simulator do
9
+ def initialize(*)
10
+ super
11
+ self.programs = {}
12
+ self.processes = {}
13
+ self.mapping = Mapping.new
14
+ self.routes = Tokens::RoutesToken.new
15
+ self.each_pair do |attr, val|
16
+ if val.nil?
17
+ self.send "#{attr}=".to_sym, []
18
+ end
19
+ end
20
+ self.simulator = nil
21
+ end
22
+ end
23
+
24
+ end
25
+ end
@@ -0,0 +1,116 @@
1
+ module RBSim
2
+ module HLModel
3
+
4
+ Node = Struct.new :name, :cpus
5
+ class Node
6
+ CPU = Struct.new :performance, :node, :tags
7
+ end
8
+
9
+ class Net
10
+ attr_reader :name, :bw, :delay, :drop, :tags
11
+
12
+ InvalidTypeOfDropParameter = Class.new RuntimeError
13
+ InvalidValueOfDropProbability = Class.new RuntimeError
14
+
15
+ def initialize(name, bw, delay = 0, drop = 0, tags = {})
16
+ @name, @bw, @delay = name, bw, delay
17
+ @drop = drop
18
+ @tags = tags
19
+ unless @drop.kind_of?(Proc) || @drop.kind_of?(Numeric)
20
+ raise InvalidTypeOfDropParameter.new(@drop.class)
21
+ end
22
+ if @drop.kind_of? Numeric
23
+ if @drop > 1 || @drop < 0
24
+ raise InvalidValueOfDropProbability.new(@drop)
25
+ end
26
+ end
27
+ @drop_next = drop_next?
28
+ end
29
+
30
+ def drop?
31
+ drop_this = @drop_next
32
+ @drop_next = drop_next?
33
+ drop_this
34
+ end
35
+
36
+ private
37
+
38
+ def drop_next?
39
+ return @drop.call if @drop.kind_of? Proc
40
+
41
+ # a little optimization ;-)
42
+ return true if @drop == 1
43
+ return false if @drop == 0
44
+
45
+ return rand <= @drop
46
+ end
47
+ end
48
+
49
+ class Routes
50
+ def initialize
51
+ @routes = {}
52
+ end
53
+
54
+ def add(route)
55
+ key = [route.src, route.dst]
56
+ @routes[key] ||= []
57
+ @routes[key] << route
58
+ if route.twoway?
59
+ @routes[key.reverse] ||= []
60
+ @routes[key.reverse] << route
61
+ end
62
+ end
63
+ alias << add
64
+
65
+ def find(src, dst)
66
+ routes = @routes[[src, dst]]
67
+ return nil if routes.nil?
68
+ route = if routes.size == 1
69
+ routes.first
70
+ else
71
+ routes[rand(routes.size)]
72
+ end
73
+ if route.src == src
74
+ route
75
+ else
76
+ route.reverse!
77
+ end
78
+ end
79
+
80
+ end
81
+
82
+ Route = Struct.new :src, :dst, :via, :twoway do
83
+
84
+ CannotReverseOnewayRoute = Class.new RuntimeError
85
+
86
+ def initialize(*)
87
+ super
88
+ self.twoway = false unless self.twoway
89
+ @net_number = 0
90
+ end
91
+
92
+ def next_net
93
+ raise StopIteration if @net_number >= via.length
94
+ net = via[@net_number]
95
+ @net_number += 1
96
+ net
97
+ end
98
+
99
+ def has_next_net?
100
+ @net_number < via.length
101
+ end
102
+
103
+ def reverse!
104
+ raise CannotReverseOnewayRoute unless self.twoway
105
+ self.via = self.via.reverse
106
+ self.src, self.dst = self.dst, self.src
107
+ @net_enum = nil
108
+ self
109
+ end
110
+
111
+ def twoway?
112
+ twoway
113
+ end
114
+ end
115
+ end
116
+ end
@@ -0,0 +1,5 @@
1
+ module RBSim
2
+ module HLModel
3
+ Mapping = Class.new Hash
4
+ end
5
+ end
@@ -0,0 +1,152 @@
1
+ module RBSim
2
+ module HLModel
3
+
4
+ class Process
5
+ NoHandlerForUserEvent = Class.new RuntimeError
6
+ InvalidEventType = Class.new RuntimeError
7
+ InvalidSystemEventPassed = Class.new RuntimeError
8
+ NoEventToServe = Class.new RuntimeError
9
+ NotAssignedToNode = Class.new RuntimeError
10
+
11
+ attr_reader :name, :program, :functions, :tags
12
+ attr_accessor :node
13
+ # to how many fragments should be
14
+ # divided data sent by this process?
15
+ attr_accessor :data_fragmentation
16
+
17
+ alias id name
18
+
19
+ # +name+: name of this process used to assign it to a node
20
+ # +program+: name of program running in this process (if any name was given); this is just for information
21
+ def initialize(name, program = nil, tags = {})
22
+ @event_handlers = { data_received: generic_data_received_handler }
23
+ @event_queue = []
24
+ @name = name
25
+ @program = program
26
+ @tags = tags || {}
27
+ @functions = {}
28
+ @data_fragmentation = 1
29
+ end
30
+
31
+ # define a new reusable function for this program
32
+ def function(name, &block)
33
+ @functions[name] = block
34
+ end
35
+
36
+ # define event handler
37
+ def on_event(event, &block)
38
+ @event_handlers[event] = block
39
+ end
40
+
41
+ # event just happened
42
+ def enqueue_event(event, args = nil)
43
+ handler = nil # system event has no handling code defined here
44
+ # it must be handled by somethig external, like
45
+ # TCPN transition!
46
+ unless system_event_names.include? event
47
+ handler = @event_handlers[event]
48
+ if handler.nil?
49
+ raise NoHandlerForUserEvent.new(event)
50
+ end
51
+ end
52
+ @event_queue << { name: event, block: handler, args: args }
53
+ end
54
+
55
+ # serve first event if it is a user event
56
+ def serve_user_event
57
+ check_if_assigned!
58
+ if has_system_event?
59
+ raise InvalidEventType.new("#{@event_queue.first[:name]} is not a user event!")
60
+ end
61
+ event = @event_queue.shift
62
+ event[:block].call self, event[:args]
63
+ end
64
+
65
+ # serve first event if it is a system event
66
+ def serve_system_event(name)
67
+ check_if_assigned!
68
+ unless has_event?
69
+ raise NoEventToServe.new
70
+ end
71
+ unless has_event? name
72
+ raise InvalidSystemEventPassed.new("You tried to serve: #{name}, but first in queue is: #{@event_queue.first[:name]}")
73
+ end
74
+ if has_user_event?
75
+ raise InvalidEventType.new("#{@event_queue.first[:name]} is not a system event!")
76
+ end
77
+ event = @event_queue.shift
78
+ { name: event[:name], args: event[:args] }
79
+ end
80
+
81
+ # is there a user event to serve?
82
+ def has_user_event?
83
+ return false unless has_event?
84
+ return false if system_event_names.include? @event_queue.first[:name]
85
+ true
86
+ end
87
+
88
+ # is there a system event to serve?
89
+ def has_system_event?
90
+ return false unless has_event?
91
+ return true if system_event_names.include? @event_queue.first[:name]
92
+ false
93
+ end
94
+
95
+ # is there any event to serve?
96
+ def has_event?(name = nil)
97
+ return false if @event_queue.empty?
98
+ return true if name == nil
99
+ return true if @event_queue.first[:name] == name
100
+ false
101
+ end
102
+
103
+ # returns name of the first event in queue
104
+ def first_event
105
+ event = @event_queue.first
106
+ if event.nil?
107
+ nil
108
+ else
109
+ event[:name]
110
+ end
111
+ end
112
+
113
+ def handlers_size
114
+ @event_handlers.size
115
+ end
116
+
117
+ def event_queue_size
118
+ @event_queue.size
119
+ end
120
+
121
+ # system events need special handling, by special
122
+ # transitions, e.g. :cpu event requires a cpu token
123
+ # :delay_for requires change in timestamp, etc.
124
+ # handlers for system events must be implemented
125
+ # in simulator!
126
+ def system_event_names
127
+ [ :cpu, :delay_for, :send_data, :new_process, :register_event, :log, :stats_start, :stats_stop, :stats, :stats_save ]
128
+ end
129
+
130
+ # Start new process on the same node
131
+ # used when creating new process during simulation
132
+ def new(program = nil)
133
+ self.class.new @node, program
134
+ end
135
+
136
+ private
137
+ def check_if_assigned!
138
+ if @node.nil?
139
+ raise NotAssignedToNode.new("process #{@name} will not serve events until assigned to a node!")
140
+ end
141
+ end
142
+
143
+ def generic_data_received_handler
144
+ proc do |process, data|
145
+ STDERR.puts "WARNING! Process #{process.name} on node #{process.node} received data, but has no handler defined! Define handler for event :data_received! Data packege will be dropped! Received data: #{data}"
146
+ end
147
+ end
148
+
149
+ end
150
+
151
+ end
152
+ end
@@ -0,0 +1,107 @@
1
+ #
2
+ # This file extends Ruby Numeric class by methods
3
+ # used in RBSim to desciribe units of time,
4
+ # data volume and network speed.
5
+ #
6
+ # Native data volume unit is one bit
7
+ # Native network speed unit is one bit per second
8
+ # Time unit is one jiffie, defined by Numric::RBSIM_JIFFIES_PER_SECOND
9
+ class Numeric
10
+
11
+ #
12
+ # Time units
13
+ #
14
+
15
+
16
+ # Defines unit of time -- jiffie is the smallest
17
+ # time unit that can be accounted by simulator
18
+ RBSIM_JIFFIES_PER_SECOND = 1000000
19
+
20
+ def seconds
21
+ self * RBSIM_JIFFIES_PER_SECOND
22
+ end
23
+
24
+ def miliseconds
25
+ self.seconds / 1000
26
+ end
27
+
28
+ def microseconds
29
+ self.miliseconds / 1000
30
+ end
31
+
32
+ def minutes
33
+ self.seconds * 60
34
+ end
35
+
36
+ def hours
37
+ self.minutes * 60
38
+ end
39
+
40
+ def days
41
+ self.hours * 24
42
+ end
43
+
44
+ def in_seconds
45
+ self / RBSIM_JIFFIES_PER_SECOND
46
+ end
47
+
48
+ def in_miliseconds
49
+ self * 1000 / RBSIM_JIFFIES_PER_SECOND
50
+ end
51
+
52
+ def in_microseconds
53
+ self * 1000 * 1000 / RBSIM_JIFFIES_PER_SECOND
54
+ end
55
+
56
+ def in_minutes
57
+ self.in_seconds / 60
58
+ end
59
+
60
+ def in_hours
61
+ self.in_minutes / 60
62
+ end
63
+
64
+ def in_days
65
+ self.in_hours / 24
66
+ end
67
+
68
+ #
69
+ # Data volume units
70
+ #
71
+
72
+ def bytes
73
+ self * 8
74
+ end
75
+
76
+ def in_bytes
77
+ self / 8
78
+ end
79
+
80
+ def in_bits
81
+ self
82
+ end
83
+
84
+ def bits
85
+ self
86
+ end
87
+
88
+ #
89
+ # Network throuput units
90
+ #
91
+
92
+ def bps
93
+ self.to_f.in_seconds
94
+ end
95
+
96
+ def Bps
97
+ (self * 8).to_f.in_seconds
98
+ end
99
+
100
+ def in_bps
101
+ self.seconds
102
+ end
103
+
104
+ def in_Bps
105
+ (self / 8).seconds
106
+ end
107
+ end
@@ -0,0 +1,184 @@
1
+ module RBSim
2
+
3
+ class Simulator
4
+ attr_reader :clock
5
+ # To how many fragments should be divided data
6
+ # sent over the network?
7
+ attr_accessor :data_fragmentation
8
+
9
+ # Create new simulator
10
+ # +block+ defines new model
11
+ # +params+ will be passed as block parameter to the model
12
+ def initialize(params = {}, &block)
13
+ @block = block
14
+ @params = params
15
+ @logger = default_logger
16
+ @stats_collector = Statistics.new
17
+ @resource_stats_collector = Statistics.new
18
+ @clock = 0
19
+ @data_fragmentation = 1
20
+ end
21
+
22
+ def data_fragmentation=(d)
23
+ raise "TCPN already initialized. Too late to set data fragmentation" unless @tcpn.nil?
24
+ @data_fragmentation = d
25
+ end
26
+
27
+ def run
28
+ simulator.run
29
+ end
30
+
31
+ def stop
32
+ simulator.stop
33
+ end
34
+
35
+ def hlmodel
36
+ if @hlmodel.nil?
37
+ @hlmodel = RBSim::HLModel::Model.new
38
+ @hlmodel.simulator = self
39
+ Docile.dsl_eval(RBSim::DSL.new(@hlmodel), @params, &@block)
40
+ end
41
+ @hlmodel
42
+ end
43
+
44
+ def tcpn
45
+ if @tcpn.nil?
46
+ @tcpn = FastTCPN.read File.expand_path '../../../tcpn/model.rb', __FILE__
47
+ hlmodel.nets.each do |net|
48
+ @tcpn.add_marking_for 'net', net
49
+ end
50
+
51
+ hlmodel.nodes.each do |node|
52
+ node.cpus.each do |cpu|
53
+ cpu.node = node.name
54
+ @tcpn.add_marking_for 'CPU', cpu
55
+ end
56
+ end
57
+
58
+ hlmodel.processes.each do |name, process|
59
+ process.node = hlmodel.mapping[name]
60
+ process.data_fragmentation = data_fragmentation
61
+ @tcpn.add_marking_for 'process', process
62
+ @tcpn.add_marking_for 'data to receive', Tokens::DataQueueToken.new(process.name, process.tags)
63
+ end
64
+
65
+
66
+ @tcpn.add_marking_for 'routes', hlmodel.routes
67
+ @tcpn.add_marking_for 'mapping', hlmodel.mapping
68
+
69
+
70
+ end
71
+
72
+ @tcpn
73
+ end
74
+
75
+ def simulator
76
+ if @simulator.nil?
77
+ @simulator = tcpn
78
+
79
+ set_logger_callbacks
80
+ set_stats_collector_callbacks
81
+ set_clock_callbacks
82
+
83
+ end
84
+ @simulator
85
+ end
86
+
87
+ def logger(&block)
88
+ @logger = block
89
+ end
90
+
91
+ def stats
92
+ { application: @stats_collector, resources: @resource_stats_collector }
93
+ end
94
+
95
+ # FIXME: not tested!
96
+ def stats_save(file)
97
+ File.open file, 'w' do |f|
98
+ f.puts Marshal.dump(stats_data)
99
+ end
100
+ end
101
+
102
+ private
103
+
104
+ def default_logger
105
+ proc do |clock, message|
106
+ puts "%.3f: #{message}" % (clock.to_f.in_seconds)
107
+ end
108
+ end
109
+
110
+ def set_logger_callbacks
111
+ @simulator.cb_for :transition, :after do |t, e|
112
+ if e.transition == 'event::log'
113
+ message = e.binding['process'].value.serve_system_event(:log)[:args]
114
+ @logger.call e.clock, message
115
+ end
116
+ end
117
+ end
118
+
119
+ def set_stats_collector_callbacks
120
+ @simulator.cb_for :transition, :after do |t, e|
121
+ if e.transition == "event::stats"
122
+ process = e.binding['process'].value
123
+ event = process.first_event
124
+ params = process.serve_system_event(event)[:args]
125
+ @stats_collector.event event.to_s.sub(/^stats_/,'').to_sym, params, e.clock
126
+ elsif e.transition == "event::cpu"
127
+ cpu = e.binding['CPU'].value
128
+ @resource_stats_collector.event :start, cpu.tags.merge({ resource: 'CPU', node: cpu.node }), e.clock
129
+ elsif e.transition == "event::cpu_finished"
130
+ cpu = e.binding['working CPU'].value[:cpu]
131
+ @resource_stats_collector.event :stop, cpu.tags.merge({ resource: 'CPU', node: cpu.node }), e.clock
132
+ elsif e.transition == "transmitted"
133
+ process = e.binding['data after net'].value.dst
134
+ queue = e.binding['data to receive'].value
135
+ @resource_stats_collector.event :start, queue.process_tags.merge({ resource: 'DATAQ WAIT', process: process }), e.clock
136
+ elsif e.transition == "event::data_received"
137
+ process = e.binding['process'].value
138
+ @resource_stats_collector.event :stop, process.tags.merge({ resource: 'DATAQ WAIT', process: process.name }), e.clock
139
+ elsif e.transition == "net"
140
+ net = e.binding['net'].value
141
+ dropped = e.binding['net'].value.drop?
142
+ if dropped
143
+ @resource_stats_collector.event :stats, net.tags.merge({ event: 'NET DROP', net: net.name }), e.clock
144
+ end
145
+ end
146
+ end
147
+
148
+ @simulator.cb_for :place, :remove do |t, e|
149
+ if e.place == 'net'
150
+ net = e.tokens.first.value
151
+ @resource_stats_collector.event :start, net.tags.merge({ resource: 'NET', name: net.name }), e.clock
152
+ end
153
+ end
154
+ @simulator.cb_for :place, :add do |t, e|
155
+ if e.place == 'net'
156
+ net = e.tokens.first[:val]
157
+ ts = e.tokens.first[:ts]
158
+ @resource_stats_collector.event :stop, net.tags.merge({ resource: 'NET', name: net.name }), ts
159
+ elsif e.place == 'data to receive'
160
+ queue = e.tokens.first[:val]
161
+ process_name = queue.process_name
162
+ process_tags = queue.process_tags
163
+ @resource_stats_collector.event :save,
164
+ { value: queue.length, tags: process_tags.merge({ resource: 'DATAQ LEN', process: process_name }) },
165
+ e.tcpn.clock
166
+ end
167
+ end
168
+ end
169
+
170
+ def set_clock_callbacks
171
+ @simulator.cb_for :clock, :after do |t, e|
172
+ self.clock = e.clock
173
+ end
174
+ end
175
+
176
+ def clock=(clock)
177
+ @clock = clock
178
+ @stats_collector.clock = clock
179
+ @resource_stats_collector.clock = clock
180
+ end
181
+
182
+ end
183
+
184
+ end