访问文件属性与访问sqlite记录

时间:2014-03-05 08:49:44

标签: ios iphone objective-c sqlite nsfilemanager

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

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

我的第二选择是创建一个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 filesystem将所有属性保存在某种数据库中 轻松访问?

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

2 个答案:

答案 0 :(得分:5)

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

1)您没有合并NSLog和while循环所花费的时间。 75%的时间由他们承担 而您只想比较intForColumn和attributesOfItemAtPath。执行此操作的正确方法是运行工具Timer Profiler并比较检索单个记录的时间。

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

3)通过对磁盘(硬盘驱动器)而不是堆的访问次数来比较X个记录的访问时间。您正在做的是在两种情况下都对数据存储进行堆访问。因此,您根本无法看到任何差异。

这是否意味着文件管理器优于FMDB,绝对不是!! 以下是几个原因:

FMDB只有在配置完成后才能正常运行。 FMDB的核心在于分页(缓存到堆)和索引两个方面。 让我一次向你解释一下。

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

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

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

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

2)索引结构,这可以追溯到SQL Lite的基础知识。您可能希望添加额外的属性调用作为对图像的访问次数,并在其上编制索引。这将大大提高人均力量。 Filemeanger不太可能。

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

2)如果数据是> 2MB用于Core Data / FMDB。

核心数据针对多线程环境进行了额外的性能调整,还有许多其他功能,如无缝集成加密。

答案 1 :(得分:0)

首先,正如@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