बड़ी डेटा फ़ाइलों को लाइन से कॉपी कैसे करें?


9

मेरे पास 35GB CSVफाइल है। मैं प्रत्येक पंक्ति को पढ़ना चाहता हूं, और यदि यह एक शर्त से मेल खाता है, तो एक नए CSV को लाइन लिखें।

try (BufferedWriter writer = Files.newBufferedWriter(Paths.get("source.csv"))) {
    try (BufferedReader br = Files.newBufferedReader(Paths.get("target.csv"))) {
        br.lines().parallel()
            .filter(line -> StringUtils.isNotBlank(line)) //bit more complex in real world
            .forEach(line -> {
                writer.write(line + "\n");
        });
    }
}

यह लगभग लेता है। 7 मिनट। क्या उस प्रक्रिया को और भी तेज करना संभव है?


1
हां, आप इसे जावा से नहीं करने का प्रयास कर सकते हैं, बल्कि इसे सीधे अपने लिनक्स / विंडोज / आदि से कर सकते हैं। ऑपरेटिंग सिस्टम। जावा की व्याख्या की गई है, और इसका उपयोग करने में हमेशा एक उपरि होगी। इसके अलावा, नहीं, मुझे इसे तेज करने का कोई स्पष्ट तरीका नहीं है, और 35GB के लिए 7 मिनट मेरे लिए उचित लगता है।
टिम बेज़ेलिसेन

1
शायद parallelयह तेजी से हटाता है ? और चारों ओर लाइनों फेरबदल नहीं करता है?
थिलो

1
कंस्ट्रक्टरBufferedWriter का उपयोग करके अपने आप को बनाएं , जो आपको बफर आकार सेट करने देता है। शायद एक बड़ा (या छोटा) बफर आकार एक फर्क पड़ेगा। मैं होस्ट ऑपरेटिंग सिस्टम बफर साइज़ को बफर साइज़ से मिलाने की कोशिश करूँगा । BufferedWriter
Abra

5
@TimBiegeleisen: "जावा की व्याख्या की गई है" सबसे अच्छा भ्रामक है और लगभग हमेशा गलत भी है। हां, कुछ अनुकूलन के लिए आपको जेवीएम की दुनिया को छोड़ने की आवश्यकता हो सकती है, लेकिन जावा में यह जल्दी करना निश्चित रूप से उल्लेखनीय है।
जोआचिम सॉयर

1
आपको यह देखने के लिए एप्लिकेशन को प्रोफाइल करना चाहिए कि क्या कोई हॉटस्पॉट है जिसके बारे में आप कुछ कर सकते हैं। आप कच्चे IO के बारे में ज्यादा कुछ नहीं कर पाएंगे (डिफ़ॉल्ट 8192 बाइट बफर उतना बुरा नहीं है, क्योंकि इसमें सेक्टर साइज आदि शामिल हैं), लेकिन हो सकने वाली चीजें (आंतरिक रूप से) हो सकती हैं जो आप कर सकते हैं। के साथ काम।
कायम

जवाबों:


4

यदि यह एक विकल्प है तो आप डिस्क I / O को कम करने के लिए GZipInputStream / GZipOutputStream का उपयोग कर सकते हैं।

Files.newBufferedReader / Writer डिफ़ॉल्ट बफर आकार का उपयोग करता है, 8 केबी मेरा मानना ​​है। आप एक बड़ा बफर आज़मा सकते हैं।

स्ट्रिंग, यूनिकोड में परिवर्तित होने से (और मेमोरी का दोगुना उपयोग होता है) धीमा हो जाता है। उपयोग किया गया UTF-8 StandardCharsets.ISO_8859_1 जितना सरल नहीं है।

सबसे अच्छा होगा यदि आप अधिकांश भाग के लिए बाइट्स के साथ काम कर सकते हैं और केवल विशिष्ट सीएसवी क्षेत्रों के लिए उन्हें स्ट्रिंग में परिवर्तित कर सकते हैं।

एक स्मृति मैप की गई फ़ाइल सबसे उपयुक्त हो सकती है। समानांतरवाद का उपयोग फ़ाइल श्रेणियों द्वारा किया जा सकता है, फ़ाइल को थूकना।

