জাভা এনআইও ফাইলচ্যানেল বনাম ফাইলআউটপুটসট্রিম কর্মক্ষমতা / উপযোগিতা


169

আমি যখন ফাইল সিস্টেমে ফাইলগুলি পড়তে এবং লিখতে FileChannelস্বাভাবিকভাবে নিও বনাম ব্যবহার করি তখন পারফরম্যান্সে (বা সুবিধাগুলি) কোনও পার্থক্য রয়েছে কিনা তা আমি খুঁজে বের করার চেষ্টা করছি FileInputStream/FileOuputStream। আমি পর্যবেক্ষণ করেছি যে আমার মেশিনে উভয়ই একই স্তরের সঞ্চালন করে, FileChannelবহুগুণে পথটি ধীর হয়। আমি কি দয়া করে এই দুটি পদ্ধতির তুলনায় আরও বিশদ জানতে পারি। আমি যে কোডটি ব্যবহার করেছি তা এখানে, আমি যে ফাইলটি দিয়ে পরীক্ষা করছি তা প্রায় 350MB। যদি আমি এলোমেলো অ্যাক্সেস বা এ জাতীয় উন্নত বৈশিষ্ট্যগুলি না দেখি তবে ফাইল আই / ওয়ের জন্য এনআইও ভিত্তিক ক্লাসগুলি ব্যবহার করা কি ভাল বিকল্প?

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ফাইলগুলি অনুলিপি করার জন্য আরও প্রচলিত হবে। যে কোনও কৌশলটি আপনার হার্ড ড্রাইভটিকে কোনও দ্রুত বা ধীর না করা উচিত, যদিও আমি অনুমান করি যে এটি যদি একবারে ছোট ছোট অংশগুলি পড়ে এবং মাথাটি অনাবিল সময় অনুসন্ধানে ব্যয় করে তবে সমস্যা হতে পারে।
টম হাটিন -

1
(আপনি কোন ওএস ব্যবহার করছেন, বা কোন জেআরই বিক্রেতা এবং সংস্করণ তা উল্লেখ করবেন না))
টম হাটিন -

ওফস দুঃখিত, আমি সান জেডিকে 6 এর সাথে এফসি 10 ব্যবহার করছি।
কেশব

উত্তর:


202

বড় ফাইল আকারের সাথে আমার অভিজ্ঞতাটি এর java.nioচেয়ে দ্রুততর হয়েছে java.ioসলিডলি দ্রুত। > 250% পরিসীমা পছন্দ করুন। এটি বলেছিল, আমি সুস্পষ্ট বাধাগুলি দূর করছি, যা আমি প্রস্তাব দিচ্ছি যে আপনার মাইক্রো-বেঞ্চমার্ক এতে ভুগতে পারে। তদন্তের সম্ভাব্য ক্ষেত্রগুলি:

বাফার আকার। মূলত আপনার যে অ্যালগরিদম রয়েছে তা হ'ল

  • ডিস্ক থেকে বাফারে অনুলিপি করুন
  • বাফার থেকে ডিস্কে অনুলিপি করুন

আমার নিজের অভিজ্ঞতা হয়েছে যে এই বাফার আকারটি টিউনিংয়ের জন্য উপযুক্ত । আমি আমার আবেদনের এক অংশের জন্য 4KB তে স্থির হয়েছি, অন্যটির জন্য 256KB। আমি সন্দেহ করি যে আপনার কোডটি এত বড় বাফার নিয়ে ভুগছে। এটি নিজের কাছে প্রমাণ করার জন্য 1KB, 2KB, 4KB, 8KB, 16KB, 32KB এবং 64KB এর বাফারগুলির সাথে কিছু মানদণ্ড চালান।

একই ডিস্কে পড়া এবং লেখার জন্য জাভা বেনমার্কগুলি সম্পাদন করবেন না।

