"হিসাবে" এবং nullable ধরণের সঙ্গে পারফরম্যান্স অবাক


330

আমি মাত্র গভীরতার সি # এর চতুর্থ অধ্যায়টি সংশোধন করছি যা অবনমিত প্রকারগুলি নিয়ে কাজ করে এবং আমি "হিসাবে" অপারেটরটি ব্যবহার সম্পর্কে একটি বিভাগ যুক্ত করছি যা আপনাকে লেখার অনুমতি দেয়:

object o = ...;
int? x = o as int?;
if (x.HasValue)
{
    ... // Use x.Value in here
}

আমি ভেবেছিলাম এটি সত্যই ঝরঝরে, এবং এটি সি # 1 সমমানের উপরের পারফরম্যান্সের উন্নতি করতে পারে, "একটি" অনুসরণ করে একটি নিক্ষিপ্ত হয় - সর্বোপরি, আমাদের কেবল একবার গতিশীল ধরণের চেকিং জিজ্ঞাসা করতে হবে, এবং তারপরে একটি সাধারণ মান চেক ।

তবে এটি ক্ষেত্রে হবে না বলে মনে হয়। আমি নীচে একটি নমুনা পরীক্ষা অ্যাপ্লিকেশন অন্তর্ভুক্ত করেছি, যা মূলত একটি অবজেক্ট অ্যারের মধ্যে সমস্ত পূর্ণসংখ্যার যোগফল করে - তবে অ্যারেতে প্রচুর নাল রেফারেন্স এবং স্ট্রিং রেফারেন্স পাশাপাশি বক্সযুক্ত পূর্ণসংখ্যা রয়েছে। মানদণ্ডটি আপনাকে সি # 1 তে যে কোডটি ব্যবহার করতে হবে তা পরিমাপ করে, "হিসাবে" অপারেটর ব্যবহার করে কোডটি, এবং কেবল একটি লিনকিউ সমাধানের জন্য। আমার বিস্ময়ের জন্য, সি # 1 কোডটি এই ক্ষেত্রে 20 গুণ বেশি দ্রুত - এবং এমনকি লিনকিউ কোড (যা আমি ধীরে ধীরে হওয়ার প্রত্যাশা করতাম, এটির পুনরাবৃত্তিকারীরা জড়িত হয়ে) "হিসাবে" কোডটি মারধর করে।

.NET বাস্তবায়নের ধরণের isinstজন্য কি আসলেই ধীর হয়? এটি কি অতিরিক্ত unbox.anyকারণ সমস্যার সৃষ্টি করে? এর জন্য আর কোন ব্যাখ্যা আছে কি? এই মুহুর্তে মনে হচ্ছে আমি পারফরম্যান্স সংবেদনশীল পরিস্থিতিতে এটি ব্যবহার করার বিরুদ্ধে একটি সতর্কতা অন্তর্ভুক্ত করতে চাই ...

ফলাফল:

কাস্ট: 10000000: 121
হিসাবে: 10000000: 2211
লিনকুই: 10000000: 2143

কোড:

using System;
using System.Diagnostics;
using System.Linq;

class Test
{
    const int Size = 30000000;

    static void Main()
    {
        object[] values = new object[Size];
        for (int i = 0; i < Size - 2; i += 3)
        {
            values[i] = null;
            values[i+1] = "";
            values[i+2] = 1;
        }

        FindSumWithCast(values);
        FindSumWithAs(values);
        FindSumWithLinq(values);
    }

    static void FindSumWithCast(object[] values)
    {
        Stopwatch sw = Stopwatch.StartNew();
        int sum = 0;
        foreach (object o in values)
        {
            if (o is int)
            {
                int x = (int) o;
                sum += x;
            }
        }
        sw.Stop();
        Console.WriteLine("Cast: {0} : {1}", sum, 
                          (long) sw.ElapsedMilliseconds);
    }

    static void FindSumWithAs(object[] values)
    {
        Stopwatch sw = Stopwatch.StartNew();
        int sum = 0;
        foreach (object o in values)
        {
            int? x = o as int?;
            if (x.HasValue)
            {
                sum += x.Value;
            }
        }
        sw.Stop();
        Console.WriteLine("As: {0} : {1}", sum, 
                          (long) sw.ElapsedMilliseconds);
    }

    static void FindSumWithLinq(object[] values)
    {
        Stopwatch sw = Stopwatch.StartNew();
        int sum = values.OfType<int>().Sum();
        sw.Stop();
        Console.WriteLine("LINQ: {0} : {1}", sum, 
                          (long) sw.ElapsedMilliseconds);
    }
}

8
জিটযুক্ত কোডটি কেন দেখছেন না? এমনকি ভিএস ডিবাগার এটি প্রদর্শন করতে পারেন।
আন্তন টিখই

2
আমি শুধু কৌতূহলী, আপনি কি সিএলআর ৪.০ দিয়ে পরীক্ষা করেছেন?
ডার্ক ভোলমার

1
@ অ্যান্টন: ভাল কথা। এক পর্যায়ে করবে (যদিও এই মুহুর্তে এটি ভিএসে নেই :) @ ডিভো: হ্যাঁ, এবং এটি সর্বদাই খারাপ। তবে এটি বিটাতে রয়েছে, সুতরাং সেখানে প্রচুর ডিবাগিং কোড থাকতে পারে।
জন স্কিটি

