Nan-boxing技术介绍

  NaN-boxing看起来像英文翻译的“南拳”,其实它是表示一个无效的double数。NaN-boxing技术:通过一个64位的数字来表示多种数据类型的技术,它通过一个nan浮点数来保存数据,根据IEEE-754浮点数标准,double类型的NAN形式为:
sign
| exponent
|     |
[0][11111111111][yyyyxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx]
                              |      |
                            tag     |
                               payload
  总共64位,最高位是一个符号位,可能是0也可能是,接下来的11位全部为1,则这个浮点数就是一个NAN,符号位如果为1则表示是一个quiet NAN,如果为1则表示是一个signed NAN。因此一个NAN只需要前面的12位来表示就行了,那么剩下的52位则可以用来编码,比如我们用剩下的52位中的前4位来表示一个数据的类型,后面的48位用来表示数据的值或地址。表示类型的4位我们称为tag,它最多可以用来表示16种类型的数据,后面的48位我们称为payload,用它来表示实际的数据或数据的地址,对于小于等于32位的数字可以直接存到payload中,对于其它类型的数据可以保存其地址到payload中,因为x86 32位和64位系统中,地址最多不超过47位,所以用48位来保存数据的地址是完全够用的。
  为了演示一下nan-boxing的应用,我写一个简单的例子来展示一下它是如何存储数据的,看起来就像一个动态类型。

#pragma once
#include <cstddef>
#include <assert.h>
#include <cstdint>
using namespace std;

enum TypeTag {
    BOOL,
    INT32,
    UINT32,
    UINT64,
    INT64,
    DOUBLE,
    CHAR_ARRAY,
    STRING,
    NIL
};

#define PAYLOAD_MASK 0x00007FFFFFFFFFFFULL
#define NAN_MASK 0x7FF8000000000000ULL
#define TAG_MASK 0xF
#define TAG_SHIFT 47

union Value {
    uint64_t ival;
    double fval;

    Value(double x) : fval(x)
    {
    }

    Value(int v)
    {
        ival = NAN_MASK | ((uint64_t)INT32 << TAG_SHIFT) | (uint64_t)v;
    }
    Value(uint32_t v)
    {
        ival = NAN_MASK | ((uint64_t)UINT32 << TAG_SHIFT) | (uint64_t)v;
    }

    Value(int64_t v)
    {
        fval = static_cast<double>(v);
    }

    Value(uint64_t v)
    {
        ival = v;
    }

    Value(bool v)
    {
        ival = NAN_MASK | ((uint64_t)BOOL << TAG_SHIFT) | (uint64_t)v;
    }

    Value(const char* v)
    {
        ival = NAN_MASK | ((uint64_t)CHAR_ARRAY << TAG_SHIFT) | (uint64_t)v;
    }

    Value(const string& v)
    {
        ival = NAN_MASK | ((uint64_t)STRING << TAG_SHIFT) | (uint64_t)&v;
    }

    Value(TypeTag tag = NIL, void *payload = nullptr) {
        assert((uint64_t)payload <= PAYLOAD_MASK);
        ival = NAN_MASK | ((uint64_t)tag << TAG_SHIFT) | (uint64_t)payload;
    }

    int toInt() const
    {
        assert(getTag() == INT32);
        return (int)getPayload();
    }

    int64_t toInt64() const
    {
        return (int64_t)fval;
    }

    uint32_t toUInt() const
    {
        assert(getTag() == UINT32);
        return (int)getPayload();
    }

    uint64_t toUInt64() const
    {
        return ival;
    }

    bool toBool() const
    {
        assert(getTag() == BOOL);
        return getPayload() != 0;
    }

    double toDouble() const
    {
        assert(getTag() == DOUBLE);
        return fval;
    }

    char *toCharArray() const
    {
        assert(getTag() == CHAR_ARRAY);
        return (char *)getPayload();
    }

    string& toString() const
    {
        assert(getTag() == STRING);
        return *(string *)getPayload();
    }

    TypeTag getTag() const
    {
        return isPayload() ? DOUBLE : TypeTag((ival >> TAG_SHIFT) & TAG_MASK);
    }

    uint64_t getPayload() const
    {
        assert(!isPayload());
        return ival & PAYLOAD_MASK;
    }

    bool operator<(const Value& other)  const
    {
        return hash()<other.hash();
    }

