索引压缩算法New PForDelta的实现

我的博客:http://blog.striveforfreedom.net

1 简介

索引压缩算法New PForDelta基于算法PForDelta,它消除了算法PForDelta对异常数位置距离的限制。算法PForDelta请看论文[Super-Scalar RAM-CPU Cache Compression],New PForDelta算法介绍请 点击这里 。本文其实只给出了pack/unpack函数,选择较优位宽和异常数处理请 点击这里 。下面给出了pack/unpack函数的三个版本的实现。

2 实现

 

2.1 简洁版

 1:  #include <stdint.h>
 2:  
 3:  static const uint32_t BIT_MASK[] =
 4:  {
 5:      0U,
 6:      1U,
 7:      ( 1U << 2 )  - 1,
 8:      ( 1U << 3 )  - 1,
 9:      ( 1U << 4 )  - 1,
10:      ( 1U << 5 )  - 1,
11:      ( 1U << 6 )  - 1,
12:      ( 1U << 7 )  - 1,
13:      ( 1U << 8 )  - 1,
14:      ( 1U << 9 )  - 1,
15:      ( 1U << 10 ) - 1,
16:      ( 1U << 11 ) - 1,
17:      ( 1U << 12 ) - 1,
18:      ( 1U << 13 ) - 1,
19:      ( 1U << 14 ) - 1,
20:      ( 1U << 15 ) - 1,
21:      ( 1U << 16 ) - 1,
22:      ( 1U << 17 ) - 1,
23:      ( 1U << 18 ) - 1,
24:      ( 1U << 19 ) - 1,
25:      ( 1U << 20 ) - 1,
26:      ( 1U << 21 ) - 1,
27:      ( 1U << 22 ) - 1,
28:      ( 1U << 23 ) - 1,
29:      ( 1U << 24 ) - 1,
30:      ( 1U << 25 ) - 1,
31:      ( 1U << 26 ) - 1,
32:      ( 1U << 27 ) - 1,
33:      ( 1U << 28 ) - 1,
34:      ( 1U << 29 ) - 1,
35:      ( 1U << 30 ) - 1,
36:      ( 1U << 31)  - 1,
37:      0xFFFFFFFFU,
38:  };    
39:  
40:  //data指向count个bit_width位数,packed指向的内存不得少于count * bit_width / 8字节
41:  void pack_general(const uint32_t* data, uint32_t count, uint32_t bit_width, uint32_t* packed)
42:  {
43:      uint32_t pos = 0U;
44:      uint32_t start_bit = 0U;
45:  
46:      for(uint32_t i = 0; i < count; ++i){
47:          packed[pos] = (packed[pos] & BIT_MASK[start_bit]) | (data[i] << start_bit);
48:          const uint32_t free_bit = 32U - start_bit;
49:          start_bit += bit_width;
50:          start_bit &= 0x1F;
51:          if(free_bit < bit_width){
52:              packed[++pos] = data[i] >> free_bit;
53:          }else if(free_bit == bit_width){
54:              ++pos;
55:          }
56:      }
57:  }
58:  
59:  //packed指向count个bit_width位数调用函数pack_general后的输出,data指向的内存不得少于count * sizeof(uint32_t)字节
60:  void unpack_general(const uint32_t* packed, uint32_t count, uint32_t bit_width, uint32_t* data)
61:  {
62:      uint32_t pos = 0U;
63:      uint32_t start_bit = 0U;
64:  
65:      for(uint32_t i = 0; i < count; ++i){
66:          data[i] = (packed[pos] >> start_bit) & BIT_MASK[bit_width];
67:          const uint32_t free_bit = 32U - start_bit;
68:          start_bit += bit_width;
69:          start_bit &= 0x1F;
70:          if(free_bit < bit_width){
71:              data[i] |= (packed[++pos] & BIT_MASK[bit_width - free_bit]) << free_bit;
72:          }else if(free_bit == bit_width){
73:              ++pos;
74:          }
75:      }
76:  }

pack_general/unpack_general的优点是代码很简洁,一个函数便处理了不同位宽数的pack或unpack。然而缺点也是显而易见的,pack_general/unpack_general性能无疑是很差的,因为每循环一次,只pack/unpack一个数,并且循环体内有分支语句,如果分支预测失败会带来很大的性能开销。因此,可以通过这两方面来提高性能,一是降低循环开销,这可以通过循环展开来达到;二是消除循环体内的分支语句,这可以通过为每种位宽的数写一个特定的函数来达到。下面给出了按照这种思路写出的位宽为5的pack/unpack函数。