যদি আপনি এটি করেন, তবে আপনি সত্যিই ডিস্কটি বেঞ্চমার্ক করছেন, জাভা নয়। আমি আরও পরামর্শ দেব যে যদি আপনার সিপিইউ ব্যস্ত না থাকে তবে আপনি সম্ভবত অন্য কোনও বাধা নিয়ে অভিজ্ঞ হয়ে যাচ্ছেন।

আপনার প্রয়োজন না হলে একটি বাফার ব্যবহার করবেন না।

আপনার লক্ষ্যটি যদি অন্য ডিস্ক বা এনআইসি হয় তবে কেন মেমোরিতে অনুলিপি করবেন? বৃহত্তর ফাইলগুলির সাথে, ক্ষতিকারক বিলম্বটি ক্ষুদ্র হয় না।

অন্যদের মতো বলেছেন, ব্যবহার করুন FileChannel.transferTo()বা FileChannel.transferFrom()। এখানে মূল সুবিধাটি হ'ল জেভিএম উপস্থিত থাকলে ডিএমএ ( ডাইরেক্ট মেমোরি অ্যাক্সেস ) এর ওএসের অ্যাক্সেস ব্যবহার করে । (এই বাস্তবায়ন নির্ভরশীল, কিন্তু সাধারণ কাজের সিপিইউ উপর আধুনিক সূর্য ও আইবিএম সংস্করণ যেতে ভাল হয়।) কি হবে ডেটা গন্তব্যে বাসে, সরাসরি ডিস্ক থেকে / যায়, এবং তারপর ... মাধ্যমে যে কোনো বর্তনী বাইপাস হয় র‌্যাম বা সিপিইউ।

আমি আমার দিনরাত্রি যে ওয়েব অ্যাপটিতে কাজ করেছি তা খুব ভারী। আমি মাইক্রো মানদণ্ড এবং বাস্তব-বিশ্বের মানদণ্ডও করেছি। এবং ফলাফলগুলি আমার ব্লগে উঠে এসেছে, একবার দেখুন:

উত্পাদন ডেটা এবং পরিবেশ ব্যবহার করুন

মাইক্রো-বেঞ্চমার্কগুলি বিকৃতির প্রবণতা। যদি আপনি পারেন তবে আপনি যে হার্ডওয়ারটি প্রত্যাশা করছেন তার উপর চাপ দিয়ে বোঝাও যে আপনি কী পরিকল্পনা করছেন তা থেকে তথ্য সংগ্রহের চেষ্টা করুন।

আমার মানদণ্ডগুলি দৃ and় এবং নির্ভরযোগ্য কারণ তারা লগগুলিতে জড়ো হওয়া একটি উত্পাদন ব্যবস্থা, একটি মৌমাছি সিস্টেম, বোঝার অধীনে একটি ব্যবস্থা নিয়েছিল। না আমার নোটবুক এর 7200 আরপিএম 2.5 "সময় SATA ড্রাইভ যখন আমি তীব্র জেভিএম কাজ হিসাবে আমার হার্ড ডিস্ক দেখেছেন।

আপনি কি চালাচ্ছেন? এটা গুরুত্বপূর্ণ।


@ এসটু থম্পসন - আপনার পোস্টের জন্য ধন্যবাদ। আমি একই বিষয়ে গবেষণা করছি যেহেতু আমি আপনার উত্তর জুড়ে এসেছি। আমি জাভা প্রোগ্রামারদের কাছে এনও প্রকাশ করে ওএসের উন্নতিগুলি বোঝার চেষ্টা করছি। তাদের মধ্যে বেশ কয়েকজন হচ্ছেন - ডিএমএ এবং মেমরি ম্যাপযুক্ত ফাইল। আপনি কি আরও অনেক উন্নতি করতে পেরেছেন? পিএস - আপনার ব্লগের লিঙ্কগুলি নষ্ট হয়ে গেছে।
অ্যান্ডি ডুফ্রেসনে

