优化此代码的方法

时间:2017-05-26 19:41:52

标签: c++ string caching optimization increment

我正在探索优化后续代码的方法。 代码循环遍历字符串。对于ith 字符串中的字符, 代码增加了2D数组中的一个条目 列。第i列的确切条目 增量取决于字符串中第i个字符的值; 字符串字母表中的每个字母都有一行。 通过循环遍历多个字符串并递增相同的2d数组(count), 为字符串计算每个字符的出现次数。

代码如下:

string read = getReadFromData();
vector< vector<int> > count(4,vector<int>(read.size(),0));
for (int i = 0; i < read.size(); i++) {
  switch(read[i]) {                                           
    case 'A': count[0][i]++; break;
    case 'T': count[1][i]++; break;
    case 'C': count[2][i]++; break;
    case 'G': count[3][i]++; break;
}     

我的优化尝试是使用1d数组 一个2d数组;旨在避免缓存未命中,如果字符串数据本身 在记忆中并不连续。 不幸的是,它没有减少计算时间;它们大致相同。

string read = getReadFromData();
int width = read.size()
vector<int> count(4*width,0);
for (int i = 0; i < width; i++) {
  switch(read[i]) {                                           
    case 'A': count[            i]++; break;
    case 'T': count[    width + i]++; break;
    case 'C': count[2 * width + i]++; break;
    case 'G': count[3 * width + i]++; break;
}

有人对如何优化此问题有任何建议吗? 切换行和列?或者改变增量的间隔? 谢谢你的建议。

更新:优化
所以,我认为主要问题是:

1)输入的不可预测性;在任何一个访问的内存位置 2D或1D版本取决于输入字符。 2)在一维示例中,相应位置之间的距离相距很远。此外,在2D中,但我正在优化一维示例。

在上面的一维例子中,i+1的潜在位置相距很远; i+1i+1 + widthi+1 + 2*widthi+1 + 3*width。我认为访问的不可预测性,加上距i+1的距离很远,导致缓存未命中 - 我会在某些时候缓存。 优化的目的是减少到i+1访问的距离。 为此,我交错了A,T,G,C计数,以便i+1元素 距离最多7个条目;距离小得多。

string read = getReadFromData();
int width = read.size()
vector<int> count(4*width,0);
for (int i = 0; i < width; i++) {
  switch(read[i]) {                                           
    case 'A': count[i*4    ]++; break;
    case 'T': count[i*4 + 1]++; break;
    case 'C': count[i*4 + 2]++; break;
    case 'G': count[i*4 + 3]++; break;
}

我得到一个&gt;现在加速5倍,很好吃。

2 个答案:

答案 0 :(得分:0)

一个想法是将数组预先计算为指针:
注意:未经测试的代码如下

vector< vector<int> > count(4,vector<int>(read.size(),0));
std::vector<int> * const p_a_counts = &count[0];
std::vector<int> * const p_t_counts = &count[1];
std::vector<int> * const p_c_counts = &count[2];
std::vector<int> * const p_g_counts = &count[3];

for (int i = 0; i < read.size(); i++) {
  switch(read[i]) {                                           
    case 'A': p_a_counts->[i]++; break;
    case 'T': p_t_counts->[i]++; break;
    case 'C': p_c_counts->[i]++; break;
    case 'G': p_g_counts->[i]++; break;
}

这个想法是减少各种数组的计算。

注意:您的代码看起来不正确。如果读取的第二个字母是'T',则第二个位置递增,而不是第一个。虽然你可以计算'T'在第二位置的次数。

编辑1:不同的视角
处理器更喜欢访问相同的数据区域,换句话说就是使用它们的数据缓存。

因此,将透视更改为数据视点:

string read = getReadFromData();
vector< vector<int> > count(4,vector<int>(read.size(),0));

for (int i = 0; i < read.size(); i++)
{
  if (read[i] == 'A')
  {                                           
    count[0][i]++;
  }
}
for (int i = 0; i < read.size(); i++)
{
  if (read[i] == 'T')
  {                                           
    count[1][i]++;
  }
}
for (int i = 0; i < read.size(); i++)
{
  if (read[i] == 'C')
  {                                           
    count[2][i]++;
  }
}
for (int i = 0; i < read.size(); i++)
{
  if (read[i] == 'G')
  {                                           
    count[3][i]++;
  }
}

以上内容应将count数组保留在数据缓存中以用于不同目的。

答案 1 :(得分:0)

正如Thomas Matthews在他的评论中建议使用不同的数据结构,通常标准库给定的数据结构比你想要的慢。尝试只使用常规数组

int *count = new int [4 * width];