2.2 循环展开版

  1:  #include <stdint.h>
  2:  
  3:  //data指向128个5位数,packed所指内存不能小于128 * 5 / 8字节
  4:  void pack_5(const uint32_t* data, uint32_t* packed)
  5:  {
  6:      packed[0] = (data[6] << 30) | (data[5] << 25) | (data[4] << 20) | (data[3] << 15) | (data[2] << 10) | (data[1] << 5) | data[0];
  7:      packed[1] = (data[12] << 28) | (data[11] << 23) | (data[10] << 18) | (data[9] << 13) | (data[8] << 8) | (data[7] << 3) | (data[6] >> 2);
  8:      packed[2] = (data[19] << 31) | (data[18] << 26) | (data[17] << 21) | (data[16] << 16) | (data[15] << 11) | (data[14] << 6) | (data[13] << 1) | (data[12] >> 4);
  9:      packed[3] = (data[25] << 29) | (data[24] << 24) | (data[23] << 19) | (data[22] << 14) | (data[21] << 9) | (data[20] << 4) | (data[19] >> 1);
 10:      packed[4] = (data[31] << 27) | (data[30] << 22) | (data[29] << 17) | (data[28] << 12) | (data[27] << 7) | (data[26] << 2) | (data[25] >> 3);
 11:      //以上为第1组,pack 32个数
 12:  
 13:      packed[5] = (data[38] << 30) | (data[37] << 25) | (data[36] << 20) | (data[35] << 15) | (data[34] << 10) | (data[33] << 5) | data[32];
 14:      packed[6] = (data[44] << 28) | (data[43] << 23) | (data[42] << 18) | (data[41] << 13) | (data[40] << 8) | (data[39] << 3) | (data[38] >> 2);
 15:      packed[7] = (data[51] << 31) | (data[50] << 26) | (data[49] << 21) | (data[48] << 16) | (data[47] << 11) | (data[46] << 6) | (data[45] << 1) | (data[44] >> 4);
 16:      packed[8] = (data[57] << 29) | (data[56] << 24) | (data[55] << 19) | (data[54] << 14) | (data[53] << 9) | (data[52] << 4) | (data[51] >> 1);
 17:      packed[9] = (data[63] << 27) | (data[62] << 22) | (data[61] << 17) | (data[60] << 12) | (data[59] << 7) | (data[58] << 2) | (data[57] >> 3);
 18:      //以上为第2组,pack 32个数
 19:  
 20:      packed[10] = (data[70] << 30) | (data[69] << 25) | (data[68] << 20) | (data[67] << 15) | (data[66] << 10) | (data[65] << 5) | data[64];
 21:      packed[11] = (data[76] << 28) | (data[75] << 23) | (data[74] << 18) | (data[73] << 13) | (data[72] << 8) | (data[71] << 3) | (data[70] >> 2);
 22:      packed[12] = (data[83] << 31) | (data[82] << 26) | (data[81] << 21) | (data[80] << 16) | (data[79] << 11) | (data[78] << 6) | (data[77] << 1) | (data[76] >> 4);
 23:      packed[13] = (data[89] << 29) | (data[88] << 24) | (data[87] << 19) | (data[86] << 14) | (data[85] << 9) | (data[84] << 4) | (data[83] >> 1);
 24:      packed[14] = (data[95] << 27) | (data[94] << 22) | (data[93] << 17) | (data[92] << 12) | (data[91] << 7) | (data[90] << 2) | (data[89] >> 3);
 25:      //以上为第3组,pack 32个数
 26:  
 27:      packed[15] = (data[102] << 30) | (data[101] << 25) | (data[100] << 20) | (data[99] << 15) | (data[98] << 10) | (data[97] << 5) | data[96];
 28:      packed[16] = (data[108] << 28) | (data[107] << 23) | (data[106] << 18) | (data[105] << 13) | (data[104] << 8) | (data[103] << 3) | (data[102] >> 2);
 29:      packed[17] = (data[115] << 31) | (data[114] << 26) | (data[113] << 21) | (data[112] << 16) | (data[111] << 11) | (data[110] << 6) | (data[109] << 1) | (data[108] >> 4);
 30:      packed[18] = (data[121] << 29) | (data[120] << 24) | (data[119] << 19) | (data[118] << 14) | (data[117] << 9) | (data[116] << 4) | (data[115] >> 1);
 31:      packed[19] = (data[127] << 27) | (data[126] << 22) | (data[125] << 17) | (data[124] << 12) | (data[123] << 7) | (data[122] << 2) | (data[121] >> 3);
 32:      //以上为第4组,pack 32个数
 33:  }
 34:  
 35:  //packed指向128个5位数调用函数pack_5后的输出,data指向的内存不得少于128 * sizeof(uint32_t)字节
 36:  void unpack_5(const uint32_t* packed, uint32_t* data)
 37:  {
 38:      data[0] = packed[0] & 0x1F;
 39:      data[1] = (packed[0] >> 5) & 0x1F;
 40:      data[2] = (packed[0] >> 10) & 0x1F;
 41:      data[3] = (packed[0] >> 15) & 0x1F;
 42:      data[4] = (packed[0] >> 20) & 0x1F;
 43:      data[5] = (packed[0] >> 25) & 0x1F;
 44:      data[6] = (packed[0] >> 30) | ((packed[1] & 0x7) << 2);
 45:  
 46:      data[7] = (packed[1] >> 3) & 0x1F;
 47:      data[8] = (packed[1] >> 8) & 0x1F;
 48:      data[9] = (packed[1] >> 13) & 0x1F;
 49:      data[10] = (packed[1] >> 18) & 0x1F;
 50:      data[11] = (packed[1] >> 23) & 0x1F;
 51:      data[12] = (packed[1] >> 28) | ((packed[2] & 0x1) << 4);
 52:  
 53:      data[13] = (packed[2] >> 1) & 0x1F;
 54:      data[14] = (packed[2] >> 6) & 0x1F;
 55:      data[15] = (packed[2] >> 11) & 0x1F;
 56:      data[16] = (packed[2] >> 16) & 0x1F;
 57:      data[17] = (packed[2] >> 21) & 0x1F;
 58:      data[18] = (packed[2] >> 26) & 0x1F;
 59:      data[19] = (packed[2] >> 31) | ((packed[3] & 0xF) << 1);
 60:  
 61:      data[20] = (packed[3] >> 4) & 0x1F;
 62:      data[21] = (packed[3] >> 9) & 0x1F;
 63:      data[22] = (packed[3] >> 14) & 0x1F;
 64:      data[23] = (packed[3] >> 19) & 0x1F;
 65:      data[24] = (packed[3] >> 24) & 0x1F;
 66:      data[25] = (packed[3] >> 29) | ((packed[4] & 0x3) << 3);
 67:  
 68:      data[26] = (packed[4] >> 2) & 0x1F;
 69:      data[27] = (packed[4] >> 7) & 0x1F;
 70:      data[28] = (packed[4] >> 12) & 0x1F;
 71:      data[29] = (packed[4] >> 17) & 0x1F;
 72:      data[30] = (packed[4] >> 22) & 0x1F;
 73:      data[31] = (packed[4] >> 27) & 0x1F;
 74:      //以上为第1组,unpack 32个数
 75:  
 76:      data[32] = packed[5] & 0x1F;
 77:      data[33] = (packed[5] >> 5) & 0x1F;
 78:      data[34] = (packed[5] >> 10) & 0x1F;
 79:      data[35] = (packed[5] >> 15) & 0x1F;
 80:      data[36] = (packed[5] >> 20) & 0x1F;
 81:      data[37] = (packed[5] >> 25) & 0x1F;
 82:      data[38] = (packed[5] >> 30) | ((packed[6] & 0x7) << 2);
 83:  
 84:      data[39] = (packed[6] >> 3) & 0x1F;
 85:      data[40] = (packed[6] >> 8) & 0x1F;
 86:      data[41] = (packed[6] >> 13) & 0x1F;
 87:      data[42] = (packed[6] >> 18) & 0x1F;
 88:      data[43] = (packed[6] >> 23) & 0x1F;
 89:      data[44] = (packed[6] >> 28) | ((packed[7] & 0x1) << 4);
 90:  
 91:      data[45] = (packed[7] >> 1) & 0x1F;
 92:      data[46] = (packed[7] >> 6) & 0x1F;
 93:      data[47] = (packed[7] >> 11) & 0x1F;
 94:      data[48] = (packed[7] >> 16) & 0x1F;
 95:      data[49] = (packed[7] >> 21) & 0x1F;
 96:      data[50] = (packed[7] >> 26) & 0x1F;
 97:      data[51] = (packed[7] >> 31) | ((packed[8] & 0xF) << 1);
 98:  
 99:      data[52] = (packed[8] >> 4) & 0x1F;
100:      data[53] = (packed[8] >> 9) & 0x1F;
101:      data[54] = (packed[8] >> 14) & 0x1F;
102:      data[55] = (packed[8] >> 19) & 0x1F;
103:      data[56] = (packed[8] >> 24) & 0x1F;
104:      data[57] = (packed[8] >> 29) | ((packed[9] & 0x3) << 3);
105:  
106:      data[58] = (packed[9] >> 2) & 0x1F;
107:      data[59] = (packed[9] >> 7) & 0x1F;
108:      data[60] = (packed[9] >> 12) & 0x1F;
109:      data[61] = (packed[9] >> 17) & 0x1F;
110:      data[62] = (packed[9] >> 22) & 0x1F;
111:      data[63] = (packed[9] >> 27) & 0x1F;
112:      //以上为第2组,unpack 32个数
113:  
114:      data[64] = packed[10] & 0x1F;
115:      data[65] = (packed[10] >> 5) & 0x1F;
116:      data[66] = (packed[10] >> 10) & 0x1F;
117:      data[67] = (packed[10] >> 15) & 0x1F;
118:      data[68] = (packed[10] >> 20) & 0x1F;
119:      data[69] = (packed[10] >> 25) & 0x1F;
120:      data[70] = (packed[10] >> 30) | ((packed[11] & 0x7) << 2);
121:  
122:      data[71] = (packed[11] >> 3) & 0x1F;
123:      data[72] = (packed[11] >> 8) & 0x1F;
124:      data[73] = (packed[11] >> 13) & 0x1F;
125:      data[74] = (packed[11] >> 18) & 0x1F;
126:      data[75] = (packed[11] >> 23) & 0x1F;
127:      data[76] = (packed[11] >> 28) | ((packed[12] & 0x1) << 4);
128:  
129:      data[77] = (packed[12] >> 1) & 0x1F;
130:      data[78] = (packed[12] >> 6) & 0x1F;
131:      data[79] = (packed[12] >> 11) & 0x1F;
132:      data[80] = (packed[12] >> 16) & 0x1F;
133:      data[81] = (packed[12] >> 21) & 0x1F;
134:      data[82] = (packed[12] >> 26) & 0x1F;
135:      data[83] = (packed[12] >> 31) | ((packed[13] & 0xF) << 1);
136:  
137:      data[84] = (packed[13] >> 4) & 0x1F;
138:      data[85] = (packed[13] >> 9) & 0x1F;
139:      data[86] = (packed[13] >> 14) & 0x1F;
140:      data[87] = (packed[13] >> 19) & 0x1F;
141:      data[88] = (packed[13] >> 24) & 0x1F;
142:      data[89] = (packed[13] >> 29) | ((packed[14] & 0x3) << 3);
143:  
144:      data[90] = (packed[14] >> 2) & 0x1F;
145:      data[91] = (packed[14] >> 7) & 0x1F;
146:      data[92] = (packed[14] >> 12) & 0x1F;
147:      data[93] = (packed[14] >> 17) & 0x1F;
148:      data[94] = (packed[14] >> 22) & 0x1F;
149:      data[95] = (packed[14] >> 27) & 0x1F;
150:      //以上为第3组,unpack 32个数
151:  
152:      data[96] = packed[15] & 0x1F;
153:      data[97] = (packed[15] >> 5) & 0x1F;
154:      data[98] = (packed[15] >> 10) & 0x1F;
155:      data[99] = (packed[15] >> 15) & 0x1F;
156:      data[100] = (packed[15] >> 20) & 0x1F;
157:      data[101] = (packed[15] >> 25) & 0x1F;
158:      data[102] = (packed[15] >> 30) | ((packed[16] & 0x7) << 2);
159:  
160:      data[103] = (packed[16] >> 3) & 0x1F;
161:      data[104] = (packed[16] >> 8) & 0x1F;
162:      data[105] = (packed[16] >> 13) & 0x1F;
163:      data[106] = (packed[16] >> 18) & 0x1F;
164:      data[107] = (packed[16] >> 23) & 0x1F;
165:      data[108] = (packed[16] >> 28) | ((packed[17] & 0x1) << 4);
166:  
167:      data[109] = (packed[17] >> 1) & 0x1F;
168:      data[110] = (packed[17] >> 6) & 0x1F;
169:      data[111] = (packed[17] >> 11) & 0x1F;
170:      data[112] = (packed[17] >> 16) & 0x1F;
171:      data[113] = (packed[17] >> 21) & 0x1F;
172:      data[114] = (packed[17] >> 26) & 0x1F;
173:      data[115] = (packed[17] >> 31) | ((packed[18] & 0xF) << 1);
174:  
175:      data[116] = (packed[18] >> 4) & 0x1F;
176:      data[117] = (packed[18] >> 9) & 0x1F;
177:      data[118] = (packed[18] >> 14) & 0x1F;
178:      data[119] = (packed[18] >> 19) & 0x1F;
179:      data[120] = (packed[18] >> 24) & 0x1F;
180:      data[121] = (packed[18] >> 29) | ((packed[19] & 0x3) << 3);
181:  
182:      data[122] = (packed[19] >> 2) & 0x1F;
183:      data[123] = (packed[19] >> 7) & 0x1F;
184:      data[124] = (packed[19] >> 12) & 0x1F;
185:      data[125] = (packed[19] >> 17) & 0x1F;
186:      data[126] = (packed[19] >> 22) & 0x1F;
187:      data[127] = (packed[19] >> 27) & 0x1F;
188:      //以上为第4组,unpack 32个数
189:  }

