【问题标题】:Memory Friendly Fast Key-Value Access Solution for Android适用于 Android 的内存友好型快速键值访问解决方案
【发布时间】:2012-05-23 10:39:18
【问题描述】:

我有一个 Android 应用程序,它遍历包含数千个整数的数组,并将它们用作键值来访问整数对(我们称它们为 id),以便使用它们进行计算。它需要尽可能快地完成,并最终返回对应用程序至关重要的结果。

我尝试将 HashMap 加载到内存中以快速访问这些数字,但它导致 OOM 异常。我还尝试将这些 id 写入 RandomAccessFile 并将它们在文件上的偏移量存储到另一个 HashMap 但它太慢了。另外,新的只存储偏移量的 HashMap 仍然占用很大的内存。

现在我正在考虑使用 SQLite,但我不确定它是否会更快。是否有任何结构或库可以帮助我解决这个问题?

编辑:密钥数量超过 2000 万,而我只需要访问数千个。我不知道我会事先访问哪些,因为它会随着用户输入而变化。

【问题讨论】:

  • 数千个整数不能占用这么多内存,你一定是做错了什么......
  • 你是如何保存密钥的?最初保存在哪里?我建议以某种方式以排序方式保存 em,并让文件成为范围适合键的值的桶......
  • 密钥被保存到 HashMap,然后在管理控制台中序列化。稍后,在 Android 应用程序中,它们被反序列化回 HashMap。所以我有机会修改这些密钥的存储方式,但我无法改变它们在我的算法中用作密钥的方式。我不明白对它们进行排序的好处。
  • 如果您的应用需要特定数量的内存,但您的部署平台不允许您使用所需的内存,没有简单的解决方案。
  • 查看我的答案以了解为什么存储桶会有所帮助

标签: java android database memory map


【解决方案1】:

我的建议是重新排列 Buckets 中的键 - 我的意思是确定(或多或少)你的键的分布,然后制作与每个键范围相对应的文件(关键是每个文件必须包含很多整数可以进入内存,仅此而已)然后当您搜索密钥时,您只需将整个文件读入内存并查找它。

例如,假设key的分布是均匀的,存储500k值对应0-500k键值,500k值对应500k-1mil键等等...

编辑:如果你确实尝试过这种方法,但它仍然很慢,我仍然有一些技巧:

  1. 首先确保所有桶之间的划分实际上接近相等。
  2. 通过制作更多的桶来尝试使桶更小。
  3. 关于按范围正确划分存储桶的想法是,当您搜索一个键时,您会转到相应的范围存储桶,并且该键要么在其中,要么不在整个集合中。所以并发读取另一个存储桶没有意义。
  4. 我从来没有这样做过,因为我不确定并发在 I\O 上是否有效,但是使用 2 个线程读取整个文件可能会有所帮助,一个从上到下,另一个从下到上,直到它们相遇。 (或类似的东西)
  5. 当您将整个存储桶读入内存时,将其拆分为 3-4 个数组列表,运行 3-4 个工作线程以在每个数组上搜索您的键,然后搜索必须以更快的速度结束。

【讨论】:

  • 我也试过这种方法。我创建了 10 个单独的哈希图,它们仅存储特定范围内的键。然而,当我在应用程序端进行搜索时,即使成功地将 hashmap 加载到内存中,由于必须逐个加载每个 hashmap 效率低下,获得最终结果所需的时间也大大增加。
  • 您是否尝试过使用并发?您将能够通过这种方式更快地搜索......另外,也许您应该将密钥拆分为更多文件。
  • 我对这个词不是很熟悉。你能再解释一下吗?我的理解是使用多个线程同时访问不同的bucket,对吗?
  • 您将无法做到这一点 - 我不知道它是否具有与在内存上运行相同的效果。无论如何我都会编辑我的答案。
  • 如果所有线程都受 IO 限制,则多线程不会给您带来性能提升。另请注意,大多数 Android 设备都使用单核处理器,因此它们根本无法从并发中受益。
【解决方案2】:

您可以使用 Trove 的 TIntLongHashMap 将原始 ints 映射到原始 longs(存储您的值对的 ints)。这为您节省了普通 Map 的对象开销,这会强制您使用包装器类型。

编辑

由于您的更新表明您有超过 2000 万个映射,因此可能会有比哈希映射更节省空间的结构。即使是最有效的哈希映射实现,一种将您的密钥划分为存储桶的方法,结合一些子密钥压缩可能会为您节省一半的内存。

【讨论】:

  • 您是否建议我将两个整数连接成一个 long 并像这样存储它们?
  • @babatenor 是的,如果您的目标是节省内存。
  • 听起来是个好主意,尽管不知道在运行时该对中的每个整数会有多少位数。因此,将它们再次拆分回单个整数将是一个问题。与您的想法类似,我尝试将这对存储为带有分隔符的单个字符串以分隔两个整数,但存储字符串的内存几乎与存储两个整数一样多。
  • 既然你说的是 integer,我以为你的意思是 32 位 int 类型。对中值的最大范围是多少?
  • 假设您的存储桶大小为 65536 (2^16)。您不需要在此存储桶中存储每个 32 位键值,而只需存储低 16 位,每个条目节省 2 个字节。或者,您可以将键存储为彼此的偏移量。如果下一个值是当前值的 +4,则可以将该信息编码为 2 位,加上标记开销。此外,如果您的值对主要是不会占用完整 32 位的低值,您可以压缩这些值以占用更少的空间。
【解决方案3】:

SQLite 是一个嵌入式关系数据库,它使用索引。我敢打赌它比使用 RandomAccessFile 快得多。你可以试试看。

【讨论】:

  • 但是没有什么比内存数据结构更能提高速度了,这似乎是 OP 的中心目标。
  • 我正在考虑将每个键添加为一列并将整数存储在这些列下。然后我可以查询这些列并获取对。您认为这会是一种快速的方法吗?
  • 是的,这是一个合理的方法。试试看,如果速度够快,肯定是最简单的解决方案。
猜你喜欢
  • 2017-08-15
  • 1970-01-01
  • 2011-02-18
  • 1970-01-01
  • 2014-02-03
  • 1970-01-01
  • 2011-08-14
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多