1
আজ আমি শিখেছি আপনি asnallable প্রকারের উপর ব্যবহার করতে পারেন । আকর্ষণীয়, কারণ এটি অন্যান্য মানের ধরণের ক্ষেত্রে ব্যবহার করা যায় না। আসলে, আরও অবাক করা।
লেপি

3
@ লেপ এটি মান ধরণের উপর কাজ না করার জন্য এটি সঠিক ধারণা দেয়। এটি সম্পর্কে চিন্তা করুন, asকোনও প্রকারে কাস্ট করার চেষ্টা করুন এবং যদি এটি ব্যর্থ হয় তবে তা বাতিল হয়ে যায়। আপনি মূল্য প্রকারগুলি বাতিল করতে পারবেন না
আর্লজ

উত্তর:


209

স্পষ্টতই JIT সংকলক প্রথম ক্ষেত্রে জেনারেট করতে পারে এমন মেশিন কোডটি অনেক বেশি দক্ষ more একটি নিয়ম যা সত্যই সেখানে সহায়তা করে তা হ'ল কোনও বস্তুকে কেবলমাত্র একটি ভেরিয়েবলের বাক্সবিন্যাস করা যেতে পারে যা বাক্সযুক্ত মানের মতোই। এটি জেআইটি সংকলককে খুব দক্ষ কোড তৈরি করতে দেয়, কোনও মান রূপান্তর বিবেচনা করতে হবে না।

হয় অপারেটর পরীক্ষা সহজ, শুধু যদি বস্তুর নাল নয় এবং প্রত্যাশিত ধরনের হয়, সময় লাগে কিন্তু কিছু মেশিন কোড নির্দেশাবলী পরীক্ষা করা হয়। Castালাইটিও সহজ, জেআইটি সংকলক বস্তুতে মান বিটের অবস্থান জানে এবং সেগুলি সরাসরি ব্যবহার করে। কোনও অনুলিপি বা রূপান্তর ঘটে না, সমস্ত মেশিন কোড ইনলাইন হয় এবং প্রায় এক ডজন নির্দেশ থাকে। বক্সিংটি যখন সাধারণ ছিল তখন এটি নেট নেট in.০ এ সত্যিই দক্ষ হওয়া দরকার।

কন্ট্রিতে কাস্টিং? আরও অনেক কাজ লাগে। বক্সযুক্ত পূর্ণসংখ্যার মান উপস্থাপনা মেমরির বিন্যাসের সাথে সামঞ্জস্যপূর্ণ নয় Nullable<int>। একটি রূপান্তর প্রয়োজন এবং কোড বাক্সযুক্ত এনাম ধরণের কারণে কোডটি জটিল। জেআইটি সংকলক কাজটি পেতে JIT_Unbox_Nlalable নামক একটি সিএলআর সহায়তা ফাংশনে কল উত্পন্ন করে। এটি কোনও মান ধরণের জন্য সাধারণ উদ্দেশ্য ফাংশন, প্রকারগুলি পরীক্ষা করার জন্য প্রচুর কোড। এবং মান কপি করা হয়। এই কোডটি mscorwks.dll এর মধ্যে লক করা আছে বলে খরচ অনুমান করা শক্ত, তবে শত শত মেশিন কোড নির্দেশাবলী সম্ভবত is

লিনক অফটাইপ () এক্সটেনশন পদ্ধতিতেও অপারেটর এবং কাস্ট ব্যবহার করা হয় । এটি অবশ্য জেনেরিক ধরণের castালাই। JIT সংকলক একটি সহায়তা ফাংশন, JIT_Unbox () এ কল দেয় যা একটি স্বেচ্ছাসেবী মানের ধরণের জন্য একটি কাস্ট করতে পারে। Nullable<int>কম কাজ করা উচিত হওয়া উচিত, কেন এটি নিক্ষিপ্ত হিসাবে ধীর কেন একটি দুর্দান্ত ব্যাখ্যা নেই । আমি সন্দেহ করি যে ngen.exe এখানে সমস্যার কারণ হতে পারে।


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

26

এটি আমার কাছে মনে হয় যে এটি isinstখুব সহজেই ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে পড়েছে। পদ্ধতিতে FindSumWithCastআমি বদলেছি

if (o is int)

প্রতি

if (o is int?)

যা কার্যকরভাবে কার্যকরভাবে ধীর করে দেয়। আইএলটিতে কেবলমাত্র ডিফারেন্সইনটি হ'ল তা

isinst     [mscorlib]System.Int32

পরিবর্তিত হয়

