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)
{
ival = static_cast<uint64_t>(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)ival;
} uint32_t toUInt() const
{
assert(getTag() == UINT32);
return (int)getPayload();
} uint64_t toUInt64() const
{
return ival;
} bool toBool() const
{
assert(getTag() == BOOL);
return getPayload() != ;
} 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);
auto r0 = v.toInt(); Value v1 = (uint32_t);
auto r1 = v1.toUInt();
Value v2 = (int64_t);
auto r2 = v2.toInt64();
Value v3 = (uint64_t);
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等等,本文的目的是抛砖引玉,关于它更多的具体应用我希望更多的人能去深入研究。

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

  1. .Net环境下的缓存技术介绍 (转)

    .Net环境下的缓存技术介绍 (转) 摘要:介绍缓存的基本概念和常用的缓存技术,给出了各种技术的实现机制的简单介绍和适用范围说明,以及设计缓存方案应该考虑的问题(共17页) 1         概念 ...

  2. RAID技术介绍

    RAID技术介绍 简介 RAID是一个我们经常能见到的名词.但却因为很少能在实际环境中体验,所以很难对其原理 能有很清楚的认识和掌握.本文将对RAID技术进行介绍和总结,以期能尽量阐明其概念. RAI ...

  3. .Net环境下的缓存技术介绍

    .Net环境下的缓存技术介绍 摘要: 介绍缓存的基本概念和常用的缓存技术,给出了各种技术的实现机制的简单介绍和适用范围说明,以及设计缓存方案应该考虑的问题(共17页) 1         概念 1.1 ...

  4. 网络存储技术介绍(1) ( based on zt)

    最近由于某同学微信发了一些网络存储的文章,开始感兴趣,稍微收集了一些 一.  网络存储技术 http://ask.zol.com.cn/q/187044.html  (yxr:很老的技术介绍吧) 网络 ...

  5. (转) SLAM系统的研究点介绍 与 Kinect视觉SLAM技术介绍

          首页 视界智尚 算法技术 每日技术 来打我呀 注册     SLAM系统的研究点介绍 本文主要谈谈SLAM中的各个研究点,为研究生们(应该是博客的多数读者吧)作一个提纲挈领的摘要.然后,我 ...

  6. VPS技术介绍以及分析

    VPS的全称为Virtual Private Server,叫做虚拟专用服务器(Godaddy称之为Virtual Dedicated Server,VDS).就是利用各种虚拟化手段把单台物理服务器虚 ...

  7. 转:Android 2.3 代码混淆proguard技术介绍

    ProGuard简介 ProGuard是一个SourceForge上非常知名的开源项目.官网网址是:http://proguard.sourceforge.net/. Java的字节码一般是非常容易反 ...

  8. WPF开发技术介绍

    本月做了一个小讲座,主要是WPF的开发技术介绍,由于是上班时间,去听的人不多,但对于自己来说至少是又巩固了Winform的知识,抽时间写一篇文章,在此分享给大家,有什么宝贵建议大家也可以提给我,谢谢. ...

  9. Portal技术介绍

      Portal技术介绍 Portal是web应用发展的一个重要趋势,目前几乎所有大的软件厂商都有自己的Portal产品.并且Portal技术已经形成规范.本文对Portal技术和产品进行了分析,目的 ...

  10. JSP技术介绍

    1. 技术介绍 JSP即Java Server Page,中文全称是Java服务器语言.它是由Sun Microsystems公司倡导.许多公司参与建立的一种动态网页技术标准,它在动态网页的建设中有强 ...

随机推荐

  1. ajax请求格式

    ajax请求格式........... var rowsData = $('#receiptPrintList').datagrid('getSelections'); $.ajax({ type: ...

  2. Java成神之路[转]

    阿里大牛珍藏架构资料,点击链接免费获取 针对本文,博主最近在写<成神之路系列文章> ,分章分节介绍所有知识点.欢迎关注. 主要版本 更新时间 备注 v1.0 2015-08-01 首次发布 ...

  3. Linux修改终端提示符

    打开~/.bashrc可以看到命令提示的内容为:\u@\h\w\$ \u表示用户名,\h表示主机名,\w表示当前目录,\$表示命令提示符(普通用户$,超级用户#) 这个命令提示符有点长,很碍事,\u@ ...

  4. 【LeetCode】199. Binary Tree Right Side View

    Binary Tree Right Side View Given a binary tree, imagine yourself standing on the right side of it, ...

  5. HDU 2492 Ping pong (数状数组)

    Ping pong Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total S ...

  6. C# Random 生成不重复随机数

    命名空间:System 表示伪随机数生成器,一种能够产生满足某些随机性统计要求的数字序列的设备. 伪随机数是以相同的概率从一组有限的数字中选取的.所选数字并不具有完全的随机性,因为它们是用一种确定的数 ...

  7. 【Android】Android解析短信操作

    目录结构: contents structure [-] 获取短信 发送短信 1.获取短信 在AndroidManifest.xml中,添加权限: <uses-permission androi ...

  8. numpy 数组迭代Iterating over arrays

    在numpy 1.6中引入的迭代器对象nditer提供了许多灵活的方式来以系统的方式访问一个或多个数组的所有元素. 1 单数组迭代 该部分位于numpy-ref-1.14.5第1.15 部分Singl ...

  9. 构建你的长寿命的API第1部分:规范驱动的API开发

    构建你的长寿命的API第1部分:规范驱动的API开发 这篇文章是由MuleSoft的Mike Stowe在nginx.conf 2016公布的演示文稿改编的.第一部分重点是规范驱动的API开发. 第二 ...

  10. Groovy 学习手册(3)

    五. Groovy 的设计模式 设计模式是一种非常好的方式,可以使你的代码变得实用,可读又具有扩展性.跟 Java 相比,在 Groovy 里使用设计模式使代码更加简洁和容易. 1. 策略模式 设想一 ...