কেন হ্যাশসেট <পয়েন্ট> হ্যাশসেট <স্ট্রিং> এর চেয়ে এত ধীর?


165

আমি সদৃশকে অনুমতি না দিয়ে কিছু পিক্সেল অবস্থান সঞ্চয় করতে চেয়েছিলাম, তাই প্রথম জিনিসটি মনে হয় HashSet<Point>বা অনুরূপ ক্লাস। তবে এটির মতো কোনও তুলনায় এটি খুব ধীর বলে মনে হচ্ছে HashSet<string>

উদাহরণস্বরূপ, এই কোড:

HashSet<Point> points = new HashSet<Point>();
using (Bitmap img = new Bitmap(1000, 1000))
{
    for (int x = 0; x < img.Width; x++)
    {
        for (int y = 0; y < img.Height; y++)
        {
            points.Add(new Point(x, y));
        }
    }
}

প্রায় 22.5 সেকেন্ড সময় নেয়।

যদিও নিম্নলিখিত কোডটি (যা সুস্পষ্ট কারণে ভাল পছন্দ নয়) লাগে মাত্র ১.6 সেকেন্ড:

HashSet<string> points = new HashSet<string>();
using (Bitmap img = new Bitmap(1000, 1000))
{
    for (int x = 0; x < img.Width; x++)
    {
        for (int y = 0; y < img.Height; y++)
        {
            points.Add(x + "," + y);
        }
    }
}

সুতরাং, আমার প্রশ্নগুলি হ'ল:

  • এর কোন কারণ আছে কি? আমি এই উত্তরটি যাচাই করেছি , তবে 22.5 সেকেন্ডটি উত্তরটিতে প্রদর্শিত সংখ্যাগুলির চেয়ে অনেক বেশি।
  • ডুপ্লিকেট ছাড়াই পয়েন্টগুলি সঞ্চয় করার আরও ভাল উপায় কী?


সংক্ষিপ্ত স্ট্রিং ব্যবহার না করার জন্য এই "সুস্পষ্ট কারণগুলি" কী? আমি যদি নিজের আইক্যুলিটি কম্পিউটারের প্রয়োগ করতে না চাই তবে এটি করার আরও ভাল উপায় কী?
ইভান ইয়ুরচেঙ্কো

উত্তর:


290

পয়েন্ট স্ট্রাক্ট দ্বারা উত্সাহিত দুটি পারফ সমস্যা আছে। আপনি Console.WriteLine(GC.CollectionCount(0));টেস্ট কোডটি যুক্ত করার সময় এমন কিছু যা দেখতে পান । আপনি দেখতে পাবেন যে পয়েন্ট পরীক্ষার জন্য 20 3720 সংগ্রহ প্রয়োজন তবে স্ট্রিং টেস্টে কেবল ~ 18 সংগ্রহ দরকার needs নিখরচায় নয়। যখন আপনি কোনও মান ধরণের দেখতে পান যাতে অনেকগুলি সংগ্রহ প্ররোচিত হয় তবে আপনার "উহ-ওহ, খুব বেশি বক্সিং" উপসংহারটি নেওয়া দরকার।

সমস্যাটি হ'ল এটির কাজটি সম্পন্ন করার জন্য HashSet<T>একটি প্রয়োজন IEqualityComparer<T>। যেহেতু আপনি একটি সরবরাহ করেন নি, এটি ফিরে আসা একজনের কাছে ফিরে যেতে হবে EqualityComparer.Default<T>()। এই পদ্ধতিটি স্ট্রিংয়ের জন্য একটি ভাল কাজ করতে পারে, এটি আইক্যাটেবেল প্রয়োগ করে। তবে পয়েন্টের জন্য নয়, এটি এমন এক ধরণের যা .NET 1.0 থেকে আসে এবং জেনেরিক প্রেমটি কখনই পায় নি। এটি যা করতে পারে তা হ'ল অবজেক্ট পদ্ধতিগুলি ব্যবহার করা।

অন্য সমস্যাটি হ'ল পয়েন্ট.গেটহ্যাশকোড () খুব বেশি সংঘর্ষে এই পরীক্ষায় একটি দুর্দান্ত কাজ করে না, তাই এটি হ'ল অবজেক্ট.একুয়ালস ()টিকে বেশ ভারী করে। স্ট্রিংয়ের একটি দুর্দান্ত গেটহ্যাশকোড বাস্তবায়ন রয়েছে।