函数pack_5非常冗长,手工写很容易出错,仔细观察一下就会发现这128个数可以分成4组,每组pack 32个数,写完第1组之后,只要对第1组的packed数组下标分别加5/10/15以及对data数组下标分别加32/64/96,就可以得到第2/3/4组,但这样对数组下标手工进行加操作还是很容易出错,emacs里有个函数query-replace-regexp可以帮我们自动完成这项操作:举个例子,我们可以复制一份第1组的代码并选定,运行这个函数,参数依次输入data\[\([0-9]+\)\]和data[\,(+ 32 \#1)]就可以自动得到第2组代码了,其中,紧接着\,的是一个Lisp表达式,这个表达式的求值结果会作为替换字符串的一部分,而\#1则表示第一匹配的括号里的内容转换成的数字。函数unpack_5同样可以利用emacs函数query-replace-regexp来对data下标进行加32/64/96和对packed下标进行加5/10/15。

2.3 SSE版

普通指令一次只能操作一份数据,而SSE指令一次能操作多份数据,SSE的这种这种特性无疑可以利用来提高程序性能,代码如下:

  1:  #include <stdint.h>
  2:  #include <emmintrin.h>    
  3:  
  4:  //data指向128个5位数,packed所指内存不能小于128 * 5 / 8字节
  5:  //函数pack_5_sse并没有使用sse指令,命名为pack_5_sse是为了和函数unpack_5_sse对应
  6:  void pack_5_sse(const uint32_t* data, uint32_t* packed)
  7:  {
  8:      packed[0] = (data[120] << 30) | (data[20] << 25) | (data[16] << 20) | (data[12] << 15) | (data[8] << 10) | (data[4] << 5) | data[0];
  9:      packed[1] = (data[121] << 30) | (data[21] << 25) | (data[17] << 20) | (data[13] << 15) | (data[9] << 10) | (data[5] << 5) | data[1];
 10:      packed[2] = (data[122] << 30) | (data[22] << 25) | (data[18] << 20) | (data[14] << 15) | (data[10] << 10) | (data[6] << 5) | data[2];
 11:      packed[3] = (data[123] << 30) | (data[23] << 25) | (data[19] << 20) | (data[15] << 15) | (data[11] << 10) | (data[7] << 5) | data[3];
 12:  
 13:      packed[4] = ((data[120] >> 2) << 30) | (data[44] << 25) | (data[40] << 20) | (data[36] << 15) | (data[32] << 10) | (data[28] << 5) | data[24];
 14:      packed[5] = ((data[121] >> 2) << 30) | (data[45] << 25) | (data[41] << 20) | (data[37] << 15) | (data[33] << 10) | (data[29] << 5) | data[25];
 15:      packed[6] = ((data[122] >> 2) << 30) | (data[46] << 25) | (data[42] << 20) | (data[38] << 15) | (data[34] << 10) | (data[30] << 5) | data[26];
 16:      packed[7] = ((data[123] >> 2) << 30) | (data[47] << 25) | (data[43] << 20) | (data[39] << 15) | (data[35] << 10) | (data[31] << 5) | data[27];
 17:  
 18:      packed[8] = ((data[124] & 0x1) << 31) | ((data[120] >> 4) << 30) | (data[68] << 25) | (data[64] << 20) | (data[60] << 15) | (data[56] << 10) | (data[52] << 5) | data[48];
 19:      packed[9] = ((data[125] & 0x1) << 31) | ((data[121] >> 4) << 30) | (data[69] << 25) | (data[65] << 20) | (data[61] << 15) | (data[57] << 10) | (data[53] << 5) | data[49];
 20:      packed[10] = ((data[126] & 0x1) << 31) | ((data[122] >> 4) << 30) | (data[70] << 25) | (data[66] << 20) | (data[62] << 15) | (data[58] << 10) | (data[54] << 5) | data[50];
 21:      packed[11] = ((data[127] & 0x1) << 31) | ((data[123] >> 4) << 30) | (data[71] << 25) | (data[67] << 20) | (data[63] << 15) | (data[59] << 10) | (data[55] << 5) | data[51];
 22:  
 23:      packed[12] = (((data[124] >> 1) & 0x3) << 30) | (data[92] << 25) | (data[88] << 20) | (data[84] << 15) | (data[80] << 10) | (data[76] << 5) | data[72];
 24:      packed[13] = (((data[125] >> 1) & 0x3) << 30) | (data[93] << 25) | (data[89] << 20) | (data[85] << 15) | (data[81] << 10) | (data[77] << 5) | data[73];
 25:      packed[14] = (((data[126] >> 1) & 0x3) << 30) | (data[94] << 25) | (data[90] << 20) | (data[86] << 15) | (data[82] << 10) | (data[78] << 5) | data[74];
 26:      packed[15] = (((data[127] >> 1) & 0x3) << 30) | (data[95] << 25) | (data[91] << 20) | (data[87] << 15) | (data[83] << 10) | (data[79] << 5) | data[75];
 27:  
 28:      packed[16] = ((data[124] >> 3) << 30) | (data[116] << 25) | (data[112] << 20) | (data[108] << 15) | (data[104] << 10) | (data[100] << 5) | data[96];
 29:      packed[17] = ((data[125] >> 3) << 30) | (data[117] << 25) | (data[113] << 20) | (data[109] << 15) | (data[105] << 10) | (data[101] << 5) | data[97];
 30:      packed[18] = ((data[126] >> 3) << 30) | (data[118] << 25) | (data[114] << 20) | (data[110] << 15) | (data[106] << 10) | (data[102] << 5) | data[98];
 31:      packed[19] = ((data[127] >> 3) << 30) | (data[119] << 25) | (data[115] << 20) | (data[111] << 15) | (data[107] << 10) | (data[103] << 5) | data[99];
 32:  }
 33:  
 34:  //packed指向128个5位数调用函数pack_5_sse后的输出,data指向的内存不得少于128 * sizeof(uint32_t)字节
 35:  //packed和data指向的内存地址需要16字节对齐
 36:  void unpack_5_sse(const uint32_t* packed, uint32_t* data)
 37:  {
 38:      const __m128i mask = _mm_set_epi32(0x1F, 0x1F, 0x1F, 0x1F);
 39:      __m128i code, code1;
 40:      __m128i unpacked, unpacked1, unpacked2, unpacked3, unpacked4, unpacked5;
 41:  
 42:      code = _mm_load_si128((__m128i*)&packed[0]);
 43:      unpacked = _mm_and_si128(code, mask);
 44:      _mm_store_si128((__m128i*)&data[0], unpacked);
 45:  
 46:      unpacked1 = _mm_srli_epi32(code, 5);
 47:      unpacked1 = _mm_and_si128(unpacked1, mask);
 48:      _mm_store_si128((__m128i*)&data[4], unpacked1);
 49:  
 50:      unpacked2 = _mm_srli_epi32(code, 10);
 51:      unpacked2 = _mm_and_si128(unpacked2, mask);
 52:      _mm_store_si128((__m128i*)&data[8], unpacked2);
 53:  
 54:      unpacked3 = _mm_srli_epi32(code, 15);
 55:      unpacked3 = _mm_and_si128(unpacked3, mask);
 56:      _mm_store_si128((__m128i*)&data[12], unpacked3);
 57:  
 58:      unpacked4 = _mm_srli_epi32(code, 20);
 59:      unpacked4 = _mm_and_si128(unpacked4, mask);
 60:      _mm_store_si128((__m128i*)&data[16], unpacked4);
 61:  
 62:      unpacked5 = _mm_srli_epi32(code, 25);
 63:      unpacked5 = _mm_and_si128(unpacked5, mask);
 64:      _mm_store_si128((__m128i*)&data[20], unpacked5);
 65:  
 66:      code1 = _mm_srli_epi32(code, 30);
 67:  
 68:  
 69:      code = _mm_load_si128((__m128i*)&packed[4]);
 70:      unpacked = _mm_and_si128(code, mask);
 71:      _mm_store_si128((__m128i*)&data[24], unpacked);
 72:  
 73:      unpacked1 = _mm_srli_epi32(code, 5);
 74:      unpacked1 = _mm_and_si128(unpacked1, mask);
 75:      _mm_store_si128((__m128i*)&data[28], unpacked1);
 76:  
 77:      unpacked2 = _mm_srli_epi32(code, 10);
 78:      unpacked2 = _mm_and_si128(unpacked2, mask);
 79:      _mm_store_si128((__m128i*)&data[32], unpacked2);
 80:  
 81:      unpacked3 = _mm_srli_epi32(code, 15);
 82:      unpacked3 = _mm_and_si128(unpacked3, mask);
 83:      _mm_store_si128((__m128i*)&data[36], unpacked3);
 84:  
 85:      unpacked4 = _mm_srli_epi32(code, 20);
 86:      unpacked4 = _mm_and_si128(unpacked4, mask);
 87:      _mm_store_si128((__m128i*)&data[40], unpacked4);
 88:  
 89:      unpacked5 = _mm_srli_epi32(code, 25);
 90:      unpacked5 = _mm_and_si128(unpacked5, mask);
 91:      _mm_store_si128((__m128i*)&data[44], unpacked5);
 92:  
 93:      code = _mm_srli_epi32(code, 30);
 94:      code = _mm_slli_epi32(code, 2);
 95:      code1 = _mm_or_si128(code1, code);
 96:  
 97:  
 98:      code = _mm_load_si128((__m128i*)&packed[8]);
 99:      unpacked = _mm_and_si128(code, mask);
100:      _mm_store_si128((__m128i*)&data[48], unpacked);
101:  
102:      unpacked1 = _mm_srli_epi32(code, 5);
103:      unpacked1 = _mm_and_si128(unpacked1, mask);
104:      _mm_store_si128((__m128i*)&data[52], unpacked1);
105:  
106:      unpacked2 = _mm_srli_epi32(code, 10);
107:      unpacked2 = _mm_and_si128(unpacked2, mask);
108:      _mm_store_si128((__m128i*)&data[56], unpacked2);
109:  
110:      unpacked3 = _mm_srli_epi32(code, 15);
111:      unpacked3 = _mm_and_si128(unpacked3, mask);
112:      _mm_store_si128((__m128i*)&data[60], unpacked3);
113:  
114:      unpacked4 = _mm_srli_epi32(code, 20);
115:      unpacked4 = _mm_and_si128(unpacked4, mask);
116:      _mm_store_si128((__m128i*)&data[64], unpacked4);
117:  
118:      unpacked5 = _mm_srli_epi32(code, 25);
119:      unpacked5 = _mm_and_si128(unpacked5, mask);
120:      _mm_store_si128((__m128i*)&data[68], unpacked5);
121:  
122:      code = _mm_srli_epi32(code, 30);
123:      code = _mm_slli_epi32(code, 4);
124:      code1 = _mm_or_si128(code1, code);
125:  
126:  
127:      code = _mm_load_si128((__m128i*)&packed[12]);
128:      unpacked = _mm_and_si128(code, mask);
129:      _mm_store_si128((__m128i*)&data[72], unpacked);
130:  
131:      unpacked1 = _mm_srli_epi32(code, 5);
132:      unpacked1 = _mm_and_si128(unpacked1, mask);
133:      _mm_store_si128((__m128i*)&data[76], unpacked1);
134:  
135:      unpacked2 = _mm_srli_epi32(code, 10);
136:      unpacked2 = _mm_and_si128(unpacked2, mask);
137:      _mm_store_si128((__m128i*)&data[80], unpacked2);
138:  
139:      unpacked3 = _mm_srli_epi32(code, 15);
140:      unpacked3 = _mm_and_si128(unpacked3, mask);
141:      _mm_store_si128((__m128i*)&data[84], unpacked3);
142:  
143:      unpacked4 = _mm_srli_epi32(code, 20);
144:      unpacked4 = _mm_and_si128(unpacked4, mask);
145:      _mm_store_si128((__m128i*)&data[88], unpacked4);
146:  
147:      unpacked5 = _mm_srli_epi32(code, 25);
148:      unpacked5 = _mm_and_si128(unpacked5, mask);
149:      _mm_store_si128((__m128i*)&data[92], unpacked5);
150:  
151:      code = _mm_srli_epi32(code, 30);
152:      code = _mm_slli_epi32(code, 6);
153:      code1 = _mm_or_si128(code1, code);
154:  
155:  
156:      code = _mm_load_si128((__m128i*)&packed[16]);
157:      unpacked = _mm_and_si128(code, mask);
158:      _mm_store_si128((__m128i*)&data[96], unpacked);
159:  
160:      unpacked1 = _mm_srli_epi32(code, 5);
161:      unpacked1 = _mm_and_si128(unpacked1, mask);
162:      _mm_store_si128((__m128i*)&data[100], unpacked1);
163:  
164:      unpacked2 = _mm_srli_epi32(code, 10);
165:      unpacked2 = _mm_and_si128(unpacked2, mask);
166:      _mm_store_si128((__m128i*)&data[104], unpacked2);
167:  
168:      unpacked3 = _mm_srli_epi32(code, 15);
169:      unpacked3 = _mm_and_si128(unpacked3, mask);
170:      _mm_store_si128((__m128i*)&data[108], unpacked3);
171:  
172:      unpacked4 = _mm_srli_epi32(code, 20);
173:      unpacked4 = _mm_and_si128(unpacked4, mask);
174:      _mm_store_si128((__m128i*)&data[112], unpacked4);
175:  
176:      unpacked5 = _mm_srli_epi32(code, 25);
177:      unpacked5 = _mm_and_si128(unpacked5, mask);
178:      _mm_store_si128((__m128i*)&data[116], unpacked5);
179:  
180:      code = _mm_srli_epi32(code, 30);
181:      code = _mm_slli_epi32(code, 8);
182:      code1 = _mm_or_si128(code1, code);
183:  
184:      unpacked = _mm_and_si128(code1, mask);
185:      _mm_store_si128((__m128i*)&data[120], unpacked);
186:  
187:      code1 = _mm_srli_epi32(code1, 5);
188:      _mm_store_si128((__m128i*)&data[124], code1);
189:  }

在函数pack_5_sse里,声明使用了9个XMM寄存器(mask,code,code1,unpacked,unpacked1~unpacked5),本意是想通过降低数据冲突(data hazard)提高CPU的并行执行度,然而gcc(version 4.1.2, 优化选项-O2)却不买账,用objdump查看汇编代码,gcc优化后只使用了4个XMM寄存器,看来要让编译器按意图来生成代码只能手写汇编了(当然,这只是一个想法,具体能不能提高并行执行度还依赖于其它因素,并且需要测试验证)。后来又一想,CPU内部使用寄存器重命名(register renaming),我这么做应该纯属多余。函数pack_5_sse/unpack_5_sse同样可以利用emacs函数query-replace-regexp来减少敲入的代码量。

3 小结

代码我只是简单地测试了一下,可能存在bug。循环展开版我这里是全部展开了,相反地可以只展开一部分比如一次pack/unpack 32个数,循环4次,比较起来,全部展开消除了分支语句,但也引起了代码膨胀,前者消除了可能的分支预测失败带来的开销,后者可能会引起code cache的缺失(cache miss),到底孰优孰劣,需要实际测试,我这里只是随手给出了一个版本,并没有实际测试过。

你可能感兴趣的:(for)