@ অ্যান্ডি ডুফ্রেসনে আমার ব্লগটি এই মুহুর্তে নিচে রয়েছে, এটিকে সরানোর প্রক্রিয়াতে - এই সপ্তাহের শেষের দিকে উঠবে।
স্টু থম্পসন

15
আর্কাইভ.আর.জে ব্লগের লিঙ্কগুলি এখানে: ওয়েব.আরচিভ.আর.ইউ.বি .2020115094827
আর্থার এডেলস্টেইন

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();

এটি একটি চ্যানেল থেকে অন্য চ্যানেলে নিজেকে বাফার করার চেয়ে ধীর হবে না এবং সম্ভাব্যভাবে দ্রুততর হবে। জাভাডোকস অনুসারে:

অনেক অপারেটিং সিস্টেমগুলি প্রকৃতপক্ষে অনুলিপি না করে সরাসরি ফাইল সিস্টেম ক্যাশে থেকে লক্ষ্য চ্যানেলে বাইটগুলি স্থানান্তর করতে পারে।


7

আমার পরীক্ষার ভিত্তিতে (উইন 64৪ বিবিট, GB জিবি র‌্যাম, জাভা)) এনআইও ট্রান্সফারফর্ম কেবলমাত্র ছোট ফাইলের সাথে দ্রুত এবং বড় ফাইলগুলিতে খুব ধীর হয়ে যায়। NIO ডেটাবফার ফ্লিপ সর্বদা স্ট্যান্ডার্ড আইওকে ছাড়িয়ে যায়।

  • 1000x2MB অনুলিপি করা হচ্ছে

    1. এনআইও (ট্রান্সফারফর্ম) ~ 2300ms
    2. এনআইও (সরাসরি ডেটাব্যাফার 5000 বি ফ্লিপ) ~ 3500ms
    3. স্ট্যান্ডার্ড আইও (বাফার 5000 বি) ~ 6000 মিমি
  • 100x20mb কপি করা হচ্ছে

    1. এনআইও (সরাসরি ডেটাব্যাফার 5000 বি ফ্লিপ) ~ 4000ms
    2. এনআইও (ট্রান্সফারফর্ম) 5000 ডলার
    3. স্ট্যান্ডার্ড আইও (বাফার 5000 বি) ~ 6500ms
  • 1x1000mb কপি করা হচ্ছে

    1. এনআইও (সরাসরি ডেটাব্যাফার 5000 বি ফ্লিপ) ~ 4500
    2. স্ট্যান্ডার্ড আইও (বাফার 5000 বি) ~ 7000 মিমি
    3. এনআইও (ট্রান্সফারফর্ম) ~ 8000ms

ট্রান্সফারটো () পদ্ধতিটি কোনও ফাইলের অংশগুলিতে কাজ করে; উচ্চ-স্তরের ফাইল অনুলিপি পদ্ধতি হিসাবে তৈরি করা হয়নি: উইন্ডোজ এক্সপিতে একটি বড় ফাইল কীভাবে অনুলিপি করবেন?


6

প্রশ্নের "উপযোগ" অংশের উত্তর দেওয়া:

ব্যবহার করার একটি বরং সূক্ষ্ম gotcha হয় FileChannelবেশি FileOutputStreamযে তার ব্লক অপারেশন (যেমন কোন কার্য সম্পাদন করে তা read()বা write()একটি থ্রেড থেকে) যে এর বিঘ্নিত রাষ্ট্র মাধ্যমের প্রতি সঙ্গে হঠাৎ কারণ হবে java.nio.channels.ClosedByInterruptException

এখন, এটি ভাল জিনিস হতে পারে যদি এর জন্য যা কিছু FileChannelব্যবহৃত হত তা থ্রেডের মূল ফাংশনের অংশ এবং ডিজাইন এটিকে বিবেচনায় নিয়েছে।

