• 古老的pike


    快速略读了一下源码,记了一些东西。

          


      先看看mapping

           mapping其实就是C++中的multimap,但是支持更多。

      array values(mapping)。这个方法可以返回所有mapping中的value,那么values()究竟作了什么呢?源码中是这样定义的:

     1 PMOD_EXPORT struct array *mapping_values(struct mapping *m)
     2 {
     3   INT32 e;
     4   struct keypair *k;
     5   struct array *a;
     6   struct svalue *s;
     7 
     8 #ifdef PIKE_DEBUG
     9   if(m->data->refs <=0)
    10     Pike_fatal("Zero refs in mapping->data
    ");
    11 #endif
    12 
    13   check_mapping_for_destruct(m);
    14 
    15   a=allocate_array(m->data->size);
    16   s=ITEM(a);
    17 
    18   /* no locking required */
    19   NEW_MAPPING_LOOP(m->data) assign_svalue(s++, & k->val);
    20 
    21   a->type_field = m->data->val_types;
    22 
    23 #ifdef PIKE_DEBUG
    24   if(d_flag > 1) check_mapping_type_fields(m);
    25 #endif
    26   
    27   return a;
    28 }

      可以看到,在第15行直接申请了足够存储所有value的内存大小,第19行是一个宏,其循环了所有的可能的value,然后assign_svalue就会将每个k->value给拷贝到返回值array中,并且在27行返回a。

      mapping也可以将字符串作为key的,但是它是如何依靠一个ke=string来迅速地找到对应的value的呢?其实mapping使用的siphash(维基百科原著论文)来哈希字符串的,也就是先将字符串哈希成一个值,再跟其他的key的哈希值来匹配,这样就容易找到了。如果有兴趣可以看pike中的实现:

     1 static size_t low_hashmem_siphash24( const void *s, size_t len, size_t nbytes, size_t key )
     2 {
     3   const unsigned char * in = (const unsigned char*)s;
     4   unsigned long long inlen = MINIMUM(len, nbytes);
     5 
     6   /* "some pseudo randomly generated bytes" 伪随机产生的字节 */
     7   unsigned INT64 v0 = 0x736f6d6570736575ULL;
     8   unsigned INT64 v1 = 0x646f72616e646f6dULL;
     9   unsigned INT64 v2 = 0x6c7967656e657261ULL;
    10   unsigned INT64 v3 = 0x7465646279746573ULL;
    11   unsigned INT64 b;
    12   unsigned INT64 k0 = (unsigned INT64)key;
    13   unsigned INT64 k1 = (unsigned INT64)key;
    14   unsigned INT64 m;
    15   const unsigned char *end = in + inlen - ( inlen % sizeof( unsigned INT64 ) );
    16   const int left = inlen & 7;
    17   b = ( ( unsigned INT64 )inlen ) << 56;
    18   v3 ^= k1;
    19   v2 ^= k0;
    20   v1 ^= k1;
    21   v0 ^= k0;
    22 
    23   for ( ; in != end; in += 8 )
    24   {
    25     m = U8TO64_LE( in );
    26     v3 ^= m;
    27     SIPROUND;
    28     SIPROUND;
    29     v0 ^= m;
    30   }
    31 
    32   switch( left )
    33   {
    34   case 7: b |= ( ( unsigned INT64 )in[ 6] )  << 48;
    35 
    36   case 6: b |= ( ( unsigned INT64 )in[ 5] )  << 40;
    37 
    38   case 5: b |= ( ( unsigned INT64 )in[ 4] )  << 32;
    39 
    40   case 4: b |= ( ( unsigned INT64 )in[ 3] )  << 24;
    41 
    42   case 3: b |= ( ( unsigned INT64 )in[ 2] )  << 16;
    43 
    44   case 2: b |= ( ( unsigned INT64 )in[ 1] )  <<  8;
    45 
    46   case 1: b |= ( ( unsigned INT64 )in[ 0] ); break;
    47 
    48   case 0: break;
    49   }
    50 
    51   v3 ^= b;
    52   SIPROUND;
    53   SIPROUND;
    54   v0 ^= b;
    55   v2 ^= 0xff;
    56   SIPROUND;
    57   SIPROUND;
    58   SIPROUND;
    59   SIPROUND;
    60   b = v0 ^ v1 ^ v2  ^ v3;
    61   return (size_t)b;
    62 }
    low_hashmen_siphash24

      将字符串哈希成一个值后,pike是以一个表来管理字符串的(不知是否为所有),定义在stralloc.c中的原型如下:

    static struct pike_string **base_table=0;

      shared string table。当字符串被哈希成一个值后,就在这个table中寻找(这是简化了的说法,其实在找之前还与htable_mask做了位与)。但是哈希值是不能保证唯一的,所以需要一个二级指针,每个桶中存放着所有的哈希值相同的字符串呢。那如果重叠率太高了呢?pike还定义了一个全局变量:

    static unsigned int need_new_hashkey_depth=0;

      这个变量在每次找不到的时候就可以会有变化,这跟“字符串哈希”所取的前缀长短有关(如果每次都将整个字符串用于哈希的话,也太长了吧?其实不知道是否是整个字符串哈希),可能是会根据这个变量值作哈希的调整。函数原型如下:

    static struct pike_string *internal_findstring(const char *s,
                                                   ptrdiff_t len,
                                                   enum size_shift size_shift,
                                                   size_t hval)        /* hval是用siphash计算出来的结果 */

       

      每对key-value的信息是以一个struct keypair来记录的,其中仅记录了一些信息,以及一个指针(应该是指向真正数据的),初始mapping时需要申请一些内存来存放keypair。

    #define MD_KEYPAIRS(MD, HSIZE)   
    ( (struct keypair *)    
    DO_ALIGN( PTR_TO_INT(MD) + OFFSETOF(mapping_data, hash) + HSIZE * sizeof(struct keypair *), 
    ALIGNOF(struct keypair)) )

      DO_ALIGN的定义:

    #define DO_ALIGN(X,Y) (((size_t)(X)+((Y)-1)) & ~((Y)-1))

      ALIGNIOF是内置函数,用于计算对齐内存所占的大小。

      mapping中提用了几个操作,很是方便,比如 &与操作,|或操作,xor异或操作等等。

      先拿xor开刀,就是将两个mapping中的具有相同key的key-value都给删除掉,留下唯一的key的那种。实现起来也没有多大的优化技巧,就是先将小的mapping先拷贝出来,然后再将大的mapping中的key-value逐个往里面插,如果key已经存在了,就一块删掉,如果不存在,就插进去。仍然需要考虑特殊情况的,比如相同mapping作异或操作等。源码:

     1 static struct mapping *xor_mappings(struct mapping *a, struct mapping *b)
     2 {
     3     struct mapping *res;
     4     struct keypair *k;
     5     struct mapping_data *a_md = a->data;    
     6     struct mapping_data *b_md = b->data;
     7     INT32 e;
     8     ONERROR err;
     9 
    10     /* First some special cases. */
    11     if (!a_md->size) return destructive_copy_mapping(b);    /* 只是有可能会删除真数据 */
    12     if (!b_md->size) return destructive_copy_mapping(a);
    13     if (a_md == b_md) return allocate_mapping(0);            /* 数据一样,返回空mapping */
    14 
    15     /* Copy the largest mapping. 保持a<=b */
    16     if (a_md->size > b_md->size) {
    17         struct mapping *tmp = a;
    18         a = b;
    19         b = tmp;
    20         a_md = b_md;
    21         b_md = b->data;
    22     }
    23     res = destructive_copy_mapping(b);    /* 先拷贝个小的进去 */
    24     SET_ONERROR(err, do_free_mapping, res);
    25 
    26     /* Add elements in a that aren't in b, and remove those that are. */
    27     NEW_MAPPING_LOOP(a_md) {
    28         size_t h = k->hval & (b_md->hashsize - 1);
    29         struct keypair *k2;
    30         for (k2 = b_md->hash[h]; k2; k2 = k2->next) 
    31         {
    32             if ((k2->hval == k->hval) && is_eq(&k2->ind, &k->ind)) /* 先比对哈希值,再比对真实数据 */
    33             {
    34                 break;
    35             }
    36         }
    37         if (!k2)        /* k2=0表示没有找到相同的    */ 
    38         {
    39             mapping_insert(res, &k->ind, &k->val);
    40         }
    41            else
    42            {
    43             map_delete(res, &k2->ind);
    44         }
    45         b_md = b->data;
    46     }
    47     UNSET_ONERROR(err);
    48     return res;
    49 }

      看看逻辑或运算,c=a|b 。这也是很普通的操作而已,实现起来也没有什么优化。思路是,1,若b比较大,那么先拷贝b到c,而对于a,逐个key判断是否存在于c,若在则不操作,否则插入。2,若a比较大,直接将a拷贝到c,再将b逐个插入到c,若key已存在,直接覆盖。

     1 static struct mapping *or_mappings(struct mapping *a, struct mapping *b)
     2 {
     3     struct mapping *res;
     4     struct keypair *k;
     5     struct mapping_data *a_md = a->data;
     6     struct mapping_data *b_md = b->data;
     7     INT32 e;
     8     ONERROR err;
     9 
    10     /* First some special cases. */
    11     if (!a_md->size) return destructive_copy_mapping(b);
    12     if (!b_md->size) return destructive_copy_mapping(a);
    13     if (a_md == b_md) return destructive_copy_mapping(a);
    14 
    15     if (a_md->size <= b_md->size) {        /* a的数据比较少 */
    16         /* Copy the second mapping. */
    17         res = destructive_copy_mapping(b);
    18         SET_ONERROR(err, do_free_mapping, res);
    19 
    20         if (!b_md->hashsize) {
    21             Pike_fatal("Invalid hashsize.
    ");
    22         }
    23 
    24         /* Add elements in a that aren't in b. */
    25         NEW_MAPPING_LOOP(a_md) {
    26             size_t h = k->hval & (b_md->hashsize - 1);
    27             struct keypair *k2;
    28             for (k2 = b_md->hash[h]; k2; k2 = k2->next) {    /* 如果b中已经存在的,a就不能再插进去了 */
    29                 if ((k2->hval == k->hval) && is_eq(&k2->ind, &k->ind)) {
    30                     break;
    31                 }
    32             }
    33             if (!k2) {
    34                 mapping_insert(res, &k->ind, &k->val);
    35                 b_md = b->data;
    36             }
    37         }
    38         UNSET_ONERROR(err);
    39     } else {
    40         /* Copy the first mapping. */
    41         res = destructive_copy_mapping(a);
    42         SET_ONERROR(err, do_free_mapping, res);
    43 
    44         /* Add all elements in b. */
    45         NEW_MAPPING_LOOP(b_md) {    /* 直接将b插进去,如果a中已经存在key,则value会自动覆盖 */
    46             mapping_insert(res, &k->ind, &k->val);
    47         }
    48         UNSET_ONERROR(err);
    49     }
    50     return res;
    51 }

      and操作,即c=a+b,对于那些“key在a和b中皆存在,key-value属于b”的key-value于c中。源码的实现思路是,先将b拷贝到c,再将a中每个元素判断key是否存在于c中,若存在,则不操作,否则,删除。

     1 static struct mapping *and_mappings(struct mapping *a, struct mapping *b)
     2 {
     3     struct mapping *res;
     4     struct keypair *k;
     5     struct mapping_data *a_md = a->data;
     6     struct mapping_data *b_md = b->data;
     7     INT32 e;
     8     ONERROR err;
     9 
    10     /* First some special cases. */
    11     if (!a_md->size || !b_md->size) return allocate_mapping(0);
    12     if (a_md == b_md) return destructive_copy_mapping(a);
    13 
    14     /* Copy the second mapping. */
    15     res = copy_mapping(b);    /* 先拷贝了b */
    16     SET_ONERROR(err, do_free_mapping, res);
    17 
    18     /* Remove elements in res that aren't in a. */
    19     NEW_MAPPING_LOOP(b_md) {        /* 如果已经存在key,则不操作;否则删除已存在的key-value */
    20         size_t h = k->hval & (a_md->hashsize - 1);
    21         struct keypair *k2;
    22         for (k2 = a_md->hash[h]; k2; k2 = k2->next) {
    23             if ((k2->hval == k->hval) && is_eq(&k2->ind, &k->ind)) {
    24                 break;
    25             }
    26         }
    27         if (!k2) {
    28             map_delete(res, &k->ind);
    29         }
    30     }
    31     UNSET_ONERROR(err);
    32     return res;
    33 }
    and_mappings

      substract操作,即c=a-b,对于所有在b中出现的key,如果a中也存在,则删除,否则不操作。

     1 static struct mapping *subtract_mappings(struct mapping *a, struct mapping *b)
     2 {
     3     struct mapping *res;
     4     struct keypair *k;
     5     struct mapping_data *a_md = a->data;
     6     struct mapping_data *b_md = b->data;
     7     INT32 e;
     8     ONERROR err;
     9 
    10     /* First some special cases. */
    11     if (!a_md->size || !b_md->size || !a_md->hashsize || !b_md->hashsize) {
    12         return destructive_copy_mapping(a);
    13     }
    14     if (a_md == b_md) {
    15         return allocate_mapping(0);
    16     }
    17     /* FIXME: The break-even point should probably be researched. */
    18     if (a_md->size < b_md->size) {
    19         /* Add the elements in a that aren't in b. */
    20         res = allocate_mapping(a_md->size);
    21         SET_ONERROR(err, do_free_mapping, res);
    22         NEW_MAPPING_LOOP(a_md) {
    23             size_t h = k->hval & (b_md->hashsize - 1);
    24             struct keypair *k2;
    25             for (k2 = b_md->hash[h]; k2; k2 = k2->next) {
    26                 if ((k2->hval == k->hval) && is_eq(&k2->ind, &k->ind)) {
    27                     break;
    28                 }
    29             }
    30             if (!k2) {
    31                 mapping_insert(res, &k->ind, &k->val);
    32             }
    33         }
    34     } else {
    35         /* Remove the elements in a that are in b. */
    36         res = destructive_copy_mapping(a);
    37         SET_ONERROR(err, do_free_mapping, res);
    38         NEW_MAPPING_LOOP(b_md) {
    39             map_delete(res, &k->ind);
    40         }
    41     }
    42     UNSET_ONERROR(err);
    43     return res;
    44 }
    subtract_mappings

      replace(mapping,old,new)函数,将mapping中所有value=old的替换成value=new。由于pike中使用了索引的概念,替换之后可能会有value需要清除了,如果它的引用次数=1的话。

     1 PMOD_EXPORT void mapping_replace(struct mapping *m, struct svalue *from, struct svalue *to)
     2 {
     3     INT32 e;
     4     struct keypair *k;
     5     struct mapping_data *md;
     6 
     7 #ifdef PIKE_DEBUG
     8     if (m->data->refs <= 0)
     9         Pike_fatal("Zero refs in mapping->data
    ");
    10 #endif
    11 
    12     md = m->data;
    13     if (md->size) {
    14         add_ref(md);    /* 增加md的引用一次 */
    15         NEW_MAPPING_LOOP(md) {    /* 扫描所有键值对 */
    16             if (is_eq(& k->val, from)) { /* 如果与from相同的话就申请内存并替换成to */
    17                 PREPARE_FOR_DATA_CHANGE();    /* 为k申请新内存 */
    18                 assign_svalue(& k->val, to);
    19                 md->val_types |= 1 << TYPEOF(*to);
    20             }
    21         }
    22         free_mapping_data(md);    /* 释放掉引用次数=1的value,不一定是真的释放 */
    23     }
    24 
    25 #ifdef PIKE_DEBUG
    26     if (d_flag > 1) check_mapping_type_fields(m);
    27 #endif
    28 }

      mkmapping(array ind,array val)函数,创建一个mapping对象,并且以ind作为索引,val作为值来组成key-value初始化该对象。这里的实现是以ind的元素个数作为mapping的元素个数的,所以如果val的元素个数比ind要大的话,是会自动忽略后面的多余部分。还有一个点,就是初始化时,hashsize(也称之为桶的数量)应该是多少的问题,这里依靠MAP_SLOTS这个宏来计算的,这样的计算方式的优劣性未明~

     1 #define MAP_SLOTS(X) ((X)?((X)+((X)>>4)+8):0)
     2 PMOD_EXPORT struct mapping *mkmapping(struct array *ind, struct array *val)
     3 {
     4     struct mapping *m;
     5     struct svalue *i, *v;
     6     INT32 e;
     7 
     8 #ifdef PIKE_DEBUG
     9     if (ind->size != val->size)
    10         Pike_fatal("mkmapping on different sized arrays.
    ");
    11 #endif
    12 
    13     m = allocate_mapping(MAP_SLOTS(ind->size)); /* 申请一个mapping,这里关系到hashsize的大小  */
    14     i = ITEM(ind);
    15     v = ITEM(val);
    16     for (e = 0; e < ind->size; e++) /* 逐个插入*/
    17         low_mapping_insert(m, i++, v++, 2);
    18 
    19     return m;
    20 }
  • 相关阅读:
    DL_WITH_PY系统学习(第2章)
    JavaScript学习require的用法
    Vue的prop属性
    vue store action与Mutation与getters的具体用法
    OpenEBS 实现 Local PV 动态持久化存储
    vue modules 使用
    【转载】 tSNE是什么? —— 使用指南
    【转载】 Do's and Don'ts of using tSNE to Understand Vision Models —— tSNE 作者写的使用指南(PPT版本)
    【转载】 机器学习数据可视化 (tSNE 使用指南)—— Why You Are Using tSNE Wrong
    【转载】 机器学习的高维数据可视化技术(tSNE 介绍) 外文博客原文:How tSNE works and Dimensionality Reduction
  • 原文地址:https://www.cnblogs.com/xcw0754/p/5085806.html
Copyright © 2020-2023  润新知