অ্যারে অনুলিপি করার জন্য লুপের চেয়ে System.arraycopy (…) ব্যবহার করা কি ভাল?


90

আমি দুটি নতুন অ্যারে একসাথে অবজেক্টগুলির একটি নতুন অ্যারে তৈরি করতে চাই।

এগুলি শূন্য হতে পারে না, তবে আকার 0 হতে পারে।

আমি এই দুটি উপায়ের মধ্যে চয়ন করতে পারি না: সেগুলি কি সমতুল্য বা আরও কার্যকর (উদাহরণস্বরূপ system.arraycopy () পুরো খণ্ডগুলি অনুলিপি করে)?

MyObject[] things = new MyObject[publicThings.length+privateThings.length];
System.arraycopy(publicThings, 0, things, 0, publicThings.length);
System.arraycopy(privateThings, 0, things,  publicThings.length, privateThings.length);

বা

MyObject[] things = new MyObject[publicThings.length+privateThings.length];
for (int i = 0; i < things.length; i++) {
    if (i<publicThings.length){
        things[i] = publicThings[i]
    } else {
        things[i] = privateThings[i-publicThings.length]        
    }
}

কোডের চেহারাটি কি কেবল তফাত?

সম্পাদনা: লিঙ্কিত প্রশ্নের জন্য ধন্যবাদ, তবে তাদের মনে হচ্ছে যে কোনও নিষ্পত্তিহীন আলোচনা হয়েছে:

এটি কি সত্যই দ্রুত হয় যদি it is not for native types: বাইট [], অবজেক্ট [], চর []? অন্য সমস্ত ক্ষেত্রে, একটি টাইপ চেক কার্যকর করা হয়, যা আমার ক্ষেত্রে এবং তাই সমান হবে ... না?

অন্য সংযুক্ত প্রশ্নে, তারা বলে যে the size matters a lotআকারের জন্য> 24 সিস্টেম.আররেইকপি () জিতবে, 10 এর চেয়ে কমের জন্য লুপের জন্য ম্যানুয়াল আরও ভাল ...

এখন আমি সত্যিই বিভ্রান্ত।


16
arraycopy()একটি নেটিভ কল, যা অবশ্যই খুব দ্রুত।
সোটিরিওস ডেলিমনোলিস

4
আপনি দুটি পৃথক বাস্তবায়ন বেঞ্চমার্ক চেষ্টা করেছেন?
অ্যালেক্স

4
: একটি বর্ণন এখানে নিন stackoverflow.com/questions/2772152/...
Sotirios Delimanolis

15
ভবিষ্যতে যাকে আপনি সবচেয়ে পঠনযোগ্য এবং সহজ বজায় রাখতে পছন্দ করেন তা চয়ন করা উচিত। কেবলমাত্র যখন আপনি নির্ধারণ করেন যে এটি কোনও বাধা-উত্সের উত্স তা আপনার দৃষ্টিভঙ্গি পরিবর্তন করা উচিত।
আরশাজি

4
চাকা পুনর্নবীকরণ করবেন না!
ক্যামিকার

উত্তর:


87
public void testHardCopyBytes()
{
    byte[] bytes = new byte[0x5000000]; /*~83mb buffer*/
    byte[] out = new byte[bytes.length];
    for(int i = 0; i < out.length; i++)
    {
        out[i] = bytes[i];
    }
}

public void testArrayCopyBytes()
{
    byte[] bytes = new byte[0x5000000]; /*~83mb buffer*/
    byte[] out = new byte[bytes.length];
    System.arraycopy(bytes, 0, out, 0, out.length);
}

আমি জানি জুনিট পরীক্ষা সত্যই বেঞ্চমার্কিংয়ের জন্য সেরা নয়, তবে
টেস্টহার্ডকপিটাইটস সম্পন্ন করতে 0.157 সেকেন্ড নিয়েছে
এবং
টেস্ট অ্যারেকপি বাইটসগুলি সম্পন্ন করতে 0.086 সেকেন্ড নিয়েছিল।

আমি মনে করি এটি ভার্চুয়াল মেশিনের উপর নির্ভর করে তবে মনে হয় এটি একক অ্যারে উপাদানগুলি অনুলিপি করার পরিবর্তে মেমরির ব্লকগুলি অনুলিপি করে। এটি একেবারে কর্মক্ষমতা বাড়িয়ে তুলবে।

