मैं Stream
अज्ञात संख्या के दूरस्थ रूप से संग्रहित JSON फ़ाइलों (फ़ाइलों की संख्या ज्ञात नहीं है) के एक विषम सेट के समानांतर प्रसंस्करण का उपयोग करना चाहता हूं । फाइलें आकार में व्यापक रूप से भिन्न हो सकती हैं, 1 JSON रिकॉर्ड प्रति फ़ाइल से कुछ अन्य फाइलों में 100,000 रिकॉर्ड तक। इस मामले में एक JSON रिकॉर्ड का अर्थ है कि फ़ाइल में एक पंक्ति के रूप में प्रतिनिधित्व किया गया एक स्व-निहित JSON ऑब्जेक्ट।
मैं वास्तव में इसके लिए स्ट्रीम का उपयोग करना चाहता हूं और इसलिए मैंने इसे लागू किया Spliterator
:
public abstract class JsonStreamSpliterator<METADATA, RECORD> extends AbstractSpliterator<RECORD> {
abstract protected JsonStreamSupport<METADATA> openInputStream(String path);
abstract protected RECORD parse(METADATA metadata, Map<String, Object> json);
private static final int ADDITIONAL_CHARACTERISTICS = Spliterator.IMMUTABLE | Spliterator.DISTINCT | Spliterator.NONNULL;
private static final int MAX_BUFFER = 100;
private final Iterator<String> paths;
private JsonStreamSupport<METADATA> reader = null;
public JsonStreamSpliterator(Iterator<String> paths) {
this(Long.MAX_VALUE, ADDITIONAL_CHARACTERISTICS, paths);
}
private JsonStreamSpliterator(long est, int additionalCharacteristics, Iterator<String> paths) {
super(est, additionalCharacteristics);
this.paths = paths;
}
private JsonStreamSpliterator(long est, int additionalCharacteristics, Iterator<String> paths, String nextPath) {
this(est, additionalCharacteristics, paths);
open(nextPath);
}
@Override
public boolean tryAdvance(Consumer<? super RECORD> action) {
if(reader == null) {
String path = takeNextPath();
if(path != null) {
open(path);
}
else {
return false;
}
}
Map<String, Object> json = reader.readJsonLine();
if(json != null) {
RECORD item = parse(reader.getMetadata(), json);
action.accept(item);
return true;
}
else {
reader.close();
reader = null;
return tryAdvance(action);
}
}
private void open(String path) {
reader = openInputStream(path);
}
private String takeNextPath() {
synchronized(paths) {
if(paths.hasNext()) {
return paths.next();
}
}
return null;
}
@Override
public Spliterator<RECORD> trySplit() {
String nextPath = takeNextPath();
if(nextPath != null) {
return new JsonStreamSpliterator<METADATA,RECORD>(Long.MAX_VALUE, ADDITIONAL_CHARACTERISTICS, paths, nextPath) {
@Override
protected JsonStreamSupport<METADATA> openInputStream(String path) {
return JsonStreamSpliterator.this.openInputStream(path);
}
@Override
protected RECORD parse(METADATA metaData, Map<String,Object> json) {
return JsonStreamSpliterator.this.parse(metaData, json);
}
};
}
else {
List<RECORD> records = new ArrayList<RECORD>();
while(tryAdvance(records::add) && records.size() < MAX_BUFFER) {
// loop
}
if(records.size() != 0) {
return records.spliterator();
}
else {
return null;
}
}
}
}
मुझे जो समस्या हो रही है, वह यह है कि जब स्ट्रीम पहली बार में सुंदर रूप से समानांतर हो जाती है, तो अंततः सबसे बड़ी फ़ाइल एकल थ्रेड में प्रसंस्करण छोड़ दी जाती है। मेरा मानना है कि समीपस्थ कारण अच्छी तरह से प्रलेखित है: स्प्लिटर "असंतुलित" है।
अधिक संक्षेप में, ऐसा प्रतीत होता है कि trySplit
विधि को Stream.forEach
's' के जीवन चक्र में एक निश्चित बिंदु के बाद नहीं कहा जाता है , इसलिए अंत में छोटे बैचों को वितरित करने के लिए अतिरिक्त तर्क trySplit
शायद ही कभी निष्पादित होता है।
ध्यान दें कि trySplit से लौटे सभी स्प्लिटर कैसे एक ही पुनरावृत्त को साझा करते paths
हैं। मैंने सोचा कि यह सभी स्प्लिटरों में काम को संतुलित करने के लिए एक बहुत ही चतुर तरीका था, लेकिन यह पूर्ण समानता प्राप्त करने के लिए पर्याप्त नहीं है।
मैं चाहता हूँ कि समानांतर प्रसंस्करण पहले फाइलों में आगे बढ़े, और फिर जब कुछ बड़ी फ़ाइलों को अभी भी छोड़ दिया जाता है, तो मैं शेष फाइलों के विखंडू में समानांतर करना चाहता हूं। यह else
ब्लॉक के इरादे के अंत में था trySplit
।
क्या इस समस्या के आसपास कोई आसान / सरल / विहित तरीका है?
Long.MAX_VALUE
से अत्यधिक और अनावश्यक विभाजन हो जाता है, जबकि इसके अलावा कोई भी अनुमान Long.MAX_VALUE
आगे रुकने के कारण, समानता की हत्या करता है। सटीक अनुमानों के मिश्रण को लौटाने से कोई बुद्धिमान अनुकूलन नहीं होता है।
AbstractSpliterator
लेकिन ओवरराइडिंग trySplit()
जो कि इसके अलावा किसी भी चीज़ के लिए एक खराब कॉम्बो है Long.MAX_VALUE
, जैसा कि आप आकार के अनुमान को स्वीकार नहीं कर रहे हैं trySplit()
। के बाद trySplit()
, आकार का अनुमान उन तत्वों की संख्या से कम किया जाना चाहिए जो अलग हो गए हैं।