[英]Why is my program slow ? How can I improve its efficiency?
我有一个程序执行Block Nested循环连接( 链接文本 )。 基本上它的作用是,它将文件(比如10GB文件)中的内容读入buffer1(比如400MB),将其放入哈希表中。 现在将第二个文件(比如10GB文件)的内容读入缓冲区2(比如说100MB),看看buffer2中的元素是否存在于哈希中。 输出结果无关紧要。 我现在只关心程序的效率。 在这个程序中,我需要从两个文件一次读取8个字节,所以我使用long long int。 问题是我的程序效率很低。 我怎样才能提高效率?
//我使用g++ -o hash hash.c -std=c++0x
编译g++ -o hash hash.c -std=c++0x
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/time.h>
#include <stdint.h>
#include <math.h>
#include <limits.h>
#include <iostream>
#include <algorithm>
#include <vector>
#include <unordered_map>
using namespace std;
typedef std::unordered_map<unsigned long long int, unsigned long long int> Mymap;
int main()
{
uint64_t block_size1 = (400*1024*1024)/sizeof(long long int); //block size of Table A - division operator used to make the block size 1 mb - refer line 26,27 malloc statements.
uint64_t block_size2 = (100*1024*1024)/sizeof(long long int); //block size of table B
int i=0,j=0, k=0;
uint64_t x,z,l=0;
unsigned long long int *buffer1 = (unsigned long long int *)malloc(block_size1 * sizeof(long long int));
unsigned long long int *buffer2 = (unsigned long long int *)malloc(block_size2 * sizeof(long long int));
Mymap c1 ; // Hash table
//Mymap::iterator it;
FILE *file1 = fopen64("10G1.bin","rb"); // Input is a binary file of 10 GB
FILE *file2 = fopen64("10G2.bin","rb");
printf("size of buffer1 : %llu \n", block_size1 * sizeof(long long int));
printf("size of buffer2 : %llu \n", block_size2 * sizeof(long long int));
while(!feof(file1))
{
k++;
printf("Iterations completed : %d \n",k);
fread(buffer1, sizeof(long long int), block_size1, file1); // Reading the contents into the memory block from first file
for ( x=0;x< block_size1;x++)
c1.insert(Mymap::value_type(buffer1[x], x)); // inserting values into the hash table
// std::cout << "The size of the hash table is" << c1.size() * sizeof(Mymap::value_type) << "\n" << endl;
/* // display contents of the hash table
for (Mymap::const_iterator it = c1.begin();it != c1.end(); ++it)
std::cout << " [" << it->first << ", " << it->second << "]";
std::cout << std::endl;
*/
while(!feof(file2))
{
i++; // Counting the number of iterations
// printf("%d\n",i);
fread(buffer2, sizeof(long long int), block_size2, file2); // Reading the contents into the memory block from second file
for ( z=0;z< block_size2;z++)
c1.find(buffer2[z]); // finding the element in hash table
// if((c1.find(buffer2[z]) != c1.end()) == true) //To check the correctness of the code
// l++;
// printf("The number of elements equal are : %llu\n",l); // If input files have exactly same contents "l" should print out the block_size2
// l=0;
}
rewind(file2);
c1.clear(); //clear the contents of the hash table
}
free(buffer1);
free(buffer2);
fclose(file1);
fclose(file2);
}
更新:
是否可以直接从文件中读取一个块(比如400 MB)并使用C ++流读取器将其直接放入哈希表中? 我认为这可以进一步减少开销。
如果你正在使用fread,那么尝试使用setvbuf() 。 标准lib文件I / O调用使用的默认缓冲区很小(通常约为4kB)。 当快速处理大量数据时,您将受到I / O限制,并且获取许多小型缓冲区数据的开销可能成为一个重要的瓶颈。 将其设置为更大的尺寸(例如64kB或256kB),您可以减少这种开销,并且可以看到显着的改进 - 尝试一些值,看看哪里可以获得最佳收益,因为您将获得递减收益。
程序的运行时间是(l 1 x bs 1 xl 2 x bs 2 )(其中l 1是第一个文件中的行数,bs 1是第一个缓冲区的块大小,l 2是因为你有四个嵌套循环,所以第二个文件中的行数和bs 2是第二个缓冲区的块大小。 由于您的块大小是常量,您可以说您的订单是O(nx 400 xmx 400)或O(1600mn),或者在最坏的情况下O(1600n 2 )基本上最终为O(n 2 )。
如果你做这样的事情你可以有一个O(n)算法(伪代码如下):
map = new Map();
duplicate = new List();
unique = new List();
for each line in file1
map.put(line, true)
end for
for each line in file2
if(map.get(line))
duplicate.add(line)
else
unique.add(line)
fi
end for
现在, duplicate
将包含重复项的列表,而unique
将包含唯一项的列表。
在原始算法中,您将不必要地遍历第一个文件中每一行的第二个文件。 所以你实际上最终失去了哈希的好处(它给你O(1)查询时间)。 当然,在这种情况下的权衡是你必须将整个10GB存储在内存中,这可能没那么有用 。 通常在这些情况下,在运行时和内存之间进行权衡。
可能有更好的方法来做到这一点。 我需要再考虑一下。 如果没有,我很确定有人会想出更好的主意:)。
UPDATE
如果你能找到一种好的方法来散列行(你从第一个文件中读入),那么你可以减少内存使用量,这样你就可以得到一个唯一的值(即行和行之间的一对一映射)。哈希值)。 基本上你会做这样的事情:
for each line in file1
map.put(hash(line), true)
end for
for each line in file2
if(map.get(hash(line)))
duplicate.add(line)
else
unique.add(line)
fi
end for
这里hash
函数是执行散列的函数。 这样您就不必将所有行存储在内存中。 您只需存储其散列值。 这可能对你有所帮助。 即便如此,在更糟糕的情况下(您要么比较两个相同或完全不同的文件),您仍然可以在内存中以10Gb结束duplicate
或unique
列表。 如果只是存储唯一或重复项目的计数而不是项目本身,则可以丢失一些信息。
long long int *ptr = mmap()
你的文件,然后将它们与块中的memcmp()进行比较。 一旦发现差异,退回一个块并更详细地比较它们。 (更多细节意味着在这种情况下长long int。)
如果您希望经常发现差异,请不要打扰memcmp(),只需编写自己的循环,将长长的int相互比较。
要知道的唯一方法是对其进行分析,例如使用gprof 。 创建当前实现的基准,然后有条不紊地尝试其他修改并重新运行基准测试。
我敢打赌,如果你阅读更大的块,你会获得更好的表现。 每次传递fread()和处理多个块。
我看到的问题是你正在读n次的第二个文件。 真的很慢。
使速度更快的最佳方法是对文件进行预排序,然后执行排序合并连接 。 根据我的经验,这种情况几乎总是值得的。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.