最简单的方式当然是针对并行计算结果的各分区文件写代码进行并行比对校验。
举个较难调试的例子:
SSSP并行计算,数据量很大(共执行了4965次超步),硬盘空间有限,难以全部打印日志进行调试,初步猜测问题可能是在多次同步计算中某个点的值计算出现问题,导致在后续的计算中扩散,最终结果不对。
怎么办呢?
我的解决办法是,首先换一个较小的数据集,其次,由于SSSP每个点的计算结果都不一致,难以排查,因此换成CC算法,求最小id,即CC+小数据集,由于CC的并行算法在计算时同分支的结果一致,就便于比对。
具体过程是:
1.先写并行结果比对程序找出值不对的点,比如322871 最终的计算结果应是1,但我的优化代码实际计算结果为23,然后将23作为起始点,写一个从原始文件中,从点1开始按层次遍历,由于322871和23必然在以1出发的分支上,因此在按层次遍历中遇到的第一个值同源文件值不同的点即为第一个出问题的点,即所谓的“案件第一发生地”,
2.由于数据量较小,能够打印全部消息发送和接收日志,所以只需要在日志文件中检索此部分日志即可观察。 最终发现是在进行窃取计算时,窃取线程过快结束并在计算完成后删除了消息容器,而此时接收消息的线程还在写入这块内存,但此时这部分内容已经被漏算了,原因是实现的时候采用了一种根据线程名进行消息容器替换的方法,但发现在计算线程完成后,IPC还在向里面写消息。。。。。后来改成了采用原容器,但加锁进行解决。
问题代码:
public <M extends Writable> MessageStore<I, M> getIncomingMessageStore() {
// return (MessageStore<I, M>) incomingMessageStore;
if(Thread.currentThread().getName().contains("Group")) {
// GraphTaskManager.LOG.info(GiraphStackTracePrinter.printStackTrace()) ;
stealNextSuperStepMessage = true ;
long start = System.currentTimeMillis() ;
currentGroupMessageStore = incomingMessageStore ;
incomingMessageStore = messageStoreFactory.newStore(conf.getOutgoingMessageValueFactory()); //组内消息容器交互
GraphTaskManager.LOG.info(Thread.currentThread().getName() + " currentGroupMessageStore is " +
currentGroupMessageStore.hashCode() + " incomingMessageStore " + incomingMessageStore.hashCode() +
" last " + (System.currentTimeMillis() - start)) ;
stealNextSuperStepMessage = false;
return (MessageStore<I, M>) currentGroupMessageStore;
} else {
int i = 0 ;
while(stealNextSuperStepMessage) { //等待直至窃取结束
i = 1 ;
}
return (MessageStore<I, M>) incomingMessageStore;
}
}
修改后:
public <M extends Writable> MessageStore<I, M> getIncomingMessageStore() {
return (MessageStore<I, M>) incomingMessageStore;
}
ConcurrentHashMap<Integer, ConcurrentHashMap<Integer, DataInputOutput>> currentGroupAllStore = mStore.getAllMessages() ;
Iterator<Map.Entry<Integer,ConcurrentHashMap<Integer, DataInputOutput>>> itr = currentGroupAllStore.entrySet().iterator();
while (itr.hasNext()) {
Map.Entry<Integer, ConcurrentHashMap<Integer, DataInputOutput>> partitionIdMessages = itr
.next();
Thread.currentThread().setName("ComputeCallable-" + threadId + ", partionMsgStore hashcode: " + partitionIdMessages.hashCode()+
" at step " + graphState.getSuperstep() +", time " +System.currentTimeMillis()) ;
// synchronized(partitionIdMessages) {
synchronized(IntByteArrayMessageStore.mutex) {
XXXX
}
}
问题部分的日志例子如下:
2018-09-30 00:05:18,602 INFO org.apache.giraph.graph.GraphTaskManager: To : 264823, Msg: 264819, hashcode: 1943863333 at step 2
2018-09-30 00:05:35,280 INFO org.apache.giraph.graph.GraphTaskManager: To : 264823, Msg: 264820, hashcode: 212791930 at step 3
2018-09-30 00:05:35,579 INFO org.apache.giraph.graph.ComputeCallable: Vertex(id=264823,value=264820,#edges=2), Msg: false, hashcode 1190311527 group-0 at step 3
2018-09-30 00:05:35,579 INFO org.apache.giraph.graph.GraphTaskManager: Vertex Vertex(id=264823,value=264820,#edges=2) , receive msg : 264820 at step: 3, phase: 2
2018-09-30 00:05:39,166 INFO org.apache.giraph.graph.GraphTaskManager: To : 264823, Msg: 264818, hashcode: 212791930 at step 3
2018-09-30 00:05:39,166 INFO org.apache.giraph.graph.GraphTaskManager: To : 264828, Msg: 264823, hashcode: 212791930 at step 3
可见点264823先计算264820在前,丢失264818消息在后。