Java NIO FileChannel बनाम FileOutputstream प्रदर्शन / उपयोगिता


169

मैं यह पता लगाने की कोशिश कर रहा हूं कि जब हम फाइल सिस्टम के लिए फाइल पढ़ने और लिखने के लिए नियो FileChannelबनाम सामान्य FileInputStream/FileOuputStreamका उपयोग करते हैं तो प्रदर्शन (या फायदे) में कोई अंतर होता है या नहीं । मैंने देखा कि मेरी मशीन पर दोनों एक ही स्तर पर प्रदर्शन करते हैं, कई बार FileChannelरास्ता धीमा भी होता है। क्या मैं इन दोनों तरीकों की तुलना में अधिक जानकारी जान सकता हूं। यहां मैंने जिस कोड का उपयोग किया है, वह फ़ाइल जिसके साथ मैं परीक्षण कर रहा हूं वह आसपास है 350MB। फ़ाइल I / O के लिए NIO आधारित कक्षाओं का उपयोग करना एक अच्छा विकल्प है, अगर मैं यादृच्छिक अभिगम या अन्य ऐसी उन्नत सुविधाओं को नहीं देख रहा हूँ?

package trialjavaprograms;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;

public class JavaNIOTest {
    public static void main(String[] args) throws Exception {
        useNormalIO();
        useFileChannel();
    }

    private static void useNormalIO() throws Exception {
        File file = new File("/home/developer/test.iso");
        File oFile = new File("/home/developer/test2");

        long time1 = System.currentTimeMillis();
        InputStream is = new FileInputStream(file);
        FileOutputStream fos = new FileOutputStream(oFile);
        byte[] buf = new byte[64 * 1024];
        int len = 0;
        while((len = is.read(buf)) != -1) {
            fos.write(buf, 0, len);
        }
        fos.flush();
        fos.close();
        is.close();
        long time2 = System.currentTimeMillis();
        System.out.println("Time taken: "+(time2-time1)+" ms");
    }

    private static void useFileChannel() throws Exception {
        File file = new File("/home/developer/test.iso");
        File oFile = new File("/home/developer/test2");

        long time1 = System.currentTimeMillis();
        FileInputStream is = new FileInputStream(file);
        FileOutputStream fos = new FileOutputStream(oFile);
        FileChannel f = is.getChannel();
        FileChannel f2 = fos.getChannel();

        ByteBuffer buf = ByteBuffer.allocateDirect(64 * 1024);
        long len = 0;
        while((len = f.read(buf)) != -1) {
            buf.flip();
            f2.write(buf);
            buf.clear();
        }

        f2.close();
        f.close();

        long time2 = System.currentTimeMillis();
        System.out.println("Time taken: "+(time2-time1)+" ms");
    }
}

5
transferTo/ transferFromफ़ाइलों की प्रतिलिपि बनाने के लिए अधिक पारंपरिक होगा। जो भी तकनीक आपकी हार्ड ड्राइव को किसी भी तेज या धीमी गति से नहीं करना चाहिए, हालांकि मुझे लगता है कि एक समस्या हो सकती है यदि यह एक बार में छोटे विखंडू को पढ़ती है और सिर को समय की एक विषम राशि खर्च करने का कारण बनती है।
टॉम हॉल्टिन - 6

1
(आप उल्लेख नहीं करते हैं कि आप किस OS का उपयोग कर रहे हैं, या कौन सा JRE विक्रेता और संस्करण है।)
टॉम हैटिन -

ओह सॉरी, मैं सन JDK6 के साथ FC10 का उपयोग कर रहा हूं।
केशव

जवाबों:


202

बड़ी फ़ाइलों के आकार के साथ मेरा अनुभव रहा है कि java.nioकी तुलना में तेज है java.ioठोस रूप से तेज। जैसे> 250% रेंज में। उस ने कहा, मैं स्पष्ट बाधाओं को दूर कर रहा हूं, जो मुझे लगता है कि आपके माइक्रो-बेंचमार्क से पीड़ित हो सकते हैं। जांच के लिए संभावित क्षेत्र:

बफर आकार। आपके पास मूल रूप से मौजूद एल्गोरिथम है

  • डिस्क से बफर में कॉपी करें
  • बफर से डिस्क पर कॉपी करें

मेरा अपना अनुभव रहा है कि यह बफर साइज़ ट्यूनिंग के लिए पका हुआ है। मैं अपने आवेदन के एक हिस्से के लिए 4KB पर बस गया हूं, दूसरे के लिए 256KB। मुझे संदेह है कि आपका कोड इतने बड़े बफर से पीड़ित है। अपने आप को साबित करने के लिए 1KB, 2KB, 4KB, 8KB, 16KB, 32KB और 64KB के बफ़र वाले कुछ बेंचमार्क चलाएँ।

जावा बेंचमार्क प्रदर्शन न करें जो एक ही डिस्क पर पढ़ें और लिखें।

