【问题标题】:Accessing files attributes vs accessing sqlite records访问文件属性与访问 sqlite 记录
【发布时间】:2014-04-07 05:07:09
【问题描述】:

在我们的一个应用程序中,我被要求记录图像的最后修改日期。这样我就可以与服务器检查某个图像是否已更改并相应地更新我的缓存。

我的第一种方法是访问文件属性并进行比较,但网上的一些地方提到了延迟方面的严重瓶颈。

我的第二个选择是创建一个 SQLite 表来管理它。 (使用fmdb

我决定编写一个简单的延迟测试。在下一个测试中,我将访问 500 个文件属性和 500 个 sqlite 记录:

- (void)latencyTest
{
    NSMutableArray *arrayTest1 = [[NSMutableArray alloc]init];
    NSMutableArray *arrayTest2 = [[NSMutableArray alloc]init];
    FMResultSet *results = [_database executeQuery:@"SELECT * FROM `tb_media`"];
    NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
    [formatter setDateFormat:@"dd-MM-yyyy HH:mm:ss:SSS"];
    NSLog(@"Time1: %@",[formatter stringFromDate:[NSDate date]]);
    int i=1;
    while(i<501)
    {
        NSString *test = [NSString stringWithFormat:@"%@/_media/media/19/%d.jpg",_outputPath,i];
        NSDictionary *attributes = [[NSFileManager defaultManager] attributesOfItemAtPath:test error:nil];
        NSDate *dateX = [attributes fileModificationDate];
        [arrayTest1 addObject:dateX];
        i++;
    }
    NSLog(@"Time2: %@",[formatter stringFromDate:[NSDate date]]);
    while([results next])
    {        
        NSDate *myDate = [NSDate dateWithTimeIntervalSince1970:[results intForColumn:@"last_update"]];
        [arrayTest2 addObject:myDate];
    }
    NSLog(@"Time3: %@",[formatter stringFromDate:[NSDate date]]);
}

结果:

    //iPhone 5 (Actual Device) 500 Pics

    Files Start:                 05-03-2014 09:31:20:375 
    Files End & Sqlite start:    05-03-2014 09:31:20:491 
    Sqlite end:                  05-03-2014 09:31:20:507

    Files Start:                 05-03-2014 09:31:56:305 
    Files End & Sqlite start:    05-03-2014 09:31:56:421 
    Sqlite end:                  05-03-2014 09:31:56:437

    Files Start:                 05-03-2014 09:32:19:053 
    Files End & Sqlite start:    05-03-2014 09:32:19:170 
    Sqlite end:                  05-03-2014 09:32:19:187

如您所见,结果几乎相同。 我的问题是:

  1. 我假设使用attributesOfItemAtPath 一次访问一个文件会 比sql要长很多。我错过了什么吗?

  2. attributesOfItemAtPath 是否真的在访问文件或 iOS 文件系统将所有属性保存在某种数据库中 方便吗?

  3. 在看到上述结果后,我决定使用 attributesOfItemAtPath 方法。还有什么我不是的 考虑传递 sqlite?

【问题讨论】:

  • 我不知道 fileModificationDate 方法,谢谢 :-)
  • 您使用什么硬件进行测试? SSD 的性能将明显优于 iOS 设备中的闪存。也就是说,500 个文件并不多,即使对于嵌入式硬件也是如此。您是否期望该量级在您的缓存范围内?还是您必须将其扩展到数千张图像?
  • @ImHuntingWabbits 这很奇怪,SSD 的运行速度比我的 iPhone 5 慢,但老实说,模拟器并没有真正让我感兴趣,只对实际设备感兴趣。在尝试查询超过 500 个文件时,我使用 sqlite 达到了某种限制,并出现错误“SQLite 错误:复合 SELECT 中的术语太多”,所以我暂时将其保持在 500。
  • 您的测试结果似乎没有表明这一点。对于 sqlite 和 ~9ms 文件系统,模拟器是 ~1ms,但上面的设备对于 sqlite 和文件系统看起来像 ~17ms 和 ~130ms。该设备的结果符合我对当前一代硬件的预期,但在当前 iPod touch 和旧 iPhone/iPad 等设备上速度较慢。
  • 你完全正确。我将发布的结果与我在非 ssd mac 上所做的其他较慢的结果混淆了。我会用我的硬件更新问题。

标签: ios iphone objective-c sqlite nsfilemanager


【解决方案1】:

在我讨论解决方案之前,您的评估策略存在一些问题。