আপনি হ্যাশসেটকে একটি ভাল তুলনামূলক সরবরাহ করে উভয় সমস্যার সমাধান করতে পারেন। এটার মত:

class PointComparer : IEqualityComparer<Point> {
    public bool Equals(Point x, Point y) {
        return x.X == y.X && x.Y == y.Y;
    }

    public int GetHashCode(Point obj) {
        // Perfect hash for practical bitmaps, their width/height is never >= 65536
        return (obj.Y << 16) ^ obj.X;
    }
}

এবং এটি ব্যবহার করুন:

HashSet<Point> list = new HashSet<Point>(new PointComparer());

এবং এটি এখন প্রায় 150 গুণ দ্রুত, স্ট্রিং টেস্টটি সহজেই পরাজিত করে।


26
গেটহ্যাশকোড পদ্ধতি বাস্তবায়নের জন্য +1। শুধু কৌতূহলের জন্য, আপনি কীভাবে সুনির্দিষ্ট obj.X << 16 | obj.Y;বাস্তবায়ন নিয়ে এসেছেন ।
আকাশ কেসি

32
এটি উইন্ডোতে মাউসটির অবস্থানটি যেভাবে পাস করে তা দ্বারা অনুপ্রাণিত হয়েছিল। আপনি যে কোনও বিটম্যাপটি প্রদর্শন করতে চান তা এটি একটি নিখুঁত হ্যাশ।
হ্যান্স প্যাস্যান্ট

2
জেনে ভালো লাগলো. আপনার মতো হ্যাশকোড লেখার জন্য কোনও ডকুমেন্টেশন বা সেরা গাইডলাইন? প্রকৃতপক্ষে, আমি এখনও জানতে চাই যে উপরের হ্যাশকোডটি আপনার অভিজ্ঞতা বা আপনি অনুসরণ করেন এমন কোনও গাইডলাইন নিয়ে আসে কিনা।
আকাশ কেসি

5
@ আকাশকেসি আমি সি # এর সাথে খুব বেশি অভিজ্ঞ নই তবে যতদূর জানি আমি পূর্ণসংখ্যার সাধারণত 32 বিট হয়। এই ক্ষেত্রে আপনি 2 সংখ্যার হ্যাশ চান এবং একটি 16 বামটি বাম-স্থানান্তরিত করে আপনি নিশ্চিত করে নিন যে প্রতিটি সংখ্যার "নিম্ন" 16 বিট অন্যটির সাথে "প্রভাবিত" হবে না |। ৩ টি সংখ্যার জন্য এটি শিফট হিসাবে ২২ এবং ১১ ব্যবহার করা বুদ্ধিমান হতে পারে। ৪ টি সংখ্যার জন্য এটি ২৪, ১ 16, ৮ হবে However তবে এখনও সংঘর্ষ হবে তবে সংখ্যাটি বড় হলেই। তবে এটি কার্যকরভাবে HashSetবাস্তবায়নের উপরও নির্ভর করে । যদি এটি "বিট ট্রানকেশন" দিয়ে ওপেন-অ্যাড্রেসিং ব্যবহার করে (আমি মনে করি না এটি করে!) বাম-শিফ্টের পদ্ধতির ক্ষতি হতে পারে।
এমসিফার্ট

3
@ হ্যান্সপাসান্ট: আমি আশ্চর্য হই যে গেটহ্যাশকোডে ওআর এর পরিবর্তে এক্সওআর ব্যবহার করা কিছুটা ভাল হতে পারে - যদি পয়েন্ট কোঅর্ডিনেটগুলি 16 বিট ছাড়িয়ে যেতে পারে (সম্ভবত সাধারণ প্রদর্শনগুলিতে নয় তবে ভবিষ্যতে নিকটবর্তী)। // এক্সওআর সাধারণত ওআর এর চেয়ে হ্যাশ ফাংশনগুলিতে আরও ভাল, যেহেতু এটি কম তথ্য হারাতে পারে, রিভার্সিবেকে যায় ইত্যাদি // // উদাহরণস্বরূপ যদি নেতিবাচক স্থানাঙ্কের অনুমতি দেওয়া হয় তবে Y এর নেতিবাচক হলে X অবদানের কী হবে তা বিবেচনা করুন।
ক্রেজি গ্লিউ

