# MT schedulers impact

**URL:** <https://forum.crystal-lang.org/t/mt-schedulers-impact/214>\
**Category:** Crystal Contrib\
**Created:** [December 27, 2018, 9:22am UTC](https://forum.crystal-lang.org/t/mt-schedulers-impact/214 "2018-12-27T09:22:27Z")\
**Posts on this page:** 20\
**Page:** 1

<div class="post-metadata">

**Author:** ![vladfaust](https://yyz2.discourse-cdn.com/flex036/user_avatar/forum.crystal-lang.org/vladfaust/32/48_2.png) [@vladfaust](https://forum.crystal-lang.org/u/vladfaust)\
**Post date:** [December 27, 2018, 9:22am UTC](https://forum.crystal-lang.org/t/mt-schedulers-impact/214/1 "2018-12-27T09:22:27Z")

</div>

[https://github.com/crystal-lang/crystal/pull/7214](https://github.com/crystal-lang/crystal/pull/7214) looks promising, but due to lack of deep understanding of how it works, I’d like to ask how it’s going to affect the language (if it’s merged)? From my point of view, the only good thing the PR brings is faster switching between fibers, but fibers themselves will still stay in a single thread, right? So no changes in the existing code are expected, i.e. objects locking etc.

---

<div class="post-metadata">

**Author:** ![epergo](https://yyz2.discourse-cdn.com/flex036/user_avatar/forum.crystal-lang.org/epergo/32/118_2.png) [@epergo](https://forum.crystal-lang.org/u/epergo)\
**Post date:** [December 27, 2018, 9:45am UTC](https://forum.crystal-lang.org/t/mt-schedulers-impact/214/2 "2018-12-27T09:45:28Z")

</div>

My understanding of the PR is limited as well, but in the description **[ysbaddaden](https://github.com/ysbaddaden)** points out improvements in performance using 1, 4 and 8 threads (all without GC)

```auto
switch[1/128]: crystal: 10000000 yields in 288 ms, 34631387 yields per second
switch[4/128]: crystal: 10000000 yields in 128 ms, 77606737 yields per second
switch[8/128]: crystal: 10000000 yields in 91 ms, 108759411 yields per second

```

So I think this is a great step towards parallelism, crystal in 2019 is gonna be great :)

---

<div class="post-metadata">

**Author:** ![asterite](https://yyz2.discourse-cdn.com/flex036/user_avatar/forum.crystal-lang.org/asterite/32/60_2.png) [@asterite](https://forum.crystal-lang.org/u/asterite)\
**Post date:** [December 27, 2018, 10:32am UTC](https://forum.crystal-lang.org/t/mt-schedulers-impact/214/3 "2018-12-27T10:32:54Z")

</div>

MT stands for multi-threaded. It does affect the semantic of the language: if you share a variable between threads it will be possible to trigger race conditions.

---

<div class="post-metadata">

**Author:** ![vladfaust](https://yyz2.discourse-cdn.com/flex036/user_avatar/forum.crystal-lang.org/vladfaust/32/48_2.png) [@vladfaust](https://forum.crystal-lang.org/u/vladfaust)\
**Post date:** [December 27, 2018, 11:01am UTC](https://forum.crystal-lang.org/t/mt-schedulers-impact/214/4 "2018-12-27T11:01:53Z")

</div>

As far as I understand, only the scheduler logic becomes multi-threaded, leaving all the fibers in a single thread?

---

<div class="post-metadata">

**Author:** ![RX14](https://yyz2.discourse-cdn.com/flex036/user_avatar/forum.crystal-lang.org/rx14/32/633_2.png) [@RX14](https://forum.crystal-lang.org/u/RX14)\
**Post date:** [December 27, 2018, 1:41pm UTC](https://forum.crystal-lang.org/t/mt-schedulers-impact/214/5 "2018-12-27T13:41:11Z")

</div>

No, it runs fibers in all threads. This is the real deal. It will break your code, because there will be race conditions and until the stdlib is audited, all the unsafe parts of the stdlib may cause segfaults. A lot of work to do. For example: should array raise or lock or segfault when accessed concurrently. I’m sure we don’t choose the latter.

---

<div class="post-metadata">

**Author:** ![vladfaust](https://yyz2.discourse-cdn.com/flex036/user_avatar/forum.crystal-lang.org/vladfaust/32/48_2.png) [@vladfaust](https://forum.crystal-lang.org/u/vladfaust)\
**Post date:** [December 27, 2018, 7:43pm UTC](https://forum.crystal-lang.org/t/mt-schedulers-impact/214/6 "2018-12-27T19:43:54Z")

</div>

> I’m sure we don’t choose the latter.

Great news 😆

Thanks for the clarification, Chris!

---

<div class="post-metadata">

**Author:** ![asterite](https://yyz2.discourse-cdn.com/flex036/user_avatar/forum.crystal-lang.org/asterite/32/60_2.png) [@asterite](https://forum.crystal-lang.org/u/asterite)\
**Post date:** [December 27, 2018, 9:16pm UTC](https://forum.crystal-lang.org/t/mt-schedulers-impact/214/7 "2018-12-27T21:16:06Z")

</div>

Note that we’ll probably choose the latter. Having Array be thread-safe by default will probably make everything super slow.

---

<div class="post-metadata">

**Author:** ![RX14](https://yyz2.discourse-cdn.com/flex036/user_avatar/forum.crystal-lang.org/rx14/32/633_2.png) [@RX14](https://forum.crystal-lang.org/u/RX14)\
**Post date:** [December 27, 2018, 9:47pm UTC](https://forum.crystal-lang.org/t/mt-schedulers-impact/214/8 "2018-12-27T21:47:08Z")

</div>

We cannot have concurrency issues become segfaults. Java raises a `ConcurrentModificationException` when you use `ArrayList` concurrently, and `ArrayList` in java is fast. We must do the same.

We may profile the fiber locks in crystal and realise the overhead for the very happy case of the lock only ever being held by one fier is small, in that case i’d support making Array thread-safe by default. But we must wait for data.

---

<div class="post-metadata">

**Author:** ![asterite](https://yyz2.discourse-cdn.com/flex036/user_avatar/forum.crystal-lang.org/asterite/32/60_2.png) [@asterite](https://forum.crystal-lang.org/u/asterite)\
**Post date:** [December 27, 2018, 11:52pm UTC](https://forum.crystal-lang.org/t/mt-schedulers-impact/214/9 "2018-12-27T23:52:12Z")

</div>

It seems Java has Vector, which is thread safe (nobody uses it because of the performance penalties) and AtrayList, which isn’t thread safe. Concurrent modification exception happens when you modify an Array while holding an Iterator, or something like that.

---

<div class="post-metadata">

**Author:** ![asterite](https://yyz2.discourse-cdn.com/flex036/user_avatar/forum.crystal-lang.org/asterite/32/60_2.png) [@asterite](https://forum.crystal-lang.org/u/asterite)\
**Post date:** [December 28, 2018, 12:01am UTC](https://forum.crystal-lang.org/t/mt-schedulers-impact/214/10 "2018-12-28T00:01:26Z")

</div>

That said, I’d like to see the performance of a thread-safe array, but we’ll see.

---

<div class="post-metadata">

**Author:** ![RX14](https://yyz2.discourse-cdn.com/flex036/user_avatar/forum.crystal-lang.org/rx14/32/633_2.png) [@RX14](https://forum.crystal-lang.org/u/RX14)\
**Post date:** [December 28, 2018, 12:20am UTC](https://forum.crystal-lang.org/t/mt-schedulers-impact/214/11 "2018-12-28T00:20:35Z")

</div>

At the very least, `Array` should not possibly cause segfaults when used in a data race. I am not convinced this is true, given realloc can change the address of `@buffer` while another thread is iterating.

---

<div class="post-metadata">

**Author:** ![RX14](https://yyz2.discourse-cdn.com/flex036/user_avatar/forum.crystal-lang.org/rx14/32/633_2.png) [@RX14](https://forum.crystal-lang.org/u/RX14)\
**Post date:** [December 28, 2018, 12:28am UTC](https://forum.crystal-lang.org/t/mt-schedulers-impact/214/12 "2018-12-28T00:28:04Z")

</div>

At the very least there seems to be a need for a RWLock around resizing arrays.

Also of note is non-atomic memory writes to memory locations containing unions. If the type id of the union is read, then another thread retires writes to the type id and pointer, then the original thread reads the new pointer, then it’s using the wrong type id with the wrong pointer, and we get a segfault because we dispatched to the wrong method.

I can’t imagine how to solve _that_ in a reasonable performance budget.

---

<div class="post-metadata">

**Author:** ![straight-shoota](https://yyz2.discourse-cdn.com/flex036/user_avatar/forum.crystal-lang.org/straight-shoota/32/36_2.png) [@straight-shoota](https://forum.crystal-lang.org/u/straight-shoota)\
**Post date:** [December 28, 2018, 12:53am UTC](https://forum.crystal-lang.org/t/mt-schedulers-impact/214/13 "2018-12-28T00:53:17Z")

</div>

> [@RX14](#):
>
> It will break your code, because there will be race conditions

Only if you’re really running multiple threads. You should always be able to choose how many threads to be spawned and that can be only one.

---

<div class="post-metadata">

**Author:** ![RX14](https://yyz2.discourse-cdn.com/flex036/user_avatar/forum.crystal-lang.org/rx14/32/633_2.png) [@RX14](https://forum.crystal-lang.org/u/RX14)\
**Post date:** [December 28, 2018, 1:08am UTC](https://forum.crystal-lang.org/t/mt-schedulers-impact/214/14 "2018-12-28T01:08:30Z")

</div>

Race conditions cannot turn into segfaults. They can turn into exceptions, bad behaviour, whatever, but not remote code executions. If that is the cost of parallelism, then its a cost nobody will be willing to pay.

---

<div class="post-metadata">

**Author:** ![asterite](https://yyz2.discourse-cdn.com/flex036/user_avatar/forum.crystal-lang.org/asterite/32/60_2.png) [@asterite](https://forum.crystal-lang.org/u/asterite)\
**Post date:** [December 28, 2018, 1:27pm UTC](https://forum.crystal-lang.org/t/mt-schedulers-impact/214/15 "2018-12-28T13:27:46Z")

</div>

> [@RX14](#):
>
> Also of note is non-atomic memory writes to memory locations containing unions. If the type id of the union is read, then another thread retires writes to the type id and pointer, then the original thread reads the new pointer, then it’s using the wrong type id with the wrong pointer, and we get a segfault because we dispatched to the wrong method.

This can be solved by not reading the type id separate from the value. The codegen must read the entire value, then dispatch based on the read type id. If another thread changes the value then it doesn’t matter, the read value is already consistent.

---

<div class="post-metadata">

**Author:** ![asterite](https://yyz2.discourse-cdn.com/flex036/user_avatar/forum.crystal-lang.org/asterite/32/60_2.png) [@asterite](https://forum.crystal-lang.org/u/asterite)\
**Post date:** [December 28, 2018, 1:36pm UTC](https://forum.crystal-lang.org/t/mt-schedulers-impact/214/16 "2018-12-28T13:36:17Z")

</div>

> [@asterite](#):
>
> This can be solved by not reading the type id separate from the value. The codegen must read the entire value, then dispatch based on the read type id. If another thread changes the value then it doesn’t matter, the read value is already consistent.

I just checked and we are not doing that right now.

---

<div class="post-metadata">

**Author:** ![RX14](https://yyz2.discourse-cdn.com/flex036/user_avatar/forum.crystal-lang.org/rx14/32/633_2.png) [@RX14](https://forum.crystal-lang.org/u/RX14)\
**Post date:** [December 28, 2018, 1:44pm UTC](https://forum.crystal-lang.org/t/mt-schedulers-impact/214/17 "2018-12-28T13:44:05Z")

</div>

Is it possible? It’d be a 128bit atomic read on 64bit and a 64bit atomic read on 32bit. Not all architectures support that.

---

<div class="post-metadata">

**Author:** ![RX14](https://yyz2.discourse-cdn.com/flex036/user_avatar/forum.crystal-lang.org/rx14/32/633_2.png) [@RX14](https://forum.crystal-lang.org/u/RX14)\
**Post date:** [December 28, 2018, 1:46pm UTC](https://forum.crystal-lang.org/t/mt-schedulers-impact/214/18 "2018-12-28T13:46:13Z")

</div>

Actually, it’d be an unbounded size atomic read, since pointers are already safe: we can save the pointer to a register _then_ read the type ID from the class header. The problem comes from unions of structs. For an `Array(Struct1 | Struct2)`, where `Struct1` and `Struct2` are large structs, then how can this work? I can’t think of a way apart from boxing all structs which leave the stack.

And if we box all structs which leave the stack, we might as well remove structs and expend the energy on accurate escape anlysis.

---

<div class="post-metadata">

**Author:** ![asterite](https://yyz2.discourse-cdn.com/flex036/user_avatar/forum.crystal-lang.org/asterite/32/60_2.png) [@asterite](https://forum.crystal-lang.org/u/asterite)\
**Post date:** [December 28, 2018, 2:11pm UTC](https://forum.crystal-lang.org/t/mt-schedulers-impact/214/19 "2018-12-28T14:11:13Z")

</div>

That’s why I think that segfaults and other undefined behavior can and will happen in Crystal if you don’t synchronize access to shared resources.

---

<div class="post-metadata">

**Author:** ![RX14](https://yyz2.discourse-cdn.com/flex036/user_avatar/forum.crystal-lang.org/rx14/32/633_2.png) [@RX14](https://forum.crystal-lang.org/u/RX14)\
**Post date:** [December 28, 2018, 2:29pm UTC](https://forum.crystal-lang.org/t/mt-schedulers-impact/214/20 "2018-12-28T14:29:45Z")

</div>

I’d rather disallow shared memory than have random segfaults and undefined behaviour. A crystal program which doesn’t use pointers or other explicitly unsafe constructs should never segfault.

[Next page](https://forum.crystal-lang.org/t/mt-schedulers-impact/214.md?page=2)