isinst     valuetype [mscorlib]System.Nullable`1<int32>

1
এটা তার চেয়েও বেশি; "cast isinstালাই " ক্ষেত্রে কেসটি নালীর জন্য একটি পরীক্ষা এবং তারপরে শর্তসাপেক্ষে একটি হয় unbox.anyশর্তহীন ক্ষেত্রে একটি শর্তহীন রয়েছেunbox.any
জন স্কিটি

হ্যাঁ, উভয়ই সক্রিয় isinstএবং unbox.anyঅযোগ্য ধরণের ধীর হয়।
ডার্ক ভোলমার 20

@ জন: আপনি কেন আমার উত্তরটি পর্যালোচনা করতে পারেন কেন কাস্টটির প্রয়োজন হয়। (আমি জানি এটি পুরানো, তবে আমি এই কিউটি আবিষ্কার করেছি এবং ভেবেছিলাম যে সিএলআর সম্পর্কে আমি যা জানি তার 2C প্রদান করা উচিত)।
জোহানেস রুডল্ফ

22

এটি হ্যান্স প্যাস্যান্টের দুর্দান্ত উত্তরের মন্তব্য হিসাবে শুরু হয়েছিল তবে এটি খুব দীর্ঘ হয়ে গেছে তাই আমি এখানে কয়েকটি বিট যুক্ত করতে চাই:

প্রথমে, সি # asঅপারেটর একটি isinstআইএল নির্দেশ নির্গত করবে ( isঅপারেটরটিও তাই )। (আর একটি আকর্ষণীয় নির্দেশ হ'ল castclass, আপনি যখন সরাসরি কাস্ট করেন তখন সংবেদী হয় এবং সংকলক জানে যে রানটাইম চেকিং বাদ দেওয়া যাবে না))

এখানে কি isinstহয় ( ইসিএমএ 335 পার্টিশন III, 4.6 ):

ফর্ম্যাট: isinst টাইপটোক

টাইপটোক একটি মেটাডেটা টোকেন (ক typeref, typedefবা typespec), পছন্দসই শ্রেণীর নির্দেশ করে।

যদি টাইপটোক একটি নন-অযোগ্য মান ধরণ বা জেনেরিক পরামিতি প্রকার হয় তবে এটি " বক্সড " টাইপটোক হিসাবে ব্যাখ্যা করা হয় ।

তাহলে typeTok একটি nullable টাইপ, Nullable<T>, এটা হিসাবে "boxed" ব্যাখ্যা করা হয়T

সবচেয়ে গুরুত্বপূর্ণভাবে:

তাহলে প্রকৃত প্রকার (না যাচাইকারী ট্র্যাক প্রকার) obj হয় যাচাইকারী-হস্তান্তরযোগ্য-টু টাইপ typeTok তারপর isinstসফল এবং obj (যেমন ফলাফলের ) অপরিবর্তিত ফিরিয়ে দেওয়া হয় যখন যাচাইকরণ তার টাইপ ট্র্যাক typeTokজবরদস্তি (§1.6) এবং রূপান্তরগুলি (.23.27) এর বিপরীতে isinstকখনও কোনও বস্তুর প্রকৃত প্রকার পরিবর্তন করে না এবং বস্তুর পরিচয় সংরক্ষণ করে (পার্টিশন I দেখুন)।

সুতরাং, পারফরম্যান্স কিলার isinstএই ক্ষেত্রে নয়, অতিরিক্ত unbox.any। এটি হানসের জবাব থেকে পরিষ্কার ছিল না, কারণ তিনি কেবল জেআইটিড কোডটি দেখেছিলেন। সাধারণভাবে, সি # সংকলক একটি unbox.anyপরে isinst T?প্রসারণ করবে (তবে আপনি isinst Tযখন Tরেফারেন্স টাইপ করবেন তখন এটি বাদ দেবে )।

কেন এটা করে? isinst T?সুস্পষ্ট হয়ে উঠতে পারে এমন প্রভাব কখনই আসে না, অর্থাৎ আপনি ফিরে আসেন T?। পরিবর্তে, এই সমস্ত নির্দেশাবলী নিশ্চিত করে যে আপনার কাছে একটি "boxed T"আনবক্স করা যেতে পারে T?T?প্রকৃতরূপে পেতে , আমাদের এখনও আমাদের এ আনবক্স "boxed T"করতে হবে T?, এজন্য সংকলক তার unbox.anyপরে বের করে isinst। আপনি যদি এটির বিষয়ে চিন্তা করেন তবে এটি অর্থবোধ করে কারণ "বাক্স ফর্ম্যাট" এর জন্য T?কেবল একটি "boxed T"এবং আনবক্স তৈরি করা castclassএবং isinstসম্পাদন করা অসম্পূর্ণ হবে।

মান থেকে কিছু তথ্য নিয়ে হ্যান্সের সন্ধানের ব্যাক আপ নেওয়া , এখানে এটি যায়:

(ECMA 335 পার্টিশন III, 4.33): unbox.any

কোনও মান প্রকারের বাক্সযুক্ত ফর্মটিতে প্রয়োগ করা হলে, unbox.anyনির্দেশটি বস্তুর (টাইপের O) মধ্যে থাকা মানটি বের করে । (এটি unboxঅনুসরণ করার সমতুল্য ldobj)) কোনও রেফারেন্স টাইপের ক্ষেত্রে প্রয়োগ করা হলে, unbox.anyনির্দেশটি castclassটাইপটকের মতোই প্রভাব ফেলে।

(ECMA 335 পার্টিশন III, 4.32): unbox

সাধারণত, unboxবাক্সযুক্ত বস্তুর ভিতরে ইতিমধ্যে উপস্থিত মানের ধরণের ঠিকানাটি সহজেই গণনা করে। মূল্যহীন মান ধরণের আনবক্সিং করার সময় এই পদ্ধতিটি সম্ভব নয়। যেহেতু বাক্স অপারেশনের সময় Nullable<T>মানগুলি বাক্সে রূপান্তরিত হয় Ts, একটি বাস্তবায়নের ক্ষেত্রে প্রায়শই Nullable<T>গাদাতে একটি নতুন উত্পাদন করা উচিত এবং সদ্য বরাদ্দ হওয়া অবজেক্টের ঠিকানাটি গণনা করতে হবে।


আমি মনে করি খুব শেষ উদ্ধৃত বাক্যটিতে টাইপ থাকতে পারে; এক্সিকিউশন স্ট্যাকের "... ... গাদা উপর ..." থাকা উচিত নয় ? মনে হয় কিছু নতুন জিসি হিপ ইভেন্টে আনবক্সিংয়ের মতো ফিরে আসার মতো একটি প্রায় অভিন্ন নতুনটির জন্য মূল সমস্যাটি অদলবদল করে।
গ্লেন স্লেডেন

19

মজার বিষয় হল, আমি অপারেটর সমর্থন সম্পর্কে প্রতিক্রিয়াটি পাস dynamicকরার জন্য একটি অর্ডার-অফ-গ্রেগনিটি ধীর হয়ে যাওয়ার Nullable<T>( এই প্রাথমিক পরীক্ষার অনুরূপ ) - খুব অনুরূপ কারণে সন্দেহ করি।

ভালবাসা আছে Nullable<T>। আরেকটি মজাদার বিষয় হ'ল জেআইটি nullস্পষ্ট নয় এমন নালাগুলির জন্য স্পটগুলি (এবং অপসারণ) , এটি এটিকে বার্ক করে Nullable<T>:

using System;
using System.Diagnostics;
static class Program {
    static void Main() { 
        // JIT
        TestUnrestricted<int>(1,5);
        TestUnrestricted<string>("abc",5);
        TestUnrestricted<int?>(1,5);
        TestNullable<int>(1, 5);

        const int LOOP = 100000000;
        Console.WriteLine(TestUnrestricted<int>(1, LOOP));
        Console.WriteLine(TestUnrestricted<string>("abc", LOOP));
        Console.WriteLine(TestUnrestricted<int?>(1, LOOP));
        Console.WriteLine(TestNullable<int>(1, LOOP));

    }
    static long TestUnrestricted<T>(T x, int loop) {
        Stopwatch watch = Stopwatch.StartNew();
        int count = 0;
        for (int i = 0; i < loop; i++) {
            if (x != null) count++;
        }
        watch.Stop();
        return watch.ElapsedMilliseconds;
    }
    static long TestNullable<T>(T? x, int loop) where T : struct {
        Stopwatch watch = Stopwatch.StartNew();
        int count = 0;
        for (int i = 0; i < loop; i++) {
            if (x != null) count++;
        }
        watch.Stop();
        return watch.ElapsedMilliseconds;
    }
}

Yowser। এটি একটি সত্যই বেদনাদায়ক পার্থক্য। Eek।
জন স্কিটি 21

যদি এই সমস্ত কিছু থেকে অন্য কোনও ভাল না আসে তবে এটি আমার মূল কোড এবং এটি উভয়ের জন্য সতর্কতা অন্তর্ভুক্ত করতে পরিচালিত করেছে :)
জোন স্কিট

আমি জানি এটি একটি পুরানো প্রশ্ন, তবে আপনি "জেআইটি স্পটগুলি (এবং nullঅপসারণযোগ্য ) নন-ন্যাবল স্ট্রাক্টস" এর দ্বারা কী বোঝাতে চান তা ব্যাখ্যা করতে পারেন ? আপনি কী বোঝাতে চেয়েছেন যে এটি nullরানটাইম চলাকালীন কোনও ডিফল্ট মান বা কিছু দিয়ে প্রতিস্থাপিত হয়?
জাস্টিন মরগান

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

সীমাবদ্ধ নয় এমন পরীক্ষাটি এখনও মাত্রাটির 2.5 অর্ডার কম er তবে আপনি যখন countভেরিয়েবলটি ব্যবহার করবেন না তখন কিছু অপ্টিমাইজেশন চলছে । উভয় ক্ষেত্রে যোগ করার Console.Write(count.ToString()+" ");পরে watch.Stop();কেবল মাত্রার একটি আদেশের অধীনে অন্যান্য পরীক্ষাগুলি ধীর করে দেয়, তবে সীমাবদ্ধ নন টেস্ট পরিবর্তন করা হয় না। নোট করুন nullপাস করার পরে আপনি কেসগুলি পরীক্ষা করার সময়ও কিছু পরিবর্তন রয়েছে , মূল কোডটি নিশ্চিত করে অন্যান্য পরীক্ষাগুলির জন্য নাল চেক এবং ইনক্রিমেন্টটি সত্যই করা হয় না। লিনকপ্যাড
মার্ক হার্ট

12

এটি উপরের FindSumWithAsAndHas এর ফলাফল: বিকল্প পাঠ

এটি FindSumWithCast এর ফলাফল: বিকল্প পাঠ

ফলাফল:

  • ব্যবহার করে as, এটি প্রথম পরীক্ষা করে যদি কোনও বস্তু অন্তর্গত 32 এর উদাহরণ; হুডের অধীনে এটি ব্যবহার করছে isinst Int32(যা হস্ত-লিখিত কোডের অনুরূপ: যদি (o ইন্ট হয়))। এবং ব্যবহার করে as, এটি নিঃশর্তভাবে অবজেক্টটিকে আনবক্সও করে। আইএল_০০২ a (কোনও প্রোডাক্ট এখনও এটি হুডের নীচে একটি ফাংশন) কল করা সত্যই পারফরম্যান্স-হত্যাকারী

  • Castালাই ব্যবহার করে, আপনি প্রথমে পরীক্ষা করে দেখুন যে কোনও বস্তু যদি একটি হয় int if (o is int); হুডের নীচে এটি ব্যবহার করছে isinst Int32। যদি এটি কোনও অন্তর্দৃষ্টির উদাহরণ হয়, তবে আপনি নিরাপদে মানটি IL_002D আনবক্স করতে পারবেন

সহজ কথায় বলতে গেলে, এটি হল asপদ্ধতির ব্যবহারের ছদ্ম-কোড :

int? x;

(x.HasValue, x.Value) = (o isinst Int32, o unbox Int32)

if (x.HasValue)
    sum += x.Value;    

এবং এটি castালাই পদ্ধতির ব্যবহারের সিউডো কোড:

if (o isinst Int32)
    sum += (o unbox Int32)

সুতরাং castালাই ( (int)a[i]ভাল সিনট্যাক্সটি likeালাইয়ের মতো দেখাচ্ছে তবে এটি আনবক্সিং, castালাই এবং আনবক্সিং একই সিনট্যাক্স ভাগ করে নেবে, পরের বারে আমি সঠিক পরিভাষাটি সহকারে প্যাডেন্টিক হব) পদ্ধতিটি দ্রুততর হয়, আপনাকে কেবল একটি মান আনবক্স করার প্রয়োজন ছিল যখন কোন বস্তু স্থিরভাবে একটি int। একই জিনিসটি asঅ্যাপ্রোচ ব্যবহার করে বলা যায় না ।


11

এই উত্তরটি আপ টু ডেট রাখার জন্য, এটি উল্লেখ করা দরকার যে এই পৃষ্ঠায় সর্বাধিক আলোচনার এখন সিক # 7.1 এবং .NET 4.7 রয়েছে যা একটি স্লিম সিনট্যাক্সকে সমর্থন করে যা সেরা আইএল কোড উত্পাদন করে produces

ওপি'র আসল উদাহরণ ...

object o = ...;
int? x = o as int?;
if (x.HasValue)
{
    // ...use x.Value in here
}

সহজ হয়ে যায় ...

if (o is int x)
{
    // ...use x in here
}

আমি খুঁজে পেয়েছি নতুন সিনট্যাক্স জন্য এক সাধারণ ব্যবহারের যে যখন আপনি একটি .NET লিখছেন মান টাইপ (অর্থাত structমধ্যে সি # ) যে কার্যকরী IEquatable<MyStruct>(যেমন সবচেয়ে উচিত)। Equals(MyStruct other)দৃ strongly ়ভাবে টাইপ করা পদ্ধতিটি প্রয়োগ করার পরে , আপনি এখন নিখুঁতভাবে টাইপ করা Equals(Object obj)ওভাররাইড (উত্তরাধিকার সূত্রে Object) এ পুনরায় পুনঃনির্দেশ করতে পারেন :

public override bool Equals(Object obj) => obj is MyStruct o && Equals(o);

 


পরিশিষ্ট:Release বিল্ড আইএল এই উত্তর (যথাক্রমে) এ উপরে দেখানো প্রথম দুই উদাহরণস্বরূপ ফাংশন কোড এখানে দেওয়া হয়। যদিও নতুন সিনট্যাক্সের আইএল কোডটি সত্যই 1 বাইট ছোট, এটি বেশিরভাগ ক্ষেত্রে জিরো কল (বনাম দুটি) করে এবং unboxসম্ভব হলে পুরোপুরি অপারেশন এড়িয়ে বড় জয় লাভ করে ।

// static void test1(Object o, ref int y)
// {
//     int? x = o as int?;
//     if (x.HasValue)
//         y = x.Value;
// }

[0] valuetype [mscorlib]Nullable`1<int32> x
        ldarg.0
        isinst [mscorlib]Nullable`1<int32>
        unbox.any [mscorlib]Nullable`1<int32>
        stloc.0
        ldloca.s x
        call instance bool [mscorlib]Nullable`1<int32>::get_HasValue()
        brfalse.s L_001e
        ldarg.1
        ldloca.s x
        call instance !0 [mscorlib]Nullable`1<int32>::get_Value()
        stind.i4
