Jedes Mal, wenn eine Konversation über die Verwendung verschiedener Datenbanken als Datenquelle beginnt, wird das Thema Datensatzkennungen, Objekte oder etwas anderes angezeigt. Manchmal können die Teilnehmer mehrere Monate lang über die Aushandlung eines Austauschprotokolls nachdenken. int
- bigint
- guid
, dann im Kreis. Bei Volumenaufgaben kann die Wahl der korrekten Darstellung solcher Kennungen hinsichtlich der Leistung kritisch sein, da nativ in R keine bigint
Unterstützung vorhanden ist (Kapazität ~ 2 ^ 64). Gibt es eine offensichtliche und universelle Problemumgehung? Im Folgenden finden Sie einige praktische Überlegungen, die in Projekten als Lackmustest verwendet werden können.
Bezeichner werden in der Regel für drei Aufgabenklassen verwendet:
- Gruppierung;
- Filtern
- Verein.
Auf dieser Grundlage werden wir verschiedene Ansätze bewerten.
Es ist eine Fortsetzung früherer Veröffentlichungen .
Als string
Die Option für kleine Daten ist ziemlich gut. Hier und die Fähigkeit, eine beliebige Länge des Bezeichners zu erfassen, und die Fähigkeit, nicht nur numerische, sondern auch alphanumerische Bezeichner zu unterstützen. Ein zusätzlicher Vorteil ist die garantierte Fähigkeit, Daten über ein nicht natives Datenbankprotokoll korrekt zu empfangen, beispielsweise über die REST-API des Gateways.
Nachteile sind auch offensichtlich. Hoher Speicherverbrauch, erhöhtes Informationsvolumen aus der Datenbank, Leistungseinbußen sowohl auf Netzwerkebene als auch auf Computerebene.
Wir verwenden das bit64
Paket
Viele, die nur den Namen dieses Pakets gehört haben, denken vielleicht, dass es hier die perfekte Lösung ist. Leider ist dies nicht ganz richtig. Dies ist nicht nur ein Add-On über numeric
(Zitat: ' Auch hier liegt die Wahl auf der Hand: R hat nur einen 64-Bit-Datentyp: Doubles. Durch die Verwendung von Doubles,
integer64 erbt einige Funktionen wie is.atomic, length, length <-, names, names <-, dim, dim <-, dimnames, dimnames. ' ), daher gibt es immer noch eine massive Erweiterung der Grundrechenarten und es gibt keine Garantie dafür, dass sie nirgendwo explodieren und es keinen Konflikt mit anderen Paketen gibt.
Wir verwenden den numeric
Typ
Dies ist ein völlig korrekter Trick, der ein guter Kompromiss für diejenigen ist, die genau wissen, was in der int64
Antwort aus der Datenbank verborgen ist. Schließlich werden dort nicht alle 64 Bit wirklich beteiligt sein. Oft kann es eine Zahl geben, die viel kleiner als 2 ^ 64 ist.
Eine solche Lösung ist aufgrund der Besonderheiten des Gleitkommaformats mit doppelter Genauigkeit möglich. Details finden Sie im beliebten Artikel im Gleitkommaformat mit doppelter Genauigkeit .
The 53-bit significand precision gives from 15 to 17 significant decimal digits precision (2−53 ≈ 1.11 × 10−16). If a decimal string with at most 15 significant digits is converted to IEEE 754 double-precision representation, and then converted back to a decimal string with the same number of digits, the final result should match the original string. If an IEEE 754 double-precision number is converted to a decimal string with at least 17 significant digits, and then converted back to double-precision representation, the final result must match the original number.
Wenn der Bezeichner 15 oder weniger Dezimalstellen enthält, können Sie numeric
ohne sich Sorgen machen zu müssen.
Der gleiche Trick ist gut, wenn Sie mit temporären Daten arbeiten müssen, insbesondere solchen, die Millisekunden enthalten. Das Übertragen temporärer Daten über das Netzwerk in Textform nimmt Zeit in POSIXct
Auf der Empfangsseite müssen Sie außerdem den Textparser -> POSIXct
, der ebenfalls äußerst ressourcenintensiv ist (zeitweise Leistungseinbußen). Die Übertragung in binärer Form ist keine Tatsache, dass alle Treiber die Übertragung der Zeitzone und der Millisekunden unterstützen. Die millisekundengenaue Übertragung der Zeit in der UTC-Zone in der Unix-Zeitstempeldarstellung (13 Dezimalstellen) wird jedoch durch das numeric
Format sehr gut und verlustfrei bereitgestellt.
Nicht so einfach und offensichtlich
Wenn wir uns die Version mit Linien genauer ansehen, gehen die Offensichtlichkeit und Kategorizität der ursprünglichen Aussage etwas zurück. Das Arbeiten mit Strings in R ist nicht ganz einfach, selbst wenn die Nuancen des Ausrichtens von Speicherblöcken und des Vorabrufens weggelassen werden. Gemessen an den Büchern und der ausführlichen Dokumentation werden Zeichenfolgenvariablen nicht einzeln in einer Variablen gespeichert, sondern in einem globalen Zeichenfolgenpool abgelegt. Alle Zeilen. Und dieser Pool wird von String-Arrays verwendet, um den Speicherverbrauch zu reduzieren. Das heißt, Ein Textvektor ist eine Reihe von Zeilen im globalen Pool + ein Vektor von Links zu Datensätzen aus diesem Pool.
library(tidyverse) library(magrittr) library(stringi) library(gmp) library(profvis) library(pryr) library(rTRNG) set.seed(46572) RcppParallel::setThreadOptions(numThreads = parallel::detectCores() - 1)
Wir sehen, dass die Hypothese auch ohne die C ++ - Ebene nicht so weit von der Wahrheit entfernt ist. Das Volumen des Zeichenfolgenvektors stimmt fast mit dem Volumen der 64-Bit-Zeiger überein, und die Variable selbst nimmt erheblich weniger Speicherplatz ein als die Datei auf der Festplatte.
File size: 62M. Constructed from file object's (m2) size: 7.65M. Pure pointer's size: 7.63M
Und der Inhalt der Vektoren vor dem Schreiben und nach dem Lesen ist identisch - bzw. Vektorelemente beziehen sich auf dieselben Speicherblöcke.
Ein genauerer Blick auf die Verwendung von Textzeichenfolgen als Bezeichner scheint also keine so verrückte Idee mehr zu sein. Benchmarks zum Gruppieren, Filtern und Zusammenführen unter Verwendung von dplyr
und data.table
liefern ungefähr ähnliche Messwerte für numeric
und character
data.table
, was eine zusätzliche Bestätigung der Optimierung aufgrund des globalen Pools liefert. Schließlich wird mit Zeigern gearbeitet, deren Größe je nach Baugruppe R (32/64) entweder 32 oder 64 Bit beträgt, und dies ist genau der numeric
Typ.
Die maximale Größe des verfügbaren R-Speichers kann fs::fs_bytes(memory.limit())
mit dem fs::fs_bytes(memory.limit())
.
Um ehrlich zu sein, sollte beachtet werden, dass dplyr
nicht immer eine schnelle dplyr
hatte, siehe den Fall "Das Verbinden durch eine Zeichenspalte ist langsam im Vergleich zum Verbinden durch eine Faktorspalte. # 1386 {Closed}" . In diesem Thread wird vorgeschlagen, die Funktionen des globalen Zeichenfolgenpools zu verwenden und nicht Zeichenfolgen als solche zu vergleichen, sondern Zeiger auf Zeichenfolgen.
Details zur Speicherverwaltung
Grundlegende Quellen
Fazit
Natürlich wird diese Frage ständig in der einen oder anderen Form gestellt, eine Reihe von Links unten.
Um jedoch bewusst zu verstehen, was richtig zu tun ist, welche Möglichkeiten und Grenzen bestehen, ist es am besten, auf die niedrigstmögliche Ebene zu gehen. Wie sich herausstellt, gibt es eine Menge nicht offensichtlicher Einzelheiten. Die Veröffentlichung ist forschender Natur, da sie ganz grundlegende Aspekte von R betrifft, die nur wenige Menschen in ihrer täglichen Arbeit verwenden. Wenn es bedeutende Ergänzungen, Kommentare oder Änderungen gibt, wird es sehr interessant sein, sie kennenzulernen.
Die vorherige Veröffentlichung lautet "Verwenden von R für Dienstprogrammaufgaben" .