try (FileChannel sourceChannel = new RandomAccessFile("source.csv","r").getChannel(); ...
MappedByteBuffer buf = sourceChannel.map(...);

यह थोड़ा बहुत कोड बन जाएगा (byte)'\n', लेकिन ठीक से जटिल नहीं।


बाइट्स पढ़ने के साथ समस्या यह है कि वास्तविक दुनिया में मुझे लाइन की शुरुआत का मूल्यांकन करना है, एक विशिष्ट चरित्र पर विकल्प और केवल रेखा के शेष भाग को संगठन में लिखना है। तो मैं शायद बाइट्स के रूप में लाइनों को नहीं पढ़ सकता हूं?
मेसाउंड

मैं सिर्फ GZipInputStream + GZipOutputStreamएक रैमडिस्क पर पूरी तरह से निष्क्रियता का परीक्षण किया । प्रदर्शन बहुत खराब था ...
सदस्य

1
Gzip पर: फिर यह एक धीमी डिस्क नहीं है। हां, बाइट्स एक विकल्प है: newlines, अल्पविराम, टैब, अर्धविराम सभी को बाइट्स के रूप में संभाला जा सकता है, और स्ट्रिंग की तुलना में काफी तेज होगा। यूटीएफ -8 से यूटीएफ -16 के रूप में बाइट्स स्टिंग से यूटीएफ -8 तक बाइट्स।
जोप एगेनजेन

1
बस समय के साथ फ़ाइल के विभिन्न हिस्सों को मैप करें। जब आप सीमा तक पहुंच जाते हैं, तो बस MappedByteBufferअंतिम ज्ञात-अच्छी स्थिति से एक नया बनाते हैं ( FileChannel.mapलंबे समय तक)।
जोकिम सॉयर

1
2019 में, उपयोग करने की कोई आवश्यकता नहीं है new RandomAccessFile(…).getChannel()। बस उपयोग करें FileChannel.open(…)
होल्गर

0

आप यह कोशिश कर सकते हैं:

try (BufferedWriter writer = new BufferedWriter(new FileWriter(targetFile), 1024 * 1024 * 64)) {
  try (BufferedReader br = new BufferedReader(new FileReader(sourceFile), 1024 * 1024 * 64)) {

मुझे लगता है कि यह आपको एक या दो मिनट बचाएगा। बफर आकार निर्दिष्ट करके परीक्षण को लगभग 4 मिनट में मेरी मशीन पर किया जा सकता है।

क्या यह तेज हो सकता है? इसे इस्तेमाल करे:

final char[] cbuf = new char[1024 * 1024 * 128];

try (Writer writer = new FileWriter(targetFile)) {
  try (Reader br = new FileReader(sourceFile)) {
    int cnt = 0;
    while ((cnt = br.read(cbuf)) > 0) {
      // add your code to process/split the buffer into lines.
      writer.write(cbuf, 0, cnt);
    }
  }
}

इससे आपको तीन या चार मिनट बचाना चाहिए।

यदि वह अभी भी पर्याप्त नहीं है। (कारण मुझे लगता है कि आप सवाल पूछते हैं शायद आपको कार्य को बार-बार निष्पादित करने की आवश्यकता है)। यदि आप इसे एक मिनट या कुछ सेकंड में प्राप्त करना चाहते हैं। तब आपको डेटा को संसाधित करना चाहिए और इसे db में सहेजना चाहिए, फिर कार्य को कई सर्वरों द्वारा संसाधित करना चाहिए।


अपने अंतिम उदाहरण के लिए: मैं तब cbufसामग्री का मूल्यांकन कैसे कर सकता हूं , और केवल भाग लिख सकता हूं ? और क्या मुझे एक बार पूर्ण बफ़र रीसेट करना होगा? (मैं कैसे पता कर सकते हैं कि बफर भरा हुआ है?)
सदस्य

0

आपके सभी सुझावों के लिए धन्यवाद, मैं जिस तेजी के साथ आया था वह लेखक के साथ आदान-प्रदान कर रहा था BufferedOutputStream, जिसने लगभग 25% सुधार दिया:

   try (BufferedReader reader = Files.newBufferedReader(Paths.get("sample.csv"))) {
        try (BufferedOutputStream writer = new BufferedOutputStream(Files.newOutputStream(Paths.get("target.csv")), 1024 * 16)) {
            reader.lines().parallel()
                    .filter(line -> StringUtils.isNotBlank(line)) //bit more complex in real world
                    .forEach(line -> {
                        writer.write((line + "\n").getBytes());
                    });
        }
    }

फिर भी मेरे मामले में BufferedReaderबेहतर प्रदर्शन करता है BufferedInputStream

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.