2016-04-05 6 views
2

Wir verwenden ChronicleMap, um Off-Heap-Persistenz in einer großen Anzahl verschiedener Speicher zu unterstützen, aber ein kleines Problem mit dem einfachsten Usecase.Verwendung von umrahmten/atomaren Werten in Scala mit Chronicle Map

Zunächst einmal ist hier die Helfer Ich schrieb Schaffung einfacher zu machen:

import java.io.File 
import java.util.concurrent.atomic.AtomicLong 

import com.madhukaraphatak.sizeof.SizeEstimator 
import net.openhft.chronicle.map.{ChronicleMap, ChronicleMapBuilder} 

import scala.reflect.ClassTag 

object ChronicleHelper { 

    def estimateSizes[Key, Value](data: Iterator[(Key, Value)], keyEstimator: AnyRef => Long = defaultEstimator, valueEstimator: AnyRef => Long = defaultEstimator): (Long, Long, Long) = { 
    println("Estimating sizes...") 

    val entries = new AtomicLong(1) 
    val keySum = new AtomicLong(1) 
    val valueSum = new AtomicLong(1) 
    var i = 0 

    val GroupSize = 5000 

    data.grouped(GroupSize).foreach { chunk => 

     chunk.par.foreach { case (key, value) => 
     entries.incrementAndGet() 
     keySum.addAndGet(keyEstimator(key.asInstanceOf[AnyRef])) 
     valueSum.addAndGet(valueEstimator(value.asInstanceOf[AnyRef])) 
     } 

     i += 1 

     println("Progress:" + i * GroupSize) 
    } 

    (entries.get(), keySum.get()/entries.get(), valueSum.get()/entries.get()) 
    } 

    def defaultEstimator(v: AnyRef): Long = SizeEstimator.estimate(v) 

    def createMap[Key: ClassTag, Value: ClassTag](data: => Iterator[(Key, Value)], file: File): ChronicleMap[Key, Value] = { 
    val keyClass = implicitly[ClassTag[Key]].runtimeClass.asInstanceOf[Class[Key]] 
    val valueClass = implicitly[ClassTag[Value]].runtimeClass.asInstanceOf[Class[Value]] 

    val (entries, averageKeySize, averageValueSize) = estimateSizes(data) 

    val builder = ChronicleMapBuilder.of(keyClass, valueClass) 
     .entries(entries) 
     .averageKeySize(averageKeySize) 
     .averageValueSize(averageValueSize) 
     .asInstanceOf[ChronicleMapBuilder[Key, Value]] 

    val cmap = builder.createPersistedTo(file) 

    val GroupSize = 5000 

    println("Inserting data...") 
    var i = 0 
    data.grouped(GroupSize).foreach { chunk => 

     chunk.par.foreach { case (key, value) => 
     cmap.put(key, value) 
     } 

     i += 1 

     println("Progress:" + i * GroupSize) 
    } 

    cmap 
    } 

    def empty[Key: ClassTag, Value: ClassTag]: ChronicleMap[Key, Value] = { 
    val keyClass = implicitly[ClassTag[Key]].runtimeClass.asInstanceOf[Class[Key]] 
    val valueClass = implicitly[ClassTag[Value]].runtimeClass.asInstanceOf[Class[Value]] 


    ChronicleMapBuilder.of(keyClass, valueClass).create() 
    } 


    def loadMap[Key: ClassTag, Value: ClassTag](file: File): ChronicleMap[Key, Value] = { 
    val keyClass = implicitly[ClassTag[Key]].runtimeClass.asInstanceOf[Class[Key]] 
    val valueClass = implicitly[ClassTag[Value]].runtimeClass.asInstanceOf[Class[Value]] 

    ChronicleMapBuilder.of(keyClass, valueClass).createPersistedTo(file) 
    } 
} 

Es verwendet https://github.com/phatak-dev/java-sizeof für Objektgröße Schätzung. Hier ist die Art der Nutzung wir unterstützen:

object TestChronicle { 
    def main(args: Array[String]) { 
    def dataIterator: Iterator[(String, Int)] = (1 to 5000).toIterator.zipWithIndex.map(x => x.copy(_1 = x._1.toString)) 

    ChronicleHelper.createMap[String, Int](dataIterator, new File("/tmp/test.map")) 

    } 
} 

Aber es gibt eine Ausnahme aus:

