`
wooce
  • 浏览: 180892 次
  • 性别: Icon_minigender_1
  • 来自: 广州
社区版块
存档分类
最新评论

技术聊天记录: 关于TAttributeSet减少lookup的改进方法的讨论

    博客分类:
  • C++
阅读更多
wooce:
我仔细思考了一下:
1)  如果不能把Server端和Client端的程序放在一起编译,  或者是由程序员定义一个include某AttributeSet中
会出现的所有键值的enum放在某头文件里,  在server端和client端所有用到某attributeset的地方都引用这个头文件,  总之必
须让所有用到attributeset的地方都知道这个attributeset里所有可能的键值, 要在编译时就完全决定
Get("abc")是Get  AttributeSet中的第几项, 是难以做到的. 
不论编译器(或发明/下载的特殊的程序预处理器)如何地聪明,  若单独编译client端的程序,  因为
client端不一定用到server端put入的所有key值,  所以server端发送过来的attributeset里存在有
编译client程序时未知的key,   编译client程序时要把这些未知的字符串类型的key映射到AttributeSet
中的某个位置上,  不论我们去找多少花哨的程序设计技巧, 实质都要归结为把一个字符串集合中的某个字
符串唯一映射到一个整数上(不同的字符串必须映射到不同的整数) ,  显然并不存在这样的映射, 那些如
HASH方法总难免把几个字符串映射到同一个整数上. 
2)  如果不想采用在server端和client端所有用到某attributeset的地方都引用包含其中的所有键值的头文件的方法,
又想提高lookup的效率而同时保持原来的弹性,  显然必须修改我们的通讯协议,  client端在接收某个AttributeSet的时侯, 如果
此前从未接收过此attributeset,  则必须和server端进行协商,  建立一张attributeset的各个key在client端的
Get()所用的整数index值和server端以后发送过来的此attributeset的相对应的存储位置的对照表, 这样client端在Get("abc")
时实际上是执行取Values[ convert[i] ] 的操作,  不再需要lookup字符串了,  提高了效率.
在一些细节上:  
    现在的使用attributeset的程序一般是:
OnRequest()
{
....
    TProtocol *pProtocol;
    XNew(pProtocol, TProtocol(....));
    pProtocol->SvrReceive(comm, asRequest);
    TAttributeSet asRequest, asResponse;
    int cmd = asRequest.Get("Cmd").AsInt();
    asRequest.Get("abc").AsString();
    asRequest.Get("efg").As....();
....
}
    可以看出要保持这种写法的弹性, 又能在第一次GET一个attributeset的时侯插入和server协商建立对照表的代码有点
麻烦. 
    我觉得比较好的改法是变上面的pProtocol->SvrReceive(comm, asRequest);为asRequest.SetCommProtocol(comm,pProtocol);
里面并不执行通讯操作,  在其后执行的第一条asRequest.Get("...")代码中, 根据某变量知道还未建立对照表,  然后调用
类似pProtocol->SvrReceive(comm, asRequest);原来的代码get到server端发来的attributeset的全部内容和全部key,  由于在编译
第一条Get语句的时侯并不知道后面的其他Get语句的键的字符串,  所以此时对照表还不能完全建立的,  只能先
        static int i=0;
        存储位置j = find(attributeset from server, "Cmd") ;
        对照表convert.Put(i, j);
        return Values[ convert[i++] ];
然后在第二条
asRequest.Get("...")上,  执行上面同样的代码(不需和Server协商了) ,
最后当get完所有key的时侯, 就知道对照表已建立好.  但如果client端不需要get其中所有的key的时侯,  那就需要在所有Get()方法后
加个asRequest.End()的方法来显式标志对照表已建立了.

XXX:


其实昨天我说那个想法的意思是,attributeset可以考虑的优化方面的东西很多,那样只是其中的一个,而且不一定可行的,我是想让你具体想想是否还有其他想法,或者那个想法是否可行:
1.attributeset目前key在程序中都是通过#define做的,实际上,
    1.1.可以考虑直接提供int类型的key(特别是内部使用的场合)
    1.2.提供const char * 的接口方式
2.由于目前程序还没有被广泛使用,所以可以考虑修改通信协议来优化
3.实际上目前attributeset的存贮结构决定了:
    3.1.即使有key lookup的优化,实际上每次还需要对buffer进行parse,知道每一块的物理位置(就时说即使知道"abc"就是1也需要知道1对应在物理buffer的哪里
    3.2.如果需要parse实际上在parse的时候已经可以做完key的lookup了,而字符串的比较在parse过程中,实际上不会比int比较慢太多的,所以简单这样的优化意义不大
    3.3.基于3.1.,3.2.实际上要做key lookup的优化,最终必须改协议增加索引表,这样实际上就是tim说的问题了,而反过来实际上通过1.1.的方式就可以优化了部分的过程,所以不一定需要做那么复杂的事情.
4.由于"内存分配"操作相对而言是比较耗费资源的(相对strcmp之类的东西),所以减少内存分配的次数实际上是优化的一个原则,这个已经体现在了TRefAttributeSet里面了,但是我觉的这样反而不好,多了一个东西别人反而不知道应该用哪一个,所以需要考虑能否把两个东西合并成为一个优化了的TAttributeSet
5.下面是我的一些想法:
    5.1.确定attributeset的使用的限制:
        至少有:每一个数据项的长度不大,如果数据项可能比较大的,通过其他方式包装,Tprotocol可以兼容
    5.2.AttributeSet内部同时存在两种态性(两种数据):一种是打包好的数据(a),一种是分散的hash或者其他的方便管理的数据(b):
        所有Get的操作,如果b里面有,就返回b里面的,否则就在a中直接查找返回,同时根据某些规则判断(比如attribute有多少)是否加入b中
        如果Put的操作,直接增加在hash列表中,
        当需要输出的时候,merge两部分数据:如只有b,就通过b生成a,如果只有a,就直接输出,否则就以b优先,和现有的a比较,生成新的a然后输出
        这样做的目的是,实际上现在很多数据get回来之后只是pass给别人,或者自己只用一两项,所以没有必要全部parse出来,另外如果只有10个以下的attribut实际上直接查找比建立hash或者list还要快
    5.3.为了优化查找,可以考虑生成的数据包里面的数据已经通过排序或者hash的规则放置:
        因为毕竟打包的操作肯定是最少的,所以这个地方的处理复杂也是可以理解的:
        考虑按hash放置,提供一个hash的index在整个attributeset前面,然后可以直接根据hash找到对应的列表然后顺序查找(打包的数据分成桶,由于数据是每次打包完成,所以不需要太复杂的管理,否则就变成DB了:))
        考虑按key排序,同时每一个Attribute附上二分查找需要的信息(其实就是一些offset,保证可以找到key值为中间的那个attribute)
       
    由于我对attributeset的了解有限,所以希望你对我上面说的东西提提意见,最好是提出更新的想法

Wooce:

1.  在打包数据里 lookup的时侯我们执行的是strstr,  并不是strcmp,  ,      我测试了一下,     已经有char p[1024]; 的情况,  p1 = new char[1024]; memcpy(p1,p)的开销不见得比strstr( p,  "...")的开销高,   特别是所lookup的字符串是在char p[1024]; 的后半段或者根本不存在的情况,    所以lookup执行strstr的开销也不能忽视,   不过幸好打包数据的格式是keyname1  ->    value1.size   ->    value1.content   -> ....  ,  所以我们可以凭借value1.size 移动跳过一大段数据,  只会比较keyname,   而不是strstr( 打包数据,key)这么做,    所以你说的节约parse的开销还是有效的.

2.   同时存在两种态性的数据的想法的确很有创造性,    //admire. 
     1)   这想法应该确能起到优化性能的作用.
     2)  存在以下复杂性:
         a.   嵌套情况,  即一个attributeset里面有另一个attributeset作为其attribute的情况. 
         b.   方便管理的结构b如果象TAttributeSet那么以HASH存储,  可能往b里存数据后,  需要处理一个不完整的树的情况.  虽然很少出现, 但既然没有做限制,
       我们就必须对其做出考虑.

3.    现在的协议的打包数据是按      keyname1  ->    value1.size   ->    value1.content   ->    keyname2   -> value2.size   ->    value2.content ....  的形式依次存储的,  本来keyname等本身就是作为索引的,   现在这样的打包方式, 使我们不知道这些keyname本身的位置,   所以你又提出来在整个打包数据前面再加一个这些keyname的hash索引的方法.   
       其实我想,    可以干脆把打包数据分成纯粹的索引和纯粹的attribute value数据两部分. 
       即:      打包数据  =  [索引][纯数据]
                 索引  =  keyname1  offset1  keyname2  offset2  .....     
                 纯数据 =  value1.content   value2.content   value3.content   ......
      索引的keyname1 offset1 .....   的前面是仍然可以再加上你所说的HASH索引的.
      这样带来的好处:  
           1)  接收attributeset方可以快速获得一个长度不会太大的包含了所有keyname和其offset的索引数据,  不会出现在一个很大的打包数据里移动的情况(在put了落入同一HASH桶的多个keyname进去attributeset的情况会如此).    这样你所说的5.1的使用限制应该可以避免. 
           2)  接收attributeset方要修改attributeset,  再put入新的attribute到其中的情况,   很显然,   在考虑按key排序的时侯,   我们在上面所说的索引数据中排序, 并且把新put入的值简单直接加到纯数据段的后面,    要比把一个key和value混合间杂的数据重新组织排序容易得多,  也会有较好的性能.
           3)  发送attributeset方以put多次创建一个 attributeset的时侯,   如果以原来的格式,  我们可以知道必须把每次put入的数据先clone进"易管理的结构b",   最后打包的时侯又必须依次从"易管理的结构"里取出来clone到生成的打包数据里面,     而按纯索引和纯数据分开的格式,  我们每次put入的数据是可以一次性直接clone进最后所生成的打包数据的.    所以又节省了一笔开销.  
           4)   在嵌套的情况,    我们仍然可以把索引定义成比较线性化的规整的形式.   使编程简单.
2
0
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics