java - > বনাম>>=বুদ্বুদ সাজানোর কারণে পারফরম্যান্সের উল্লেখযোগ্য পার্থক্যের সৃষ্টি হয়
c++ performance (3)
আমি কেবল কিছুতেই হোঁচট খেয়েছি। প্রথমে আমি ভেবেছিলাম এটি শাখাগুলি ভুল ধারণা হওয়ার ঘটনা হতে পারে যেমনটি এই ক্ষেত্রে হয় তবে আমি কেন ব্যাখ্যা করতে পারি না যে কেন শাখার ভুল ধারণা এই ঘটনা ঘটায়।
আমি জাভাতে বুদ্বুদ সাজানোর দুটি সংস্করণ প্রয়োগ করেছি এবং কিছু পারফরম্যান্স পরীক্ষা করেছি:
import java.util.Random;
public class BubbleSortAnnomaly {
public static void main(String... args) {
final int ARRAY_SIZE = Integer.parseInt(args[0]);
final int LIMIT = Integer.parseInt(args[1]);
final int RUNS = Integer.parseInt(args[2]);
int[] a = new int[ARRAY_SIZE];
int[] b = new int[ARRAY_SIZE];
Random r = new Random();
for (int run = 0; RUNS > run; ++run) {
for (int i = 0; i < ARRAY_SIZE; i++) {
a[i] = r.nextInt(LIMIT);
b[i] = a[i];
}
System.out.print("Sorting with sortA: ");
long start = System.nanoTime();
int swaps = bubbleSortA(a);
System.out.println( (System.nanoTime() - start) + " ns. "
+ "It used " + swaps + " swaps.");
System.out.print("Sorting with sortB: ");
start = System.nanoTime();
swaps = bubbleSortB(b);
System.out.println( (System.nanoTime() - start) + " ns. "
+ "It used " + swaps + " swaps.");
}
}
public static int bubbleSortA(int[] a) {
int counter = 0;
for (int i = a.length - 1; i >= 0; --i) {
for (int j = 0; j < i; ++j) {
if (a[j] > a[j + 1]) {
swap(a, j, j + 1);
++counter;
}
}
}
return (counter);
}
public static int bubbleSortB(int[] a) {
int counter = 0;
for (int i = a.length - 1; i >= 0; --i) {
for (int j = 0; j < i; ++j) {
if (a[j] >= a[j + 1]) {
swap(a, j, j + 1);
++counter;
}
}
}
return (counter);
}
private static void swap(int[] a, int j, int i) {
int h = a[i];
a[i] = a[j];
a[j] = h;
}
}
আপনি দেখতে পাচ্ছেন যে, এই দুটি বাছাই পদ্ধতির মধ্যে কেবলমাত্র পার্থক্যটি হল
>
বনাম>>
>=
java BubbleSortAnnomaly 50000 10 10
দিয়ে প্রোগ্রামটি চালানোর সময়, আপনি অবশ্যই প্রত্যাশা করবেন যে
sortB
চেয়ে ধীর গতির কারণ এটি আরও
swap(...)
চালাতে হবে।
তবে আমি তিনটি ভিন্ন মেশিনে নিম্নলিখিত (বা অনুরূপ) আউটপুট পেয়েছি:
Sorting with sortA: 4.214 seconds. It used 564960211 swaps.
Sorting with sortB: 2.278 seconds. It used 1249750569 swaps.
Sorting with sortA: 4.199 seconds. It used 563355818 swaps.
Sorting with sortB: 2.254 seconds. It used 1249750348 swaps.
Sorting with sortA: 4.189 seconds. It used 560825110 swaps.
Sorting with sortB: 2.264 seconds. It used 1249749572 swaps.
Sorting with sortA: 4.17 seconds. It used 561924561 swaps.
Sorting with sortB: 2.256 seconds. It used 1249749766 swaps.
Sorting with sortA: 4.198 seconds. It used 562613693 swaps.
Sorting with sortB: 2.266 seconds. It used 1249749880 swaps.
Sorting with sortA: 4.19 seconds. It used 561658723 swaps.
Sorting with sortB: 2.281 seconds. It used 1249751070 swaps.
Sorting with sortA: 4.193 seconds. It used 564986461 swaps.
Sorting with sortB: 2.266 seconds. It used 1249749681 swaps.
Sorting with sortA: 4.203 seconds. It used 562526980 swaps.
Sorting with sortB: 2.27 seconds. It used 1249749609 swaps.
Sorting with sortA: 4.176 seconds. It used 561070571 swaps.
Sorting with sortB: 2.241 seconds. It used 1249749831 swaps.
Sorting with sortA: 4.191 seconds. It used 559883210 swaps.
Sorting with sortB: 2.257 seconds. It used 1249749371 swaps.
আপনি যখন
java BubbleSortAnnomaly 50000 50000 10
জন্য প্যারামিটারটি সেট করেন, যেমন,
50000
(
java BubbleSortAnnomaly 50000 50000 10
), আপনি প্রত্যাশিত ফলাফল পাবেন:
Sorting with sortA: 3.983 seconds. It used 625941897 swaps.
Sorting with sortB: 4.658 seconds. It used 789391382 swaps.
এই সমস্যাটি জাভা-নির্দিষ্ট কিনা তা নির্ধারণ করার জন্য আমি প্রোগ্রামটি সি ++ এ পোর্ট করেছি। এখানে সি ++ কোড রয়েছে।
#include <cstdlib>
#include <iostream>
#include <omp.h>
#ifndef ARRAY_SIZE
#define ARRAY_SIZE 50000
#endif
#ifndef LIMIT
#define LIMIT 10
#endif
#ifndef RUNS
#define RUNS 10
#endif
void swap(int * a, int i, int j)
{
int h = a[i];
a[i] = a[j];
a[j] = h;
}
int bubbleSortA(int * a)
{
const int LAST = ARRAY_SIZE - 1;
int counter = 0;
for (int i = LAST; 0 < i; --i)
{
for (int j = 0; j < i; ++j)
{
int next = j + 1;
if (a[j] > a[next])
{
swap(a, j, next);
++counter;
}
}
}
return (counter);
}
int bubbleSortB(int * a)
{
const int LAST = ARRAY_SIZE - 1;
int counter = 0;
for (int i = LAST; 0 < i; --i)
{
for (int j = 0; j < i; ++j)
{
int next = j + 1;
if (a[j] >= a[next])
{
swap(a, j, next);
++counter;
}
}
}
return (counter);
}
int main()
{
int * a = (int *) malloc(ARRAY_SIZE * sizeof(int));
int * b = (int *) malloc(ARRAY_SIZE * sizeof(int));
for (int run = 0; RUNS > run; ++run)
{
for (int idx = 0; ARRAY_SIZE > idx; ++idx)
{
a[idx] = std::rand() % LIMIT;
b[idx] = a[idx];
}
std::cout << "Sorting with sortA: ";
double start = omp_get_wtime();
int swaps = bubbleSortA(a);
std::cout << (omp_get_wtime() - start) << " seconds. It used " << swaps
<< " swaps." << std::endl;
std::cout << "Sorting with sortB: ";
start = omp_get_wtime();
swaps = bubbleSortB(b);
std::cout << (omp_get_wtime() - start) << " seconds. It used " << swaps
<< " swaps." << std::endl;
}
free(a);
free(b);
return (0);
}
এই প্রোগ্রামটি একই আচরণ দেখায়। এখানে কি ঘটছে তা কেউ ব্যাখ্যা করতে পারেন?
প্রথমে
sortB
এবং তারপরে
sortA
ফলাফলগুলি পরিবর্তন করে না।
সম্পাদনা 2: বেশিরভাগ ক্ষেত্রে এই উত্তরটি সম্ভবত ভুল, কম যখন আমি বলি উপরের সমস্ত কিছু সঠিক এখনও তবু সত্য, তবে নীচের অংশটি বেশিরভাগ প্রসেসরের আর্কিটেকচারের জন্য সত্য নয়, মন্তব্যগুলি দেখুন। তবে, আমি বলব যে এটি এখনও তাত্ত্বিকভাবে সম্ভব কিছু ওএস / আর্কিটেকচারে কিছু জেভিএম রয়েছে যা এটি করে তবে জেভিএম সম্ভবত খারাপভাবে প্রয়োগ করা হয়নি বা এটি একটি অদ্ভুত আর্কিটেকচার। এছাড়াও, এটি তাত্ত্বিকভাবে এই অর্থে সম্ভব যে সর্বাধিক অনুমানযোগ্য জিনিস তাত্ত্বিকভাবে সম্ভব, তাই আমি লবণের দানা দিয়ে শেষ অংশটি গ্রহণ করতাম।
প্রথমত, আমি সি ++ সম্পর্কে নিশ্চিত নই, তবে জাভা সম্পর্কে কিছু কথা বলতে পারি।
এখানে কিছু কোড,
public class Example {
public static boolean less(final int a, final int b) {
return a < b;
}
public static boolean lessOrEqual(final int a, final int b) {
return a <= b;
}
}
এতে
javap -c
চলছে আমি
javap -c
পেয়ে যাচ্ছি
public class Example {
public Example();
Code:
0: aload_0
1: invokespecial #8 // Method java/lang/Object."<init>":()V
4: return
public static boolean less(int, int);
Code:
0: iload_0
1: iload_1
2: if_icmpge 7
5: iconst_1
6: ireturn
7: iconst_0
8: ireturn
public static boolean lessOrEqual(int, int);
Code:
0: iload_0
1: iload_1
2: if_icmpgt 7
5: iconst_1
6: ireturn
7: iconst_0
8: ireturn
}
আপনি কেবলমাত্র পার্থক্যটি লক্ষ্য করতে পারবেন
if_icmpge
(যদি বৃহত্তর / সমান তুলনা করা হয়) বনাম
if_icmpgt
(যদি এর চেয়ে বড় তুলনা করা হয়)।
উপরের সমস্ত কিছুই সত্য, বাকিটা আমার সেরা অনুমান যে আমি কীভাবে সমাবেশের ভাষা গ্রহণ করেছি এমন একটি কলেজ কোর্সের ভিত্তিতে
if_icmpge
এবং
if_icmpgt
পরিচালনা করা হয়।
আরও ভাল উত্তর পেতে আপনার জেভিএম কীভাবে এগুলি পরিচালনা করে তা সন্ধান করা উচিত।
আমার ধারণা হ'ল সি ++ একই ধরণের অপারেশনে সংকলন করে।
সম্পাদনা করুন:
if_i<cond>
এ ডকুমেন্টেশন here
কম্পিউটারগুলি যেভাবে সংখ্যার তুলনা করে তা অন্যের থেকে বিয়োগ করে এবং সেই সংখ্যাটি 0 হয় কিনা তা যাচাই করে, সুতরাং
a < b
করার সময়
a < b
থেকে
b
বিয়োগ করে এবং দেখায় ফলাফলটি 0 এর চেয়ে কম কিনা মানটির চিহ্নটি পরীক্ষা করে (
b - a < 0
)।
a <= b
করার জন্য এটি অতিরিক্ত পদক্ষেপ এবং 1 (
b - a - 1 < 0
) বিয়োগ করতে হবে।
সাধারণত এটি খুব ক্ষুদ্রতর পার্থক্য, তবে এটি কোনও কোড নয়, এটি বুদ্বুদ সাজানোর প্রবণতা! ও (এন ^ 2) হ'ল গড় পরিমাণ আমরা এই নির্দিষ্ট তুলনাটি করে যাচ্ছি কারণ এটি অভ্যন্তরের সর্বাধিক লুপে রয়েছে।
হ্যাঁ, শাখার পূর্বাভাসের সাথে এটির কিছু করার থাকতে পারে আমি নিশ্চিত নই, আমি এতে বিশেষজ্ঞ নই, তবে আমি মনে করি এটি একটি অ-তাত্পর্যপূর্ণ ভূমিকাও নিতে পারে।
আমি মনে করি এটি শাখার ভুল ধারণা দ্বারা সত্যই ব্যাখ্যা করা যেতে পারে।
উদাহরণস্বরূপ,
sortB
= 11, এবং
sortB
।
বাহ্যিক লুপের প্রথম পুনরাবৃত্তিতে এটি খুব দ্রুত 10 এর সমান উপাদানের উপর চাপিয়ে দেবে will সুতরাং এটির
a[j]=10
এবং সুতরাং অবশ্যই
a[j]
>=a[next]
হবে 10-এর চেয়ে বড় কোনও উপাদান নয় তাই অতএব, এটি অদলবদল করবে, তারপরে
j
একটি পদক্ষেপ করুন কেবল এটিই
a[j]=10
আবার খুঁজে পেতে (একই অদলবদল মান)।
সুতরাং আবার এটি হবে
a[j]>=a[next]
, এবং তাই এক।
একেবারে শুরুতে বেশ কয়েকটি বাদে প্রতিটি তুলনা সত্য হবে।
একইভাবে এটি বাইরের লুপের পরবর্তী পুনরাবৃত্তিতে চলবে।
sortA
জন্য একই নয়।
এটি মোটামুটি একইভাবে শুরু হবে,
a[j]=10
উপর হোঁচট খাবে, একইভাবে কিছু অদলবদল করুন, তবে কেবলমাত্র তখনই যখন এটি
a[next]=10
যায় finds
তাহলে শর্তটি মিথ্যা হবে এবং কোনও অদলবদল করা হবে না।
এ জাতীয়: প্রতিবার এটি
a[next]=10
হোঁচট খায়, শর্তটি মিথ্যা এবং কোনও অদলবদল করা হয় না।
অতএব, এই শর্তটি 11 এর মধ্যে 10 বার সত্য (0 থেকে 9
a[next]
মান) এবং ১১ এর মধ্যে ১ টি ক্ষেত্রে মিথ্যা branch
perf stat
কমান্ডের সাথে প্রদত্ত সি ++ কোড ব্যবহার করে (সময় গণনা অপসারণ) আমি এমন ফলাফল পেয়েছি যা ব্রাচ-মিস তত্ত্বটি নিশ্চিত করে।
Limit = 10
, শাখার পূর্বাভাস (0.01% মিস করে) থেকে বুবলসোর্টবি উচ্চতর সুবিধা উপকার করে তবে
Limit = 50000
শাখার পূর্বাভাসটি বুবলসোর্টা (যথাক্রমে 12.69% এবং 12.76% মিস করে) এর চেয়েও বেশি (15.65% মিস করে) ব্যর্থ হয়।
বাবলসোর্টএ সীমা = 10:
Performance counter stats for './bubbleA.out':
46670.947364 task-clock # 0.998 CPUs utilized
73 context-switches # 0.000 M/sec
28 CPU-migrations # 0.000 M/sec
379 page-faults # 0.000 M/sec
117,298,787,242 cycles # 2.513 GHz
117,471,719,598 instructions # 1.00 insns per cycle
25,104,504,912 branches # 537.904 M/sec
3,185,376,029 branch-misses # 12.69% of all branches
46.779031563 seconds time elapsed
বাবলসোর্টএ সীমা = 50000:
Performance counter stats for './bubbleA.out':
46023.785539 task-clock # 0.998 CPUs utilized
59 context-switches # 0.000 M/sec
8 CPU-migrations # 0.000 M/sec
379 page-faults # 0.000 M/sec
118,261,821,200 cycles # 2.570 GHz
119,230,362,230 instructions # 1.01 insns per cycle
25,089,204,844 branches # 545.136 M/sec
3,200,514,556 branch-misses # 12.76% of all branches
46.126274884 seconds time elapsed
বাবলসোর্টবি সীমা = 10:
Performance counter stats for './bubbleB.out':
26091.323705 task-clock # 0.998 CPUs utilized
28 context-switches # 0.000 M/sec
2 CPU-migrations # 0.000 M/sec
379 page-faults # 0.000 M/sec
64,822,368,062 cycles # 2.484 GHz
137,780,774,165 instructions # 2.13 insns per cycle
25,052,329,633 branches # 960.179 M/sec
3,019,138 branch-misses # 0.01% of all branches
26.149447493 seconds time elapsed
বাবলসোর্টবি সীমা = 50000:
Performance counter stats for './bubbleB.out':
51644.210268 task-clock # 0.983 CPUs utilized
2,138 context-switches # 0.000 M/sec
69 CPU-migrations # 0.000 M/sec
378 page-faults # 0.000 M/sec
144,600,738,759 cycles # 2.800 GHz
124,273,104,207 instructions # 0.86 insns per cycle
25,104,320,436 branches # 486.101 M/sec
3,929,572,460 branch-misses # 15.65% of all branches
52.511233236 seconds time elapsed