85

পারফরম্যান্স হ্রাসের মূল কারণ হ'ল সমস্ত বক্সিং চলছে ( হ্যান্স প্যাস্যান্টের উত্তরে ইতিমধ্যে ব্যাখ্যা করা হয়েছে )।

তা ছাড়া, হ্যাশ কোড অ্যালগরিদম সমস্যাটিকে আরও খারাপ করে, কারণ এটি Equals(object obj)বক্সিং রূপান্তরগুলির পরিমাণ বাড়ানোর জন্য আরও কল করে calls

এছাড়াও নোট করুন যে হ্যাশ কোডPoint দ্বারা গণনা করা হয় x ^ y। এটি আপনার ডেটা ব্যাপ্তিতে খুব সামান্য বিচ্ছুরণ তৈরি করে, এবং সেইজন্য এর বালতিগুলি HashSetঅত্যধিক জনবহুল - এমন কিছু যা ঘটে না string, যেখানে হ্যাশগুলির বিস্তৃতি অনেক বড়।

আপনি আপনার নিজের Pointস্ট্রাক্ট (তুচ্ছ) প্রয়োগ করে এবং আপনার প্রত্যাশিত ডেটা রেঞ্জের জন্য আরও ভাল হ্যাশ অ্যালগরিদম ব্যবহার করে সমস্যাটি সমাধান করতে পারেন , উদাহরণস্বরূপ স্থানাঙ্কগুলি স্থানান্তর করে:

(x << 16) ^ y

কিছু ভাল পরামর্শের জন্য যখন হ্যাশ কোডের কথা আসে তখন এরিক লিপার্টের ব্লগ পোস্টটি পড়ুন


4
পারফর্মের পয়েন্টের রেফারেন্স উত্সটি দেখে GetHashCode: unchecked(x ^ y)যদিও stringএটি আরও জটিল দেখায় ..
গিলাদ গ্রিন

2
হুম .. ঠিক আছে, আপনার অনুমানটি সঠিক কিনা তা পরীক্ষা করার জন্য আমি কেবল HashSet<long>()পরিবর্তে ব্যবহারের চেষ্টা করেছি এবং list.Add(unchecked(x ^ y));হ্যাশসেটে মান যুক্ত করার চেষ্টা করেছি । এটি আসলে HashSet<string> (345 এমএস) এর চেয়ে আরও দ্রুত ছিল । আপনার বর্ণিত থেকে এটি কি কোনওরকম আলাদা?
আহমেদ আবদেলহামেদ

4
@ আহমেদআবেলহাদেদ সম্ভবত এটি কারণ আপনি নিজের হ্যাশ সেটে আপনার চেয়ে কম সদস্য যুক্ত করছেন কারণ আপনি বুঝতে পেরেছেন (আবার হ্যাশ কোড অ্যালগরিদমের ভয়াবহ বিচ্ছুরণের কারণে)। আপনি listযখন এটি পপুলিং শেষ করেছেন তখন তার গণনা কী ?
ইনপুট

4
@ আহমেদআবেদলহেমেদ আপনার পরীক্ষাটি ভুল। আপনি বারবার একই লম্বা যোগ করছেন, তাই আসলে আপনি সন্নিবেশ করছেন খুব কম উপাদান রয়েছে। সন্নিবেশ করার সময় point, HashSetঅভ্যন্তরীণভাবে কল হবে GetHashCodeএবং একই হ্যাশকোডযুক্ত সেই পয়েন্টগুলির জন্য, Equalsএটি ইতিমধ্যে বিদ্যমান কিনা তা নির্ধারণ করার জন্য কল করবে
অফির ওয়াইনগার্টেন

49
Pointআপনি যখন ক্লাস তৈরি করতে পারেন এবং গরিবদের না থাকার সুবিধা এবং বাক্সে প্রবেশের প্রয়োজনীয়তা না পেয়ে যখন IEqualityComparer<Point>কাজ করেন এমন অন্যান্য জিনিসের সাথে সামঞ্জস্য রাখতে পারেন তখন প্রয়োগ করার দরকার নেই । PointGetHashCodeEquals()
জন হান্না
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.