TSL 访问器
设计原理: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 访问器的更多相关文章
- JavaScript 数据属性和访问器属性
在JavaScript中对象被定义为"无序属性的集合,其属性可以包含基本值.对象或函数."通俗点讲,我们可以把对象理解为一组一组的名值对,其中值可以是数据或函数. 创建自定义对象通 ...
- JavaScript 属性类型(数据属性和访问器属性)
数据属性 数据属性包含一个数据值的位置.在这个位置可以读取和写入值.数据属性有 4 个描述其行为的特性. [[Configurable]]:表示能否通过 delete 删除属性从而重新定义属性,能否修 ...
- ECMAScript 5中的数据属性和访问器属性
简介 ECMAScript 定义的对象中有两种特殊的属性, 这两种特殊的属性在你定义对象属性时就会赋予, 我们在必要时可以改写这两种特殊的属性让其属性的访问更加的合理化, 这两种特殊的属性称呼及作用如 ...
- JavaScript数据属性与访问器属性
ES5中对象的属性可以分为‘数据属性’和‘访问器属性’两种. 数据属性一般用于存储数据数值,访问器属性对应的是set/get操作,不能直接存储数据值. 数据属性特性:value.writable.en ...
- JS之访问器
1.在对象中定义get,set访问器属性 <script> var test = { _name:"pmx", _age:18, _born:1990, get nam ...
- 反编译ILSpy 无法显式调用运算符或访问器 错误处理方法 转
反汇编一个dll类库,导出的项目会报出很多bug,其中主要的就是“无法显式调用运算符或访问器”这个错误,看了一下,发现问题是在调用属性的时候,都 变成了方法,例如:pivotPoint.set_X(0 ...
- 14.C#属性访问器、命名空间、pragma指令(七章7.3-7.5)
看到一些零星的知识片,今天就用自己的理解说明下,也是因为太简单了,一下就过的,也是我们日常开发中常用.留下一个脚印,当书不在手上的,也能翻出来看看.说下属性访问器.命名空间和pragma指令. 属性访 ...
- C#属性访问器
属性的访问器包含与获取或设置属性有关的可执行语句.访问器声明可以包含 get 访问器或 set 访问器,或者两者均包含.声明采用下列形式之一:get {}set {} get 访问器get 访问器体与 ...
- JavaScript中的Get和Set访问器
今天要和大家分享的是JavaScript中的Get和Set访问器,和C#中的访问器非常相似. 标准的Get和Set访问器的实现 function Field(val){ this.va ...
随机推荐
- 【先验知识归纳】Flask快速入门
本文参考:快速入门 - Flask 0.10.1 文档 路由 Flask使用route修饰器来关联URL与程序函数: @app.route('/') def hello_world(): return ...
- 爬虫时遇到的' 编码错误gbk ' 的解决方案
# 每次请求一次,然后写文件,这样可以规避多次请求触发反爬虫 r = requests.get('https://www.pearvideo.com/video_1522192') html = r. ...
- LeetCode 第五题 最长的回文字符串 (JAVA)
Longest Palindromic Substring 简介:字符串中最长的回文字符串 回文字符串:中心对称的字符串 ,如 mom,noon 问题详解: 给定一个字符串s,寻找字符串中最长的回文字 ...
- Fatal error: cannot initialize AIO sub-system
在一台服务器中以各数据库的备份文件为数据文件启动多个MySQL实例供SQL Review使用.之前运行一直没有问题(最多的时候有23个MySQL实例同时运行),后来新配置了一台服务器,启动其对应的实例 ...
- 【原创】大叔问题定位分享(30)mesos agent启动失败:Failed to perform recovery: Incompatible agent info detected
mesos agent启动失败,报错如下: Feb 15 22:03:18 server1.bj mesos-slave[1190]: E0215 22:03:18.622994 1192 slave ...
- C++入门篇三
引用:& &放在左边就是引用,放在右边就是取地址 int main() { //引用的类型必须相同,一经引用,不可以在被引用 ; int &b = a;//b引用a之后,两个同 ...
- jQuery选择器 :eq() 不能识别变量参数的问题解决方案
问题: js语法中,引号内变量会直接解释为字符串,因此使用:eq()时参数将被识别为字符串而不是变量指代的内容 如下错误写法: $(".circle span:eq(count-1)&quo ...
- Java的家庭记账本程序(K)
日期:2019.3.10 博客期:043 星期日 呕吼~这里是编程菜鸟小Master,今天加油的把第二个模板套用了,更改了许多的设定,我想这一个程序的网页版也就到这里结束了,下面是一部分的展示图,想要 ...
- apache安装软负载的配置说明
安装Apache:yum -y install httpd 首先要查看apache的安装版本 命令:httpd –v 第一种: 若安装是2.2版本,则把复制 mod_wl_22.so和mod_wl.s ...
- python之re正则简单够用
0. 1.参考 Python正则表达式指南 https://docs.python.org/2/library/re.html https://docs.python.org/2/howto/rege ...