我从头到尾编写了一个简单的神经网络模拟器(生物物理类),并希望得到一些反馈,说明我如何加快速度,或者我可以改进的任何C++ /编译最佳实践。
代码在这个储存库。
OpenMP似乎并不是在提速。
代码的性能关键部分在src/网络/Spikingnet.cpp中,但是要获得更多的上下文,请参见存储库中的其余代码。
#pragma omp parallel for
for (size_t li=0; li < nL; ++li) {
SpikingLayer *layer = net->layers[li];
Stim *stim = rs->stimuli[li];
boolvec doSpike = stim->yield();
conn_vec pre_arr = net->pre[li];
updateLayer(layer, pre_arr, doSpike, t);
recordSpikes(results.mutable_spikes(li), layer, i);
}
// update transmission & stdp
#pragma omp parallel for
for (size_t li=0; li < nL; ++li) {
SpikingLayer *layer = net->layers[li];
// transmission
for (SpikingConnection *conn : net->post[li]) {
for (SpikingSynapse* syn : conn->synapses) {
updateTransmission(syn, layer->units[syn->s]);
}
}
// STDP
for (SpikingConnection *conn : net->pre[li]) {
SpikingLayer *source = net->layers[conn->s];
SpikingLayer *target = net->layers[conn->t];
if (conn->stdp_enabled) {
#pragma omp parallel for
for (SpikingSynapse* syn : conn->synapses) {
updateSTDP(syn, source->units[syn->s], target->units[syn->t]);
}
}
} // end STDP
} // end for网络由L=9“层”组成,每层都有100到900个单元。按L^2“连接”(层间突触束)的顺序排列,每个“连接”有2000个突触(突触稀疏)。
在每个更新周期中,所有的层(神经元)被更新(以连接为条件),然后所有的连接(突触)被更新(以层为条件)。也就是说,层更新是独立于连接的,连接更新是独立于层的。
考虑到有那么多神经元和突触,程序自然会花费大部分时间更新层和突触(第133行)。我认为在每层循环上使用OpenMP,甚至是每一个神经元/突触,都会加快速度,但情况似乎并非如此。
如果读者感兴趣的话,我的机器有一个4.0Ghz CPU,有8个核心。在单线程或OpenMP支持的构建上,12K步骤大约在30秒内运行。
我知道我应该使用智能指针,但是在这个简单的模拟器中,内存管理相当简单,所以我选择了危险的生活。
一般的C++编程风格提示/指出我的坏做法也是受欢迎的!
发布于 2015-05-25 21:58:38
通常建议在绝对的最外层并行化循环。创建新的OS线程、划分相关的循环以及为每个线程在私有地址空间中分配数据都需要时间,在并行任务结束后同步也是如此。通常,这种开销可能会超过并行性最初可能带来的任何好处。每当您使用OpenMP并行任务或循环时,它必须足够大,以摊销生成线程的成本。
我会将尽可能多的并行指令提升到最外层的循环,并避免嵌套并行指令。您可能必须从使用omp parallel for切换到仅在最外层使用omp parallel,然后有一些逻辑来显式地决定每个线程根据其线程号处理哪一部分数据。
您必须判断这是否是实际的问题;我只对您的代码做了浅浅的阅读,而在内部循环中并行化的工作实际上可能足够大,这根本不是考虑的问题。
最后,对于共享内存并行编程,还有一些替代OpenMP的方法,例如英特尔的线程构建块。OpenMP在总体方案上相当粗糙,因此您可能会发现基于任务的并行处理更容易或更有趣。您可以假装拥有任意数量的并行执行线程,并且库管理您为现有OS线程定义的任务。
https://codereview.stackexchange.com/questions/87395
复制相似问题