সম্পাদনা:
দেখে মনে হচ্ছে System.arraycopy এর অভিনয়টি পুরো জায়গা জুড়ে রয়েছে। যখন স্ট্রিংগুলি বাইটের পরিবর্তে ব্যবহৃত হয় এবং অ্যারেগুলি ছোট (আকার 10) হয়, তখন আমি এই ফলাফলগুলি পাই:

    String HC:  60306 ns
    String AC:  4812 ns
    byte HC:    4490 ns
    byte AC:    9945 ns

অ্যারে 0x1000000 আকারে এ কেমন লাগে তা এখানে। দেখে মনে হচ্ছে System.arraycopy অবশ্যই বড় বড় অ্যারেগুলিতে জয়ী হয়।

    Strs HC:  51730575 ns
    Strs AC:  24033154 ns
    Bytes HC: 28521827 ns
    Bytes AC: 5264961 ns

কত অদ্ভুত!

রেফারেন্সগুলি আলাদাভাবে অনুলিপি করে দেখানোর জন্য ড্যারেন ধন্যবাদ। এটি আরও অনেক আকর্ষণীয় সমস্যা তৈরি করেছে!


4
আপনার প্রচেষ্টার জন্য আপনাকে ধন্যবাদ, তবে আপনি আপাতদৃষ্টিতে অত্যন্ত গুরুত্বপূর্ণ পয়েন্টগুলি মিস করেছেন: অ-নেটিভ টাইপগুলি (যে কোনও বিন্যাসে একটি এলোমেলো ক্লাস তৈরি করুন যাতে অ্যারেটিতে রেফারেন্স রয়েছে) এবং আকার ... ছোট অ্যারের আকারের জন্য মনে হয়, ম্যানুয়ালটির জন্য লুপটি দ্রুততর। এটি সংশোধন করার জন্য যত্ন?
ডেরেন

4
ওহ, আপনি ঠিক বলেছেন! এটা আগ্রহী। এই অ্যারেগুলিতে বাইটের পরিবর্তে স্ট্রিংগুলি রাখার ফলে একটি বিশাল পার্থক্য আসে: <<< টেস্টহার্ডকপিস্টারস: 0.161 গুলি >>> <<< টেস্টআরারিকপিস্টারস: 0.170s >>>
ট্রেন্ট ছোট

তাহলে কী ফলাফল পাচ্ছেন? অ্যারে আকার = 10 দিয়ে চেষ্টা করাও আকর্ষণীয় হবে ... ধন্যবাদ! (আমি আশা করি আমার আইডিই এখানে থাকি, আমি সংকলক ছাড়াই কোডিং করছি)।
ডেরেন

ভাল এখন আমি এগুলিকে কিছু System.nanoTime () কলগুলিতে আবদ্ধ করে রেখেছি এবং প্রতিটি আকারে কত ন্যানোসেকেন্ড নেয় তা দেখতে = = 10 সেট করে। মনে হচ্ছে ছোট আদিম অ্যারেগুলির জন্য, লুপগুলি আরও ভাল; রেফারেন্সের জন্য, অ্যারেকপিটি আরও ভাল .: <<< টেস্টহার্ডকপি বিটস: 4491 এনএস >>> <<< টেস্টহার্ডকপিস্টারস: 56778 এনএসএস >>> <<< পরীক্ষাআরাইকপিটাইটস: 10265 এনএসএস >>> <<< পরীক্ষাআরাইকপিস্টারস: 4490 এনএস >>>
ট্রেন্ট ছোট

খুব আকর্ষণীয় ফলাফল! তোমাকে অনেক ধন্যবাদ! আপনি কি দয়া করে এটিকে অন্তর্ভুক্ত করার জন্য আপনার উত্তরটি সম্পাদনা করতে পারেন, এবং আমি আনন্দের সাথে এটি গ্রহণ করব যাতে এটি সবার জন্য প্রথম দেখা যায় ... আপনি আমার ভোট ইতিমধ্যে পেয়ে গেছেন। :)
Daren

36

Arrays.copyOf(T[], int)পড়া সহজ। ইন্টার্নি এটি ব্যবহার করেSystem.arraycopy() যা একটি নেটিভ কল।

আপনি এটি দ্রুত পেতে পারবেন না!


দেখে মনে হচ্ছে আপনি বেশ কয়েকটি প্রথম বিষয়ের উপর নির্ভর করতে পারেন, তবে সেই ফুকশনটি নির্দেশ করার জন্য ধন্যবাদ যা আমি জানতাম না এবং এটি পড়া সহজ ছিল। :)
ডেরেন

হ্যাঁ! @ স্বেটোস্লাভ সোসালোভ যেমন বলেছিলেন এটি বেশ কয়েকটি বিষয়ের উপর নির্ভর করে। আমি কেবল অ্যারেস.কপিঅফ
ফিলিপ স্যান্ডার

4
copyOfসর্বদা প্রতিস্থাপন করতে পারে না arraycopyতবে এটি ব্যবহারের ক্ষেত্রে এটি উপযুক্ত।
ব্লেজারব্লেড

4
NB আপনি যদি পারফরম্যান্সের দিকে তাকান তবে এটি মেমরি বরাদ্দকরণের প্রয়োজন হিসাবে System.arraycopy () এর মতো দ্রুত হবে না। যদি এটি একটি লুপ থাকে তবে বারবার বরাদ্দগুলি আবর্জনা সংগ্রহের দিকে নিয়ে যায় যা একটি বিশাল পারফরম্যান্স হিট হবে।
উইল ক্যালডারউড

4
@ ফিলিপস্যান্ডার কেবল খালি আমার বোকা ছিল না তা খতিয়ে দেখার জন্য আমি আমার গেমের লুপটিতে 1MB অ্যারের অনুলিপি কোড যুক্ত করেছি, যা প্রায়শই কখনও জিসিকে গুলি চালায় না। অ্যারে.কপিঅফ () দিয়ে আমার ডিভিএম প্রতি সেকেন্ডে 5 বার জিসি কল করেছিল এবং গেমটি চূড়ান্তভাবে পিছিয়ে গেছে। আমি মনে করি এটি মেমরি বরাদ্দ আছে বলা নিরাপদ।
কল্ডারউড

17

এটি ভার্চুয়াল মেশিনের উপর নির্ভর করে, তবে System.arraycopy আপনাকে দেশীয় কার্য সম্পাদন করতে পারে এমন নিকটতম স্থানটি দেবে।

আমি এম্বেড থাকা সিস্টেমগুলির জন্য জাভা বিকাশকারী হিসাবে 2 বছর ধরে কাজ করেছি (যেখানে কর্মক্ষমতা একটি বিশাল অগ্রাধিকার) এবং যেখানেই System.arraycopy ব্যবহার করা যেতে পারে, আমি বেশিরভাগই এটি ব্যবহার করেছি / এটি বিদ্যমান কোডটিতে ব্যবহার করে দেখেছি। কর্মক্ষমতা যখন ইস্যু হয় তখন লুপের চেয়ে সর্বদা পছন্দ করা হয়। পারফরম্যান্স যদি কোনও বড় সমস্যা না হয় তবে আমি লুপটি সহ যাব। পড়তে অনেক সহজ।


অ্যারের আকার এবং প্রকারের মতো (বুনিয়াদি বনাম উত্তরাধিকার সূত্রে) কার্য সম্পাদনকে প্রভাবিত করে।
ডেরেন

4
হ্যাঁ, এটি প্রতি 'দেশীয় পারফরম্যান্স' নয়, এ কারণেই আমি বলেছিলাম যে আমি যেখানে 'পারি' বেশিরভাগ ক্ষেত্রেই ব্যবহার করি (আপনি দেখতে পাবেন এটি বেশিরভাগ লুপ অনুলিপিতে জিতবে)। আমি অনুমান করার কারণটি হ'ল: এটি যখন আদিম ধরণের একটি ছোট অ্যারে হয় তখন 'কস্ট-টু-কল' পারফরম্যান্স বৃদ্ধির চেয়ে বড় হয়। জেএনআই ব্যবহার করা একই কারণে কর্মক্ষমতা হ্রাস করতে পারে - নেটিভ কোডটি নিজেই দ্রুত, তবে এটি জাভা প্রক্রিয়া থেকে কল করা - এত বেশি নয়।