L_001e: ret

// static void test2(Object o, ref int y)
// {
//     if (o is int x)
//         y = x;
// }

[0] int32 x,
[1] object obj2
        ldarg.0
        stloc.1
        ldloc.1
        isinst int32
        ldnull
        cgt.un
        dup
        brtrue.s L_0011
        ldc.i4.0
        br.s L_0017
L_0011: ldloc.1
        unbox.any int32
L_0017: stloc.0
        brfalse.s L_001d
        ldarg.1
        ldloc.0
        stind.i4
L_001d: ret

আরও পরীক্ষার জন্য যা পূর্ববর্তী উপলব্ধ বিকল্পগুলি ছাড়িয়ে নতুন সি # 7 সিনট্যাক্সের কর্মক্ষমতা সম্পর্কে আমার মন্তব্যকে প্রমাণিত করে, এখানে দেখুন (বিশেষত উদাহরণস্বরূপ 'ডি')।


9

আরও প্রোফাইলিং:

using System;
using System.Diagnostics;

class Program
{
    const int Size = 30000000;

    static void Main(string[] args)
    {
        object[] values = new object[Size];
        for (int i = 0; i < Size - 2; i += 3)
        {
            values[i] = null;
            values[i + 1] = "";
            values[i + 2] = 1;
        }

        FindSumWithIsThenCast(values);

        FindSumWithAsThenHasThenValue(values);
        FindSumWithAsThenHasThenCast(values);

        FindSumWithManualAs(values);
        FindSumWithAsThenManualHasThenValue(values);



        Console.ReadLine();
    }

