[#100689] [Ruby master Feature#17303] Make webrick to bundled gems or remove from stdlib — hsbt@...
Issue #17303 has been reported by hsbt (Hiroshi SHIBATA).
11 messages
2020/11/02
[#100852] [Ruby master Feature#17326] Add Kernel#must! to the standard library — zimmerman.jake@...
Issue #17326 has been reported by jez (Jake Zimmerman).
24 messages
2020/11/14
[#100930] [Ruby master Feature#17333] Enumerable#many? — masafumi.o1988@...
Issue #17333 has been reported by okuramasafumi (Masafumi OKURA).
10 messages
2020/11/18
[#101071] [Ruby master Feature#17342] Hash#fetch_set — hunter_spawn@...
Issue #17342 has been reported by MaxLap (Maxime Lapointe).
26 messages
2020/11/25
[ruby-core:100687] [Ruby master Feature#17298] Ractor's basket communication APIs
From:
ko1@...
Date:
2020-11-02 05:41:36 UTC
List:
ruby-core #100687
Issue #17298 has been updated by ko1 (Koichi Sasada).
I rewrote copying logic without marshal protocol (https://github.com/ruby/ruby/pull/3728).
Additional benchmark:
```ruby
Warning[:experimental] = false
if ENV['MODE'] == 'share'
MODE = :copy
SHAREABLE = true
else
MODE = ENV['MODE'].to_sym
SHAREABLE = false
end
receive2yield = Ractor.new do
loop do
case MODE
when :basket
Ractor.yield_basket Ractor.receive_basket
when :move
Ractor.yield Ractor.receive, move: true
when :copy
Ractor.yield Ractor.receive
else
raise
end
end
end
to = receive2yield
receive2send = Ractor.new to do |to|
loop do
case MODE
when :basket
obj = Ractor.receive_basket
to.send_basket obj
when :move
to.send Ractor.receive, move: true
when :copy
to.send Ractor.receive
end
end
end
take2yield = Ractor.new receive2yield do |from|
loop do
case MODE
when :basket
Ractor.yield_basket from.take_basket
when :move
Ractor.yield from.take, move: true
when :copy
Ractor.yield from.take
end
end
end
take2send = Ractor.new take2yield, Ractor.current do |from, to|
loop do
case MODE
when :basket
to.send_basket from.take_basket
when :move
to.send from.take, move: true
when :copy
to.send from.take
end
end
end
AN = ENV['AN'].to_i
LN = 10_000
obj = Array.new(AN/10){Array.new{AN}}
Ractor.make_shareable(obj) if SHAREABLE
LN.times{
receive2send.send obj
Ractor.receive
}
__END__
obj = Array.new(AN){}
user system total real
share/0 0.000235 0.000000 2.084210 ( 1.244361)
basket/0 0.000186 0.000000 2.157927 ( 1.309404)
move/0 0.000193 0.000000 2.117845 ( 1.294796)
copy/0 0.000164 0.000000 2.324445 ( 1.431408)
share/30 0.000113 0.000000 2.086155 ( 1.235768)
basket/30 0.000158 0.000000 2.197975 ( 1.335114)
move/30 0.000211 0.000000 2.190369 ( 1.352113)
copy/30 0.000171 0.000000 2.389695 ( 1.490143)
share/60 0.000206 0.000000 1.997523 ( 1.191689)
basket/60 0.000160 0.000000 2.237804 ( 1.356830)
move/60 0.000145 0.000000 2.268976 ( 1.397970)
copy/60 0.000185 0.000000 2.538057 ( 1.580106)
share/90 0.000168 0.000000 2.069107 ( 1.224712)
basket/90 0.000148 0.000000 2.186683 ( 1.327779)
move/90 0.000158 0.000000 2.154334 ( 1.330732)
copy/90 0.000139 0.000000 2.504655 ( 1.561725)
share/120 0.000189 0.000000 2.120684 ( 1.255453)
basket/120 0.000202 0.000000 2.270650 ( 1.375921)
move/120 0.000151 0.000000 2.416082 ( 1.491159)
copy/120 0.000156 0.000000 2.436406 ( 1.554357)
share/150 0.000216 0.000000 2.103629 ( 1.248967)
basket/150 0.000219 0.000000 2.323634 ( 1.408301)
move/150 0.000187 0.000000 2.123332 ( 1.325794)
copy/150 0.000000 0.000205 2.519390 ( 1.592612)
share/180 0.000000 0.000149 1.916865 ( 1.143092)
basket/180 0.000000 0.000229 2.339701 ( 1.416612)
move/180 0.000000 0.000188 2.384908 ( 1.497792)
copy/180 0.000000 0.000163 2.503268 ( 1.593776)
share/210 0.000000 0.000128 1.987748 ( 1.176819)
basket/210 0.000000 0.000167 2.228943 ( 1.353803)
move/210 0.000000 0.000177 2.431323 ( 1.519974)
copy/210 0.000000 0.000190 2.592614 ( 1.652112)
share/240 0.000000 0.000206 2.063813 ( 1.221907)
basket/240 0.000000 0.000222 2.266633 ( 1.383860)
move/240 0.000000 0.000140 2.291410 ( 1.442910)
copy/240 0.000000 0.000129 2.360580 ( 1.519305)
share/270 0.000000 0.000200 2.187821 ( 1.292601)
basket/270 0.000000 0.000141 2.186951 ( 1.335748)
move/270 0.000000 0.000175 2.331970 ( 1.462081)
copy/270 0.000000 0.000201 2.544173 ( 1.631321)
share/300 0.000000 0.000220 2.103491 ( 1.259986)
basket/300 0.000000 0.000208 2.470096 ( 1.533962)
move/300 0.000000 0.000177 2.477407 ( 1.575883)
copy/300 0.000000 0.000161 2.577555 ( 1.664693)
obj = Array.new(AN/10){Array.new{AN}}
user system total real
share/0 0.000178 0.000000 1.999250 ( 1.192009)
basket/0 0.000186 0.000000 2.113732 ( 1.276346)
move/0 0.000111 0.000000 2.079669 ( 1.272024)
copy/0 0.000261 0.000000 2.301534 ( 1.419984)
share/30 0.000141 0.000000 2.133833 ( 1.264703)
basket/30 0.000157 0.000000 2.314221 ( 1.400378)
move/30 0.000152 0.000000 2.349555 ( 1.461622)
copy/30 0.000180 0.000000 2.255883 ( 1.421459)
share/60 0.000217 0.000000 2.095241 ( 1.243948)
basket/60 0.000206 0.000000 2.239443 ( 1.372667)
move/60 0.000129 0.000000 2.477502 ( 1.550481)
copy/60 0.000298 0.000000 2.510642 ( 1.610342)
share/90 0.000159 0.000000 2.241600 ( 1.323923)
basket/90 0.000293 0.000000 2.184617 ( 1.339768)
move/90 0.000239 0.000000 2.524579 ( 1.590508)
copy/90 0.000157 0.000000 2.610485 ( 1.696873)
share/120 0.000130 0.000000 2.164260 ( 1.283117)
basket/120 0.000190 0.000000 2.250227 ( 1.383655)
move/120 0.000239 0.000000 2.299717 ( 1.459356)
copy/120 0.000220 0.000000 2.624845 ( 1.720889)
share/150 0.000175 0.000000 2.024881 ( 1.203234)
basket/150 0.000214 0.000000 2.213022 ( 1.367973)
move/150 0.000230 0.000000 2.351162 ( 1.508806)
copy/150 0.000261 0.000000 2.600007 ( 1.729760)
share/180 0.000128 0.000000 1.981322 ( 1.176595)
basket/180 0.000218 0.000000 2.202996 ( 1.368278)
move/180 0.000190 0.000000 2.684911 ( 1.733626)
copy/180 0.000176 0.000000 2.877426 ( 1.940715)
share/210 0.000162 0.000000 2.096774 ( 1.243471)
basket/210 0.000119 0.000000 2.227222 ( 1.387271)
move/210 0.000277 0.000000 2.654121 ( 1.725943)
copy/210 0.000198 0.000000 2.823281 ( 1.916791)
share/240 0.000166 0.000000 2.209040 ( 1.309859)
basket/240 0.000169 0.000000 2.367142 ( 1.475902)
move/240 0.000144 0.000000 2.600334 ( 1.702996)
copy/240 0.000164 0.000000 2.859249 ( 1.956904)
share/270 0.000212 0.000000 2.100495 ( 1.244568)
basket/270 0.000234 0.000000 2.346845 ( 1.464750)
move/270 0.000165 0.000000 2.695428 ( 1.770649)
copy/270 0.000184 0.000000 2.934659 ( 2.020429)
share/300 0.000169 0.000000 2.145326 ( 1.269930)
basket/300 0.000160 0.000000 2.286473 ( 1.426499)
move/300 0.000213 0.000000 2.706252 ( 1.789096)
copy/300 0.000270 0.000000 3.063131 ( 2.136394)
obj = "*" * (100 * AN)
user system total real
share/0 0.000000 0.000225 2.113924 ( 1.256272)
basket/0 0.000000 0.000234 2.103315 ( 1.275444)
move/0 0.000000 0.000170 2.270854 ( 1.393821)
copy/0 0.000000 0.000160 2.311942 ( 1.424493)
share/30 0.000000 0.000209 2.190677 ( 1.290621)
basket/30 0.000000 0.000126 2.201340 ( 1.338486)
move/30 0.000000 0.000133 2.362480 ( 1.438814)
copy/30 0.000000 0.000155 2.564007 ( 1.623138)
share/60 0.000000 0.000230 2.045274 ( 1.215421)
basket/60 0.000000 0.000175 2.240818 ( 1.358289)
move/60 0.000000 0.000230 2.241105 ( 1.366148)
copy/60 0.000147 0.000000 2.582220 ( 1.621733)
share/90 0.000181 0.000000 2.045982 ( 1.219508)
basket/90 0.000152 0.000000 2.259071 ( 1.368956)
move/90 0.000229 0.000000 2.215262 ( 1.353770)
copy/90 0.000193 0.000000 2.311903 ( 1.465815)
share/120 0.000189 0.000000 2.220438 ( 1.309290)
basket/120 0.000182 0.000000 2.337383 ( 1.415203)
move/120 0.000216 0.000000 2.185714 ( 1.342087)
copy/120 0.000174 0.000000 2.547647 ( 1.612203)
share/150 0.000175 0.000000 2.111274 ( 1.247957)
basket/150 0.000199 0.000000 2.352135 ( 1.436637)
move/150 0.000232 0.000000 2.341878 ( 1.440591)
copy/150 0.000119 0.000000 2.567323 ( 1.629583)
share/180 0.000176 0.000000 2.206774 ( 1.303557)
basket/180 0.000153 0.000000 2.123505 ( 1.295564)
move/180 0.000272 0.000000 2.295597 ( 1.406772)
copy/180 0.000201 0.000000 2.537667 ( 1.620875)
share/210 0.000236 0.000000 2.159986 ( 1.278420)
basket/210 0.000231 0.000000 2.357100 ( 1.449504)
move/210 0.000187 0.000000 2.434216 ( 1.514514)
copy/210 0.000273 0.000000 2.581147 ( 1.654714)
share/240 0.000220 0.000000 2.187199 ( 1.290690)
basket/240 0.000134 0.000000 2.125558 ( 1.308496)
move/240 0.000205 0.000000 2.289436 ( 1.411198)
copy/240 0.000158 0.000000 2.561948 ( 1.649240)
share/270 0.000184 0.000000 1.949823 ( 1.151623)
basket/270 0.000181 0.000000 2.305483 ( 1.408048)
move/270 0.000202 0.000000 2.097124 ( 1.294308)
copy/270 0.000139 0.000000 2.613040 ( 1.683508)
share/300 0.000264 0.000000 2.147143 ( 1.270971)
basket/300 0.000165 0.000000 2.260574 ( 1.377311)
move/300 0.000224 0.000000 2.186270 ( 1.345459)
copy/300 0.000280 0.000000 2.640306 ( 1.709781)
```
* Most of case, tuple or something small objects are used to communicate (`[command, param1, param2]` for example) and it is enough fast (on current inefficient implementation).
```
obj = Array.new(AN){}
user system total real
share/0 0.000235 0.000000 2.084210 ( 1.244361)
basket/0 0.000186 0.000000 2.157927 ( 1.309404)
move/0 0.000193 0.000000 2.117845 ( 1.294796)
copy/0 0.000164 0.000000 2.324445 ( 1.431408)
share/30 0.000113 0.000000 2.086155 ( 1.235768)
basket/30 0.000158 0.000000 2.197975 ( 1.335114)
move/30 0.000211 0.000000 2.190369 ( 1.352113)
copy/30 0.000171 0.000000 2.389695 ( 1.490143)
obj = Array.new(AN/10){Array.new{AN}}
user system total real
share/0 0.000178 0.000000 1.999250 ( 1.192009)
basket/0 0.000186 0.000000 2.113732 ( 1.276346)
move/0 0.000111 0.000000 2.079669 ( 1.272024)
copy/0 0.000261 0.000000 2.301534 ( 1.419984)
share/30 0.000141 0.000000 2.133833 ( 1.264703)
basket/30 0.000157 0.000000 2.314221 ( 1.400378)
move/30 0.000152 0.000000 2.349555 ( 1.461622)
copy/30 0.000180 0.000000 2.255883 ( 1.421459)
obj = "*" * (100 * AN)
user system total real
share/0 0.000000 0.000225 2.113924 ( 1.256272)
basket/0 0.000000 0.000234 2.103315 ( 1.275444)
move/0 0.000000 0.000170 2.270854 ( 1.393821)
copy/0 0.000000 0.000160 2.311942 ( 1.424493)
share/30 0.000000 0.000209 2.190677 ( 1.290621)
basket/30 0.000000 0.000126 2.201340 ( 1.338486)
move/30 0.000000 0.000133 2.362480 ( 1.438814)
copy/30 0.000000 0.000155 2.564007 ( 1.623138)
````
* On nested data structure, "basket" seems fine.
----------------------------------------
Feature #17298: Ractor's basket communication APIs
https://bugs.ruby-lang.org/issues/17298#change-88328
* Author: ko1 (Koichi Sasada)
* Status: Open
* Priority: Normal
----------------------------------------
This ticket proposes `send_basket`/`send_receive`, `yield_basket`/`take_basket` APIs to make effective and flexible bridge ractors.
## Background
When we want to send an object as a message, we usually need to copy it. Copying is achieved according to marshal protocol, and the receiver loads it immediately.
If we want to make a bridge ractor that receives a message and sends it to another ractor, immediate loading is not effective.
```ruby
bridge = Ractor.new do
Ractor.yield Ractor.receive
end
consumer = Ractor.new bridge do |from|
obj = from.take
do_task(obj)
end
msg = [1, 2, 3]
bridge.send msg
```
In this case, the array (`[1, 2, 3]`) is
* (1) dumped at the first `bridge.send msg`
* (2) loaded at `Ractor.receive`
* (3) dumped again at `Ractor.yield`
* (4) loaded at `from.take`
Essentially, we only need one dump/load pair, but now it needs two pairs.
Mixing "moving" status is more complex. Now there is no way to pass the "moving" status to bridge ractors, so we cannot make a moving bridge.
## Proposal
To make more effective and flexible bridge ractors, we propose new basket APIs
* `Ractor.receive_basket`
* `Ractor#send_basket`
* `Ractor.take_basket`
* `Ractor.yield_basket`
They receive a message, retains the dumped state, and sends it without dumping again. We can rewrite the above example with these APIs.
```ruby
bridge = Ractor.new do
Ractor.yield_basket Ractor.receive_basket
end
consumer = Ractor.new bridge do |from|
obj = from.take
do_task(obj)
end
msg = [1, 2, 3]
bridge.send msg
```
In this case,
* (1) dumped at the first `bridge.send msg`
* (2) loaded at `from.take`
we only need one dump/load pair.
## Implementation
https://github.com/ruby/ruby/pull/3725
## Evaluation
The following program makes four types of bridges and passes an array as a message through them.
```ruby
USE_BASKET = false
receive2yield = Ractor.new do
loop do
if USE_BASKET
Ractor.yield_basket Ractor.receive_basket
else
Ractor.yield Ractor.receive
end
end
end
receive2send = Ractor.new receive2yield do |r|
loop do
if USE_BASKET
r.send_basket Ractor.receive_basket
else
r.send Ractor.receive
end
end
end
take2yield = Ractor.new receive2yield do |from|
loop do
if USE_BASKET
Ractor.yield_basket from.take_basket
else
Ractor.yield from.take
end
end
end
take2send = Ractor.new take2yield, Ractor.current do |from, to|
loop do
if USE_BASKET
to.send_basket from.take_basket
else
to.send from.take
end
end
end
AN = 1_000
LN = 10_000
ary = Array.new(AN) # 1000
LN.times{
receive2send << ary
Ractor.receive
}
# This program passes the message as:
# main ->
# receive2send ->
# receive2yield ->
# take2yield ->
# take2send ->
# main
```
The result is:
```
w/ basket API 0m2.056s
w/o basket API 0m5.974s
```
on my machine (=~ x3 faster).
(BTW, if we have a TVar, we can change the value `USE_BASKET` dynamically)
## Discussion
### Naming
Of course, naming is an issue. Now, I named it "_basket" because the source code uses this terminology. There are other candidates:
* container metaphor
* package
* parcel
* box
* envelope
* packet (maybe bad idea because of confusion of networking)
* bundle (maybe bad idea because of confusion of bin/bundle)
* "don't touch the content" metaphor
* raw
* sealed
* unopened
I like "basket" because I like picnic.
### Feature
Now, basket is represented by "Ractor::Basket" and there are no methods. We can add the following feature:
* `Ractor::Basket#sender` returns the sending ractor.
* `Ractor::Basket#sender = a_ractor` changes the sending ractor.
* `Ractor::Basket#value` returns the content.
There was another proposal `Ractor.recvfrom`, but we only need these APIs.
--
https://bugs.ruby-lang.org/
Unsubscribe: <mailto:ruby-core-request@ruby-lang.org?subject=unsubscribe>
<http://lists.ruby-lang.org/cgi-bin/mailman/options/ruby-core>