设计原理:GE有一个分布式内存基础设施,成为内存云。内存云由一组内存主干组成。集群中的每台机器承载256个内存中继。我们将一台机器的本地内存空间划分为多个内存中继的原因有两方面:1)中继级别的并行性可以没有任何锁定开销的情况下实现;2)内存中继使用散列机制进行内存寻址。由于哈希冲突发生的概率较高,单个大哈希表的性能不够理想。

GE内存云提供键值访问接口,键是64位全局唯一标识符。只是任意长度的二进制数。由于内存云分布在多台机器上,我们无法使用其物理内存地址来处理键值对。为了定位给定键的值,我们首先确定存储键值对的机器,然后在该机器上的一个内存中定位键值对。

对于不同的GE应用程序,键-值对中的值组件具有不同的数据结构或数据模式。我们使用单元格来表示值组件。例如,让我们考虑具有以下内容的单元格:1)32位整数Id;2)64位整数的列表。换句话说,我们希望实现一个可变长度列表(带有Id)作为值组件。在c#中,这样的结构可以定义为:

struct CellA
{
  int Id;
  List<long> Links;
}

如何有效的存储结构化类型化数据并支持高效而优雅的操作是一个挑战。

我们可以直接使用大多数面向对象语言(如c++或c#)支持的对象来建模用户数据。这提供了一种方便直观的数据操作方式。我们可以通过对象的接口来操作对象,例如int Id = cell.Id 或者 cell.Links[0] = 1000001其中cell是类型的对象。这种方法虽然简单而优雅,但有明显的缺点。首先,在内存中保存对象的存储开销非常高。其次,语言运行时通常不是为处理大量对象而设计的。随着对象的增加,系统性能几句下降。第三,加载和存储数据需要大量的时间,因为序列化/反序列化对象非常耗时,尤其是数据量大。

我们可以将值组件视为二进制数并通过指针访问数据。将数据存储为二进制可以最小化内存开销。它还可以提高数据操作性能,因为数据操作不涉及数据反序列化。但是系统不知道数据的模式。在实际操作二进制数中的数据之前,我们需要知道确切的内存布局。话句话说,我们需要使用指针和地址偏移量来访问二进制数中的数据元素。这使得编程变得困难和容易出错。

按照上面描述的数据结构,我们需要知道字段Id存储在偏移量0处,连接的第一个元素位于二进制数中的偏移量8处。要获取字段Id的值,并设置Links[0]的值,他需要编写一下代码:

byte* ptr = GetBlobPtr(123); // Get the memory pointer
int Id = *(int*)ptr; // Get Id
ptr += sizeof(int);
int listLen = *(int*)ptr;
ptr+=sizeof(int);
*(long*)ptr = 1000001; // Set Links[0] to 1000001

注意,我们不能天真的将二进制数转换为使用c#等编程语言提供的内置关键字结构定义的结构。也就是说,下面显示的代码将会失败:

// C# code snippet
struct
{
  int Id;
  List<long> Links;
}
....

struct * cell_p = (struct*) GetBlobPtr();
int id = *cell_p.Id;
*cell_p.Links[0] = 100001;

这是因为上面示例中的链接等数据字段是引用数据字段。这样一个结构体的数据域病没有被平铺在内存中。我们不能使用结构化数据指针操作平面内存区域。

我们还可以将值组件视为二进制数,并通过高级语言声明和访问数据。在第三种方法中,我们通过声明行语言(如SQL)定义和操作数据。然而,通常情况下,声明性语言要么表达能力非常有限,要么没有有效的实现。但对GE来说,表达能力和效率极其重要。

GE将用户数据存储为二进制数而不是运行时对象,这样可以最小化存储开销。同时,GE使我们能够以面向对象的方式访问数据,就像我们在c#或java中所做的那样。例如,在GE中,我们可以执行以下操作,即使我们操作的数据是一个二进制数。

CellA cell = new CellA();
int Id = cell.Id;
cell.Links[0] = 1000001;

换句话说,我们仍然可以以一种优雅的、面向对象的方式操作二进制数。GE通过单元访问机制实现了这一点。具体来说,我们首先使用TSL脚本声明数据模式。GE编译脚本并为TSL脚本中定义的单元结构生成单元访问器。然后,我们可以通过单元访问器访问二进制数据,就好像数据是运行时c#对象一样,但实际上,是单元访问器将单元结构中声明的字段映射到正确的内存位置。所有数据访问操作将被正确映射到正确的内存位置,而不会产生任何内存复制开销。

让我们用一个例子来演示单元访问器是如何工作的。要使用单元访问器,我们必须首先指定其单元结构。这是使用TSL完成的。对于前面的例子,我们定义TSL中的数据结构如下:

cell struct CellA
{
  int Id;
  List<long> Links;
}

注意CellA不是c#中的struct定义,尽管它看起来很相似。这个代码片段将由TSL编译器编译成CellA_Accessor。编译器生成用于将CellA字段上的操作转换为底层二进制数上的内存操作的代码。编译后,我们可以使用面向对象的数据访问接口访问二进制中的数据,如下图所示。

除了直观的数据操作接口外,单元访问器还提供了线程安全的数据操作保证。GE被设计成在一个高度多线程的环境中运行,在这个环境中,大量的单元以非常复杂的模式相互作用。为了简化应用程序开发人员的工作,GE通过单元访问提供了线程安全的单元操作接口。

用法:

在使用访问器时,必须应用一些使用规则。

访问器无法缓存:

  访问器工作起来就像一个数据指针,由于访问器指向的内存块可能会被其他访问器操作移动,因此无法缓存以供将来使用。

例如,如果我们有一个被定义为:

cell struct MyCell
{
  List<string> list;
}

TrinityConfig.CurrentRunningMode = RunningMode.Embedded;

Global.LocalStorage.SaveMyCell(0, new List<string> { "aaa", "bbb", "ccc", "ddd"});

using (var cell = Global.LocalStorage.UseMyCell(0))

{

   Console.WriteLine("Example of non-cached accessors:");

  IEnumerable<StringAccessor> enumerable_accessor_collection = cell.list.Where(element => element.Length >= 3);

  foreach(var accessor in enumerable_accessor_collection)

  {

    Console.WriteLine(accessor);

  }

  Console.WriteLine("Example of cached accessors:");

   List<StringAccessor> cached_accessor_list = cell.list.Where(element => element.Length >= 3).ToList();

  // Note the ToList() at the end

   foreach (var accessor in cached_accessor_list)

  {

    Console.WriteLine(accessor);

  }

}

上面显示的代码片段将输出:

Example of non-cached accessors:

aaa

bbb

ccc

ddd

Example of cached accessors:

ddd

ddd

ddd

ddd

对于非缓存访问器的示例,将在使用访问器时计算访问器的值。对于缓存访问器的示例,由cel.list返回的访问器列表。(=>元素。Length>=3).ToList()实际上只是指向同一个访问器的引用,这个访问器指向cell.list的最后一个元素。

单元格访问器和消息访问器在使用后必须进行处理:

单元格访问器是一次性对象。单元格访问器使用后必须进行处理。在c#中,一次性对象可以 通过使用构造来处理。

long cellId = 314;
using (var myAccessor = Global.LocalStorage.UseMyCell(cellId))
{
  // Do something with myAccessor
}

单元格访问器或消息访问器必须正确处理。如果访问器在使用后没有被处理,就会发生非托管资源泄露。

TSL协议的请求/响应阅读器和写入器成为消息访问器。他们也是一次性物品。使用后必须妥善处理。

using (var request = new MyRequestWriter(...))
{
  using (var response = Global.CloudStorage.MyProtocolToMyServer(0, request))
  {
    // Do something with the response
  }
}

单元格访问器不能以嵌套方式使用

每个单元格访问器都有一个自旋锁。单元格访问器的嵌套使用可能导致死锁。下面代码片段中显示的单元格访问器使用不当。

using (var accessorA = Global.LocalStorage.UseMyCellA(cellIdA))
{
  using(var accessorB = Global.LocalStorage.UseMyCellB(cellIdB))
  {
    // Nested cell accessors may cause deadlocks
  }
}

单元格访问器不应嵌套在另一个单元格中。两个或多个嵌套单元格访问器可能导致死锁。

注意,单元格访问器和一个或多个请求/响应阅读器/写入器可以以嵌套的方式使用。

TSL 访问器的更多相关文章

  1. JavaScript 数据属性和访问器属性

    在JavaScript中对象被定义为"无序属性的集合,其属性可以包含基本值.对象或函数."通俗点讲,我们可以把对象理解为一组一组的名值对,其中值可以是数据或函数. 创建自定义对象通 ...

  2. JavaScript 属性类型(数据属性和访问器属性)

    数据属性 数据属性包含一个数据值的位置.在这个位置可以读取和写入值.数据属性有 4 个描述其行为的特性. [[Configurable]]:表示能否通过 delete 删除属性从而重新定义属性,能否修 ...

  3. ECMAScript 5中的数据属性和访问器属性

    简介 ECMAScript 定义的对象中有两种特殊的属性, 这两种特殊的属性在你定义对象属性时就会赋予, 我们在必要时可以改写这两种特殊的属性让其属性的访问更加的合理化, 这两种特殊的属性称呼及作用如 ...

  4. JavaScript数据属性与访问器属性

    ES5中对象的属性可以分为‘数据属性’和‘访问器属性’两种. 数据属性一般用于存储数据数值,访问器属性对应的是set/get操作,不能直接存储数据值. 数据属性特性:value.writable.en ...

  5. JS之访问器

    1.在对象中定义get,set访问器属性 <script> var test = { _name:"pmx", _age:18, _born:1990, get nam ...

  6. 反编译ILSpy 无法显式调用运算符或访问器 错误处理方法 转

    反汇编一个dll类库,导出的项目会报出很多bug,其中主要的就是“无法显式调用运算符或访问器”这个错误,看了一下,发现问题是在调用属性的时候,都 变成了方法,例如:pivotPoint.set_X(0 ...

  7. 14.C#属性访问器、命名空间、pragma指令(七章7.3-7.5)

    看到一些零星的知识片,今天就用自己的理解说明下,也是因为太简单了,一下就过的,也是我们日常开发中常用.留下一个脚印,当书不在手上的,也能翻出来看看.说下属性访问器.命名空间和pragma指令. 属性访 ...

  8. C#属性访问器

    属性的访问器包含与获取或设置属性有关的可执行语句.访问器声明可以包含 get 访问器或 set 访问器,或者两者均包含.声明采用下列形式之一:get {}set {} get 访问器get 访问器体与 ...

  9. JavaScript中的Get和Set访问器

    今天要和大家分享的是JavaScript中的Get和Set访问器,和C#中的访问器非常相似. 标准的Get和Set访问器的实现   function Field(val){       this.va ...

随机推荐

  1. Hello jna

    记录下这几天用jna3.5.0调c++写的dll的经历 os:win7 用jna调dll首先需要一个dll文件并有可调的方法,然后根据方法的名称,参数,返回值编写一个interface c++需要包含 ...

  2. C#任务同步

    using System; using System.Diagnostics; using System.Threading; using System.Threading.Tasks; using ...

  3. Python--subprocess

    本节内容 os与commands模块 subprocess模块 subprocess.Popen类 总结 我们几乎可以在任何操作系统上通过命令行指令与操作系统进行交互,比如Linux平台下的shell ...

  4. pm2,部署nodejs,使用方法及自己使用后总结的经验

    pm2是一个带有负载均衡功能的应用进程管理器,可以用它来管理你的node进程,并查看node进程的状态,当然也支持性能监控,进程守护等功能.他会确定重启开机之后,能够保证程序也能运行起来.目前还没有操 ...

  5. Vofuria ARCamera相机问题

    想要发射 射线 Camera.allCameras[0] 该语句来选择相机:Camera.main 此语句只能找到Tag为MainCamera的相机:

  6. idea整合 spring boot jsp mybatis

    spring  boot  开发起来确实要简单许多 ,spring boot  包含了 spring mvc ;内置tomcat   ;启动只需要主方法即可 1.使用idea新建一个spring bo ...

  7. k-近邻算法概述

    2.1 k-近邻算法概述 k-近邻算法采用测量不同特征值之间的距离方法进行分类. 优点:精度高.对异常值不敏感.无数据输入假定. 确定:计算复杂度高.空间复杂度高. 适用数据范围:数值型和标称型. 工 ...

  8. Scala数组| 集合

    arrays :+ 5尾部   头部5 +: arrays TODO 声明不可变数组,不能删; 默认情况下,scala中集合的声明全都是不可变的 val arrays: Array[Int] = Ar ...

  9. Linux终端复用神器-Tmux使用梳理

    Tmux是一个优秀的终端复用软件,类似GNU Screen,但来自于OpenBSD,采用BSD授权.使用它最直观的好处就是,通过一个终端登录远程主机并运行tmux后,在其中可以开启多个控制台而无需再“ ...

  10. Problem B. Beer Refrigerator

    http://codeforces.com/gym/241680/problem/B比赛的时候考虑的是,它们3个尽可能接近,然后好麻烦,不如暴力枚举,这里不需要质因数分解,而是两重循环枚举所有因数,第 ...