Thread-safe and Efficient Data Representations in Dynamically-typed Languages
Benoit Daloze
Abstract
We are in the multi-core era. Dynamically-typed languages are in widespread use, but their support
for multithreading still lags behind. Specifically, their runtime systems either lack support for parallelism
completely or they have many drawbacks, such as not scaling, having a significant overhead for single-threaded
applications and exposing implementation-level races to the user. Runtime systems use sophisticated techniques
to efficiently represent the dynamic objects and versatile collections present in dynamically-typed languages.
One of the major reasons for not supporting well parallelism is these techniques are often unsafe in
multi-threaded environments. In this thesis, we present a solution for efficient and thread-safe
shared-memory parallelism in dynamically-typed language runtimes. First, we design a new efficient and
thread-safe object model for dynamic languages, only requiring synchronization when writing to objects
shared between multiple threads. Accesses to objects reachable by only a single thread remain unsynchronized
and maintain single-threaded performance. Then, we devise a thread-safe and scalable design for collections
in dynamic languages, which we apply to array-like and dictionary-like collections. We use multiple levels
of synchronization to optimize for the usage pattern of each collection instance. Collections operations are
unsynchronized until the collection becomes reachable by multiple threads. For array accesses within bounds,
we use a new mechanism for global synchronization, avoiding overhead for this usage pattern. For the general
case, we introduce a new lock that provides scalability for both read and write accesses, while supporting
the versatile operations that dynamic languages provide and retaining the sophisticated techniques for
representing collections efficiently in memory. We evaluate our work in TruffleRuby, a state of the art
high-performance implementation of the Ruby programming language. We show that our thread-safe objects and
collections do not cause any overhead when they are reachable by only a single thread. Our experiments show
that our approach scales linearly for object, array and dictionary accesses, achieves the same scalability
as statically-typed languages for classic parallel algorithms, and scales better than other Ruby
implementations on Ruby workloads.
Kurzfassung
Wir befinden uns in der Multicore-Ära. Dynamisch typisierte Sprachen sind weit verbreitet, aber ihre
Unterstützung für Multi-Threading hinkt immer noch hinterher. Insbesondere ihre Laufzeitumgebungen haben
entweder keine vollständige Unterstützung für Parallelität oder viele Nachteile, wie z.B. mangelnde Skalierung,
Laufzeitnachteile von Programme die in einem einzigen Thread ausgeführt werden oder Race-Conditions der
Implementierung die Einfluss auf die Benutzeranwendung haben können. Laufzeitumgebungen verwenden ausgefeilte
Techniken, um die dynamischen Objekte und vielseitigen Collections-Frameworks von dynamischer Sprachen zu
implementieren. Einer der Hauptgründe für die mangelnde Unterstützung von parallelen Sprachfeatures ist,
dass diese Techniken in Multi-Threading-Umgebungen oft zu Abstürzen führen. In dieser Arbeit stellen
wir eine Lösung für die effiziente und threadsichere Ausführung von Programmen mit gemeinsamen Speicherbereichen
in Laufzeitumgebungen für dynamisch typisierte Sprachen vor. Zuerst stellen wir ein neues, effizientes und
threadsicheres Objektmodell für dynamische Sprachen vor. Dieses Objektmodell erfordert Synchronisierung nur
für Schreiboperationen auf Objekte die von mehreren Threads genutzt werden. Zugriffe auf Objekte, die nur von
einem einzigen Thread erreicht werden können, bleiben unsynchronisiert und erreichen die gleiche Performance
wie single-threaded Programme. Dann entwickeln wir ein threadsicheres und skalierbares Design für Collections
in dynamischen Sprachen, die wir auf Array- und Dictionary-Datenstrukturen anwenden. Wir verwenden mehrere
Synchronisationsebenen, um das Nutzungsmuster jeder Collection-Instanz zu optimieren. Die Collections-Vorgänge
werden unsynchronisiert ausgeführt, bis die Instanz von mehreren Threads erreichbar wird. Für Array-Zugriffe
innerhalb der Speichergrenzen verwenden wir einen neuen Mechanismus zur globalen Synchronisation, der den
Overhead für dieses Nutzungsmuster vermeidet. Für den allgemeinen Fall stellen wir eine neue Sperrtechnik vor,
die Skalierbarkeit für Lese- und Schreibzugriffe bietet und gleichzeitig die vielseitigen Operationen unterstützt,
die dynamische Sprachen bieten. Diese Lösung unterstützt die bereits existierenden Techniken, Collections
effizient im Speicher abzulegen. Wir bewerten unsere Arbeit in TruffleRuby, einer hochmodernen, leistungsstarken
Implementierung der Programmiersprache Ruby. Wir zeigen, dass unsere threadsicheren Objekte und Collections
keinen Overhead verursachen, wenn sie nur von einem einzigen Thread erreichbar sind. Unsere Experimente zeigen,
dass unser Ansatz linear für Objekt-, Array- und Dictionary-Zugriffe skaliert. Die Lösung erreicht die gleiche
Skalierbarkeit wie statisch typisierte Sprachen für klassische parallele Algorithmen, und skaliert besser als
andere Ruby-Implementierungen auf Ruby-Workloads.
PhD thesis, Johannes Kepler University Linz, October 2019
Download als pdf
|