当前位置: 首页 > 知识库问答 >
问题:

在Scala中使用盒装/原子值和编年史地图

庞乐池
2023-03-14

我们使用编年史地图在大量不同的存储中支持堆外持久性,但在最简单的用例中遇到了一些问题。

首先,以下是我为简化创建而编写的帮助程序:

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)
  }
}

它使用https://github.com/phatak-dev/java-sizeof进行对象大小估计。以下是我们想要支持的用法类型:

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"))

  }
}

但它抛出了一个例外:

[错误]线程“main”java中出现异常。ClassCastException:键必须是int,但它是java类。lang.Integer[错误]在网络上。openhft。编年史搞砸impl。瓦尼拉。net上的checkKey(vanillahorgehash.java:661)[错误]。openhft。编年史地图瓦尼利亚地图。queryContext(VanillaChronicleMap.java:281)[错误]位于net。openhft。编年史地图瓦尼利亚地图。将(VanillaChronicleMap.java:390)[错误]放在。。。

我可以看到它可能与Scala的Int的原子性有关,而不是Java的整数,但是我如何绕过它呢?

Scala 2.11.7

纪事地图3.8.0

共有1个答案

查修谨
2023-03-14

>

  • 似乎怀疑在您的测试中是Iterator[(String,Int)](而不是Iterator[(Int,String)]),因为键类型是String,值类型是Int,而错误消息是关于键的类型(Int/Integer)
  • 如果错误消息说键必须是%type%,则表示您在第一个编年史映射生成器中配置了该类型。of(keyType,valueType)语句。因此,在您的例子中,这意味着您配置了int.classclass对象,表示Java中的原语int类型),这是不允许的,并且提供了Java。lang.Integer实例映射的方法(可能您提供了原语ints,但由于装箱,它们变成了Integer),这是允许的。您应该确保您提供的是java。lang.Integer。类(或其他一些Scala类)到编年史MapBuilder。of(keyType,valueType)调用
  • 我不知道这个项目的规模估计是多少:https://github.com/phatak-dev/java-sizeof,但在任何情况下,都应该以字节为单位指定对象将采用序列化形式的大小。序列化表单本身取决于默认序列化程序(为历史记录映射中的特定类型选择)(并且可能在历史记录映射版本之间更改)或为特定历史记录映射生成器配置的自定义序列化程序。因此,使用有关键/值“大小”的任何信息来配置历史记录映射(除了历史记录映射本身之外)是脆弱的。可以使用以下步骤更可靠地估计尺寸:

    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();
        }
    }
    

    您可以在此测试中找到它:https://github.com/OpenHFT/Chronicle-Map/blob/7aedfba7a814578a023f7975ef15ba88b4d435db/src/test/java/eg/AverageValueSizeTest.java

    这个程序很粗糙,但现在没有更好的选择。

    另一项建议:

    • 如果您的键或值是某种原语(整数、长整数、双精度,但已装箱),或者任何其他类型的键或值总是大小相同,则不应使用averageKey/averageValue/averageKeySize/averageValueSize方法,最好使用constantKeySizeBySample/constantValueSizeBySample方法。专门针对java。lang.IntegerLongDouble即使不需要,编年史地图已经知道这些类型的大小是不断变化的

  •  类似资料:
    • 在描述中说: 编年史映射提供内存访问速度,并支持超低垃圾收集。编年史地图可以支持最苛刻的应用程序。

    • 我们有一个在50台服务器上使用相同数据集(键值对)的系统。对该数据集的更新数量约为每小时1000次,并且必须在这50台服务器上复制。我们有一个主系统接收这些更新,并负责将这些更新传播到其他服务器。目前,我们每小时以文件的形式将整个数据集(而不是增量更新)同步到所有服务器。然后将这些数据加载到不可变的Koloboke映射中。每个服务器每秒处理大约25000个请求,每个请求对这个映射进行30次查找。在

    • 一个简单的问题:我看到chronicle Map3x正在将一些功能转移到引擎产品中。然而,引擎本身依赖于MAP2X。我有点困惑,我怎么能把它们一起用呢?我想我错过了什么,但不确定到底是什么。

    • 我有以下映射定义,其中map.containsKey()显然不起作用: 我使用的是编年史地图2.4.17,在我的项目中迁移到版本3太难了。

    • 问题内容: 所以今天有人问我这个问题。 该程序将输出什么?它返回true。我回答由于我对自动装箱(和自动装箱)的了解,它将始终打印为false。我的印象是,为Integer分配a = 3将创建一个新的Integer(3),以便an ==将对引用进行评估,而不是原始值。 谁能解释一下? 问题答案: 缓存在-128到127之间的框值。装箱使用方法,该方法使用缓存。超出范围的值不会被缓存,并且始终创建为