यदि आप करते हैं, तो आप वास्तव में डिस्क को बेंचमार्क कर रहे हैं, और जावा नहीं। मैं यह भी सुझाव दूंगा कि यदि आपका सीपीयू व्यस्त नहीं है, तो आप शायद किसी और अड़चन का सामना कर रहे हैं।

यदि आपको आवश्यकता न हो तो बफर का उपयोग न करें।

यदि आपका लक्ष्य कोई अन्य डिस्क या NIC है तो स्मृति की प्रतिलिपि क्यों करें? बड़ी फ़ाइलों के साथ, अव्यक्तता गैर-तुच्छ है।

जैसे अन्य ने कहा है, उपयोग FileChannel.transferTo()या FileChannel.transferFrom()। यहाँ मुख्य लाभ यह है कि JVM OS की पहुंच DMA ( डायरेक्ट मेमोरी एक्सेस ) का उपयोग करता है , यदि मौजूद है। (यह कार्यान्वयन पर निर्भर है, लेकिन सामान्य उद्देश्य पर आधुनिक सन और आईबीएम संस्करण सीपीयू जाना अच्छा है।) क्या होता है डेटा सीधे डिस्क से / बस तक, और फिर गंतव्य तक जाता है ... किसी भी सर्किट को दरकिनार करके रैम या सीपीयू।

जिस वेब ऐप पर मैंने अपने दिन और रात बिताए हैं, वह बहुत ही भारी है। मैंने माइक्रो बेंचमार्क और रियल-वर्ल्ड बेंचमार्क भी किया है। और परिणाम मेरे ब्लॉग पर हैं, एक नज़र डालें:

उत्पादन डेटा और वातावरण का उपयोग करें

माइक्रो-बेंचमार्क विरूपण के लिए प्रवण हैं। यदि आप कर सकते हैं, तो उस डेटा को इकट्ठा करने का प्रयास करें जो आप करने की योजना बनाते हैं, उस लोड के साथ जो आप अपेक्षा करते हैं, हार्डवेयर पर आप अपेक्षा करते हैं।

मेरे बेंचमार्क ठोस और विश्वसनीय हैं क्योंकि वे एक उत्पादन प्रणाली, एक गोमांस प्रणाली, लोड के तहत एक प्रणाली, लॉग में एकत्र हुए। नहीं मेरी नोटबुक के 7200 RPM 2.5 "SATA ड्राइव जबकि मैंने तीव्रता से देखा क्योंकि JVM मेरी हार्ड डिस्क का काम करता है।

क्या चल रहा है? यह मायने रखता है।


@Stu थॉम्पसन - आपकी पोस्ट के लिए धन्यवाद। जब से मैं एक ही विषय पर शोध कर रहा हूं, मैं आपके उत्तर पर आया हूं। मैं ओएस सुधारों को समझने की कोशिश कर रहा हूं जो एनआईओ जावा प्रोग्रामर्स को उजागर करता है। उनमें से एक जोड़े - डीएमए और मेमोरी मैप्ड फाइलें। क्या आप इस तरह के सुधारों से अधिक आए हैं? PS - आपके ब्लॉग लिंक टूट गए हैं।
एंडी डफ्रेसने

@AndyDufresne मेरा ब्लॉग इस समय नीचे है, इस सप्ताह के अंत में होगा - इसे स्थानांतरित करने की प्रक्रिया में।
स्टु थॉम्पसन


1
कैसे सिर्फ एक निर्देशिका से दूसरे में फ़ाइलों की प्रतिलिपि बनाने के बारे में? (प्रत्येक अलग डिस्क ड्राइव)
डेकार्ड


38

यदि आप जिस चीज की तुलना करना चाहते हैं, वह फाइल कॉपी का प्रदर्शन है, तो चैनल टेस्ट के लिए आपको इसके बजाय ऐसा करना चाहिए:

final FileInputStream inputStream = new FileInputStream(src);
final FileOutputStream outputStream = new FileOutputStream(dest);
final FileChannel inChannel = inputStream.getChannel();
final FileChannel outChannel = outputStream.getChannel();
inChannel.transferTo(0, inChannel.size(), outChannel);
inChannel.close();
outChannel.close();
inputStream.close();
outputStream.close();

यह अपने आप को एक चैनल से दूसरे चैनल में बफर करने की तुलना में धीमा नहीं होगा, और संभवतः बड़े पैमाने पर तेजी से होगा। Javadocs के अनुसार:

कई ऑपरेटिंग सिस्टम वास्तव में कॉपी किए बिना फाइलसिस्टम कैश से सीधे लक्ष्य चैनल पर बाइट्स को स्थानांतरित कर सकते हैं।


7