গৌণ সংশোধন, অ্যারেসকপি জেএনআই নয়, এটি একটি স্বতন্ত্র। ইন্টারএনসিকগুলি জেএনআইয়ের তুলনায় অনেক দ্রুত। কীভাবে এবং কখন জেআইটি সংকলক এটিকে অভ্যন্তরীণে রূপান্তরিত করে তা ব্যবহৃত জেভিএম / সংকলক নির্ভর করে।
নিতসান ওয়াকার্ট

4
Arrays.copyবিদ্যমান নেই, Arrays.copyOfএটি একটি লাইব্রেরি ফাংশন।
ব্লেজারব্লেড

14

অনুমান এবং সম্ভবত পুরানো তথ্যের উপর নির্ভর করার পরিবর্তে আমি কয়েকটি বেঞ্চমার্ক ব্যবহার করে চালিয়েছি । প্রকৃতপক্ষে, ক্যালিপার কিছু উদাহরণ নিয়ে আসেন, যার মধ্যে রয়েছে CopyArrayBenchmarkএই প্রশ্নটির সঠিক সমাধান! আপনাকে যা করতে হবে তা চালাতে হবে

mvn exec:java -Dexec.mainClass=com.google.caliper.runner.CaliperMain -Dexec.args=examples.CopyArrayBenchmark

আমার ফলাফলগুলি অরাকলের জাভা হটস্পট (টিএম) 64-বিট সার্ভার ভিএম, 1.8.0_31-বি 13 এর উপর ভিত্তি করে 2010 সালের মাঝামাঝি ম্যাকবুক প্রো (ইন্টেল আরানডেল আই 7, 8 জিবি র‌্যাম সহ 10.11.6) চলছে। আমি বিশ্বাস করি না যে কাঁচা সময় ডেটা পোস্ট করা দরকারী useful বরং, আমি সমর্থনকারী ভিজ্যুয়ালাইজেশনের সাথে সিদ্ধান্তগুলি সংক্ষিপ্ত করব।

সংক্ষেপে:

  • forএকটি নতুন তাত্ক্ষণিক অ্যারেতে প্রতিটি উপাদানকে অনুলিপি করার জন্য একটি ম্যানুয়াল লুপ লেখা কখনই সুবিধাজনক হয় না, স্বল্প অ্যারে বা দীর্ঘ অ্যারে হোক।
  • Arrays.copyOf(array, array.length)এবং array.clone()উভয়ই ধারাবাহিকভাবে দ্রুত। এই দুটি কৌশল কার্য সম্পাদনে প্রায় অভিন্ন; আপনি কোনটি পছন্দ করেন তা স্বাদের বিষয়।
  • System.arraycopy(src, 0, dest, 0, src.length)প্রায় হিসাবে দ্রুত এবং খুব ধারাবাহিকভাবে না। (কেসটি 50000 এর জন্য দেখুন )) এর কারণে এবং কলটির ভারসোসিটির কারণে, আমি আপনাকে পরামর্শ দিচ্ছি যে কোন উপাদানগুলি কোথায় অনুলিপি করা হয়েছে তার উপর যদি আপনার সূক্ষ্ম নিয়ন্ত্রণের প্রয়োজন হয়।Arrays.copyOf(array, array.length)array.clone()intSystem.arraycopy()

সময় প্লট এখানে:

দৈর্ঘ্যের 5 টি অ্যারে অনুলিপি করার সময় দৈর্ঘ্যের 500 টি অ্যারে অনুলিপি করার সময় 50000 দৈর্ঘ্যের অ্যারে অনুলিপি করার সময়


4
ইন্ট কপি করার বিষয়ে কি অদ্ভুত কিছু আছে? এটি আশ্চর্যজনক বলে মনে হচ্ছে এটি বড় আকারের স্কেলের ইনটগুলিতে অ্যারেকপিটি ধীর হবে।
তিমিবার্গ

6

নেটিভ পদ্ধতিগুলি কার্যকর করার মতো Arrays.copyOf(T[], int)কিছু ওভারহেড থাকে তবে এর অর্থ এই নয় যে আপনি জেএনআই ব্যবহার করে এটি কার্যকর করছেন বলে এটি দ্রুত নয়।

সবচেয়ে সহজ উপায় হ'ল একটি মানদণ্ড এবং পরীক্ষা লিখুন।

Arrays.copyOf(T[], int)আপনার সাধারণ forলুপের চেয়ে দ্রুত এটি পরীক্ষা করতে পারেন ।