[Fehler] Exception in thread "main" java.lang.ClassCastException: Key muss ein int aber war eine Klasse java.lang.Integer [Fehler] um net.openhft.chronicle.hash.impl.VanillaChronicleHash.checkKey (VanillaChronicleHash.java:661) [Fehler] um net.openhft.chronicle.map.VanillaChronicleMap. queryContext (VanillaChronicleMap.java:281) [Fehler] um net .openhft.chronicle.map.VanillaChronicleMap.put (VanillaChronicleMap.java:390) [Fehler] bei ...

ich sehen kann, dass es etwas haben könnte, mit Unteilbarkeit von Scala Int zu tun, wie auf Java im Gegensatz Ganzzahl, aber wie umgehe ich das?

2.11.7 Scala

Chronik Karte 3.8.0

Antwort

1
  • scheint verdächtig, dass es Iterator[(String, Int)] in Ihrem Test (statt Iterator[(Int, String)]) für Schlüsseltyp ist String und Werttyp ist Int, während der Fehler Nachricht enthält über Schlüsseltyp (int/Integer)
  • Wenn die Fehlermeldung Key must be a %type% besagt, bedeutet dies, dass Sie diesen Typ in der ersten ChronicleMapBuilder.of(keyType, valueType)-Anweisung konfiguriert haben. Also in Ihrem Fall bedeutet dies, dass Sie konfigurierte int.class (das Class Objekt, die den primitiven int Typen in Java), das nicht erlaubt ist, und die Bereitstellung java.lang.Integer Instanz Methoden zur Karte (wahrscheinlich Sie primitiven int s, aber sie werden Integer aufgrund Boxen), das ist erlaubt. Sie sollten sicherstellen, dass Sie java.lang.Integer.class (oder eine andere Klasse von Scala) unter ChronicleMapBuilder.of(keyType, valueType) anrufen.
  • Ich weiß nicht, welche Größe Schätzung dieses Projekt gibt: https://github.com/phatak-dev/java-sizeof, aber in jedem Fall sollten Sie eine Größe in Bytes angeben, dass das Objekt in serialisierte Form nehmen. Das serialisierte Formular selbst hängt von Standard-Serialisierern ab, die für einen bestimmten Typ in der Chronikkarte ausgewählt wurden (und möglicherweise zwischen den Chronikkartenversionen geändert werden), oder für benutzerdefinierte Serializer, die für bestimmte ChronicleMapBuilder konfiguriert sind. Daher ist die Verwendung von Informationen über Schlüssel/Wert- "Größen" zur Konfiguration einer Chronikkarte, die nicht in der Chronikkarte selbst enthalten ist, fragil.Sie können das folgende Verfahren verwenden Größen zuverlässiger zu schätzen:

    public static <V> double averageValueSize(Class<V> valueClass, Iterable<V> values) { 
        try (ChronicleMap<Integer, V> testMap = ChronicleMap.of(Integer.class, valueClass) 
         // doesn't matter, anyway not a single value will be written to a map 
          .averageValueSize(1) 
          .entries(1) 
          .create()) { 
         LongSummaryStatistics statistics = new LongSummaryStatistics(); 
         for (V value : values) { 
          try (MapSegmentContext<Integer, V, ?> c = testMap.segmentContext(0)) { 
           statistics.accept(c.wrapValueAsData(value).size()); 
          } 
         } 
         return statistics.getAverage(); 
        } 
    } 
    

    Sie können es in diesem Test finden: https://github.com/OpenHFT/Chronicle-Map/blob/7aedfba7a814578a023f7975ef15ba88b4d435db/src/test/java/eg/AverageValueSizeTest.java

    Dieses Verfahren hackish ist, aber es gibt keine besseren Optionen jetzt richtig.

Eine weitere Empfehlung:

  • Wenn Sie Ihre Schlüssel oder Werte Art von Primitiven (Ints, sehnt sich verdoppelt, aber boxed), oder jede andere Art, die immer von der gleichen Größe ist, shouldn Sie Verwenden Sie keine averageKey/averageValue/averageKeySize/averageValueSize Methoden, besser verwenden Sie constantKeySizeBySample/constantValueSizeBySample Methode. Speziell für java.lang.Integer, Long und Double wird auch dies nicht benötigt, Chronikkarte weiß bereits, dass diese Typen ständig dimensioniert sind.
Verwandte Themen