美文网首页程序员@IT·互联网
Tagged pointer 一种能够提升性能、节省内存的技术

Tagged pointer 一种能够提升性能、节省内存的技术

作者: CoderKo1o | 来源:发表于2016-10-30 22:40 被阅读524次

    概念

    在计算机领域中,tagged pointer 是一个指针(内存地址),这种指针关联着额外的数据信息,例如indirection bit(间接位)或者refrence count(引用技术),这些数据通常被“folded”到指针中。这些额外数据称作为“标签”,“标签”是用来指定类型。

    应用

    关于Tagged pointer应用在很多地方,这里主要谈论在Objective-C中的应用,如果想要有更多了解,可以参考WikipediA的Tagged pointer这篇文章。下面我们就来谈谈Tagged pointer技术在Objective-C中的应用:

    在OS X 10.10中,Apple就开始使用Tagged pointer技术了,OC中的class points(类指针)是字对齐的,也就是说对象在内存中是对齐的,它们的地址总是指针大小的整数倍,通常是16的倍数,OC中对象指针是一个64位整数,如果指针的最低有效位为1(奇数),这个指针就是Tagged pointer,紧接着3位表示类表的索引,剩下的60位为类所用。Tagged pointer中的“标签”用于许多用途,例如:存储引用技术和是否含义析构函数。

    Tagged pointer在OC中有一个简单的应用就是NSNumber,接下来我们来看看NSNumber是如何应用Tagged pointer:

    下面是我在64位 Mac上Xcode 8.0的运行代码:

    for (int i = 0; i < 11; i++) {
        NSNumber *num = [NSNumber numberWithInt:i];
        // 打印出地址
        NSLog(@"%p\n", num);
    }
        
    输出如下:
    2016-10-30 15:13:36.073573 LearningOC-NSObject[39610:2880360] 0x27
    2016-10-30 15:13:36.073605 LearningOC-NSObject[39610:2880360] 0x127
    2016-10-30 15:13:36.073620 LearningOC-NSObject[39610:2880360] 0x227
    2016-10-30 15:13:36.073722 LearningOC-NSObject[39610:2880360] 0x327
    2016-10-30 15:13:36.073739 LearningOC-NSObject[39610:2880360] 0x427
    2016-10-30 15:13:36.073755 LearningOC-NSObject[39610:2880360] 0x527
    2016-10-30 15:13:36.073769 LearningOC-NSObject[39610:2880360] 0x627
    2016-10-30 15:13:36.073782 LearningOC-NSObject[39610:2880360] 0x727
    2016-10-30 15:13:36.073797 LearningOC-NSObject[39610:2880360] 0x827
    2016-10-30 15:13:36.073812 LearningOC-NSObject[39610:2880360] 0x927
    2016-10-30 15:13:36.073850 LearningOC-NSObject[39610:2880360] 0xa27
    

    由输出可以看出num明显不是对象指针,而是Tagged pointer。理由很简单:因为在64位Mac地址空间的开头里,4GB零页是未映射且不可映射的,所以上面输出的地址明显很小,不可能是对象指针。

    输出地址中,最后一位7用二进制表示为: 1001
    最后一位1表示该指针为 Tagged pointer; 紧跟着三位100表示NSNumber这个类;倒数第二位2用二进制表示为0010,表示数据类型(int),这个也可以从以下实验看出:

    NSNumber *floatNum = [NSNumber numberWithFloat:1];
    NSLog(@"floatNum:%p\n", floatNum);
    NSNumber *intNum = [NSNumber numberWithInt:1];
    NSLog(@"intNum:%p\n", intNum);
    NSNumber *longNum = [NSNumber numberWithLong:1];
    NSLog(@"longNum:%p\n", longNum);
    NSNumber *charNum = [NSNumber numberWithChar:1];
    NSLog(@"charNum:%p\n", charNum);
    
    输出如下:
    2016-10-30 15:42:44.870889 LearningOC-NSObject[41617:2922729] floatNum:0x147
    2016-10-30 15:42:44.870912 LearningOC-NSObject[41617:2922729] intNum:0x127
    2016-10-30 15:42:44.870931 LearningOC-NSObject[41617:2922729] longNum:0x137
    2016-10-30 15:42:44.870951 LearningOC-NSObject[41617:2922729] charNum:0x107
    

    现在,有上述内容可以看出,64位的地址已经花费了8个位用来表示数据类型、类、指针类型了。留下56位来表示数值了。在int数据中,第一位是符号位,所以我们能表达的最大数值为2^55 - 1,当数值超过2^55 - 1则用NSNumber对象指针来存储,以下实验足以证明这个结论:

    NSNumber *num1 = [NSNumber numberWithLong:((long)pow(2, 55)) - 1];
    NSNumber *num2 = [NSNumber numberWithLong:((long)pow(2, 55))];
    NSLog(@"num1:%p  numb2:%p\n", num1, num2);
    
    输出如下:
    2016-10-30 16:07:24.943873 LearningOC-NSObject[43567:2961854] num1:0x7fffffffffffff37  numb2:0x100200370
    
    

    接下来我们来看看关于引用计数,现在我已经把Xcode环境配置为MRC,然后执行以下代码:

    NSNumber *intNum = [NSNumber numberWithInt:1];
    NSLog(@"retainCount:%lu", (unsigned long) intNum.retainCount);
    
    输出如下:
    2016-10-30 16:37:28.496776 LearningOC-NSObject[45654:3007376] retainCount:9223372036854775807
    

    可以看出retainCount是一个巨大的数值,也就表示该对象该内存中不被释放,这样也省去了释放内存操作。

    以上是apple使用Tagged pointer应用在NSNumber,下面我们来分析一下这样用的好处:
    首先,当创建一个NSNumber对象时,使用Tagged pointer可剩下一个真正的对象内存的分配(因为数值是存在指针里,而不是指针所指向的内存里),其次在调用相应方法是,如doubleValue方法时,是直接取指针中高60位表示的数值,这样可以省下一次间接取值的时间。最后,由于引用计数可以是空指令,因为没有内存需要释放。这对于常用的类,性能也是巨大的提升!

    总结

    Tagged pointer确实是能够提升内存和性能的有趣的技术.当然,NSNumber只是一个简单的应用,吸引Apple官方对Tagged pointer应用花更多时间处理的当然是NSString(NSMutableString)字符串处理了。这里就不多描述了。感兴趣的可以去看看这篇文章:【译】采用Tagged Pointer的字符串

    参考文献

    相关文章

      网友评论

        本文标题:Tagged pointer 一种能够提升性能、节省内存的技术

        本文链接:https://www.haomeiwen.com/subject/crruuttx.html