मेरे परीक्षणों (Win7 64bit, 6GB RAM, Java6) के आधार पर, NIO ट्रांसफ़रफ़ॉर्म केवल छोटी फ़ाइलों के साथ तेज़ है और बड़ी फ़ाइलों पर बहुत धीमी हो जाती है। NIO डेटाबफ़र फ्लिप हमेशा मानक IO से बेहतर प्रदर्शन करता है।

  • 1000x2MB कॉपी करना

    1. NIO (ट्रांसफरफ्रॉम) ~ 2300ms
    2. NIO (प्रत्यक्ष डेटाबफ़र 5000 बी फ्लिप) ~ 3500ms
    3. मानक IO (बफर 5000 बी) ~ 6000ms
  • नकल 100x20mb

    1. NIO (प्रत्यक्ष डेटाबफ़र 5000 बी फ्लिप) ~ 4000ms
    2. NIO (ट्रांसफरफ्रॉम) ~ 5000 मी
    3. मानक IO (बफर 5000 बी) ~ 6500ms
  • नकल 1x1000mb

    1. NIO (प्रत्यक्ष डेटाबफ़र 5000 बी फ्लिप) ~ 4500s
    2. मानक आईओ (बफर 5000 बी) ~ 7000 मी
    3. NIO (ट्रांसफरफ्रॉम) ~ 8000ms

TransferTo () विधि एक फ़ाइल के विखंडन पर काम करती है; एक उच्च-स्तरीय फ़ाइल कॉपी विधि के रूप में इरादा नहीं किया गया था: विंडोज एक्सपी में एक बड़ी फ़ाइल की प्रतिलिपि कैसे करें?


6

प्रश्न के "उपयोगिता" भाग का उत्तर देते हुए:

FileChannelओवर के उपयोग की एक सूक्ष्म गुत्थी FileOutputStreamयह है कि किसी भी अवरोधक स्थिति (जैसे read()या write()) को किसी थ्रेड से बाधित करने वाली स्थिति में प्रदर्शन करने से चैनल अचानक बंद हो जाएगाjava.nio.channels.ClosedByInterruptException

अब, यह एक अच्छी बात हो सकती है अगर जो कुछ भी FileChannelइस्तेमाल किया गया था वह थ्रेड के मुख्य कार्य का हिस्सा है, और डिजाइन ने इसे ध्यान में रखा।

लेकिन कुछ सहायक फीचर जैसे कि लॉगिंग फंक्शन द्वारा उपयोग किए जाने पर यह pesky भी हो सकता है। उदाहरण के लिए, आप अपने लॉगिंग आउटपुट को अचानक बंद कर सकते हैं यदि लॉगिंग फ़ंक्शन एक थ्रेड द्वारा कॉल किया जाता है जो बाधित भी है।

यह दुर्भाग्यपूर्ण है कि यह इतना सूक्ष्म है क्योंकि इसके लिए लेखांकन न करने से कीड़े हो सकते हैं जो लेखन अखंडता को प्रभावित करते हैं। [1] [2]


3

मैं Base64 एन्कोडेड फ़ाइलों को डिकोड करने के लिए FileInputStream बनाम FileChannel के प्रदर्शन का परीक्षण किया। अपने एक्सपीरियंस में मैंने बड़ी फ़ाइल और पारंपरिक io का परीक्षण किया था जो कि nio की तुलना में थोड़ा तेज़ था।

FileChannel को jv के पूर्व संस्करणों में एक लाभ हो सकता है क्योंकि कई io संबंधित वर्गों में सिंक्रोनाइजेशन ओवरहेड है, लेकिन आधुनिक jvm अनावश्यक लॉक हटाने में बहुत अच्छे हैं।


2

यदि आप ट्रांसफ़ोटो फीचर या नॉन-ब्लॉकिंग फीचर्स का उपयोग नहीं कर रहे हैं, तो आपको पारंपरिक IO और NIO (2) के बीच अंतर नहीं दिखाई देगा क्योंकि पारंपरिक IO NIO के लिए मैप करता है।

लेकिन यदि आप NIO की विशेषताओं को TransferFrom / To या बफ़र का उपयोग करना चाहते हैं, तो निश्चित रूप से NIO जाने का तरीका है।


0

मेरा अनुभव है, कि NIO छोटी फाइलों के साथ ज्यादा तेज है। लेकिन जब बड़ी फ़ाइलों की बात आती है तो FileInputStream / FileOutputStream ज्यादा तेज होती है।


4
क्या आपको वह मिला हुआ था? मेरा खुद का अनुभव यह है कि बड़ी फ़ाइलों की तुलना में java.nioतेज़ है , न कि छोटी। java.io
स्टु थॉम्पसन

नहीं, मेरा अनुभव दूसरा तरीका है। java.nioजब तक फ़ाइल स्मृति के लिए मैप करने के लिए पर्याप्त छोटा है तब तक तेज़ है। यदि यह बड़ा हो जाता है (200 एमबी और अधिक) java.ioतेज।
tangens

वाह। मेरे विपरीत कुल। ध्यान दें कि आपको इसे पढ़ने के लिए फ़ाइल को मैप करने की आवश्यकता नहीं है - एक से पढ़ सकते हैं FileChannel.read()। फ़ाइलों का उपयोग करने के लिए पढ़ने के लिए सिर्फ एक ही दृष्टिकोण नहीं है java.nio
स्टु थॉम्पसन

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