    static void FindSumWithIsThenCast(object[] values)
    {
        Stopwatch sw = Stopwatch.StartNew();
        int sum = 0;
        foreach (object o in values)
        {
            if (o is int)
            {
                int x = (int)o;
                sum += x;
            }
        }
        sw.Stop();
        Console.WriteLine("Is then Cast: {0} : {1}", sum,
                            (long)sw.ElapsedMilliseconds);
    }

    static void FindSumWithAsThenHasThenValue(object[] values)
    {
        Stopwatch sw = Stopwatch.StartNew();
        int sum = 0;
        foreach (object o in values)
        {
            int? x = o as int?;

            if (x.HasValue)
            {
                sum += x.Value;
            }
        }
        sw.Stop();
        Console.WriteLine("As then Has then Value: {0} : {1}", sum,
                            (long)sw.ElapsedMilliseconds);
    }

    static void FindSumWithAsThenHasThenCast(object[] values)
    {
        Stopwatch sw = Stopwatch.StartNew();
        int sum = 0;
        foreach (object o in values)
        {
            int? x = o as int?;

            if (x.HasValue)
            {
                sum += (int)o;
            }
        }
        sw.Stop();
        Console.WriteLine("As then Has then Cast: {0} : {1}", sum,
                            (long)sw.ElapsedMilliseconds);
    }