    bool operator==(const Value& other)  const
    {
        return hash() == other.hash();
    }
private:
    bool isPayload() const
    {
        return (int64_t)ival <= (int64_t)NAN_MASK;
    }

    uint64_t toHashUInt64() const
    {
        assert(getTag() < INT64);
        if (getTag() == UINT64)
            return ival;

        return (uint64_t)getPayload();
    }

    int64_t toHashInt64() const
    {
        assert(getTag() == INT64);
        return toInt64();
    }

    std::size_t hash() const {
        switch (getTag()) {
        case         UINT64:
        case         INT32:
        case         UINT32:
        case           BOOL:
            return std::hash<uint64_t>()(toHashUInt64());
        case         INT64:
            return std::hash<int64_t>()(toHashInt64());
        case DOUBLE:
            return std::hash<double>()(toDouble());
        case STRING:
            return std::hash<std::string>()(toString());
        default:
            throw std::invalid_argument("can not find this type");
        }
    }
};

测试代码:

void Test()
{
    Value t="a";
    cout << t.toCharArray() << endl;

    Value v = (int)2;
    auto r0 = v.toInt();

    Value v1 = (uint32_t)2;
    auto r1 = v1.toUInt();
    Value v2 = (int64_t)2;
    auto r2 = v2.toInt64();
    Value v3 = (uint64_t)2;
    auto r3 = v3.toUInt64();

    Value v4 = 2.5;
    auto r4 = v4.toDouble();

    string s1 = "a";
    Value v5 = s1;
    auto r5 = v5.toString();
    string s = r5;

    Value b = false;
    bool r = b.toBool();
    b = true;
    r = b.toBool();
}

  通过nan-boxing技术,Value可以表示多种类型了,比如int double string等类型,这个Value看起来像一个动态类型了。这一切都很简单,看起来似乎很好,但是这只是我们玩了一个小把戏而已,就是把值或地址放到payload中了,这存在一个很大的问题:即存放在payload中的地址需要保证有效性,如果存放一个临时变量的地址就悲剧了,尤其是对于非数字类型来说,比如string或者一个自定义类型,这时我们需要在外面保证这些变量的声明周期了,这极大的影响了Value的使用。解决这个问题的办法有两个,一个是自定义类型的话就new出来,然后自己去管理其生命周期;另外一个方法是通过一个内存池来创建对象,通过内存池来保持对象地址的有效性。

  nan-boxing技术最开始是亚马逊的工程师应用到javascript的引擎中的,luajit和rubyjit也用它作为动态类型来存储数据,我相信nan-boxing技术还有很多应用场景,比如可以用它来表示json的value等等,本文的目的是抛砖引玉,关于它更多的具体应用我希望更多的人能去深入研究。

时间: 2024-12-09 15:10:31

Nan-boxing技术介绍的相关文章

Qinq技术介绍与实战

说明:本文介绍部分是我在网络收集整理并添加提供. Qinq技术介绍与实战 Qinq介绍 Qinq就是为用户提供一种较为简单的二层VPN隧道.最核心的思想就是将用户私网VLAN tag封装到公网VLANtag上,报文带着两层tag穿越服务商的骨干网络.在用户端口上使QinQ功能时,都会为每个用户分配一个Customer-ID.其中报文中的两层tag标签包括内层ce-vid--VLAN标签和外层pe-vid-Qint标签. 那么问题来了,为什么要带两层标签呢? 我们知道,普通VLAN中的一个VLAN

《Getting Started with WebRTC》第二章 WebRTC技术介绍

本章作WebRTC的技术介绍,主要讲以下的概念: .  如何建立P2P的通信 .  有效的信令选项 .  关键API的关系 2.1 设置通信 尽管WebRTC通信的基础是P2P的, 但设置这个通信的初始步骤是要求一些协作的. 这些动作通常由Web服务器和/或信令服务器提供. 这个协作可以允许两个或多个WebRTC设备或端找到彼此,交换通信的细节, 协商定义了他们如何通信的会话, 最后建立它们之间的直播P2P媒体流. 2.2 一般流程 应用场景其实是很多的,从简单的页面DEMO到多方会议. 这里只

Windows Workflow Foundation技术介绍(基于.NET Framework 4.5)

Windows Workflow Foundation技术介绍(基于.NET Framework 4.5) 转自:http://www.cpiso.cn/jsyj/ghxx/2014/5/15/459.shtml Microsoft Windows Workflow Foundation (WF) 是一个可扩展框架,用于在 Windows 平台上开发工作流解决方案.Windows Workflow Foundation 同时提供了 API 和一些工具,用于开发和执行基于工作流的应用程序.Wind