1) 您没有合并 NSLog 和 while 循环所花费的时间。他们占用了 75% 的时间 而您只想比较 intForColumn 与 attributesOfItemAtPath。正确的做法是运行仪器Timer Profiler 并比较检索单个记录的时间。

2) 您已将 FMDB 用作文件管理器。在内部 FMDB 序列化文件中的数据。 FMDB / SQL Lite 的核心在于它的数据结构,特别是你根本没有使用过的索引。 因此,即使您比较记录所需的时间,您也会观察到 FMDB 比文件管理器花费更多的时间,因为以特定格式序列化数据的额外开销。

3) X 条记录的访问时间与对磁盘(硬盘驱动器)而非堆的访问次数进行比较。在这两种情况下,您正在做的是对数据存储的堆访问。所以你根本看不到任何区别。

这是否意味着 File Manager 比 FMDB 更好,绝对不是! 以下是几个原因:

FMDB 仅在配置为这样做时才能正常运行。 FMDB 的核心在于分页(缓存到堆中)和索引两件事。 让我一一解释。

1)假设您正在尝试访问 100 张图像的时间戳。每个图像都有 1000 个时间戳。这意味着您必须对数据存储进行 100*1000 = 100,000 次访问。 如果图像很小,则 Filemanager 会将文件加载到堆中,并且访问速度将比 FMDB 快,但如果您没有足够的堆空间,您的应用程序将发出内存警告并从磁盘访问文件,而不是从缓存访问文件,这会慢得多。

所以它的二进制状态要么全部来自堆,要么全部来自磁盘

FMDB 优于此状态,并根据可用堆空间检索部分记录。当您有大量记录时,这可以加快访问速度。

测试此场景的理想方法是对至少 10,000 张图像(不是时间戳)运行 fucntion latencyTest。这样,与总时间相比,日志时间和迭代速度可以忽略不计。

2) 索引结构,这又回到了 SQL Lite 的基础。您可能希望添加一个额外的属性调用作为对图像的访问次数并在此索引您的表。这将显着提高性能。 Filemeanger 不太可能。

我推荐的解决方案。
1)如果您的数据小于 2 MB(图像加上时间戳),请使用 Filemenager

2) 如果数据 > 2MB,则选择 Core Data / FMDB。

核心数据具有针对多线程环境的额外性能调整以及更多额外功能,例如用于加密的无缝集成。

【讨论】:

    【解决方案2】:

    首先,正如@kunal 所说,您的基准测试方法不是确定性的,可能会误导您的决定。

    话虽如此,如果您只需要修改日期,attributesOfItemAtPath: 会产生一点开销,而性能对您来说确实是个问题。你可以做的是改用lstat。这与您的情况类似(请注意,我删除了数组以避免在基准测试中产生不必要的开销):

    #import <sys/stat.h>
    
    - (void)latencyTest
    {
        // *********************** Test 1 ***********************************//
        double t = CACurrentMediaTime();
        for (NSInteger i = 1; i < 748; i++)
        {
            NSString *path = [NSString stringWithFormat:kMediaPath, i];
            NSDictionary *attributes = [[NSFileManager defaultManager] attributesOfItemAtPath:path
                                                                                        error:nil];
            NSDate *dateX = [attributes fileModificationDate];
        }
        double total = CACurrentMediaTime() - t;
        NSLog(@"Total time fileManager: %fs, average per read: %fs", total, total / 747.f);
    
        // *********************** Test 2 ***********************************//
        struct stat linfo;
        t = CACurrentMediaTime();
        for (NSInteger i = 1; i < 748; i++)
        {
            NSString *path = [NSString stringWithFormat:kMediaPath, i];
            lstat([path cStringUsingEncoding:NSUTF8StringEncoding], &linfo);
            NSDate *dateX = [NSDate dateWithTimeIntervalSince1970:linfo.st_mtime];
        }
        total = CACurrentMediaTime() - t;
        NSLog(@"Total time lstat: %fs, average per read: %fs", total, total / 747.f);
    }
    

    对于 748 张图片的输入,我的结果是:

    // Simulator (iOS 7.1)
    
    Total time fileManager: 0.061365s, average per read: 0.000082s
    Total time lstat: 0.004313s, average per read: 0.000006s
    
    // iPhone 5s Device (iOS 7.1)
    
    Total time fileManager: 0.019299, average per read: 0.000026
    Total time lstat: 0.008520, average per read: 0.000011
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2020-08-25
      • 1970-01-01
      • 2014-11-24
      • 1970-01-01
      • 2018-01-03
      • 1970-01-01
      • 1970-01-01
      • 2017-09-25
      相关资源
      最近更新 更多