miner_mover 0.0.0.4 → 0.1.0.1
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 +4 -4
- data/README.md +81 -13
- data/Rakefile +17 -2
- data/VERSION +1 -1
- data/demo/fiber.rb +11 -17
- data/demo/fiber_scheduler.rb +13 -20
- data/demo/{process.rb → process_pipe.rb} +37 -38
- data/demo/process_socket.rb +111 -0
- data/demo/ractor.rb +10 -28
- data/demo/serial.rb +8 -15
- data/demo/thread.rb +11 -21
- data/lib/miner_mover/config.rb +6 -3
- data/lib/miner_mover/run.rb +25 -8
- data/lib/miner_mover/timer.rb +67 -0
- data/lib/miner_mover/worker.rb +27 -14
- data/lib/miner_mover.rb +47 -3
- data/miner_mover.gemspec +7 -4
- metadata +15 -37
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7d6d5e0e85b066dc78e4e6c7b86d751245f1e2b3809ab176f9b69e7047c2d330
|
4
|
+
data.tar.gz: cd0f57ea360871fefdb7927eddef3651a277c8e8116d41a2da085dcf02a3ed73
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 1a8d2759692483f004c0d512a17b2b4ec6a10e6d26b3e66b8785b28a0510cc3eaf7ad029b95f0a11a37b0264dc028e3c65a5cbccffbc64e5446365cdaf9ae2ce
|
7
|
+
data.tar.gz: d88946c92b88073fc01f4b57ae0f13ba157ca7a72c85ae6ad405a0569b311e5420689d76014c5cda88c709b2dfbfa2dbed6b090e2c001894fdf4267686cbd87d
|
data/README.md
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
[](https://github.com/rickhull/miner_mover/actions/workflows/test.yaml)
|
2
|
+
[](https://github.com/rickhull/miner_mover/actions/workflows/demo.yaml)
|
2
3
|
|
3
4
|
# Miner Mover
|
4
5
|
|
@@ -20,8 +21,9 @@ independent and may be randomized.
|
|
20
21
|
https://github.com/rickhull/miner_mover/blob/bd76ea400944aab8eab9e3ffcac85d1e28353eff/lib/miner_mover/worker.rb#L85-L99
|
21
22
|
|
22
23
|
In this case, miners are rewarded by calculating `fibonacci(depth)`, using
|
23
|
-
classic, inefficient fibonacci.
|
24
|
-
|
24
|
+
classic, inefficient fibonacci.
|
25
|
+
`fibonacci(35)` yields around 10M ore, while `fibonacci(30)` yields under
|
26
|
+
1M ore.
|
25
27
|
|
26
28
|
## Moving
|
27
29
|
|
@@ -42,24 +44,72 @@ configured via `:work_type`
|
|
42
44
|
|
43
45
|
## Install
|
44
46
|
|
45
|
-
You'll want to use Ruby 3.x to make the most of Fibers.
|
47
|
+
You'll want to use **Ruby 3.x** to make the most of Fibers.
|
46
48
|
|
47
|
-
|
49
|
+
### Dependencies
|
48
50
|
|
49
51
|
* rake
|
50
52
|
* minitest
|
51
|
-
* compsci
|
52
53
|
* dotcfg
|
53
54
|
* fiber_scheduler
|
54
55
|
|
55
|
-
`gem install
|
56
|
+
`gem install miner_mover` will take care of the following:
|
57
|
+
|
58
|
+
`gem install rake minitest dotcfg fiber_scheduler`
|
59
|
+
|
60
|
+
### Clone
|
61
|
+
|
62
|
+
```
|
63
|
+
git clone https://github.com/rickhull/miner_mover
|
64
|
+
cd miner_mover
|
65
|
+
```
|
66
|
+
|
67
|
+
### Try rake
|
68
|
+
|
69
|
+
Try: `rake -T` to see available [Rake tasks](Rakefile)
|
70
|
+
|
71
|
+
```
|
72
|
+
$ rake -T
|
73
|
+
|
74
|
+
rake alt_demo # run all demos minus fiber_scheduler / ractor / process
|
75
|
+
rake config # run demo/config
|
76
|
+
rake default # rake test
|
77
|
+
rake demo # run all demos
|
78
|
+
rake fiber # run demo/fiber
|
79
|
+
rake fiber_scheduler # run demo/fiber_scheduler
|
80
|
+
rake process_pipe # run demo/process_pipe
|
81
|
+
rake process_socket # run demo/process_socket
|
82
|
+
rake ractor # run demo/ractor
|
83
|
+
rake serial # run demo/serial
|
84
|
+
rake test # Run tests
|
85
|
+
rake thread # run demo/thread
|
86
|
+
```
|
87
|
+
|
88
|
+
## Rake Tasks
|
89
|
+
|
90
|
+
Included demonstration scripts can be executed via Rake tasks.
|
91
|
+
The following order is recommended:
|
92
|
+
|
93
|
+
* `rake config`
|
94
|
+
* `rake serial`
|
95
|
+
* `rake fiber`
|
96
|
+
* `rake fiber_scheduler`
|
97
|
+
* `rake thread`
|
98
|
+
* `rake process_pipe`
|
99
|
+
* `rake process_socket`
|
100
|
+
|
101
|
+
Try each task; there will be about 6 seconds worth of many lines of output
|
102
|
+
logging. These rake tasks correspond to the scripts within [`demo/`](demo/).
|
56
103
|
|
57
104
|
## Satisfy `LOAD_PATH`
|
58
105
|
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
106
|
+
Rake tasks take care of `LOAD_PATH`, so the following is
|
107
|
+
**only necessary when *not* using rake tasks**:
|
108
|
+
|
109
|
+
* Execute scripts and irb sessions from the project root, e.g. `~/miner_mover`
|
110
|
+
* Use `-I lib` as a flag to `ruby` or `irb` to update `LOAD_PATH` so that
|
111
|
+
`require 'miner_mover'` will work.
|
112
|
+
* This project does not use `require_relative`
|
63
113
|
|
64
114
|
## Exploration in `irb`
|
65
115
|
|
@@ -105,13 +155,15 @@ multitasking paradigms in Ruby.
|
|
105
155
|
* [`demo/fiber_scheduler.rb`](demo/fiber_scheduler.rb)
|
106
156
|
* [`demo/thread.rb`](demo/thread.rb)
|
107
157
|
* [`demo/ractor.rb`](demo/ractor.rb)
|
158
|
+
* [`demo/process_pipe.rb`](demo/process_pipe.rb)
|
159
|
+
* [`demo/process_socket.rb`](demo/process_socket.rb)
|
108
160
|
|
109
161
|
See [config/example.cfg](config/example.cfg) for configuration.
|
110
162
|
It will be loaded by default.
|
111
|
-
Note that serial.rb and fiber.rb have no concurrency and cannot use
|
112
|
-
miners or movers.
|
163
|
+
Note that `serial.rb` and `fiber.rb` have no concurrency and cannot use
|
164
|
+
multiple miners or movers.
|
113
165
|
|
114
|
-
Execute via e.g. `ruby -Ilib demo/
|
166
|
+
Execute via e.g. `ruby -Ilib demo/thread.rb`
|
115
167
|
|
116
168
|
# Multitasking
|
117
169
|
|
@@ -319,3 +371,19 @@ Ractor.receive - returns a message from the current Ractor's incoming port
|
|
319
371
|
Ractor.yield - current Ractor sends a message on the outgoing port
|
320
372
|
Ractor#take - returns the next outgoing message from a Ractor
|
321
373
|
```
|
374
|
+
|
375
|
+
## Processes
|
376
|
+
|
377
|
+
There are many ways to create a process in Ruby, some more useful than
|
378
|
+
others. My favorites:
|
379
|
+
|
380
|
+
* `Process.fork` - when called with a block, the block is only executed in the
|
381
|
+
child subprocess
|
382
|
+
* `Process.spawn` - extensive options, nonblocking, call Process.wait(pid)
|
383
|
+
to get the result
|
384
|
+
* `Open3.popen3` - for access to STDIN, STDOUT, STDERR
|
385
|
+
|
386
|
+
### IPC
|
387
|
+
|
388
|
+
* Pipes - streaming bytes: `IO.pipe`
|
389
|
+
* Unix sockets - messages with datagrams: `Socket.pair(:UNIX, :DGRAM, 0)`
|
data/Rakefile
CHANGED
@@ -5,16 +5,31 @@ Rake::TestTask.new :test do |t|
|
|
5
5
|
t.warning = true
|
6
6
|
end
|
7
7
|
|
8
|
+
desc "rake test"
|
8
9
|
task default: :test
|
9
10
|
|
11
|
+
Dir['demo/*.rb'].each { |demo_path|
|
12
|
+
name = File.basename(demo_path, '.rb')
|
13
|
+
desc "run demo/#{name}"
|
14
|
+
task(name) { sh "ruby -Ilib #{demo_path}" }
|
15
|
+
}
|
16
|
+
|
17
|
+
# jruby / truffleruby lack fiber_scheduler, Ractor, and Process#fork
|
18
|
+
desc "run all demos minus fiber_scheduler / ractor / process"
|
19
|
+
task jvm_demo: [:serial, :fiber, :thread]
|
20
|
+
|
21
|
+
desc "run all demos"
|
22
|
+
task demo: [:serial, :fiber, :fiber_scheduler,
|
23
|
+
:thread, :ractor, :process_pipe, :process_socket]
|
24
|
+
|
10
25
|
begin
|
11
26
|
require 'buildar'
|
12
27
|
|
13
|
-
|
28
|
+
Buildar.new do |b|
|
14
29
|
b.gemspec_file = 'miner_mover.gemspec'
|
15
30
|
b.version_file = 'VERSION'
|
16
31
|
b.use_git = true
|
17
32
|
end
|
18
33
|
rescue LoadError
|
19
|
-
warn "buildar tasks unavailable"
|
34
|
+
# warn "buildar tasks unavailable"
|
20
35
|
end
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.1.0.1
|
data/demo/fiber.rb
CHANGED
@@ -2,13 +2,13 @@ require 'miner_mover/run'
|
|
2
2
|
|
3
3
|
include MinerMover
|
4
4
|
|
5
|
-
run = Run.new.cfg_banner!(duration: 1)
|
6
|
-
run.
|
7
|
-
run.log "Starting"
|
5
|
+
run = Run.new.cfg_banner!(duration: 1).start!
|
6
|
+
run.timestamp!
|
7
|
+
run.log "Starting #{__FILE__}"
|
8
8
|
|
9
9
|
stop_mining = false
|
10
10
|
Signal.trap("INT") {
|
11
|
-
run.
|
11
|
+
run.timestamp!
|
12
12
|
run.log " *** SIGINT *** Stop Mining"
|
13
13
|
stop_mining = true
|
14
14
|
}
|
@@ -17,20 +17,16 @@ Signal.trap("INT") {
|
|
17
17
|
miner = Fiber.new(blocking: true) {
|
18
18
|
run.log "MINE Mining operation started [ctrl-c] to stop"
|
19
19
|
m = run.new_miner
|
20
|
-
|
21
20
|
ore_mined = 0
|
22
21
|
|
23
|
-
|
24
|
-
while !stop_mining
|
22
|
+
while !stop_mining # SIGINT will trigger stop_mining = true
|
25
23
|
ore = m.mine_ore
|
26
|
-
|
27
|
-
# send any ore mined to the mover
|
28
|
-
Fiber.yield ore if ore > 0
|
29
24
|
ore_mined += ore
|
25
|
+
Fiber.yield ore if ore > 0
|
30
26
|
|
31
27
|
# stop mining after a while
|
32
28
|
if run.time_limit? or run.ore_limit?(ore_mined)
|
33
|
-
run.
|
29
|
+
run.timestamp!
|
34
30
|
m.log format("Mining limit reached: %s", Ore.display(ore_mined))
|
35
31
|
stop_mining = true
|
36
32
|
end
|
@@ -45,16 +41,14 @@ mover = run.new_mover
|
|
45
41
|
run.log "MOVE Moving operation started"
|
46
42
|
run.log "WAIT Waiting for ore ..."
|
47
43
|
|
44
|
+
# mover pulls from the queue, loads the ore, and moves it
|
48
45
|
loop {
|
49
|
-
# pick up ore yielded by the miner
|
50
46
|
ore = miner.resume
|
51
47
|
break if ore == :quit
|
52
|
-
|
53
|
-
# load (and possibly move) the ore
|
54
|
-
mover.load_ore ore if ore > 0
|
48
|
+
mover.load_ore ore if ore > 0 # move_batch happens when a batch is full
|
55
49
|
}
|
56
50
|
|
57
|
-
#
|
51
|
+
# move any remaining ore and quit
|
58
52
|
mover.move_batch while mover.batch > 0
|
59
53
|
run.log "QUIT #{mover.status}"
|
60
54
|
|
@@ -62,4 +56,4 @@ ore_mined = miner.resume
|
|
62
56
|
ore_moved = mover.ore_moved
|
63
57
|
run.log format("MINE %s mined (%i)", Ore.display(ore_mined), ore_mined)
|
64
58
|
run.log format("MOVE %s moved (%i)", Ore.display(ore_moved), ore_moved)
|
65
|
-
run.
|
59
|
+
run.timestamp!
|
data/demo/fiber_scheduler.rb
CHANGED
@@ -3,13 +3,13 @@ require 'fiber_scheduler'
|
|
3
3
|
|
4
4
|
include MinerMover
|
5
5
|
|
6
|
-
run = Run.new.cfg_banner!(duration: 1)
|
7
|
-
run.
|
8
|
-
run.log "Starting"
|
6
|
+
run = Run.new.cfg_banner!(duration: 1).start!
|
7
|
+
run.timestamp!
|
8
|
+
run.log "Starting #{__FILE__}"
|
9
9
|
|
10
10
|
stop_mining = false
|
11
11
|
Signal.trap("INT") {
|
12
|
-
run.
|
12
|
+
run.timestamp!
|
13
13
|
run.log " *** SIGINT *** Stop Mining"
|
14
14
|
stop_mining = true
|
15
15
|
}
|
@@ -35,20 +35,16 @@ FiberScheduler do
|
|
35
35
|
Fiber.schedule do
|
36
36
|
m = run.new_miner
|
37
37
|
m.log "MINE Miner #{i} started"
|
38
|
-
|
39
38
|
ore_mined = 0
|
40
39
|
|
41
|
-
|
42
|
-
while !stop_mining
|
40
|
+
while !stop_mining # SIGINT will trigger stop_mining = true
|
43
41
|
ore = m.mine_ore
|
44
|
-
|
45
|
-
# send any ore mined to the mover
|
46
|
-
queue.push(ore) if ore > 0
|
47
42
|
ore_mined += ore
|
43
|
+
queue.push(ore) if ore > 0
|
48
44
|
|
49
45
|
# stop mining after a while
|
50
46
|
if run.time_limit? or run.ore_limit?(ore_mined)
|
51
|
-
run.
|
47
|
+
run.timestamp!
|
52
48
|
m.log format("Mining limit reached: %s", Ore.display(ore_mined))
|
53
49
|
stop_mining = true
|
54
50
|
end
|
@@ -73,16 +69,14 @@ FiberScheduler do
|
|
73
69
|
m = run.new_mover
|
74
70
|
m.log "MOVE Mover #{i} started"
|
75
71
|
|
72
|
+
# movers pull from the queue, load the ore, and move it
|
76
73
|
loop {
|
77
|
-
# pick up ore from the miner until we get a :quit message
|
78
74
|
ore = queue.pop
|
79
75
|
break if ore == :quit
|
80
|
-
|
81
|
-
# load (and possibly move) the ore
|
82
|
-
m.load_ore ore if ore > 0
|
76
|
+
m.load_ore ore if ore > 0 # move_batch happens when a batch is full
|
83
77
|
}
|
84
78
|
|
85
|
-
#
|
79
|
+
# move any remaining ore and quit
|
86
80
|
m.move_batch while m.batch > 0
|
87
81
|
m.log "QUIT #{m.status}"
|
88
82
|
|
@@ -100,9 +94,8 @@ FiberScheduler do
|
|
100
94
|
# tell every mover to quit
|
101
95
|
run.num_movers.times { queue.push(:quit) }
|
102
96
|
|
103
|
-
# queue closes once it is empty
|
104
|
-
# should helpfully cause errors if something is out of sync
|
105
|
-
queue.close
|
97
|
+
# queue only closes once it is empty
|
98
|
+
queue.close # should helpfully cause errors if something is out of sync
|
106
99
|
end
|
107
100
|
end
|
108
101
|
|
@@ -114,4 +107,4 @@ total_moved += moved.pop until moved.empty?
|
|
114
107
|
|
115
108
|
run.log format("MINE %s mined (%i)", Ore.display(total_mined), total_mined)
|
116
109
|
run.log format("MOVE %s moved (%i)", Ore.display(total_moved), total_moved)
|
117
|
-
run.
|
110
|
+
run.timestamp!
|
@@ -3,22 +3,32 @@ require 'thread'
|
|
3
3
|
|
4
4
|
include MinerMover
|
5
5
|
|
6
|
-
run = Run.new.cfg_banner!(duration: 1)
|
7
|
-
run.
|
8
|
-
run.log "Starting"
|
6
|
+
run = Run.new.cfg_banner!(duration: 1).start!
|
7
|
+
run.timestamp!
|
8
|
+
run.log "Starting #{__FILE__}"
|
9
9
|
|
10
10
|
stop_mining = false
|
11
11
|
Signal.trap("INT") {
|
12
|
-
run.
|
12
|
+
run.timestamp!
|
13
13
|
run.log " *** SIGINT *** Stop Mining"
|
14
14
|
stop_mining = true
|
15
15
|
}
|
16
16
|
|
17
|
+
pipe_reader, pipe_writer = IO.pipe
|
18
|
+
|
19
|
+
def pipe_reader.pop
|
20
|
+
Ore.decode self.read(Ore::WORD_LENGTH)
|
21
|
+
end
|
22
|
+
|
23
|
+
def pipe_writer.push amt
|
24
|
+
self.write(Ore.encode(amt))
|
25
|
+
end
|
26
|
+
|
27
|
+
# the moving operation executes in its own Process
|
17
28
|
mover = Process.fork {
|
18
29
|
run.log "MOVE Moving operation started"
|
19
|
-
|
20
|
-
# mover
|
21
|
-
queue = Thread::Queue.new
|
30
|
+
pipe_writer.close # we're only using pipe_reader in this process
|
31
|
+
queue = Thread::Queue.new # distribute incoming ore to mover threads
|
22
32
|
|
23
33
|
# store the mover threads in an array
|
24
34
|
movers = Array.new(run.num_movers) { |i|
|
@@ -26,16 +36,11 @@ mover = Process.fork {
|
|
26
36
|
m = run.new_mover
|
27
37
|
m.log "MOVE Mover #{i} started"
|
28
38
|
|
39
|
+
# movers pull from the queue, load the ore, and move it
|
29
40
|
loop {
|
30
|
-
# a mover picks up ore from the queue
|
31
|
-
run.debug && m.log("POP ")
|
32
41
|
ore = queue.pop
|
33
|
-
run.debug && m.log("POPD #{ore}")
|
34
|
-
|
35
42
|
break if ore == :quit
|
36
|
-
|
37
|
-
# load (and possibly move) the ore
|
38
|
-
m.load_ore ore
|
43
|
+
m.load_ore ore # move_batch happens when a batch is full
|
39
44
|
}
|
40
45
|
|
41
46
|
# move any remaining ore and quit
|
@@ -45,22 +50,25 @@ mover = Process.fork {
|
|
45
50
|
}
|
46
51
|
}
|
47
52
|
|
53
|
+
# Miners feed this Process with ore
|
54
|
+
# Pass the ore into a queue for the movers
|
55
|
+
# When the miners say to quit, tell the movers to quit
|
48
56
|
run.log "WAIT Waiting for ore ..."
|
49
|
-
|
50
|
-
# TODO: pipe or unix socket for ore
|
51
|
-
|
52
|
-
TODO = :notyet
|
53
|
-
|
54
57
|
loop {
|
55
|
-
|
56
|
-
ore
|
57
|
-
break if ore == :quit
|
58
|
+
ore = pipe_reader.pop
|
59
|
+
break if ore == 0 # signal to quit
|
58
60
|
queue.push ore
|
59
61
|
}
|
62
|
+
|
63
|
+
# tell all the movers to quit and gather their results
|
64
|
+
run.num_movers.times { queue.push :quit }
|
65
|
+
ore_moved = movers.map { |thr| thr.value.ore_moved }.sum
|
66
|
+
run.log format("MOVE %s moved (%i)", Ore.display(ore_moved), ore_moved)
|
60
67
|
}
|
61
68
|
|
62
|
-
# our mining operation executes in the main
|
69
|
+
# our mining operation executes in the main process, here
|
63
70
|
run.log "MINE Mining operation started [ctrl-c] to stop"
|
71
|
+
pipe_reader.close # we're only using pipe_writer in this process
|
64
72
|
|
65
73
|
# store the miner threads in an array
|
66
74
|
miners = Array.new(run.num_miners) { |i|
|
@@ -69,22 +77,14 @@ miners = Array.new(run.num_miners) { |i|
|
|
69
77
|
m.log "MINE Miner #{i} started"
|
70
78
|
ore_mined = 0
|
71
79
|
|
72
|
-
|
73
|
-
while !stop_mining
|
80
|
+
while !stop_mining # SIGINT will trigger stop_mining = true
|
74
81
|
ore = m.mine_ore
|
75
|
-
|
76
|
-
# send any ore mined to the mover Ractor
|
77
|
-
if ore > 0
|
78
|
-
run.debug && m.log("SEND #{ore}")
|
79
|
-
mover.send ore
|
80
|
-
run.debug && m.log("SENT #{ore}")
|
81
|
-
end
|
82
|
-
|
83
82
|
ore_mined += ore
|
83
|
+
pipe_writer.push(ore) if ore > 0
|
84
84
|
|
85
85
|
# stop mining after a while
|
86
86
|
if run.time_limit? or run.ore_limit?(ore_mined)
|
87
|
-
run.
|
87
|
+
run.timestamp!
|
88
88
|
m.log format("Mining limit reached: %s", Ore.display(ore_mined))
|
89
89
|
stop_mining = true
|
90
90
|
end
|
@@ -101,9 +101,8 @@ ore_mined = miners.map { |thr| thr.value }.sum
|
|
101
101
|
run.log format("MINE %s mined (%i)", Ore.display(ore_mined), ore_mined)
|
102
102
|
|
103
103
|
# tell mover to quit
|
104
|
-
|
104
|
+
pipe_writer.push 0
|
105
105
|
|
106
106
|
# wait for results
|
107
|
-
|
108
|
-
run.
|
109
|
-
run.timer.timestamp!
|
107
|
+
Process.wait
|
108
|
+
run.timestamp!
|
@@ -0,0 +1,111 @@
|
|
1
|
+
require 'miner_mover/run'
|
2
|
+
require 'thread'
|
3
|
+
require 'socket'
|
4
|
+
|
5
|
+
include MinerMover
|
6
|
+
|
7
|
+
run = Run.new.cfg_banner!(duration: 1).start!
|
8
|
+
run.timestamp!
|
9
|
+
run.log "Starting #{__FILE__}"
|
10
|
+
|
11
|
+
stop_mining = false
|
12
|
+
Signal.trap("INT") {
|
13
|
+
run.timestamp!
|
14
|
+
run.log " *** SIGINT *** Stop Mining"
|
15
|
+
stop_mining = true
|
16
|
+
}
|
17
|
+
|
18
|
+
socktype = [:DGRAM, :STREAM, :RAW].sample
|
19
|
+
run.log "SOCK #{socktype}"
|
20
|
+
csock, psock = UNIXSocket.pair(socktype)
|
21
|
+
|
22
|
+
def csock.pop
|
23
|
+
Ore.decode self.recv(Ore::WORD_LENGTH)
|
24
|
+
end
|
25
|
+
|
26
|
+
def psock.push amt
|
27
|
+
self.send(Ore.encode(amt), 0)
|
28
|
+
end
|
29
|
+
|
30
|
+
# the moving operation executes in its own Process
|
31
|
+
mover = Process.fork {
|
32
|
+
run.log "MOVE Moving operation started"
|
33
|
+
psock.close # we're only using csock in this process
|
34
|
+
queue = Thread::Queue.new # distribute incoming ore to mover threads
|
35
|
+
|
36
|
+
# store the mover threads in an array
|
37
|
+
movers = Array.new(run.num_movers) { |i|
|
38
|
+
Thread.new {
|
39
|
+
m = run.new_mover
|
40
|
+
m.log "MOVE Mover #{i} started"
|
41
|
+
|
42
|
+
# movers pull from the queue, load the ore, and move it
|
43
|
+
loop {
|
44
|
+
ore = queue.pop
|
45
|
+
break if ore == :quit
|
46
|
+
m.load_ore ore # move_batch happens when a batch is full
|
47
|
+
}
|
48
|
+
|
49
|
+
# move any remaining ore and quit
|
50
|
+
m.move_batch while m.batch > 0
|
51
|
+
m.log "QUIT #{m.status}"
|
52
|
+
m
|
53
|
+
}
|
54
|
+
}
|
55
|
+
|
56
|
+
# Miners feed this Process with ore
|
57
|
+
# Pass the ore into a queue for the movers
|
58
|
+
# When the miners say to quit, tell the movers to quit
|
59
|
+
run.log "WAIT Waiting for ore ..."
|
60
|
+
loop {
|
61
|
+
ore = csock.pop
|
62
|
+
break if ore == 0 # signal to quit
|
63
|
+
queue.push ore
|
64
|
+
}
|
65
|
+
|
66
|
+
# tell all the movers to quit and gather their results
|
67
|
+
run.num_movers.times { queue.push :quit }
|
68
|
+
ore_moved = movers.map { |thr| thr.value.ore_moved }.sum
|
69
|
+
run.log format("MOVE %s moved (%i)", Ore.display(ore_moved), ore_moved)
|
70
|
+
}
|
71
|
+
|
72
|
+
# our mining operation executes in the main process, here
|
73
|
+
run.log "MINE Mining operation started [ctrl-c] to stop"
|
74
|
+
csock.close # we're only using psock in this process
|
75
|
+
|
76
|
+
# store the miner threads in an array
|
77
|
+
miners = Array.new(run.num_miners) { |i|
|
78
|
+
Thread.new {
|
79
|
+
m = run.new_miner
|
80
|
+
m.log "MINE Miner #{i} started"
|
81
|
+
ore_mined = 0
|
82
|
+
|
83
|
+
while !stop_mining # SIGINT will trigger stop_mining = true
|
84
|
+
ore = m.mine_ore
|
85
|
+
ore_mined += ore
|
86
|
+
psock.push(ore) if ore > 0
|
87
|
+
|
88
|
+
# stop mining after a while
|
89
|
+
if run.time_limit? or run.ore_limit?(ore_mined)
|
90
|
+
run.timestamp!
|
91
|
+
m.log format("Mining limit reached: %s", Ore.display(ore_mined))
|
92
|
+
stop_mining = true
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
m.log format("MINE Miner %i finished after mining %s",
|
97
|
+
i, Ore.display(ore_mined))
|
98
|
+
ore_mined
|
99
|
+
}
|
100
|
+
}
|
101
|
+
|
102
|
+
# wait on all mining threads to stop
|
103
|
+
ore_mined = miners.map { |thr| thr.value }.sum
|
104
|
+
run.log format("MINE %s mined (%i)", Ore.display(ore_mined), ore_mined)
|
105
|
+
|
106
|
+
# tell mover to quit
|
107
|
+
psock.push 0
|
108
|
+
|
109
|
+
# wait for results
|
110
|
+
Process.wait
|
111
|
+
run.timestamp!
|
data/demo/ractor.rb
CHANGED
@@ -3,13 +3,13 @@ require 'thread'
|
|
3
3
|
|
4
4
|
include MinerMover
|
5
5
|
|
6
|
-
run = Run.new.cfg_banner!(duration: 1)
|
7
|
-
run.
|
8
|
-
run.log "Starting"
|
6
|
+
run = Run.new.cfg_banner!(duration: 1).start!
|
7
|
+
run.timestamp!
|
8
|
+
run.log "Starting #{__FILE__}"
|
9
9
|
|
10
10
|
stop_mining = false
|
11
11
|
Signal.trap("INT") {
|
12
|
-
run.
|
12
|
+
run.timestamp!
|
13
13
|
run.log " *** SIGINT *** Stop Mining"
|
14
14
|
stop_mining = true
|
15
15
|
}
|
@@ -27,16 +27,11 @@ mover = Ractor.new(run) { |r|
|
|
27
27
|
m = r.new_mover
|
28
28
|
m.log "MOVE Mover #{i} started"
|
29
29
|
|
30
|
+
# movers pull from the queue, load the ore, and move it
|
30
31
|
loop {
|
31
|
-
# a mover picks up ore from the queue
|
32
|
-
r.debug && m.log("POP ")
|
33
32
|
ore = queue.pop
|
34
|
-
r.debug && m.log("POPD #{ore}")
|
35
|
-
|
36
33
|
break if ore == :quit
|
37
|
-
|
38
|
-
# load (and possibly move) the ore
|
39
|
-
m.load_ore ore
|
34
|
+
m.load_ore ore # move_batch happens when a batch is full
|
40
35
|
}
|
41
36
|
|
42
37
|
# move any remaining ore and quit
|
@@ -53,13 +48,8 @@ mover = Ractor.new(run) { |r|
|
|
53
48
|
loop {
|
54
49
|
# when the Ractor gets ore, push it into the queue
|
55
50
|
ore = Ractor.recv
|
56
|
-
r.debug && r.log("RECV #{ore}")
|
57
|
-
|
58
51
|
break if ore == :quit
|
59
|
-
|
60
|
-
r.debug && r.log("PUSH #{ore}")
|
61
52
|
queue.push ore
|
62
|
-
r.debug && r.log("PSHD #{ore}")
|
63
53
|
}
|
64
54
|
|
65
55
|
# tell all the movers to quit and gather their results
|
@@ -77,22 +67,14 @@ miners = Array.new(run.num_miners) { |i|
|
|
77
67
|
m.log "MINE Miner #{i} started"
|
78
68
|
ore_mined = 0
|
79
69
|
|
80
|
-
|
81
|
-
while !stop_mining
|
70
|
+
while !stop_mining # SIGINT will trigger stop_mining = true
|
82
71
|
ore = m.mine_ore
|
83
|
-
|
84
|
-
# send any ore mined to the mover Ractor
|
85
|
-
if ore > 0
|
86
|
-
run.debug && m.log("SEND #{ore}")
|
87
|
-
mover.send ore
|
88
|
-
run.debug && m.log("SENT #{ore}")
|
89
|
-
end
|
90
|
-
|
91
72
|
ore_mined += ore
|
73
|
+
mover.send ore if ore > 0 # send any ore mined to the mover Ractor
|
92
74
|
|
93
75
|
# stop mining after a while
|
94
76
|
if run.time_limit? or run.ore_limit?(ore_mined)
|
95
|
-
run.
|
77
|
+
run.timestamp!
|
96
78
|
m.log format("Mining limit reached: %s", Ore.display(ore_mined))
|
97
79
|
stop_mining = true
|
98
80
|
end
|
@@ -114,4 +96,4 @@ mover.send :quit
|
|
114
96
|
# wait for results
|
115
97
|
ore_moved = mover.take
|
116
98
|
run.log format("MOVE %s moved (%i)", Ore.display(ore_moved), ore_moved)
|
117
|
-
run.
|
99
|
+
run.timestamp!
|
data/demo/serial.rb
CHANGED
@@ -2,39 +2,32 @@ require 'miner_mover/run'
|
|
2
2
|
|
3
3
|
include MinerMover
|
4
4
|
|
5
|
-
run = Run.new.cfg_banner!(duration: 1)
|
6
|
-
run.
|
7
|
-
run.log "Starting"
|
5
|
+
run = Run.new.cfg_banner!(duration: 1).start!
|
6
|
+
run.timestamp!
|
7
|
+
run.log "Starting #{__FILE__}"
|
8
8
|
|
9
9
|
stop_mining = false
|
10
10
|
Signal.trap("INT") {
|
11
|
-
run.
|
11
|
+
run.timestamp!
|
12
12
|
run.log " *** SIGINT *** Stop Mining"
|
13
13
|
stop_mining = true
|
14
14
|
}
|
15
15
|
|
16
|
-
# system 'cpulimit', "--pid=#{Process.pid}", '--limit=1', '--background'
|
17
|
-
|
18
16
|
miner = run.new_miner
|
19
17
|
run.log "MINE Mining operation initialized [ctrl-c] to stop"
|
20
18
|
|
21
19
|
mover = run.new_mover
|
22
20
|
run.log "MOVE Moving operation initialized"
|
23
|
-
|
24
21
|
ore_mined = 0
|
25
22
|
|
26
|
-
|
27
|
-
while !stop_mining
|
28
|
-
# mine the ore
|
23
|
+
while !stop_mining # SIGINT will trigger stop_mining = true
|
29
24
|
ore = miner.mine_ore
|
30
25
|
ore_mined += ore
|
31
|
-
|
32
|
-
# load (and possibly move) the ore
|
33
|
-
mover.load_ore ore if ore > 0
|
26
|
+
mover.load_ore ore if ore > 0 # move_batch happens when a batch is full
|
34
27
|
|
35
28
|
# stop mining after a while
|
36
29
|
if run.time_limit? or run.ore_limit?(ore_mined)
|
37
|
-
run.
|
30
|
+
run.timestamp!
|
38
31
|
miner.log format("Mining limit reached: %s", Ore.display(ore_mined))
|
39
32
|
stop_mining = true
|
40
33
|
end
|
@@ -47,4 +40,4 @@ run.log "QUIT #{mover.status}"
|
|
47
40
|
ore_moved = mover.ore_moved
|
48
41
|
run.log format("MINE %s mined (%i)", Ore.display(ore_mined), ore_mined)
|
49
42
|
run.log format("MOVE %s moved (%i)", Ore.display(ore_moved), ore_moved)
|
50
|
-
run.
|
43
|
+
run.timestamp!
|
data/demo/thread.rb
CHANGED
@@ -3,13 +3,13 @@ require 'thread'
|
|
3
3
|
|
4
4
|
include MinerMover
|
5
5
|
|
6
|
-
run = Run.new.cfg_banner!(duration: 1)
|
7
|
-
run.
|
8
|
-
run.log "Starting"
|
6
|
+
run = Run.new.cfg_banner!(duration: 1).start!
|
7
|
+
run.timestamp!
|
8
|
+
run.log "Starting #{__FILE__}"
|
9
9
|
|
10
10
|
stop_mining = false
|
11
11
|
Signal.trap("INT") {
|
12
|
-
run.
|
12
|
+
run.timestamp!
|
13
13
|
run.log " *** SIGINT *** Stop Mining"
|
14
14
|
stop_mining = true
|
15
15
|
}
|
@@ -24,16 +24,11 @@ movers = Array.new(run.num_movers) { |i|
|
|
24
24
|
m = run.new_mover
|
25
25
|
run.log "MOVE Mover #{i} started"
|
26
26
|
|
27
|
+
# movers pull from the queue, load the ore, and move it
|
27
28
|
loop {
|
28
|
-
# a mover picks up mined ore from the queue
|
29
|
-
run.debug && m.log("POP ")
|
30
29
|
ore = queue.pop
|
31
|
-
run.debug && m.log("POPD #{ore}")
|
32
|
-
|
33
30
|
break if ore == :quit
|
34
|
-
|
35
|
-
# load (and possibly move) the ore
|
36
|
-
m.load_ore ore
|
31
|
+
m.load_ore ore # moving of ore possibly happens here (on a full batch)
|
37
32
|
}
|
38
33
|
|
39
34
|
# move any remaining ore and quit
|
@@ -45,6 +40,7 @@ movers = Array.new(run.num_movers) { |i|
|
|
45
40
|
|
46
41
|
|
47
42
|
run.log "MINE Mining operation started [ctrl-c] to stop"
|
43
|
+
|
48
44
|
# store the miner threads in an array
|
49
45
|
miners = Array.new(run.num_miners) { |i|
|
50
46
|
Thread.new {
|
@@ -55,19 +51,12 @@ miners = Array.new(run.num_miners) { |i|
|
|
55
51
|
# miners wait for the SIGINT signal to quit
|
56
52
|
while !stop_mining
|
57
53
|
ore = m.mine_ore
|
58
|
-
|
59
|
-
# send any ore mined to the movers
|
60
|
-
if ore > 0
|
61
|
-
run.debug && m.log("PUSH #{ore}")
|
62
|
-
queue.push ore
|
63
|
-
run.debug && m.log("PSHD #{ore}")
|
64
|
-
end
|
65
|
-
|
66
54
|
ore_mined += ore
|
55
|
+
queue.push ore if ore > 0 # send any ore mined to the movers
|
67
56
|
|
68
57
|
# stop mining after a while
|
69
58
|
if run.time_limit? or run.ore_limit?(ore_mined)
|
70
|
-
run.
|
59
|
+
run.timestamp!
|
71
60
|
m.log format("Mining limit reached: %s", Ore.display(ore_mined))
|
72
61
|
stop_mining = true
|
73
62
|
end
|
@@ -86,6 +75,7 @@ run.log format("MINE %s mined (%i)", Ore.display(ore_mined), ore_mined)
|
|
86
75
|
# tell all the movers to quit; gather their results
|
87
76
|
run.num_movers.times { queue.push :quit }
|
88
77
|
|
78
|
+
# wait for results
|
89
79
|
ore_moved = movers.map { |thr| thr.value.ore_moved }.sum
|
90
80
|
run.log format("MOVE %s moved (%i)", Ore.display(ore_moved), ore_moved)
|
91
|
-
run.
|
81
|
+
run.timestamp!
|
data/lib/miner_mover/config.rb
CHANGED
@@ -14,19 +14,22 @@ module MinerMover
|
|
14
14
|
time_limit: 5,
|
15
15
|
ore_limit: 100,
|
16
16
|
logging: true,
|
17
|
+
debugging: false,
|
17
18
|
}.freeze,
|
18
19
|
miner: {
|
19
20
|
depth: 30,
|
20
21
|
partial_reward: false,
|
21
22
|
variance: 0,
|
22
23
|
logging: true,
|
24
|
+
debugging: false,
|
23
25
|
}.freeze,
|
24
26
|
mover: {
|
25
|
-
batch_size:
|
26
|
-
rate:
|
27
|
+
batch_size: 8,
|
28
|
+
rate: 4,
|
27
29
|
work_type: :wait,
|
28
30
|
variance: 0,
|
29
31
|
logging: true,
|
32
|
+
debugging: false,
|
30
33
|
}.freeze,
|
31
34
|
}.freeze
|
32
35
|
|
@@ -60,7 +63,7 @@ module MinerMover
|
|
60
63
|
mover = cfg[:mover] || {}
|
61
64
|
main = cfg[:main] || {}
|
62
65
|
else
|
63
|
-
|
66
|
+
miner, mover, main = {}, {}, {}
|
64
67
|
end
|
65
68
|
{ miner: DEFAULT[:miner].merge(miner),
|
66
69
|
mover: DEFAULT[:mover].merge(mover),
|
data/lib/miner_mover/run.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
require 'miner_mover/worker'
|
2
2
|
require 'miner_mover/config'
|
3
|
+
require 'miner_mover/timer'
|
3
4
|
|
4
5
|
module MinerMover
|
5
6
|
class Run
|
@@ -15,35 +16,47 @@ module MinerMover
|
|
15
16
|
f
|
16
17
|
end
|
17
18
|
|
18
|
-
attr_accessor :
|
19
|
+
attr_accessor :debugging, :logging
|
19
20
|
attr_accessor :num_miners, :num_movers
|
20
21
|
attr_accessor :cfg_file, :cfg, :miner, :mover, :timer
|
21
22
|
attr_accessor :time_limit, :ore_limit
|
22
23
|
|
23
|
-
def initialize(cfg_file: nil, timer: nil
|
24
|
+
def initialize(cfg_file: nil, timer: nil)
|
24
25
|
@cfg_file = self.class.cfg_file(cfg_file)
|
25
26
|
@cfg = Config.process @cfg_file
|
26
27
|
main = @cfg.fetch :main
|
27
28
|
@miner = @cfg.fetch :miner
|
28
29
|
@mover = @cfg.fetch :mover
|
29
30
|
|
31
|
+
@num_miners = main.fetch :num_miners
|
32
|
+
@num_movers = main.fetch :num_movers
|
33
|
+
|
30
34
|
@time_limit = main.fetch :time_limit
|
31
35
|
@ore_limit = main.fetch :ore_limit
|
32
36
|
@logging = main.fetch :logging
|
33
|
-
@
|
34
|
-
@num_movers = main.fetch :num_movers
|
37
|
+
@debugging = main.fetch :debugging
|
35
38
|
|
36
|
-
@timer = timer ||
|
37
|
-
@debug = debug
|
39
|
+
@timer = timer || Timer.new
|
38
40
|
end
|
39
41
|
|
40
42
|
def cfg_banner!(duration: 0)
|
41
|
-
|
43
|
+
MinerMover.puts "USING: #{@cfg_file}"
|
42
44
|
pp @cfg
|
43
45
|
sleep duration if duration > 0
|
44
46
|
self
|
45
47
|
end
|
46
48
|
|
49
|
+
def start!
|
50
|
+
@timer.restart
|
51
|
+
self
|
52
|
+
end
|
53
|
+
|
54
|
+
def timestamp!
|
55
|
+
dash = '-' * 70
|
56
|
+
str = [dash, Timer.timestamp, dash].join(MinerMover::LINE_SEP)
|
57
|
+
MinerMover.puts str
|
58
|
+
end
|
59
|
+
|
47
60
|
def new_miner
|
48
61
|
Miner.new(**@miner)
|
49
62
|
end
|
@@ -61,7 +74,11 @@ module MinerMover
|
|
61
74
|
end
|
62
75
|
|
63
76
|
def log msg
|
64
|
-
@logging and
|
77
|
+
@logging and MinerMover.log @timer, ' (main) ', msg
|
78
|
+
end
|
79
|
+
|
80
|
+
def debug msg
|
81
|
+
@debugging and MinerMover.log @timer, '(debug) ', msg
|
65
82
|
end
|
66
83
|
end
|
67
84
|
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
module MinerMover
|
2
|
+
class Timer
|
3
|
+
SECS_PER_MIN = 60
|
4
|
+
MINS_PER_HOUR = 60
|
5
|
+
SECS_PER_HOUR = SECS_PER_MIN * MINS_PER_HOUR
|
6
|
+
|
7
|
+
# returns a float representing seconds since epoch
|
8
|
+
if defined? Process::CLOCK_MONOTONIC
|
9
|
+
def self.now
|
10
|
+
Process.clock_gettime Process::CLOCK_MONOTONIC
|
11
|
+
end
|
12
|
+
else
|
13
|
+
def self.now
|
14
|
+
Time.now.to_f
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.since f
|
19
|
+
self.now - f
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.elapsed &work
|
23
|
+
f = self.now
|
24
|
+
return yield, self.since(f)
|
25
|
+
end
|
26
|
+
|
27
|
+
# HH::MM::SS.mmm.uuuuuuuu
|
28
|
+
def self.elapsed_display(elapsed_ms, show_micro: false)
|
29
|
+
elapsed_s, ms = elapsed_ms.divmod 1000
|
30
|
+
ms_only, ms_fraction = ms.round(8).divmod 1
|
31
|
+
|
32
|
+
h = elapsed_s / SECS_PER_HOUR
|
33
|
+
elapsed_s -= h * SECS_PER_HOUR
|
34
|
+
m, s = elapsed_s.divmod SECS_PER_MIN
|
35
|
+
|
36
|
+
hmsms = [[h, m, s].map { |i| i.to_s.rjust(2, '0') }.join(':'),
|
37
|
+
ms_only.to_s.rjust(3, '0')]
|
38
|
+
hmsms << (ms_fraction * 10 ** 8).round.to_s.ljust(8, '0') if show_micro
|
39
|
+
hmsms.join('.')
|
40
|
+
end
|
41
|
+
|
42
|
+
# YYYY-MM-DD HH::MM::SS.mmm
|
43
|
+
def self.timestamp(t = Time.now)
|
44
|
+
t.strftime "%Y-%m-%d %H:%M:%S.%L"
|
45
|
+
end
|
46
|
+
|
47
|
+
def restart(t = Time.now)
|
48
|
+
@start = t
|
49
|
+
self
|
50
|
+
end
|
51
|
+
alias_method :initialize, :restart
|
52
|
+
|
53
|
+
def elapsed(t = Time.now)
|
54
|
+
t - @start
|
55
|
+
end
|
56
|
+
|
57
|
+
def elapsed_ms(t = Time.now)
|
58
|
+
elapsed(t) * 1000
|
59
|
+
end
|
60
|
+
|
61
|
+
def elapsed_display(t = Time.now)
|
62
|
+
Timer.elapsed_display(elapsed_ms(t))
|
63
|
+
end
|
64
|
+
alias_method :to_s, :elapsed_display
|
65
|
+
alias_method :inspect, :elapsed_display
|
66
|
+
end
|
67
|
+
end
|
data/lib/miner_mover/worker.rb
CHANGED
@@ -1,16 +1,15 @@
|
|
1
1
|
require 'miner_mover'
|
2
|
-
require '
|
3
|
-
require 'compsci/fibonacci'
|
2
|
+
require 'miner_mover/timer'
|
4
3
|
|
5
4
|
module MinerMover
|
6
|
-
def self.work(duration, type = :wait,
|
5
|
+
def self.work(duration, type = :wait, fib_target = 30)
|
7
6
|
case type
|
8
7
|
when :wait
|
9
8
|
sleep duration
|
10
9
|
duration
|
11
10
|
when :cpu
|
12
|
-
t =
|
13
|
-
|
11
|
+
t = Timer.new
|
12
|
+
self.fib(fib_target) while t.elapsed < duration
|
14
13
|
t.elapsed
|
15
14
|
when :instant
|
16
15
|
0
|
@@ -19,14 +18,19 @@ module MinerMover
|
|
19
18
|
end
|
20
19
|
end
|
21
20
|
|
21
|
+
def self.fib n
|
22
|
+
(n < 2) ? n : fib(n-1) + fib(n-2)
|
23
|
+
end
|
24
|
+
|
22
25
|
class Worker
|
23
|
-
attr_accessor :variance, :logging
|
26
|
+
attr_accessor :variance, :logging, :debugging
|
24
27
|
attr_reader :timer
|
25
28
|
|
26
|
-
def initialize(variance: 0, logging: false, timer: nil)
|
29
|
+
def initialize(variance: 0, logging: false, debugging: false, timer: nil)
|
27
30
|
@variance = variance
|
28
31
|
@logging = logging
|
29
|
-
@
|
32
|
+
@debugging = debugging
|
33
|
+
@timer = timer || Timer.new
|
30
34
|
end
|
31
35
|
|
32
36
|
def id
|
@@ -36,6 +40,7 @@ module MinerMover
|
|
36
40
|
def state
|
37
41
|
{ id: self.id,
|
38
42
|
logging: @logging,
|
43
|
+
debugging: @debugging,
|
39
44
|
timer: @timer.elapsed_ms.round,
|
40
45
|
variance: @variance }
|
41
46
|
end
|
@@ -45,7 +50,11 @@ module MinerMover
|
|
45
50
|
end
|
46
51
|
|
47
52
|
def log msg
|
48
|
-
@logging
|
53
|
+
@logging and MinerMover.log @timer, self.id, msg
|
54
|
+
end
|
55
|
+
|
56
|
+
def debug msg
|
57
|
+
@debugging and MinerMover.log @timer, self.id, msg
|
49
58
|
end
|
50
59
|
|
51
60
|
# 4 levels:
|
@@ -72,10 +81,12 @@ module MinerMover
|
|
72
81
|
partial_reward: true,
|
73
82
|
variance: 0,
|
74
83
|
logging: false,
|
84
|
+
debugging: false,
|
75
85
|
timer: nil)
|
76
86
|
@partial_reward = partial_reward
|
77
87
|
@depth = depth
|
78
|
-
super(variance: variance, logging: logging,
|
88
|
+
super(variance: variance, logging: logging,
|
89
|
+
debugging: debugging, timer: timer)
|
79
90
|
end
|
80
91
|
|
81
92
|
def state
|
@@ -84,11 +95,11 @@ module MinerMover
|
|
84
95
|
|
85
96
|
def mine_ore(depth = @depth)
|
86
97
|
log format("MINE Depth %i", depth)
|
87
|
-
ores, elapsed =
|
98
|
+
ores, elapsed = Timer.elapsed {
|
88
99
|
# every new depth is a new mining operation
|
89
100
|
Array.new(depth) { |d|
|
90
101
|
# mine ore by calculating fibonacci for that depth
|
91
|
-
mined =
|
102
|
+
mined = MinerMover.fib self.varied(d).round
|
92
103
|
@partial_reward ? rand(1 + mined) : mined
|
93
104
|
}
|
94
105
|
}
|
@@ -107,12 +118,14 @@ module MinerMover
|
|
107
118
|
work_type: :cpu,
|
108
119
|
variance: 0,
|
109
120
|
logging: false,
|
121
|
+
debugging: false,
|
110
122
|
timer: nil)
|
111
123
|
@batch_size = batch_size * Ore::BLOCK
|
112
124
|
@rate = rate.to_f * Ore::BLOCK
|
113
125
|
@work_type = work_type
|
114
126
|
@batch, @batches, @ore_moved = 0, 0, 0
|
115
|
-
super(variance: variance, logging: logging,
|
127
|
+
super(variance: variance, logging: logging,
|
128
|
+
debugging: debugging, timer: timer)
|
116
129
|
end
|
117
130
|
|
118
131
|
def state
|
@@ -149,7 +162,7 @@ module MinerMover
|
|
149
162
|
duration = self.varied(amt / @rate)
|
150
163
|
|
151
164
|
log format("MOVE %s (%.2f s)", Ore.display(amt), duration)
|
152
|
-
_, elapsed =
|
165
|
+
_, elapsed = Timer.elapsed { self.move(duration) }
|
153
166
|
log format("MOVD %s (%.2f s)", Ore.display(amt), elapsed)
|
154
167
|
|
155
168
|
# accounting
|
data/lib/miner_mover.rb
CHANGED
@@ -1,7 +1,30 @@
|
|
1
1
|
module MinerMover
|
2
|
-
|
2
|
+
LINE_SEP = $/.freeze
|
3
|
+
|
4
|
+
# $/ is the default line separator: "\n"
|
5
|
+
def self.puts(str, io = $stdout, separator = LINE_SEP)
|
6
|
+
self.write_nonblock(io, str + separator)
|
7
|
+
end
|
8
|
+
|
9
|
+
def self.write_nonblock(io, str)
|
10
|
+
begin
|
11
|
+
# nonblocking write attempt; ensure the full string is written
|
12
|
+
size = str.bytesize
|
13
|
+
num_bytes = io.write_nonblock(str)
|
14
|
+
# blocking write if nonblocking write comes up short
|
15
|
+
io.write str.byteslice(num_bytes, size - num_bytes) if num_bytes < size
|
16
|
+
rescue IO::WaitWritable, Errno::EINTR
|
17
|
+
IO.select([], [io]) # wait until writable
|
18
|
+
retry
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
3
22
|
def self.log_fmt(timer, id, msg)
|
4
|
-
format("%s %s %s", timer
|
23
|
+
format("%s %s %s", timer, id, msg)
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.log(timer, id, msg)
|
27
|
+
self.puts self.log_fmt(timer, id, msg)
|
5
28
|
end
|
6
29
|
|
7
30
|
# i +- 50% at squeeze 0
|
@@ -18,7 +41,28 @@ module MinerMover
|
|
18
41
|
|
19
42
|
# ore is handled in blocks of 1M
|
20
43
|
module Ore
|
21
|
-
BLOCK = 1_000_000
|
44
|
+
BLOCK = 1_000_000 # between fib(30) and fib(31)
|
45
|
+
WORD_LENGTH = 4 # bytes
|
46
|
+
WORD_MAX = 256 ** WORD_LENGTH # up to 4.3B ore
|
47
|
+
HEX_UNPACK = "H#{WORD_LENGTH * 2}" # 4 bytes is 8 hex digits
|
48
|
+
|
49
|
+
# return 4 bytes, unsigned 32 bit integer, network order
|
50
|
+
def self.encode(ore)
|
51
|
+
raise "WORD_MAX overflow: #{self.block(ore)}M ore" if ore > WORD_MAX
|
52
|
+
[ore].pack('N')
|
53
|
+
end
|
54
|
+
|
55
|
+
# return an integer from the first 4 bytes
|
56
|
+
def self.decode(word)
|
57
|
+
raise "unexpected size: #{word.bytesize}" unless word.bytesize == 4
|
58
|
+
word.unpack('N').first
|
59
|
+
end
|
60
|
+
|
61
|
+
# return "0x01020304" for "\x01\x02\x03\x04"
|
62
|
+
def self.hex(word)
|
63
|
+
raise "unexpected size: #{word.bytesize}" unless word.bytesize == 4
|
64
|
+
"0x" + word.unpack(HEX_UNPACK).first
|
65
|
+
end
|
22
66
|
|
23
67
|
# raw ore in, blocks out
|
24
68
|
def self.block(ore, size = BLOCK)
|
data/miner_mover.gemspec
CHANGED
@@ -18,13 +18,16 @@ EOF
|
|
18
18
|
s.files += Dir['test/**/*.rb']
|
19
19
|
s.files += Dir['demo/**/*.rb']
|
20
20
|
|
21
|
-
s.add_dependency "compsci", "~> 0.3"
|
22
21
|
s.add_dependency "dotcfg", "~> 1.0"
|
23
|
-
s.add_dependency "fiber_scheduler", "~> 0.13"
|
24
22
|
|
25
|
-
s.
|
26
|
-
s.
|
23
|
+
s.requirements << "For all features and best performance:"
|
24
|
+
s.requirements << "gem install rake minitest fiber_scheduler"
|
25
|
+
s.requirements << "For dev tools:"
|
26
|
+
s.requirements << "gem install buildar flog flay"
|
27
|
+
|
27
28
|
s.add_development_dependency "rake", "~> 13.0" # CVE-2020-8130
|
29
|
+
s.add_development_dependency "minitest", "~> 5.0"
|
30
|
+
s.add_development_dependency "buildar", "~> 3.0"
|
28
31
|
s.add_development_dependency "flog", "~> 0"
|
29
32
|
s.add_development_dependency "flay", "~> 0"
|
30
33
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: miner_mover
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.1.0.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Rick Hull
|
@@ -10,20 +10,6 @@ bindir: bin
|
|
10
10
|
cert_chain: []
|
11
11
|
date: 1980-01-01 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
|
-
- !ruby/object:Gem::Dependency
|
14
|
-
name: compsci
|
15
|
-
requirement: !ruby/object:Gem::Requirement
|
16
|
-
requirements:
|
17
|
-
- - "~>"
|
18
|
-
- !ruby/object:Gem::Version
|
19
|
-
version: '0.3'
|
20
|
-
type: :runtime
|
21
|
-
prerelease: false
|
22
|
-
version_requirements: !ruby/object:Gem::Requirement
|
23
|
-
requirements:
|
24
|
-
- - "~>"
|
25
|
-
- !ruby/object:Gem::Version
|
26
|
-
version: '0.3'
|
27
13
|
- !ruby/object:Gem::Dependency
|
28
14
|
name: dotcfg
|
29
15
|
requirement: !ruby/object:Gem::Requirement
|
@@ -39,33 +25,19 @@ dependencies:
|
|
39
25
|
- !ruby/object:Gem::Version
|
40
26
|
version: '1.0'
|
41
27
|
- !ruby/object:Gem::Dependency
|
42
|
-
name:
|
43
|
-
requirement: !ruby/object:Gem::Requirement
|
44
|
-
requirements:
|
45
|
-
- - "~>"
|
46
|
-
- !ruby/object:Gem::Version
|
47
|
-
version: '0.13'
|
48
|
-
type: :runtime
|
49
|
-
prerelease: false
|
50
|
-
version_requirements: !ruby/object:Gem::Requirement
|
51
|
-
requirements:
|
52
|
-
- - "~>"
|
53
|
-
- !ruby/object:Gem::Version
|
54
|
-
version: '0.13'
|
55
|
-
- !ruby/object:Gem::Dependency
|
56
|
-
name: buildar
|
28
|
+
name: rake
|
57
29
|
requirement: !ruby/object:Gem::Requirement
|
58
30
|
requirements:
|
59
31
|
- - "~>"
|
60
32
|
- !ruby/object:Gem::Version
|
61
|
-
version: '
|
33
|
+
version: '13.0'
|
62
34
|
type: :development
|
63
35
|
prerelease: false
|
64
36
|
version_requirements: !ruby/object:Gem::Requirement
|
65
37
|
requirements:
|
66
38
|
- - "~>"
|
67
39
|
- !ruby/object:Gem::Version
|
68
|
-
version: '
|
40
|
+
version: '13.0'
|
69
41
|
- !ruby/object:Gem::Dependency
|
70
42
|
name: minitest
|
71
43
|
requirement: !ruby/object:Gem::Requirement
|
@@ -81,19 +53,19 @@ dependencies:
|
|
81
53
|
- !ruby/object:Gem::Version
|
82
54
|
version: '5.0'
|
83
55
|
- !ruby/object:Gem::Dependency
|
84
|
-
name:
|
56
|
+
name: buildar
|
85
57
|
requirement: !ruby/object:Gem::Requirement
|
86
58
|
requirements:
|
87
59
|
- - "~>"
|
88
60
|
- !ruby/object:Gem::Version
|
89
|
-
version: '
|
61
|
+
version: '3.0'
|
90
62
|
type: :development
|
91
63
|
prerelease: false
|
92
64
|
version_requirements: !ruby/object:Gem::Requirement
|
93
65
|
requirements:
|
94
66
|
- - "~>"
|
95
67
|
- !ruby/object:Gem::Version
|
96
|
-
version: '
|
68
|
+
version: '3.0'
|
97
69
|
- !ruby/object:Gem::Dependency
|
98
70
|
name: flog
|
99
71
|
requirement: !ruby/object:Gem::Requirement
|
@@ -139,13 +111,15 @@ files:
|
|
139
111
|
- demo/config.rb
|
140
112
|
- demo/fiber.rb
|
141
113
|
- demo/fiber_scheduler.rb
|
142
|
-
- demo/
|
114
|
+
- demo/process_pipe.rb
|
115
|
+
- demo/process_socket.rb
|
143
116
|
- demo/ractor.rb
|
144
117
|
- demo/serial.rb
|
145
118
|
- demo/thread.rb
|
146
119
|
- lib/miner_mover.rb
|
147
120
|
- lib/miner_mover/config.rb
|
148
121
|
- lib/miner_mover/run.rb
|
122
|
+
- lib/miner_mover/timer.rb
|
149
123
|
- lib/miner_mover/worker.rb
|
150
124
|
- miner_mover.gemspec
|
151
125
|
- test/miner_mover.rb
|
@@ -167,7 +141,11 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
167
141
|
- - ">="
|
168
142
|
- !ruby/object:Gem::Version
|
169
143
|
version: '0'
|
170
|
-
requirements:
|
144
|
+
requirements:
|
145
|
+
- 'For all features and best performance:'
|
146
|
+
- gem install rake minitest fiber_scheduler
|
147
|
+
- 'For dev tools:'
|
148
|
+
- gem install buildar flog flay
|
171
149
|
rubygems_version: 3.3.20
|
172
150
|
signing_key:
|
173
151
|
specification_version: 4
|