【发布时间】:2021-10-29 03:26:03
【问题描述】:
我有一个大约 13G 的大文本文件,其内容是图形的边缘列表。每行有两个整数u和v代表边的端点。我想把它读成向量的向量作为图的邻接向量。
然后是下面的代码。
const int N = 3328557;
vector<vector<int> >adj{N};
int main() {
FILE * pFile;
pFile = fopen("path/to/edge/list", "r");
int u, v;
while (fscanf(pFile, "%d%d", &u, &v) == 2) {
adj[u].push_back(v);
adj[v].push_back(u);
}
fclose(pFile);
}
大约需要 7 分钟。经过一些分析,我发现adj[u].push_back(v)和adj[v].push_back(u)由于地址随机而消耗的时间最多。
然后我使用一个二维数组作为缓存。填满后,我将所有值复制到向量并清除它。
const int N = 3328557;
const int threshold = 100;
vector<vector<int> >adj{N};
int ln[N];
int cache[N][threshold];
void write2vec(int node) {
for (int i = 0; i < ln[node]; i++)
adj[node].push_back(cache[node][i]);
ln[node] = 0;
}
int main() {
FILE * pFile;
pFile = fopen("path/to/edge/list", "r");
int u, v;
while (fscanf(pFile, "%d%d", &u, &v) == 2) {
cache[u][ln[u]++] = v;
if (ln[u] == threshold)
write2vec(u);
cache[v][ln[v]++] = u;
if (ln[v] == threshold)
write2vec(v);
}
for (int i = 1; i < N; i++)
write2vec(i);
fclose(pFile);
}
这次耗时 5.5 分钟。还是太长了。那么我认为第一个代码中的两个push_back可以并行化。但我不知道该怎么做。还有没有人有其他想法?
谢谢。
编辑。
我认为我的第二种方法更快的原因是对向量的向量寻址更慢。向量的向量地址不连续,所以访问adj[u]需要两个操作,先是adj,然后是adj[u]。
所以我想知道是否可以使用多处理来使寻址并行化。
【问题讨论】:
-
我怀疑并行化
push_back()调用会改变很多,因为fscanf()调用隐式同步每个循环迭代,并且 I/O 可能对相当多的经过时间负责。您可能想要估计(上限)adj[u]和adj[v]的大小,并且 - 使用向量 - 相应地保留,而不是依赖于可能在每次循环迭代中重新分配/调整大小。 -
@Peter I/O 消耗大约 2.5 分钟。而且我不认为调整矢量的大小是瓶颈。因为我已经测试了
push_back到向量的向量随机消耗的时间是push_back排序的十倍。 -
13G 足够大,您可能会开始考虑其他选择。但你需要退后一步思考。显然你不止一次地解析这个文件,因为你可以轻松地花费超过 7 分钟来优化它。总是一样的13G吗?在这种情况下,对它进行预处理可能是明智的。例如。标准化使得 u
v 并在完成加载后创建反向映射 v->u。 -
一个alternative approach。我不知道它的表现如何。你必须在你的文件上测试它。
-
@MSalters 但是创建反向映射也是随机访问。