    static void FindSumWithManualAs(object[] values)
    {
        Stopwatch sw = Stopwatch.StartNew();
        int sum = 0;
        foreach (object o in values)
        {
            bool hasValue = o is int;
            int x = hasValue ? (int)o : 0;

            if (hasValue)
            {
                sum += x;
            }
        }
        sw.Stop();
        Console.WriteLine("Manual As: {0} : {1}", sum,
                            (long)sw.ElapsedMilliseconds);
    }

    static void FindSumWithAsThenManualHasThenValue(object[] values)
    {
        Stopwatch sw = Stopwatch.StartNew();
        int sum = 0;
        foreach (object o in values)
        {
            int? x = o as int?;

            if (o is int)
            {
                sum += x.Value;
            }
        }
        sw.Stop();
        Console.WriteLine("As then Manual Has then Value: {0} : {1}", sum,
                            (long)sw.ElapsedMilliseconds);
    }

}

আউটপুট:

Is then Cast: 10000000 : 303
As then Has then Value: 10000000 : 3524
As then Has then Cast: 10000000 : 3272
Manual As: 10000000 : 395
As then Manual Has then Value: 10000000 : 3282

এই পরিসংখ্যান থেকে আমরা কী অনুমান করতে পারি?

  • প্রথমত, হয়-তারপর-ঢালাই পদ্ধতির উল্লেখযোগ্যভাবে চেয়ে দ্রুত হিসাবে পদ্ধতির। 303 বনাম 3524
  • দ্বিতীয়, .মান কাস্টিংয়ের তুলনায় সামান্য ধীর। 3524 বনাম 3272
  • তৃতীয়ত, .HasValue সীমিতভাবে ধীর চেয়ে ম্যানুয়াল ব্যবহার আছে (অর্থাত ব্যবহার করছে হয় )। 3524 বনাম 3282
  • চতুর্থত, আপেল-থেকে-আপেল তুলনা করা (যেমন উভয় সিমুলেটেড হাস্যালোয় নির্ধারণ করা এবং সিমুলেটেড মান দুটি রূপান্তরিত হয়) সিমুলেটেড হিসাবে এবং বাস্তব হিসাবে , আমরা দেখতে পারি কৃত্রিম যেমন এখনও উল্লেখযোগ্যভাবে দ্রুততর চেয়ে মত বাস্তবিক । 395 বনাম 3524
  • শেষ অবধি, প্রথম এবং চতুর্থ উপসংহারের ভিত্তিতে, বাস্তবায়ন হিসাবে কিছু ভুল আছে ^ _ ^ ^

8

চেষ্টা করার মতো সময় আমার কাছে নেই, তবে আপনি এটি পেতে চাইতে পারেন:

