سؤال

أحاول معرفة ما إذا كان هناك أي اختلاف في الأداء (أو المزايا) عندما نستخدم nio FileChannel مقابل العادي FileInputStream/FileOuputStream لقراءة وكتابة الملفات إلى نظام الملفات.لقد لاحظت أن كلاهما يعمل على نفس المستوى على جهازي، وأيضًا عدة مرات FileChannel الطريقة أبطأ.هل يمكنني معرفة المزيد من التفاصيل مقارنة هاتين الطريقتين.هذا هو الكود الذي استخدمته، والملف الذي أختبره موجود 350MB.هل يعد استخدام الفئات المستندة إلى 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");
    }
}
هل كانت مفيدة؟

المحلول

تجربتي مع أحجام الملفات الكبيرة كانت كذلك java.nio أسرع من java.io. أسرع بقوة. كما هو الحال في نطاق> 250%.ومع ذلك، فأنا أقوم بإزالة الاختناقات الواضحة، والتي أقترح أن المعيار الجزئي الخاص بك قد يعاني منها.المجالات المحتملة للتحقيق:

حجم المخزن المؤقت. الخوارزمية التي لديك في الأساس هي

  • نسخ من القرص إلى المخزن المؤقت
  • نسخ من المخزن المؤقت إلى القرص

لقد كانت تجربتي الخاصة أن حجم المخزن المؤقت هذا هو ناضج لضبط.لقد استقرت على 4 كيلو بايت لجزء واحد من طلبي، و256 كيلو بايت للجزء الآخر.أظن أن الكود الخاص بك يعاني من مثل هذا المخزن المؤقت الكبير.قم بتشغيل بعض المعايير باستخدام مخازن مؤقتة بسعة 1 كيلو بايت و2 كيلو بايت و4 كيلو بايت و8 كيلو بايت و16 كيلو بايت و32 كيلو بايت و64 كيلو بايت لتثبت ذلك لنفسك.

لا تقم بتنفيذ معايير جافا التي تقرأ وتكتب على نفس القرص.

إذا قمت بذلك، فأنت في الواقع تقوم بقياس أداء القرص، وليس Java.أود أيضًا أن أقترح أنه إذا لم تكن وحدة المعالجة المركزية لديك مشغولة، فمن المحتمل أنك تواجه بعض الاختناقات الأخرى.

لا تستخدم المخزن المؤقت إذا لم تكن بحاجة إلى ذلك.

لماذا النسخ إلى الذاكرة إذا كان هدفك هو قرص آخر أو بطاقة واجهة الشبكة (NIC)؟مع الملفات الأكبر حجمًا، يكون زمن الاستجابة غير بسيط.

كما قال الآخرون، استخدم FileChannel.transferTo() أو FileChannel.transferFrom().الميزة الرئيسية هنا هي أن JVM يستخدم وصول نظام التشغيل إلى DMA (الوصول المباشر للذاكرة)، إذا كان موجودا. (يعتمد هذا على التنفيذ، ولكن إصدارات Sun وIBM الحديثة على وحدات المعالجة المركزية للأغراض العامة جيدة للاستخدام.) ما يحدث هو أن البيانات تنتقل مباشرة من/إلى القرص، إلى الحافلة، ثم إلى الوجهة...تجاوز أي دائرة من خلال ذاكرة الوصول العشوائي أو وحدة المعالجة المركزية.

تطبيق الويب الذي قضيت أيامي وليالي في العمل عليه ثقيل للغاية في عمليات الإدخال والإخراج.لقد قمت بإجراء معايير دقيقة ومعايير واقعية أيضًا.والنتائج موجودة على مدونتي، ألقِ نظرة وانظر:

استخدام بيانات الإنتاج والبيئات

المعايير الجزئية عرضة للتشويه.إذا استطعت، فابذل جهدًا لجمع البيانات مما تخطط للقيام به بالضبط، مع الحمل الذي تتوقعه، على الأجهزة التي تتوقعها.

معاييري قوية وموثوقة لأنها حدثت على نظام إنتاج، نظام قوي، نظام تحت الحمل، تم جمعها في السجلات. لا محرك الأقراص SATA الخاص بجهاز الكمبيوتر المحمول الخاص بي بسرعة 7200 دورة في الدقيقة مقاس 2.5 بوصة بينما كنت أشاهد بشكل مكثف بينما يعمل JVM على القرص الصلب الخاص بي.

ما الذي تعمل عليه؟لا يهم.

