Meines Wissens unterscheidet Swift nicht zwischen Thread-lokalen und Thread-globalen Datenstrukturen. Somit ist bei Swift für jeden Refcount ein pthread_mutex_lock()+pthread_mutex_unlock() (o.ä.) erforderlich. Oder eine teure atomare, read-modify-write Instruktion, welche viel Rechenzeit kostet.
Also: jede Löschung/Neuzuweisung/Out of Scope gehen eines Zeigers erfordert eine teure Operation bei Swift.
Das ist bei Sappeur nur für multithreaded-Objekte (darauf können verschiedene Threads quasiparallel zugreifen) erforderlich.
Java hat so eine "halbe" Multithread-Speichersicherheit über das Schlüsselwort synchronized.
Allerdings gibt es bei Java keinerlei Unterscheidung zwischen thread-lokalen und thread-globalen Datentypen. Somit können dann Threads im Rahmen eines Programmierfehlers zum Beispiel parallel eine Ganzzahl auffrischen (weil die Methode nicht synchronized ist). Das ist nicht so schlimm wie die moeglichen MT-Probleme von C++ (komplette Zerstoerung der Heapverwaltung usw), erzeugt jedoch ebenfalls ein nichtdeterministisches Verhalten.
Bei Sappeur erzwingt der Compiler die Nutzung der Mutex, wodurch also das Hochzählen einer Zahl innerhalb einer multithreaded-Klasse deterministisch wird. In der multithreaded Klasse drin können wiederum single-threaded Datenstrukturen angelegt werden, da diese ja immer nur von genau einem thread angefasst werden. Der Compiler erzwingt zudem, dass diese Datenstrukturen niemals aus dem Scope der mt-Klasse herausgereicht werden - das koennte auch schwere Speicherfehler verursachen.
Ein Sappeurprogramm kann innerhalb von wenigen Millisekunden hochfahren, seine Arbeit machen und sich wieder beenden. Damit kann ein Sappeur-Programm sinnvoll innerhalb von Shellskripten benutzt werden. Das geht mit Java eher nicht.
Ein Serverprogramm in Sappeur braucht im Mittel nur 50% des RAM-Bedarfs eines vergleichbaren Java-Programms. Der Grund dafür ist einfach: Im Mittel muss immer eine erhebliche Menge heap-Speicher als "Müll" vorliegen, ansonsten würde der GC ständig laufen und der Durchsatz in den Keller gehen.
Javaprogramme haben fast immer (sofern heap-Objekte dynamisch angefordert und freigegeben werden) nichtvorhersagbare GC-Pausen, welche z.B. für Benutzerschnittstellen sehr störend sind. Sappeur benutzt Verweiszähler, wodurch der Entwickler die Loeschung von Objekten einigermassen steuern kann.
1.) Man kann in Sappeur Objekte (fast) beliebiger Art auf demStack anlegen und danach per Referenz in eine Methode weitergeben, auch mehrfach. Sappeur unterscheidet scharf zwischen Zeiger und Referenz, sonst könnte man leicht schwere Speicherfehler erzeugen.
1.2) Auch das RAII Muster wird unterstützt, um sicher mit dynamischem Speicher oder Dateihenkeln, Sockets usw umzugehen. Zum Beispiel allokiert die Klasse String_16 16 Oktets auf dem Stack und expandiert danach automatisch in den Heap, falls die Zeichenkette grösser 16 Oktet lang wird.
1.3) Es gibt bei Sappeur Destruktoren (fast) wie bei C++. Der Entwickler kann sehr genau steürn, wann diese gerufen werden.
2.) Rust hat mit dem Borrowing-Mechanismus sicher die allerbeste nebenläufige Speichersicherheit.
Bei Sappeur hingegen hat jede Multithreaded-Klasse einen Mutex/Monitor und der Compiler stellt sicher, dass jeder mulithreaded-Zugriff erst die Mutex sperrt. Das ist vielleicht nicht die schnellste Methode, aber sie ist in vielen Anwendungsfällen mehr als ausreichend.
3.) Ja, es ist bisher ein Ein-Mann-Projekt. Das hat aber auch viele Vorteile, vor allem weil es dadurch sehr überschaubar ist. Der Compiler hat nur etwa 15000 Zeilen. Viele fähige Leute konnen bei diesem geringen Umfang Fehler beheben, was bei grösseren Projekten eher nicht der Fall ist.
Meiner Erfahrung nach kann man locker Millionen Sperrungen/s anfordern. Kritisch wird es meistens nur, wenn das gesamte Programm dieselbe Mutex/Sperre benutzt.
Sowas kann man aber meistens vermeiden, z.B. bei einer parallel auffrischbaren Hashtabelle mittles Sharding. Die HT besteht also innendrin aus x Unter-Tabellen, welche jeweils eine eigene Mutex haben. Die Zahl x sollte dabei ungefähr die Anzahl der Rechenkerne sein.
Mit diesem Ansatz habe ich in einem Projekt zur Massendatenauswertung sehr gute Erfahrung gemacht. Sperren ist per se kein Problem; sperren einer einzigen Mutex sehr wohl.
Bzgl. der Referenzen in Sappeur: Im Gegensatz zu C und C++ kann ein Zeiger ausschliesslich auf ein Heap-Objekt zeigen. Eine Referenz hingegen kann immer nur den Stack "hinaufgereicht" werden, niemals aber irgendwie im Heap gespeichert werden.
Das ist für die Integrität des Stacks zwingend.
Single-Threaded Programme kann man in C++ mittels Feld-Grenzprüfung (kein vector::operator[] sondern vector::at()), RAII und Smart Pointers näherungsweise speichersicher machen.
Sobald aber mehrere Threads laufen, kann C++ überhaupt nicht mehr die Integrität der (potentiell geteilten) Speicherstrukturen garantieren.
Das ist bei Rust und Sappeur wesentlich besser. Da muss man sich anstrengen um nebenläufige Race-Conditions zu erzeugen.