এখান থেকে মানদণ্ডের কোড : -

public void test(int copySize, int copyCount, int testRep) {
    System.out.println("Copy size = " + copySize);
    System.out.println("Copy count = " + copyCount);
    System.out.println();
    for (int i = testRep; i > 0; --i) {
        copy(copySize, copyCount);
        loop(copySize, copyCount);
    }
    System.out.println();
}

public void copy(int copySize, int copyCount) {
    int[] src = newSrc(copySize + 1);
    int[] dst = new int[copySize + 1];
    long begin = System.nanoTime();
    for (int count = copyCount; count > 0; --count) {
        System.arraycopy(src, 1, dst, 0, copySize);
        dst[copySize] = src[copySize] + 1;
        System.arraycopy(dst, 0, src, 0, copySize);
        src[copySize] = dst[copySize];
    }
    long end = System.nanoTime();
    System.out.println("Arraycopy: " + (end - begin) / 1e9 + " s");
}

public void loop(int copySize, int copyCount) {
    int[] src = newSrc(copySize + 1);
    int[] dst = new int[copySize + 1];
    long begin = System.nanoTime();
    for (int count = copyCount; count > 0; --count) {
        for (int i = copySize - 1; i >= 0; --i) {
            dst[i] = src[i + 1];
        }
        dst[copySize] = src[copySize] + 1;
        for (int i = copySize - 1; i >= 0; --i) {
            src[i] = dst[i];
        }
        src[copySize] = dst[copySize];
    }
    long end = System.nanoTime();
    System.out.println("Man. loop: " + (end - begin) / 1e9 + " s");
}

public int[] newSrc(int arraySize) {
    int[] src = new int[arraySize];
    for (int i = arraySize - 1; i >= 0; --i) {
        src[i] = i;
    }
    return src;
}

System.arraycopy()একটি অ্যারের (বা এর অংশগুলি) অনুলিপি করতে JNI (জাভা নেটিভ ইন্টারফেস) ব্যবহার করে, সুতরাং এটি নির্লজ্জভাবে দ্রুত হয়, আপনি এখানে নিশ্চিত করতে পারেন


এই কোডটি ইনট ব্যবহার করে [] আপনি কী স্ট্রিং দিয়ে চেষ্টা করতে পারেন [] (বিভিন্ন মান দিয়ে আদিতে: "1", "2", প্রভৃতি যেহেতু তারা পরিবর্তনযোগ্য নয়
Daren

4
জেএনআই খুব ধীরSystem.arraycopyএটি ব্যবহার করে না।
চই টি। রেক্স

না, System.arraycopyজেএনআই ব্যবহার করে না, এটি কেবল তৃতীয় পক্ষের লাইব্রেরি কল করার জন্য। পরিবর্তে এটি একটি নেটিভ কল, যার অর্থ এটি ভিএম-তে একটি নেটিভ বাস্তবায়ন রয়েছে।
spheenik

6

এর বাস্তবায়ন যেহেতু Arrays.copyOfদ্রুততর হয় তা সম্ভব নয় : System.arraycopycopyOf

public static int[] copyOf(int[] original, int newLength) {
    int[] copy = new int[newLength];
    System.arraycopy(original, 0, copy, 0,
                     Math.min(original.length, newLength));
    return copy;
}

4

System.arraycopy()একটি নেটিভ কল যা মেমোরিতে সরাসরি অনুলিপি অপারেশন করে। একক মেমরি অনুলিপি আপনার লুপের চেয়ে সবসময় দ্রুত হবে


4
আমি পড়েছি যে অ দেশীয় প্রকারের জন্য (আমার মতো তৈরি হওয়া কোনও শ্রেণি) এটি এতটা দক্ষ নাও হতে পারে ... এবং ছোট আকারের মাপের জন্য (আমার ক্ষেত্রে) লুপের জন্য ম্যানুয়ালটি আরও ভাল হতে পারে ... মন্তব্য করার জন্য যত্নশীল?
ডেরেন

প্রকৃতপক্ষে, System.arraycopy () এর কিছু ওভারহেড রয়েছে তাই ছোট অ্যারেগুলির জন্য (n = ~ 10) একটি লুপটি আসলে দ্রুততর হয়
পুনরাবৃত্তিমূলক এক্সেপশন
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.