লগিং ফাংশন যেমন কিছু সহায়ক বৈশিষ্ট্য দ্বারা ব্যবহৃত হয় তবে এটি সমস্যাযুক্তও হতে পারে। উদাহরণস্বরূপ, আপনি যদি লগিং ফাংশনটি কোনও থ্রেড দ্বারা ডেকে আনে যা ঘটেছিল তা হঠাৎ বন্ধ হয়ে যায় তবে আপনি আপনার লগিং আউটপুটটি হঠাৎ বন্ধ দেখতে পাবেন।

এটি দুর্ভাগ্যজনক যে এটি এত সূক্ষ্ম কারণ এর জন্য অ্যাকাউন্ট না দেওয়ার ফলে লেখাগুলির অখণ্ডতায় প্রভাব ফেলতে পারে এমন বাগগুলি হতে পারে। [1] [2]


3

বেস 64 এনকোডযুক্ত ফাইলগুলি ডিকোডিংয়ের জন্য আমি ফাইলআইনপুটস্ট্রিম বনাম ফাইলচ্যানেলের পারফরম্যান্স পরীক্ষা করেছি। আমার পরীক্ষাগুলিতে আমি বরং বড় ফাইল পরীক্ষা করেছি এবং traditionalতিহ্যবাহী আইও নিওয়ের চেয়ে কিছুটা দ্রুত ছিল।

ফাইল চ্যানেলের jvm এর পূর্ববর্তী সংস্করণগুলিতে বেশ কয়েকটি আইও সম্পর্কিত ক্লাসে সিনক্রোনাইজেশন ওভারহেডের কারণে একটি সুবিধা থাকতে পারে তবে আধুনিক জেভিএম অপ্রয়োজনীয় লকগুলি অপসারণে বেশ ভাল।


2

আপনি যদি ট্রান্সফারটো বৈশিষ্ট্য বা অবরুদ্ধকরণের বৈশিষ্ট্যগুলি ব্যবহার না করে থাকেন তবে আপনি traditionalতিহ্যবাহী আইও এবং এনআইও (2) এর মধ্যে কোনও পার্থক্য লক্ষ্য করবেন না কারণ theতিহ্যবাহী আইও মানচিত্রটি এনআইওতে মানচিত্র করে।

তবে আপনি যদি ট্রান্সফারফর্ম / টু বা এনিয়েও যেমন বাফার ব্যবহার করতে চান যেমন এনআইও বৈশিষ্ট্যগুলি ব্যবহার করতে পারেন তবে অবশ্যই এনআইও হ'ল উপায়।


0

আমার অভিজ্ঞতা হ'ল, ছোট ফাইলগুলির সাথে এনআইও আরও দ্রুত। তবে এটি যখন বড় ফাইলগুলির ক্ষেত্রে আসে তখন ফাইলআইনপুটস্ট্রিম / ফাইলআউটপুটস্ট্রিমটি অনেক দ্রুত হয়।


4
তুমি কি মিশে গেছ? আমার নিজের অভিজ্ঞতাটি এটি বৃহত্তর ফাইলগুলির চেয়ে java.nioদ্রুত , ছোট নয়। java.io
স্টু থম্পসন 21

না, আমার অভিজ্ঞতাটি অন্যরকম। java.nioমেমরি ম্যাপ করার জন্য ফাইলটি যতক্ষণ ছোট যথেষ্ট তত দ্রুত। যদি এটি বড় হয় (200 এমবি এবং আরও) java.ioদ্রুত হয়।
স্পর্শকাতর

কি দারুন. আমার সম্পূর্ণ বিপরীত। নোট করুন যে কোনও ফাইল পড়ার জন্য আপনাকে অগত্যা মানচিত্রের প্রয়োজন হবে না - এটির কাছ থেকে কেউ পড়তে পারে FileChannel.read()। ফাইলগুলি ব্যবহার করে কেবল একটি একক পন্থা নেই java.nio
স্টু থম্পসন

2
আপনি কি এটি পরীক্ষা করেছেন?
সাইনডেম
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.