نصائح أخرى

إذا الشيء الذي تريد المقارنة هو أداء نسخ الملف، ثم لاختبار القناة يجب ان تفعل هذا بدلا من ذلك:

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:

<اقتباس فقرة>   

والعديد من أنظمة التشغيل يمكن نقل بايت مباشرة من ذاكرة التخزين المؤقت نظام الملفات إلى القناة المستهدفة دون نسخ منها في الواقع.

بناءً على اختباراتي (Win7 64bit و6GB RAM وJava6)، يكون NIO TransferFrom سريعًا فقط مع الملفات الصغيرة ويصبح بطيئًا جدًا مع الملفات الكبيرة.يتفوق مخزن بيانات NIO دائمًا على أداء الإدخال والإخراج القياسي.

  • نسخ 1000x2 ميجابايت

    1. NIO (النقل من) ~ 2300 مللي ثانية
    2. NIO (مخزن بيانات مباشر 5000b فليب) ~ 3500 مللي ثانية
    3. الإدخال/الإخراج القياسي (المخزن المؤقت 5000b) ~6000 مللي ثانية
  • نسخ 100x20 ميجابايت

    1. NIO (مخزن بيانات مباشر 5000b فليب) ~ 4000 مللي ثانية
    2. NIO (النقل من) ~ 5000 مللي ثانية
    3. الإدخال/الإخراج القياسي (المخزن المؤقت 5000b) ~6500 مللي ثانية
  • نسخ 1x1000 ميجابايت

    1. NIO (مخزن بيانات مباشر 5000b فليب) ~ 4500 ثانية
    2. الإدخال/الإخراج القياسي (المخزن المؤقت 5000b) ~7000 مللي ثانية
    3. NIO (النقل من) ~ 8000 مللي ثانية

تعمل طريقة TransferTo() على أجزاء من الملف؛لم يكن المقصود منه أن يكون طريقة لنسخ الملفات عالية المستوى:كيفية نسخ ملف كبير في نظام التشغيل Windows XP؟

الإجابة على الجزء "المفيد" من السؤال:

مسكتك خفية إلى حد ما من الاستخدام FileChannel زيادة FileOutputStream هو أن يؤدي أيًا من عمليات الحظر الخاصة به (على سبيل المثال. read() أو write()) من موضوع موجود حالة متقطعة سيؤدي إلى إغلاق القناة فجأة java.nio.channels.ClosedByInterruptException.

الآن، قد يكون هذا أمرًا جيدًا إذا كان الأمر كذلك FileChannel تم استخدامه كجزء من الوظيفة الرئيسية للخيط، وقد أخذ التصميم ذلك في الاعتبار.

ولكن قد يكون الأمر مزعجًا أيضًا إذا تم استخدامه بواسطة بعض الميزات الإضافية مثل وظيفة التسجيل.على سبيل المثال، يمكنك العثور على مخرجات التسجيل الخاصة بك مغلقة فجأة إذا تم استدعاء وظيفة التسجيل بواسطة مؤشر ترابط تمت مقاطعته أيضًا.

من المؤسف أن هذا الأمر دقيق للغاية لأن عدم مراعاة ذلك يمكن أن يؤدي إلى أخطاء تؤثر على سلامة الكتابة.[1][2]

وأنا اختبار أداء فيلينبوتستريام مقابل FileChannel لفك base64 في الملفات المشفرة. في experients بلدي اختبرت ملف كبير نوعا ما، وكان الإعلام والتوعية التقليدية في كل حين أسرع قليلا من NIO.

وFileChannel ربما كان ميزة في الإصدارات السابقة من JVM بسبب النفقات العامة synchonization في عدة فصول تتعلق الإعلام والتوعية، ولكن JVM الحديثة هي جيدة جدا في إزالة الأقفال لزوم لها.

إذا كنت لا تستخدم ميزة أو ميزات transferTo غير مؤمن أنك لن تلاحظ الفرق بين IO التقليدية وNIO (2) لأن IO التقليدي يعين NIO.

ولكن إذا كنت تستطيع استخدام ميزات NIO مثل transferFrom / لأو تريد استخدام المخازن، ثم بالطبع NIO هو الطريق للذهاب.

وتجربتي هي، أن NIO هو أسرع بكثير مع ملفات صغيرة. ولكن عندما يتعلق الأمر الملفات الكبيرة فيلينبوتستريام / FileOutputStream هو أسرع بكثير.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top