miner_mover 0.0.0.3 → 0.1.0.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
[![Test Status](https://github.com/rickhull/miner_mover/actions/workflows/test.yaml/badge.svg)](https://github.com/rickhull/miner_mover/actions/workflows/test.yaml)
|
2
|
+
[![Demo Status](https://github.com/rickhull/miner_mover/actions/workflows/demo.yaml/badge.svg)](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_scehduler", "~> 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
|