foreach (object o in values)
        {
            int? x = o as int?;

যেমন

int? x;
foreach (object o in values)
        {
            x = o as int?;

আপনি প্রতিবার একটি নতুন অবজেক্ট তৈরি করছেন যা সমস্যাটিকে পুরোপুরি ব্যাখ্যা করবে না, তবে অবদান রাখতে পারে।


1
না, আমি এটি চালিয়েছি এবং এটি সামান্য ধীর er
হেন্ক হলটারম্যান

2
আমার অভিজ্ঞতায় ভেরিয়েবলটি ধরা পড়লে (কোন স্থানে এটি প্রকৃত শব্দার্থকে প্রভাবিত করে) যখন কোনও ভিন্ন স্থানে ভেরিয়েবল ঘোষণা করা তখন উত্পন্ন কোডটি উল্লেখযোগ্যভাবে প্রভাবিত করে। মনে রাখবেন যে এটি গাদাতে একটি নতুন অবজেক্ট তৈরি করছে না, যদিও এটি অবশ্যই int?স্ট্যাকের সাহায্যে একটি নতুন উদাহরণ তৈরি করছে unbox.any। আমি সন্দেহ করি এটিই সমস্যা - আমার ধারণা হস্তশিল্প দ্বারা তৈরি আইএল এখানে উভয় বিকল্পকে পরাজিত করতে পারে ... যদিও এটিও সম্ভব যে, জেআইটি হ'ল / কাস্ট মামলার স্বীকৃতি জানাতে অনুকূলিত হয়েছে এবং কেবল একবার চেক করেছে।
জন স্কিটি

আমি ভাবছিলাম যে castালাই সম্ভবত এতদিন ধরে অনুকূলিত হয়েছে যেহেতু এটি প্রায় দীর্ঘকাল ধরে রয়েছে।
জেমস ব্ল্যাক

1
is / optimালাই অপ্টিমাইজেশনের জন্য একটি সহজ টার্গেট, এটি একটি বিরক্তিকর সাধারণ প্রতিমা।
আন্তন টিখি

4
পদ্ধতির স্ট্যাক ফ্রেম তৈরি হওয়ার সময় স্থানীয় ভেরিয়েবলগুলি স্ট্যাকের জন্য বরাদ্দ করা হয়, সুতরাং যেখানে আপনি পদ্ধতিটিতে ভেরিয়েবল ঘোষণা করেন তা মোটেই কোনও পার্থক্য করে না। (যদি না এটি অবশ্যই বন্ধ হয়ে যায় তবে এখানে বিষয়টি তেমন নয়)
গুফা 21

8

আমি সঠিক ধরণের চেক কনস্ট্রাক্ট চেষ্টা করেছি

typeof(int) == item.GetType(), যা item is intসংস্করণ হিসাবে তত দ্রুত সঞ্চালিত হয় এবং সর্বদা সংখ্যাটি ফেরত দেয় (জোর দেওয়া: আপনি Nullable<int>অ্যারেতে একটি লিখলেও, আপনার ব্যবহারের প্রয়োজন হবে typeof(int))। আপনার null != itemএখানে অতিরিক্ত চেকও দরকার ।

যাহোক

typeof(int?) == item.GetType()দ্রুত থাকে (বিপরীতে item is int?), তবে সর্বদা মিথ্যা দেয়।

টাইপ অফ -কনস্ট্রাক্ট আমার নজরে সঠিক টাইপ পরীক্ষার জন্য দ্রুততম উপায় , কারণ এটি রানটাইমটাইপহ্যান্ডল ব্যবহার করে। যেহেতু এই ক্ষেত্রে যথাযথ প্রকারগুলি অযোগ্যতার সাথে মেলে না, তাই আমার অনুমান, is/asএটি আসলে একটি নলাবল টাইপের উদাহরণ কিনা তা নিশ্চিত করার জন্য এখানে অতিরিক্ত ভারী উত্তোলন করতে হবে।

এবং সত্যই: আপনার কি is Nullable<xxx> plus HasValueআপনাকে কিনে? কিছুই নেই। আপনি সর্বদা অন্তর্নিহিত (মান) প্রকারে (এই ক্ষেত্রে) সরাসরি যেতে পারেন। আপনি হয় মান পাবেন বা "না, আপনি যে ধরণের জন্য জিজ্ঞাসা করেছিলেন তার উদাহরণ নয়"। এমনকি আপনি (int?)nullঅ্যারেতে লিখলেও, টাইপ চেকটি মিথ্যা ফিরে আসবে।


জবর ... ব্যবহার করার ব্যাপারে "হিসাবে," + HasValue (না হয় যে এটি শুধুমাত্র প্রকার চেক করণ এর প্লাস HasValue, নোট) একবার দুইবার পরিবর্তে। এটি একক পদক্ষেপে "চেক এবং আনবক্স" করছে। এটির দ্রুত হওয়া উচিত বলে মনে হয় ... তবে এটি স্পষ্টভাবে তা নয়। আপনি শেষ বাক্যটি দিয়ে কী বোঝাতে চাইছেন তা নিশ্চিত নই, তবে বাক্সযুক্ত বলে কোনও জিনিস নেই int?- আপনি যদি কোনও int?মান বাক্স করেন তবে এটি একটি বাক্সড ইন্ট বা nullরেফারেন্স হিসাবে শেষ হয় ।
জন স্কিটি

7
using System;
using System.Diagnostics;
using System.Linq;

class Test
{
    const int Size = 30000000;

    static void Main()
    {
        object[] values = new object[Size];
        for (int i = 0; i < Size - 2; i += 3)
        {
            values[i] = null;
            values[i + 1] = "";
            values[i + 2] = 1;
        }

        FindSumWithCast(values);
        FindSumWithAsAndHas(values);
        FindSumWithAsAndIs(values);


        FindSumWithIsThenAs(values);
        FindSumWithIsThenConvert(values);

        FindSumWithLinq(values);



        Console.ReadLine();
    }

    static void FindSumWithCast(object[] values)
    {
        Stopwatch sw = Stopwatch.StartNew();
        int sum = 0;
        foreach (object o in values)
        {
            if (o is int)
            {
                int x = (int)o;
                sum += x;
            }
        }
        sw.Stop();
        Console.WriteLine("Cast: {0} : {1}", sum,
                          (long)sw.ElapsedMilliseconds);
    }

    static void FindSumWithAsAndHas(object[] values)
    {
        Stopwatch sw = Stopwatch.StartNew();
        int sum = 0;
        foreach (object o in values)
        {
            int? x = o as int?;
            if (x.HasValue)
            {
                sum += x.Value;
            }
        }
        sw.Stop();
        Console.WriteLine("As and Has: {0} : {1}", sum,
                          (long)sw.ElapsedMilliseconds);
    }


    static void FindSumWithAsAndIs(object[] values)
    {
        Stopwatch sw = Stopwatch.StartNew();
        int sum = 0;
        foreach (object o in values)
        {
            int? x = o as int?;
            if (o is int)
            {
                sum += x.Value;
            }
        }
        sw.Stop();
        Console.WriteLine("As and Is: {0} : {1}", sum,
                          (long)sw.ElapsedMilliseconds);
    }







    static void FindSumWithIsThenAs(object[] values)
    {
        // Apple-to-apple comparison with Cast routine above.
        // Using the similar steps in Cast routine above,
        // the AS here cannot be slower than Linq.



        Stopwatch sw = Stopwatch.StartNew();
        int sum = 0;
        foreach (object o in values)
        {

            if (o is int)
            {
                int? x = o as int?;
                sum += x.Value;
            }
        }
        sw.Stop();
        Console.WriteLine("Is then As: {0} : {1}", sum,
                          (long)sw.ElapsedMilliseconds);
    }

    static void FindSumWithIsThenConvert(object[] values)
    {
        Stopwatch sw = Stopwatch.StartNew();
        int sum = 0;
        foreach (object o in values)
        {            
            if (o is int)
            {
                int x = Convert.ToInt32(o);
                sum += x;
            }
        }
        sw.Stop();
        Console.WriteLine("Is then Convert: {0} : {1}", sum,
                          (long)sw.ElapsedMilliseconds);
    }



    static void FindSumWithLinq(object[] values)
    {
        Stopwatch sw = Stopwatch.StartNew();
        int sum = values.OfType<int>().Sum();
        sw.Stop();
        Console.WriteLine("LINQ: {0} : {1}", sum,
                          (long)sw.ElapsedMilliseconds);
    }
}

আউটপুট:

Cast: 10000000 : 456
As and Has: 10000000 : 2103
As and Is: 10000000 : 2029
Is then As: 10000000 : 1376
Is then Convert: 10000000 : 566
LINQ: 10000000 : 1811

[সম্পাদনা: 2010-06-19]

দ্রষ্টব্য: পূর্ববর্তী পরীক্ষাটি ভিএস, কনফিগারেশন ডিবাগ, ভিএস2009 ব্যবহার করে, কোর আই 7 (সংস্থার বিকাশ মেশিন) ব্যবহার করে করা হয়েছিল।

নিম্নলিখিত ভিএস 2010 ব্যবহার করে কোর 2 ডুও ব্যবহার করে আমার মেশিনে করা হয়েছিল

Inside VS, Configuration: Debug

Cast: 10000000 : 309
As and Has: 10000000 : 3322
As and Is: 10000000 : 3249
Is then As: 10000000 : 1926
Is then Convert: 10000000 : 410
LINQ: 10000000 : 2018




Outside VS, Configuration: Debug

Cast: 10000000 : 303
As and Has: 10000000 : 3314
As and Is: 10000000 : 3230
Is then As: 10000000 : 1942
Is then Convert: 10000000 : 418
LINQ: 10000000 : 1944




Inside VS, Configuration: Release

Cast: 10000000 : 305
As and Has: 10000000 : 3327
As and Is: 10000000 : 3265
Is then As: 10000000 : 1942
Is then Convert: 10000000 : 414
LINQ: 10000000 : 1932




Outside VS, Configuration: Release

Cast: 10000000 : 301
As and Has: 10000000 : 3274
As and Is: 10000000 : 3240
Is then As: 10000000 : 1904
Is then Convert: 10000000 : 414
LINQ: 10000000 : 1936

আগ্রহের বাইরে আপনি কোন ফ্রেমওয়ার্ক সংস্করণটি ব্যবহার করছেন? আমার নেটবুকের ফলাফল (.NET 4RC ব্যবহার করে) আরও বেশি নাটকীয় - হিসাবে ব্যবহার করা সংস্করণগুলি আপনার ফলাফলের চেয়ে অনেক খারাপ। সম্ভবত তারা। নেট 4 আরটিএম এর জন্য এটি উন্নত করেছে? আমি এখনও মনে করি এটি আরও দ্রুত হতে পারে ...
জন স্কিটি

@ মিশেল: আপনি কি বিনা প্রতিরোধী বিল্ড চালাচ্ছেন, বা ডিবাগারে চলছে?
জন স্কিটি

@ জোন: অপরিশোধিত বিল্ড, ডিবাগারের আওতায়
মাইকেল বুয়েন

1
@ মিশেল: ডান - আমি একটি ডিবাগারের অধীনে পারফরম্যান্সের ফলাফলগুলি অনেকাংশে অপ্রাসঙ্গিক হিসাবে দেখি :)
জন স্কিটে

@ জোন: যদি ডিবাগারের অধীনে, ভিএস এর অভ্যন্তরে অর্থ; হ্যাঁ পূর্ববর্তী মানদণ্ডটি ডিবাগারের অধীনে করা হয়েছিল। আমি আবার, ভিএস এর ভিতরে এবং এর বাইরেও বেঞ্চমার্ক করেছি এবং ডিবাগ হিসাবে সংকলন করেছি এবং প্রকাশের মতো সংকলিত। সম্পাদনাটি দেখুন
মাইকেল বুয়েন
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.