微信游戏《全民炫舞》公司的引擎开发和布料系统技术介绍

微信<全民炫舞>上线了. 整理了一下过去技术开发历史,还有技术ppt,有兴趣看下: 公司游戏引擎技术介绍: http://www.h3d.com.cn/hr/hr.htm 布料系统技术介绍: 2012 China Game Developer Conference大会演讲资料下载: http://www.h3d.com.cn/hr/downlond/QQX52_CGDC.ppt NVIDIA QQ炫舞2 布料技术演示录像地址: http://www.h3d.com.cn/hr/donnlond

TIBCO Rendezvous — 技术介绍

TIBCO Rendezvous — 技术介绍 1.1.1.      TIBCO Rendezvous — 技术介绍 TIBCO Rendezvous(或称为TIBCO RV)产品是一种中间件,它具有发布/订阅(Publish/Subscribe).基于主题寻址(Subject-Based Addressing) 和自定义数据信息(Self-Describing Data Messages)等专利技术功能,使不同应用平台上的信息在一个共享的虚拟总线Information Bus(TIB)上进行

网络视频相关技术介绍

AnyChat音视频互动开发平台(SDK)是一套跨平台的即时通讯解决方案,基于先进的H.264视频编码标准.AAC音频编码标准与P2P技术,支持高清视频,整合了佰锐科技在音视频编码.多媒体通讯领域领先的开发技术和丰富的产品经验而设计的高质量.宽适应性.分布式.模块化的网络音视频互动平台.        AnyChat音视频互动开发平台(SDK)包含了音视频处理模块(采集.编解码).流媒体管理模块(丢包重传.抖动平滑.动态缓冲).流媒体播放模块(多路混音.音视频同步)以及P2P网络模块(NAT穿透

RAID技术介绍

RAID技术介绍 简介 RAID是一个我们经常能见到的名词.但却因为很少能在实际环境中体验,所以很难对其原理 能有很清楚的认识和掌握.本文将对RAID技术进行介绍和总结,以期能尽量阐明其概念. RAID全称为独立磁盘冗余阵列(Redundant Array of Independent Disks),基本思想就是把多个相对便宜的硬盘组合起来,成为一个硬盘阵列组,使性能达到甚至超过一个价格昂贵. 容量巨大的硬盘.RAID通常被用在服务器电脑上,使用完全相同的硬盘组成一个逻辑扇区,因此操作系统只会把

DHTML(动态HTML)前台页面技术介绍

一. DHTML(动态HTML)前台页面技术介绍 1. DHTML介绍 DHTML包含以四个方面的内容: (1).HTML 4.0 :超文本标记语言,网页文档的主体,以文本文件形式存储,由浏览器翻译后展现出丰富多彩的页面. (2).CSSL:客户端脚本语言,主要有JavaScript(JS),VBScript(VBS),JScript.Netscape主要支持JS,IE主要支持JS,VBS和JScript. (3).DOM:文档对象模型,是W3C推广的web技术标准之一,它将网页中的内容抽象成对

差分时钟、DQS与DQM - DDRx的关键技术介绍(上)

作者:一博科技 在上一篇的问题里面问到了DDRX相对于前一代来说的关键技术突破在哪里,虽然没有人回答得完全正确,但这个也是很正常的,因为通过几句话要想说清楚也确实是不容易的,所以还是通过文章来把这些关键技术再给大家介绍一下. 差分时钟技术 差分时钟是DDR的一个重要且必要的设计,但大家对CK#(CKN)的作用认识很少,很多人理解为第二个触发时钟,其实它的真实作用是起到触发时钟校准的作用. 由于数据是在CK的上下沿触发,造成传输周期缩短了一半,因此必须要保证传输周期的稳定以确保数据的正确传输,这就

Java远程技术介绍学习

Java远程技术介绍学习 RMI [既Remote Method Invoke 远程方法调用] 实现方式为,类extend了java.rmi.Remote接口,即成为存在于服务器端的远程对象,提供客户端访问. PS: extends了Remote接口的类或者其他接口中的方法若是声明抛出了RemoteException异常,则表明该方法可被客户端远程访问调用. 同时,远程对象必须实现java.rmi.server.UniCastRemoteObject类,这样才能保证客户端访问获得远程对象时,该远