【问题标题】:Saving memory in an array/class members without losing too much performance在数组/类成员中节省内存而不会损失太多性能
【发布时间】:2020-12-22 18:42:51
【问题描述】:

我有一个程序可以存储 100.000 个文件及其信息。通常类定义是这样的:

class DSPhotoFile: NSObject, NSCopying, Comparable {
    let key: Int                    // may I need an numeric ID? Easy and fast
    let initUrl              : URL  // initial, never changed (could also be used as key)
    // derived data from filepath
    // string parts
    var fullpath        = String()   // "/rootfolder/sub/sub2/pic1234.jpg"
    var path            = String()   // "/rootfolder/sub/sub2"  (path without tailing /)
    ...                              //  12345678901234567890 12345678901 
}

我想节省内存,但不是不惜一切代价。 第一:如何动态计算此定义的实例使用的字节数?有一些字符串成员是动态的!所以我需要完整的占用空间,包括所有子类型。

如果需要,我的下一个想法是只存储一个 Int 作为后缀或前缀的长度。例如。 path 是 fullpath 的前 20 项。如果我存储 Int16 “20”,它需要 2 个字节,然后我将成员路径计算为 fullpath.prefix(20)。这也可以对依赖另一个常量字符串的所有其他成员进行。 关于 String 与 String.prefix 的性能差异的任何想法?有什么建议吗?

【问题讨论】:

    标签: swift performance memory


    【解决方案1】:

    没有简单的方法来计算一条记录的实际内存需求。正如您所注意到的,数据以多种复杂的方式存储,这令人沮丧。更好的做法是在生成 10 万条真实记录的应用上运行 Instruments 并查看总内存使用情况。

    与结构(甚至是纯 Swift 类,但我还是从结构开始)相比,将其设为 NSObject 子类可能会很浪费。

    如果您的路径可能很长,您可以通过单独存储目录信息并仅存储名称来节省大量内存。字符串的副本享有写时复制的好处,但恰好相同(但来自不同来源)的字符串不会被重复数据删除。所以如果你有类似的东西:

    let directory = "..."
    let file1 = File(directory: directory, name: "file1")
    let file2 = File(directory: directory, name: "file2")
    

    那么您应该期望目录的两个副本共享存储。如果目录可能很长,这可能很重要。然而:

    let file1Name = "longdirectorypath/file1"
    let file2Name = "longdirectorypath/file2"
    let file1dir = String(file1Name.prefix(dirlen))
    let file2dir = String(file2Name.prefix(dirlen))
    

    在这种情况下,您不应该期望 file1dir 和 file2dir 共享存储空间,即使字符串恰好具有相同的值。

    或者,如果您将数据存储在树结构中(可能使用 Swift 类,而不是结构),那么您可以避免存储整个路径。只需存储您的父母并通过遍历树来计算路径。

    String.prefix 返回一个子字符串,这是一个 O(1) 操作,访问速度非常快。所以这是一个很好的方法,但我认为如果您有多个相同目录路径的副本,成本会更高。

    我将探讨initUrl 的 URL 与字符串的成本。我预计 URL 会大一些(如果需要,您可以计算 URL)。

    如果您只是要拥有大量随机顺序的 URL,因此很难共享部分字符串,那么我可能只是将 initUrl 存储为字符串,然后将其分割为如果可能,完整路径和路径。只要它们一起存在于一个结构中,您就可以存储一个子字符串(它只是一个范围),或者使用两个整数来指向路径开始和文件名开始。有了这些,您可以计算其余部分。

    如果您要预先计算性能,请务必仔细分析以确保成功。使用大量内存也可能会大大降低速度(由于内存分配、位置问题以及处理 RAM 的其他成本)。在时间/空间权衡方面有一个古老的 CS 信念,但在现代系统中通常没有这样的权衡。节省空间可以以不直观的方式节省时间。个人资料个人资料。

    有了这种规模的记录,明智的数据结构和良好的算法变得非常重要。对于这种结构,我肯定会查看 trie 或前缀树。根据我的经验,如果它存在于数组中,我会严重倾向于结构。但对于列表或树状结构,您通常需要 Swift 中的类。

    【讨论】:

      猜你喜欢
      • 2015-08-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-07-06
      • 2017-11-20
      • 2019-01-04
      • 2021-07-17
      • 1970-01-01
      相关资源
      最近更新 更多