-
Notifications
You must be signed in to change notification settings - Fork 11
/
Copy pathsimulate.cr
168 lines (138 loc) · 3.86 KB
/
simulate.cr
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
struct Peer
property data
property already_shared
property num_inbound
property evil
def initialize(
@data : Int32,
@already_shared : Bool,
@num_inbound : Int32,
@evil : Bool
)
end
end
module Sim
extend self
DESIRED_DATA = 99
NUM_PEERS = 1000
NUM_SAMPLED = 5
PROPORTION_EVIL = 0.1
@@peers = Array(Peer).new
def convergence_ratio
num_with_data = 0
@@peers.each do |peer|
if peer.data == DESIRED_DATA
num_with_data += 1
end
end
return (num_with_data.to_f / NUM_PEERS.to_f).significant(2)
end
def propagate
bandwidth = 0
wasted = 0
new_peers = @@peers.dup
NUM_PEERS.times do |index|
peer = @@peers[index]
if peer.data == DESIRED_DATA && !peer.already_shared && !peer.evil
# Share with sampled peers
NUM_SAMPLED.times do
r_index = (Random.rand * NUM_PEERS).to_i
bandwidth += 1
if @@peers[r_index].data == DESIRED_DATA
wasted += 1
new_peers[r_index] = Peer.new(
@@peers[r_index].data,
@@peers[r_index].already_shared,
@@peers[r_index].num_inbound + 1,
@@peers[r_index].evil
)
else
new_num_inbound = @@peers[r_index].num_inbound + 1
new_peers[r_index] = Peer.new(
DESIRED_DATA,
@@peers[r_index].already_shared,
new_num_inbound,
@@peers[r_index].evil
)
end
end
# Mark that I shared with sampled peers
new_peers[index] = Peer.new(DESIRED_DATA, true, peer.num_inbound, peer.evil)
end
end
@@peers = new_peers
return {bandwidth, wasted}
end
def run
avg_steps = 0
avg_bandwidth = 0
avg_inbound_per_peer = 0
avg_wasted = 0
did_not_converge = 0
max_inbound_per_peer = 0
50.times do |n|
puts "---"
puts "Run #{n}"
@@peers = Array(Peer).new
NUM_PEERS.times do |i|
evil = Random.rand < PROPORTION_EVIL ? true : false
new_peer = Peer.new(0, false, 0, evil)
@@peers.push(new_peer)
end
@@peers[0] = Peer.new(DESIRED_DATA, false, 0, false)
steps = 0
bandwidth = 0
wasted = 0
while steps < 100
added_bandwidth, added_wasted = propagate
bandwidth += added_bandwidth
wasted += added_wasted
convergence = convergence_ratio
puts "Step #{steps}: #{convergence}"
if convergence >= 1.0
break
end
steps += 1
end
if steps >= 100
did_not_converge += 1
end
inbound_per_peer = 0
@@peers.each do |peer|
inbound_per_peer += peer.num_inbound
if (peer.num_inbound > max_inbound_per_peer)
max_inbound_per_peer = peer.num_inbound
end
end
inbound_per_peer /= @@peers.size
if (avg_inbound_per_peer == 0)
avg_inbound_per_peer = inbound_per_peer
else
avg_inbound_per_peer = (avg_inbound_per_peer + inbound_per_peer) / 2.0
end
if (avg_steps == 0)
avg_steps = steps
else
avg_steps = (avg_steps + steps) / 2.0
end
if (avg_bandwidth == 0)
avg_bandwidth = bandwidth
else
avg_bandwidth = (avg_bandwidth + bandwidth) / 2.0
end
if (avg_wasted == 0)
avg_wasted = wasted
else
avg_wasted = (avg_wasted + wasted) / 2.0
end
end
puts "Num did not converge: #{did_not_converge}"
puts "Avg inbound reqs per peer: #{avg_inbound_per_peer}"
puts "Max inbound reqs for any peer, in any run: #{max_inbound_per_peer}"
puts "Avg time steps: #{avg_steps}"
puts "Avg bandwidth: #{avg_bandwidth}"
puts "Avg wasted bandwidth: #{avg_wasted}"
puts "which is multiplier of #{(avg_bandwidth / NUM_PEERS).significant(1)}X direct"
end
end
Sim.run