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
data/TODO ADDED
@@ -0,0 +1,28 @@
1
+ * Error handling:
2
+ ** when there is not route between two processes, packets sent
3
+ between them are silently ignored -- no error message
4
+
5
+ * Add to README.md description of preferred RBSim usage scenario describing
6
+ usage of RBSim::Experiment. Good usage example can be found in
7
+ nauka/mrzasa_dynosize/
8
+ * Maybe it would be useful to add optional `:preemption` parameter to `cpu`
9
+ statement in process model to enable user to define preemption levels.
10
+ Currently, it can be done with the following piece of code:
11
+
12
+ 10.times do # can be preempted 10 times
13
+ cpu do |cpu|
14
+ cpu_time / 10 / cpu.performance
15
+ end
16
+ end
17
+
18
+ Obviously, it slows down simulation and makes stats swell (every cpu event
19
+ is logged in stats), but sometimes may be required. The preemption
20
+ parameter could itself do the above.
21
+
22
+ * Data to self imlementation will spoil DATAQ WAIT stats -- no
23
+ stats_start is called when network is passed over! Neet to add
24
+ stats_start
25
+ * To speed-up simulation, we may create a token with one data queue
26
+ for each net segment in place 'data with route', if multiple data
27
+ packages are send in one moment, simulator will not choke on large
28
+ cartesian product in transition 'net'
data/basic_sim.rb ADDED
@@ -0,0 +1,62 @@
1
+ require 'rbsim'
2
+
3
+ model = RBSim.model do
4
+
5
+ program :wget do |opts|
6
+ sent = 0
7
+ on_event :send do
8
+ cpu do |cpu|
9
+ (150 / cpu.performance).miliseconds
10
+ end
11
+ send_data to: opts[:target], size: 1024.bytes, type: :request, content: sent
12
+ sent += 1
13
+ register_event :send, delay: 5.miliseconds if sent < opts[:count]
14
+ end
15
+
16
+ on_event :data_received do |data|
17
+ log "Got data #{data} in process #{process.name}"
18
+ stats :request_served, process.name
19
+ end
20
+
21
+ register_event :send
22
+ end
23
+
24
+ program :apache do
25
+ on_event :data_received do |data|
26
+ stats_start :apache, process.name
27
+ cpu do |cpu|
28
+ (100 * data.size.in_bytes / cpu.performance).miliseconds
29
+ end
30
+ send_data to: data.src, size: data.size * 10, type: :response, content: data.content
31
+ stats_stop :apache, process.name
32
+ end
33
+ end
34
+
35
+ node :desktop do
36
+ cpu 100
37
+ end
38
+
39
+ node :gandalf do
40
+ cpu 1400
41
+ end
42
+
43
+ new_process :client1, program: :wget, args: { target: :server, count: 10 }
44
+ new_process :client2, program: :wget, args: { target: :server, count: 10 }
45
+ new_process :server, program: :apache, args: 'apache1'
46
+
47
+ net :net01, bw: 1024.bps
48
+ net :net02, bw: 510.bps
49
+
50
+ route from: :desktop, to: :gandalf, via: [ :net01, :net02 ], twoway: true
51
+
52
+ put :server, on: :gandalf
53
+ put :client1, on: :desktop
54
+ put :client2, on: :desktop
55
+
56
+ end
57
+
58
+
59
+ model.run
60
+
61
+ model.stats_print
62
+ p model.stats_summary
data/fast-tcpn.rb ADDED
@@ -0,0 +1,3 @@
1
+ require 'fast-tcpn'
2
+
3
+ FastTCPN.read 'tcpn/model.rb'
data/lib/rbsim.rb ADDED
@@ -0,0 +1,14 @@
1
+ require 'fast-tcpn'
2
+ require 'rbsim/dsl'
3
+ require 'rbsim/hlmodel'
4
+ require 'rbsim/simulator'
5
+ require 'rbsim/statistics'
6
+ require 'rbsim/numeric_units'
7
+ require 'rbsim/experiment'
8
+ require 'rbsim/version'
9
+
10
+ module RBSim
11
+ def self.stats_read(file)
12
+ Marshal.load File.read(file)
13
+ end
14
+ end
data/lib/rbsim/dsl.rb ADDED
@@ -0,0 +1,30 @@
1
+ require 'docile'
2
+ require 'rbsim/tokens'
3
+ require 'rbsim/dsl/infrastructure'
4
+ require 'rbsim/dsl/program'
5
+ require 'rbsim/dsl/process'
6
+ require 'rbsim/dsl/mapping'
7
+
8
+ module RBSim
9
+
10
+ def self.dsl(&block)
11
+ hlmodel = RBSim::HLModel::Model.new
12
+ Docile.dsl_eval(RBSim::DSL.new(hlmodel), &block)
13
+ hlmodel
14
+ end
15
+
16
+ # TODO: after implementing marking TCPN from the HLModel, update spec/integration/new_process_spec.rb!
17
+
18
+ def self.model(params = {}, &block)
19
+ Simulator.new params, &block
20
+ end
21
+
22
+ # Read model from a +file+.
23
+ # +params+ will be passes as to the model as 'params' variable
24
+ def self.read(file, params = {})
25
+ p = "proc { |params| #{File.read file} }"
26
+ block = instance_eval p, file
27
+ model params, &block
28
+ end
29
+
30
+ end
@@ -0,0 +1,48 @@
1
+ module RBSim
2
+ class DSL
3
+ def initialize(model)
4
+ @model = model
5
+ end
6
+
7
+ def node(name, &block)
8
+ @model.nodes << Docile.dsl_eval(NodeDSL.new(name), &block).build
9
+ rescue => e
10
+ puts e
11
+ raise
12
+ end
13
+
14
+ def net(name, args = {})
15
+ bw = args[:bw]
16
+ delay = args[:delay] || 0
17
+ drop = args[:drop] || 0
18
+ tags = args[:tags] || {}
19
+ @model.nets << Tokens::NetToken.new(name, bw, delay, drop, tags)
20
+ end
21
+
22
+ def route(args = {})
23
+ twoway = if args[:twoway] || args[:twoway] == :true
24
+ true
25
+ else
26
+ false
27
+ end
28
+ @model.routes << HLModel::Route.new(args[:from], args[:to], args[:via], twoway)
29
+ end
30
+ end
31
+
32
+ class NodeDSL
33
+ def initialize(name)
34
+ @name = name
35
+ @cpus = []
36
+ end
37
+
38
+ def cpu(performance, args = {})
39
+ tags = args[:tags] || {}
40
+ @cpus << Tokens::CPUToken.new(performance, nil, tags)
41
+ end
42
+
43
+ def build
44
+ HLModel::Node.new(@name, @cpus)
45
+ end
46
+
47
+ end
48
+ end
@@ -0,0 +1,32 @@
1
+ module RBSim
2
+ class DSL
3
+ IncorrectMapping = Class.new RuntimeError
4
+
5
+ def put(process_name, opts = nil)
6
+ if !process_name.instance_of?(Hash) && opts.nil?
7
+ raise IncorrectMapping.new("Does not define node for proces #{process_name}")
8
+ end
9
+
10
+ if process_name.instance_of? Hash
11
+ opts = process_name
12
+ else
13
+ opts[:process] = process_name
14
+ end
15
+
16
+ process = opts[:process]
17
+ node = opts[:on]
18
+
19
+ if process.nil?
20
+ msg = " to put on node #{node}" unless node.nil?
21
+ raise IncorrectMapping.new("Does not define process#{msg}")
22
+ end
23
+ if node.nil?
24
+ raise IncorrectMapping.new("Does not define node for proces #{process}")
25
+ end
26
+
27
+ @model.mapping[process] = node
28
+
29
+ end
30
+ end
31
+ end
32
+
@@ -0,0 +1,129 @@
1
+ module RBSim
2
+ class DSL
3
+ def new_process(name, opts = {}, &block)
4
+ ProcessDSL.new_process(@model, name, opts, &block)
5
+ end
6
+ end
7
+
8
+ class ProcessDSL
9
+ UnknownProgramName = Class.new RuntimeError
10
+ NeitherBlockNorProgramGiven = Class.new RuntimeError
11
+
12
+ attr_reader :process
13
+
14
+ # Create new process.
15
+ # +name+ process name
16
+ # +opts+ +{ program: name_of_program_to_run, args: args_to_pass_to_program_block }+
17
+ # +block+ block defining new process (if given +opts+ are ignored)
18
+ def self.new_process(model, name, opts = {}, &block)
19
+ args = nil
20
+ program = nil
21
+ unless block_given?
22
+ program = opts[:program]
23
+ raise NeitherBlockNorProgamGiven.new("for new_process #{name}") if program.nil?
24
+ args = opts[:args]
25
+ block = model.programs[program]
26
+ raise UnknownProgramName.new("#{program} for new_process #{name}") if block.nil?
27
+ end
28
+ process = Tokens::ProcessToken.new(name, program, opts[:tags])
29
+ process = Docile.dsl_eval(ProcessDSL.new(model, name, program, process), args, &block).process
30
+ model.processes[name] = process
31
+ end
32
+
33
+ def initialize(model, name, program, process)
34
+ @name = name
35
+ @model = model
36
+ @program = program
37
+ @process = process
38
+ end
39
+
40
+ def on_event(event, &block)
41
+ # Cannot use self as eval context and
42
+ # must pass process because after clonning in TCPN it will be
43
+ # completely different process object then it is now!
44
+ handler = proc do |process, args|
45
+ process.functions.each do |name, definition|
46
+ # This is actually a kind of hack... but there is
47
+ # no other way to hit exactly this class with define_method, since
48
+ # almost all other methods from this class are removed by docile.
49
+ # Therefore every attempt to do metaprogramming on objects of this class
50
+ # hit the objects to whom this class forwards method calls...
51
+ # But this way we actually can define required methods that will operate
52
+ # in the context of proper object representing a process and that will
53
+ # cause side effects (e.g. changes in event queue) in proper objects.
54
+ Docile::FallbackContextProxy.__send__ :define_method, name, definition
55
+ end
56
+ changed_process = Docile.dsl_eval(ProcessDSL.new(@model, @name, @program, process), args, &block).process
57
+ process.functions.each do |name, definition|
58
+ # remove newly defined methods not to mess things up in the Docile::FallbackContextProxy
59
+ # unless removed, these methods would be available for the other processes!
60
+ Docile::FallbackContextProxy.__send__ :undef_method
61
+ end
62
+ changed_process
63
+ end
64
+ @process.on_event(event, &handler)
65
+ end
66
+
67
+ def function(name, &block)
68
+ @process.function name, &block
69
+ end
70
+
71
+ # register_event name, delay: 100, args: { event related args }
72
+ def register_event(event, opts = {})
73
+ args = opts[:args]
74
+ delay = opts[:delay] || 0
75
+ @process.enqueue_event(:register_event, event: event, delay: delay, event_args: args)
76
+ end
77
+
78
+ def cpu(&block)
79
+ @process.enqueue_event(:cpu, block: block)
80
+ end
81
+
82
+ def delay_for(args)
83
+ if args.kind_of? Numeric
84
+ args = { time: args }
85
+ end
86
+ @process.enqueue_event(:delay_for, args)
87
+ end
88
+
89
+ def send_data(args)
90
+ @process.enqueue_event(:send_data, args)
91
+ end
92
+
93
+ def log(message)
94
+ @process.enqueue_event(:log, message)
95
+ end
96
+
97
+ def stats_start(tags)
98
+ @process.enqueue_event(:stats_start, tags)
99
+ end
100
+
101
+ def stats_stop(tags)
102
+ @process.enqueue_event(:stats_stop, tags)
103
+ end
104
+
105
+ def stats(tags)
106
+ @process.enqueue_event(:stats, tags)
107
+ end
108
+
109
+ def stats_save(value, tags)
110
+ params = { tags: tags, value: value }
111
+ @process.enqueue_event(:stats_save, params)
112
+ end
113
+
114
+ def new_process(name, args = {}, &block)
115
+ constructor = proc do |args|
116
+ new_process = self.class.new_process(@model, name, args, &block)
117
+ new_process.node = @process.node
118
+ new_process
119
+ end
120
+ @process.enqueue_event(:new_process, constructor_args: args, constructor: constructor)
121
+ end
122
+
123
+ # returns time at which the event occured
124
+ def event_time
125
+ @model.simulator.clock
126
+ end
127
+ end
128
+ end
129
+
@@ -0,0 +1,10 @@
1
+ module RBSim
2
+ class DSL
3
+ # program statement just remembers
4
+ # program's block under specified name,
5
+ # to be used later when defining/starting processes
6
+ def program(name, &block)
7
+ @model.programs[name] = block
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,110 @@
1
+ # This is a base class to create RBSim based experiment
2
+ # that is able to load your model and start simulation.
3
+ # It encapsulates model, parameters and statistics
4
+ # and also all your methods used to compute simulation results.
5
+
6
+ require 'pathname'
7
+
8
+ module RBSim
9
+
10
+ class Experiment
11
+ attr_accessor :file, :params, :time_limit, :data_fragmentation
12
+ attr_reader :model
13
+
14
+ RECORD_SEPARATOR = "__ END OF RECORD __"
15
+
16
+ def initialize(params = nil, stats = nil)
17
+ @params, @stats = params, stats
18
+ end
19
+
20
+ def stats
21
+ return @stats unless @stats.nil?
22
+ @stats = model.stats
23
+ end
24
+
25
+ # Run specified model with its params
26
+ # and collect statistics
27
+ def run(file, params)
28
+ read_model(file, params)
29
+ @model.run
30
+ end
31
+
32
+ # Save statistics to a file
33
+ def save_stats(file)
34
+ File.open file, 'a' do |f|
35
+ f.print Marshal.dump [params, stats]
36
+ f.print RECORD_SEPARATOR
37
+ end
38
+ end
39
+
40
+ # Read statistics from a file, return Enumerator of
41
+ # objects, each opject represents separate experiment
42
+ def self.read_stats(file, dots = false)
43
+ size = 0
44
+ File.open(file) do |file|
45
+ size = file.each_line(RECORD_SEPARATOR).count
46
+ end
47
+
48
+ e = Enumerator.new size do |y|
49
+ File.open(file) do |file|
50
+ begin
51
+ while !file.eof?
52
+ file.each_line(RECORD_SEPARATOR) do |line|
53
+ print "." if dots
54
+ params, stats = Marshal.restore line
55
+ y << self.new(params, stats)
56
+ end
57
+ end
58
+ rescue ArgumentError => e
59
+ raise ArgumentError.new "#{caller.first} got #{e.inspect} after reading #{objects.length} objects!"
60
+ end
61
+ end
62
+ end
63
+
64
+ puts if dots
65
+ e
66
+ end
67
+
68
+ def res_stats
69
+ stats[:resources]
70
+ end
71
+
72
+ def app_stats
73
+ stats[:application]
74
+ end
75
+
76
+ private
77
+
78
+ def read_model(file, params)
79
+ @file = file
80
+ @params = params
81
+ @model = RBSim.read file, params
82
+ @model.data_fragmentation = data_fragmentation unless data_fragmentation.nil?
83
+ @model.tcpn.cb_for :clock, :after, &self.method(:timer)
84
+ @model.tcpn.cb_for :clock, :after, &self.method(:time_limit_observer)
85
+ end
86
+
87
+ def timer(tag, event)
88
+ @prev_seconds = 0 if @prev_seconds.nil?
89
+ @timer_length = 0 if @timer_length.nil?
90
+
91
+ seconds = event.clock.in_seconds.truncate
92
+ if seconds > @prev_seconds
93
+ print "\b"*@timer_length
94
+ timer = "Time: #{seconds} sec."
95
+ print timer
96
+ @timer_length = timer.length
97
+ end
98
+ @prev_seconds = seconds
99
+ end
100
+
101
+ def time_limit_observer(tag, event)
102
+ return if @time_limit.nil?
103
+ if event.clock > @time_limit
104
+ @model.stop
105
+ end
106
+ end
107
+
108
+ end
109
+
110
+ end