Swift 在不损失太多性能的情况下在数组/类成员中节省内存

Swift 在不损失太多性能的情况下在数组/类成员中节省内存,swift,performance,memory,Swift,Performance,Memory,我有一个存储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

我有一个存储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。例如,路径是完整路径的前20项。如果我存储Int16“20”,它需要2个字节,然后我将成员路径计算为fullpath.prefix(20)。对于依赖另一个常量字符串的所有其他成员,也可以这样做。
知道String和String.prefix的性能差异吗?一些建议?

计算一条记录的实际内存需求没有简单的方法。正如您所注意到的,数据是以多种复杂的方式存储的,这会阻碍数据的存储。更好的方法是在一个能够生成10万条真实记录的应用程序上运行仪器,并查看总内存使用情况

与struct(甚至是纯Swift类,但我还是从struct开始)相比,将其作为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与String的成本。我希望URL要大一点(如果需要,可以计算URL)

如果你有大量的URL是以随机顺序排列的,所以很难共享字符串的一部分,那么我可能会将
initUrl
存储为一个字符串,如果可能的话,将其切分为fullpath和path。只要它们都生活在一个结构中,就可以存储一个子字符串(这只是一个范围),或者使用两个整数来指向路径开始和文件名开始。用这些你可以计算出剩下的

如果您正在为性能预先计算一些东西,请确保仔细地分析这些内容,以确保这是一场胜利。使用大量内存也会使您在速度上付出巨大代价(由于内存分配、局部性问题以及处理RAM的其他成本)。在时间/空间的权衡上有一个古老的CS信念,但在现代系统中通常没有这样的权衡。节省空间也可以以非直观的方式节省时间。配置文件

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