कौन सी विधि बेहतर प्रदर्शन करती है: .Any () बनाम .Count ()> 0?


576

में System.Linqनाम स्थान, अब हम हमारे विस्तार कर सकते हैं IEnumerable है के लिए किसी भी () और गणना () विस्तार के तरीकों

मुझे हाल ही में कहा गया था कि अगर मैं यह जांचना चाहता हूं कि संग्रह में 1 या अधिक आइटम हैं, तो मुझे .Any()एक्सटेंशन विधि के बजाय एक्सटेंशन विधि का उपयोग करना चाहिए .Count() > 0क्योंकि .Count()एक्सटेंशन विधि को सभी आइटमों के माध्यम से पुनरावृति करना है।

दूसरे, कुछ संग्रहों में एक संपत्ति है (विस्तार विधि नहीं) जो है Countया है Length। यह बजाय, उन का उपयोग करने के बेहतर होगा .Any()या .Count()?

हाँ / ना?


संग्रह पर गणना और गणना पर किसी भी () का उपयोग करने के लिए बेहतर है। अगर किसी को लगता है कि लेखन '(somecollection.Count> 0)' पठनीयता के मुद्दों को उलझाएगा या पैदा करेगा, तो इसे विस्तार विधि नाम से लिखें। तब सभी ने संतुष्ट किया। प्रदर्शन-वार और साथ ही पठनीय-वार। ताकि आपके सभी कोड में स्थिरता हो और आपके प्रोजेक्ट में अलग-अलग डेवलपर को Count vs Any चुनने की चिंता न हो।
महेश बोंगानी

जवाबों:


708

यदि आपको किसी ऐसे है कि के साथ शुरू कर रहे हैं .Lengthया .Count(जैसे ICollection<T>, IList<T>, List<T>, आदि) - तो यह सबसे तेजी से विकल्प, हो जाएगा के बाद से यह माध्यम से जाने की जरूरत नहीं है GetEnumerator()/ MoveNext()/ Dispose()अनुक्रम के लिए आवश्यक Any()एक गैर खाली की जांच करने के IEnumerable<T>अनुक्रम ।

बस के लिए IEnumerable<T>है, तो Any()होगा आम तौर पर , तेज़ी से किया जा के रूप में यह केवल एक ही यात्रा को देखने के लिए है। हालाँकि, ध्यान दें कि LINQ-to-Objects कार्यान्वयन Count()करता है ICollection<T>( .Countएक अनुकूलन के रूप में उपयोग ) के लिए जाँच - तो अगर आपके अंतर्निहित डेटा-स्रोत सीधे एक सूची / संग्रह है, तो बहुत बड़ा अंतर नहीं होगा। मुझसे मत पूछो कि यह गैर-जेनेरिक का उपयोग क्यों नहीं करता है ICollection...

बेशक, यदि आपने इसे ( Whereआदि) फ़िल्टर करने के लिए LINQ का उपयोग किया है, तो आपके पास एक इट्रेटर-ब्लॉक आधारित अनुक्रम होगा, और इसलिए यह ICollection<T>अनुकूलन बेकार है।

सामान्य तौर पर IEnumerable<T>: छड़ी के साथ Any(); -पी


9
मार्क: ICollection <T> वास्तव में ICollection से नहीं निकलता है। मैं भी हैरान था, लेकिन रिफलेक्टर झूठ नहीं बोलता।
ब्रायन वॉट्स

7
क्या ICollection इंटरफ़ेस के लिए कोई भी () कार्यान्वयन जाँच और गणना गुण के लिए जाँच नहीं करता है?
derigel

313
मुझे लगता है कि किसी भी समय () का उपयोग करने का एक और कारण है। यह डेवलपर के सटीक इरादे का संकेत देता है। यदि आप वस्तुओं की संख्या जानने में रुचि नहीं रखते हैं, लेकिन केवल अगर कुछ हैं, तो somecollection.Any () somecollection की तुलना में सरल और स्पष्ट है ।ाउंट> 0
TJKjaer

13
@huttelihut - आप कितने डेवलपर्स को जानते हैं जो वास्तव में बयान से भ्रमित हैं (somecollection.Count > 0)? क्या LINQ's .Any () पद्धति की शुरुआत से पहले हमारे सभी कोड समझने में मुश्किल थे?
क्रेगटीपी

25
@JLRhehe - मुझे अभी भी लगता है कि someCollection.Count > 0जैसा कि स्पष्ट है someCollection.Any()और अधिक प्रदर्शन का अतिरिक्त लाभ है और LINQ की आवश्यकता नहीं है। दी, यह एक बहुत ही सरल मामला है और LINQ ऑपरेटर्स का उपयोग करने वाले अन्य कंस्ट्रक्शन डेवलपर्स को समान गैर-लाइनर विकल्प की तुलना में बहुत स्पष्ट होने का इरादा करेंगे।
क्रेगटीपी

65

नोट: मैंने यह उत्तर तब लिखा था जब Entity Framework 4 वास्तविक था। इस उत्तर की बात तुच्छ .Any()बनाम .Count()प्रदर्शन परीक्षण में नहीं थी। संकेत यह था कि ईएफ एकदम सही है। नए संस्करण बेहतर हैं ... लेकिन अगर आपके पास कोड का हिस्सा धीमा है और यह EF का उपयोग करता है, तो प्रत्यक्ष TSQL के साथ परीक्षण करें और मान्यताओं पर भरोसा करने के बजाय प्रदर्शन की तुलना करें (जो कि .Any()हमेशा की तुलना में तेज़ है .Count() > 0)।


हालांकि मैं अधिकांश मतदान-उत्तर और टिप्पणियों से सहमत हूं - विशेष रूप से पॉइंट Anyसिग्नल डेवलपर की मंशा से बेहतर है Count() > 0- मेरे पास ऐसी स्थिति है जिसमें SQL सर्वर पर परिमाण के क्रम से गिनती तेज है (EntityFramework 4)।

यहाँ Anyउस समय समाप्ति अपवाद (~ 200.000 रिकॉर्ड पर) के साथ प्रश्न है :

con = db.Contacts.
    Where(a => a.CompanyId == companyId && a.ContactStatusId <= (int) Const.ContactStatusEnum.Reactivated
        && !a.NewsletterLogs.Any(b => b.NewsletterLogTypeId == (int) Const.NewsletterLogTypeEnum.Unsubscr)
    ).OrderBy(a => a.ContactId).
    Skip(position - 1).
    Take(1).FirstOrDefault();

Count मिलीसेकंड के मामले में निष्पादित संस्करण:

con = db.Contacts.
    Where(a => a.CompanyId == companyId && a.ContactStatusId <= (int) Const.ContactStatusEnum.Reactivated
        && a.NewsletterLogs.Count(b => b.NewsletterLogTypeId == (int) Const.NewsletterLogTypeEnum.Unsubscr) == 0
    ).OrderBy(a => a.ContactId).
    Skip(position - 1).
    Take(1).FirstOrDefault();

मुझे यह देखने का एक तरीका खोजने की आवश्यकता है कि LINQs का सटीक एसक्यूएल दोनों क्या उत्पादन करते हैं - लेकिन यह स्पष्ट है कि कुछ मामलों में Countऔर इसके बीच बहुत बड़ा प्रदर्शन अंतर है Any, और दुर्भाग्य से ऐसा लगता है कि आप Anyसभी मामलों में बस नहीं टिक सकते ।

संपादित करें: यहाँ एसक्यूएल उत्पन्न होते हैं। सुंदरियों के रूप में आप देख सकते हैं;)

ANY:

निष्पादित sp_executesql N'SELECT TOP (1) 
[Project2]। [ContactId] AS [ContactId], 
[Project2]। [CompanyId] AS [CompanyId], 
[Project2]। [संपर्क नाम] के रूप में [ContactName], 
[Project2]। [FullName] AS [FullName], 
[Project2]। [ContactStatusId] AS [ContactStatusId], 
[प्रोजेक्ट 2]। [बनाया गया] एएस [निर्मित]
FROM (SELECT [Project2]। [ContactId] AS [ContactId], [Project2]। [CompanyId] AS [CompanyId], [Project2], [ContactName] AS [ContactName], [Project2] [FullName] AS [FullName] के रूप में। , [Project2]। [ContactStatusId] AS [ContactStatusId], [Project2]। [बनाया गया] AS [बनाया गया], row_number () OVER (ORDER BY [Project2]] [ContactId] ASC) AS [row_number]।
    FROM (Select 
        [Extent1]। [ContactId] AS [ContactId], 
        [एक्सटेंट 1]। [कंपनीआईड] एएस [कंपनीआईड], 
        [विस्तार 1]। [संपर्क नाम] के रूप में [संपर्क नाम], 
        [विस्तार 1]। [FullName] AS [FullName], 
        [Extent1]। [ContactStatusId] AS [ContactStatusId]] 
        [विस्तार १]। [बनाया गया] एएस [निर्मित]
        [Dbo] से [संपर्क] AS [Extent1]
        जहां ([एक्सटेंड 1]। 
            1 AS [C1]
            [Dbo] से [न्यूज़लैटरलॉग] AS [Extent2]
            WHERE ([एक्सटेंड 1]। [कांटेक्टआईड] = [एक्सटेंट 2]। [कॉन्टैक्टआईड]) और (6 = [एक्सटेंट 2]। [न्यूज़लैटरलॉगटाइप्ड]]
        ))
    ) के रूप में [Project2]
) के रूप में [Project2]
जहां [Project2]। [row_number]> 99
ORDER BY [Project2]। [ContactId] ASC ', N' @ p__linq__0 int ', @ p__linq__0 = 4

COUNT:

निष्पादित sp_executesql N'SELECT TOP (1) 
[Project2]। [ContactId] AS [ContactId], 
[Project2]। [CompanyId] AS [CompanyId], 
[Project2]। [संपर्क नाम] के रूप में [ContactName], 
[Project2]। [FullName] AS [FullName], 
[Project2]। [ContactStatusId] AS [ContactStatusId], 
[प्रोजेक्ट 2]। [बनाया गया] एएस [निर्मित]
FROM (SELECT [Project2]। [ContactId] AS [ContactId], [Project2]। [CompanyId] AS [CompanyId], [Project2], [ContactName] AS [ContactName], [Project2] [FullName] AS [FullName] के रूप में। , [Project2]। [ContactStatusId] AS [ContactStatusId], [Project2]। [बनाया गया] AS [बनाया गया], row_number () OVER (ORDER BY [Project2]] [ContactId] ASC) AS [row_number]।
    FROM (Select 
        [Project1]। [ContactId] AS [ContactId], 
        [Project1]। [CompanyId] AS [CompanyId], 
        [Project1]। [संपर्क नाम] के रूप में [ContactName], 
        [Project1]। [FullName] AS [FullName], 
        [Project1]। [ContactStatusId] AS [ContactStatusId], 
        [प्रोजेक्ट 1]। [बनाया गया] एएस [निर्मित]
        FROM (Select 
            [Extent1]। [ContactId] AS [ContactId], 
            [एक्सटेंट 1]। [कंपनीआईड] एएस [कंपनीआईड], 
            [विस्तार 1]। [संपर्क नाम] के रूप में [संपर्क नाम], 
            [विस्तार 1]। [FullName] AS [FullName], 
            [Extent1]। [ContactStatusId] AS [ContactStatusId] 
            [विस्तार १]। [बनाया गया] एएस [निर्मित], 
            (चुनते हैं 
                COUNT (1) AS [A1]
                [Dbo] से [न्यूज़लैटरलॉग] AS [Extent2]
                WHERE ([Extent1]। [ContactId] = [Extent2]। [ContactId]) और (6 = [Extent2]। [NewsletterLogTypeId])) AS [C1]।
            [Dbo] से [संपर्क] AS [Extent1]
        ) के रूप में [Project1]
        WHERE ([Project1]। [CompanyId] = @ p__linq__0) AND ([Project1]। [ContactStatusId] <= 3) और (0 = [Project1]। [C1])
    ) के रूप में [Project2]
) के रूप में [Project2]
जहां [Project2]। [row_number]> 99
ORDER BY [Project2]। [ContactId] ASC ', N' @ p__linq__0 int ', @ p__linq__0 = 4

लगता है कि शुद्ध जहां EXISTS के साथ गणना की तुलना में बहुत बुरा काम करता है और फिर कहां गणना == 0 के साथ कर रहा है।

अगर आप लोग मेरे निष्कर्षों में कुछ त्रुटि देखते हैं तो मुझे बताएं। किसी भी बनाम गणना चर्चा की परवाह किए बिना इस सब से बाहर ले जाया जा सकता है कि किसी भी अधिक जटिल LINQ को बेहतर तरीके से बंद कर दिया जाता है जब संग्रहीत प्रक्रिया के रूप में फिर से लिखा जाता है;)।


2
कुछ Sql Query योजनाओं को देखना पसंद करेंगे जो प्रत्येक परिदृश्य के लिए प्रत्येक linq-query द्वारा उत्पन्न होती हैं।
प्योर.क्रोम

43
एसक्यूएल के आधार पर, मैं केवल इतना कह सकता हूं: दोनों प्रश्न भयानक लग रहे हैं। मुझे पता था कि एक कारण मैं सामान्य रूप से अपने ही TSQL बारे में नहीं था ...
मार्क Gravell

किसी भी गिनती के रूप में सभी पंक्तियों के माध्यम से देखना होगा। कि आपका उदाहरण इस तरह के एक भयावह परिणाम देता है, थोड़ा अजीब है, सबसे खराब स्थिति में! कोई भी गणना से थोड़ा धीमा होना चाहिए। आपके मामले में मैं चयन को सरल बनाने के तरीकों की तलाश करूंगा, शायद इसे चरणों में विभाजित करना या यदि संभव हो तो शर्तों को फिर से व्यवस्थित करना। लेकिन आपका कहना है कि कोई भी गणना से बेहतर है नियम के लिए पकड़ नहीं है! कोई भी गिनती से बेहतर है एक बहुत अच्छा है।
बेंट

25

चूंकि यह एक लोकप्रिय विषय है और उत्तर अलग-अलग हैं, इसलिए मुझे समस्या पर नए सिरे से विचार करना पड़ा।

परीक्षण env: EF 6.1.3, SQL सर्वर, 300k रिकॉर्ड

टेबल मॉडल :

class TestTable
{
    [Key]
    public int Id { get; set; }

    public string Name { get; set; }

    public string Surname { get; set; }
}

टेस्ट कोड:

class Program
{
    static void Main()
    {
        using (var context = new TestContext())
        {
            context.Database.Log = Console.WriteLine;

            context.TestTables.Where(x => x.Surname.Contains("Surname")).Any(x => x.Id > 1000);
            context.TestTables.Where(x => x.Surname.Contains("Surname") && x.Name.Contains("Name")).Any(x => x.Id > 1000);
            context.TestTables.Where(x => x.Surname.Contains("Surname")).Count(x => x.Id > 1000);
            context.TestTables.Where(x => x.Surname.Contains("Surname") && x.Name.Contains("Name")).Count(x => x.Id > 1000);

            Console.ReadLine();
        }
    }
}

परिणाम:

कोई भी () ~ 3ms

गणना () ~ पहली क्वेरी के लिए 230ms, दूसरी के लिए ~ 400ms

टिप्पणियों:

मेरे मामले के लिए, EF ने अपनी पोस्ट में उल्लिखित @Ben की तरह SQL उत्पन्न नहीं किया।


4
एक उचित तुलना के लिए, आपको करना चाहिए Count() > 0। : डी
एंड्रयू

1
एंड्रयू, काउंट ()> 0 इस विशेष टेस्ट में काउंट () से अलग नहीं चल रहा है।
कोडमोंकिफ़ोरहेयर

11

EDIT: इसे EF संस्करण 6.1.1 में तय किया गया था। और यह उत्तर अधिक वास्तविक नहीं है

SQL सर्वर और EF4-6 के लिए, काउंट () किसी भी () से लगभग दो गुना तेज प्रदर्शन करता है।

जब आप Table.Any () चलाते हैं, तो यह कुछ ऐसा उत्पन्न करेगा ( अलर्ट: इसे समझने की कोशिश करने वाले मस्तिष्क को चोट न पहुंचे )

SELECT 
CASE WHEN ( EXISTS (SELECT 
    1 AS [C1]
    FROM [Table] AS [Extent1]
)) THEN cast(1 as bit) WHEN ( NOT EXISTS (SELECT 
    1 AS [C1]
    FROM [Table] AS [Extent2]
)) THEN cast(0 as bit) END AS [C1]
FROM  ( SELECT 1 AS X ) AS [SingleRowTable1]

आपकी स्थिति के साथ पंक्तियों के 2 स्कैन की आवश्यकता है।

मुझे लिखना पसंद नहीं है Count() > 0क्योंकि यह मेरा इरादा छुपाता है। मैं इसके लिए कस्टम विधेय का उपयोग करना पसंद करता हूं:

public static class QueryExtensions
{
    public static bool Exists<TSource>(this IQueryable<TSource> source, Expression<Func<TSource, bool>> predicate)
    {
        return source.Count(predicate) > 0;
    }
}

मैंने इस पर भी गौर किया। किसी भी () SQL का कोई मतलब नहीं है। मुझे यकीन नहीं है कि वे ऐसा क्यों नहीं करते हैं: मामला कब (EXISTS (sql)) 1 ELSE 0 END। मैं एक कारण के बारे में नहीं सोच सकता कि उन्हें 0. वापस करने के लिए एक NOT EXISTS करने की आवश्यकता क्यों है
scott.korin

यह गलत है। आपको यादृच्छिक मौका द्वारा एक खराब क्वेरी योजना मिली। ऐसा होता है। कोई भी, लगभग हमेशा, तेज है।
यूएसआर

मैंने 6.1.3 में उत्पन्न sql की जाँच की, उन्होंने इसे तय किया: सेलेक्ट सीन WHEN (EXISTS (AS 1] [C1] FROM [dbo] से। TestTables] AS [Extent1] WHERE [Extent1] [Id]> 1000))। डाली (1 बिट के रूप में) ELSE कास्ट (0 बिट के रूप में) END AS [C1] FROM (Select 1 AS X) AS [SingleRowTable1]
बेन

6

यह निर्भर करता है, डेटा सेट कितना बड़ा है और आपकी प्रदर्शन आवश्यकताएं क्या हैं?

यदि यह कुछ भी नहीं है विशालतम पठनीय रूप का उपयोग करें, जो स्वयं के लिए कोई भी हो, क्योंकि यह समीकरण के बजाय छोटा और पठनीय है।


2

के बारे में गणना () विधि, अगर IEnumarable एक है ICollection , तो हम कर सकते हैं नहीं पुनरावृति सभी आइटम में है क्योंकि हम प्राप्त कर सकते हैं गणना के क्षेत्र ICollection , अगर IEnumerable एक नहीं है ICollection हम पुनरावृति एक का उपयोग कर सभी आइटम में होना चाहिए , जबकि साथ एक MoveNext , .NET फ्रेमवर्क कोड पर एक नज़र डालें:

public static int Count<TSource>(this IEnumerable<TSource> source)
{
    if (source == null) 
        throw Error.ArgumentNull("source");

    ICollection<TSource> collectionoft = source as ICollection<TSource>;
    if (collectionoft != null) 
        return collectionoft.Count;

    ICollection collection = source as ICollection;
    if (collection != null) 
        return collection.Count;

    int count = 0;
    using (IEnumerator<TSource> e = source.GetEnumerator())
    {
        checked
        {
            while (e.MoveNext()) count++;
        }
    }
    return count;
}

संदर्भ: संदर्भ स्रोत असंख्य


2

आप इसका पता लगाने के लिए एक साधारण परीक्षण कर सकते हैं:

var query = //make any query here
var timeCount = new Stopwatch();
timeCount.Start();
if (query.Count > 0)
{
}
timeCount.Stop();
var testCount = timeCount.Elapsed;

var timeAny = new Stopwatch();
timeAny.Start();
if (query.Any())
{
}
timeAny.Stop();
var testAny = timeAny.Elapsed;

TestCount और testAny के मूल्यों की जाँच करें।


1
यहाँ गिनती संपत्ति बनाम किसी के लिए आपके कोड के साथ परीक्षण किया गया है (किसी भी + + 2x के साथ गणना संपत्ति जीत बनाम कोई) - लिंक
स्टानिस्लाव प्रूसैक

1
बेहतर परिणाम के लिए, आप इन तुलनाओं को 1000 गुना (या अधिक) कर सकते हैं। यह परिणामों को औसत करने और किसी भी यादृच्छिक स्पाइक्स से बचने में मदद करता है।
रोमन

जब आप उपर्युक्त विधि की तरह परीक्षण कर रहे हैं, तो आपको कई और कारकों पर विचार करने की आवश्यकता है, जैसे आपके डेटाबेस / नेटवर्क पर लोड, डेटाबेस साइड में कैशिंग की योजना, आदि। इसलिए एक सटीक परीक्षण करने के लिए आपको एक पृथक और सटीक वातावरण डिजाइन करना चाहिए।
वाहिद फराहमंदियन

तुलना करने के लिए बेहतर Countतरीके से विधि की गणना की जानी चाहिए () बनाम। किसी भी तरह की संपत्ति नहीं। आपको पुनरावृत्तियों के समय की आवश्यकता है।
daremachine

0

यदि आप एंटिटी फ्रेमवर्क का उपयोग कर रहे हैं और कई रिकॉर्डों के साथ एक विशाल तालिका है (तो) बहुत तेज होगा। मुझे याद है कि एक बार मैं यह देखना चाहता था कि क्या एक मेज खाली थी और उसमें लाखों पंक्तियाँ थीं। गणना में ()> 0 को पूरा करने में 20-30 सेकंड का समय लगा। यह किसी भी () के साथ तत्काल था ।

कोई भी () एक प्रदर्शन वृद्धि हो सकती है क्योंकि उसे चीजों की संख्या प्राप्त करने के लिए संग्रह को पुनरावृत्त नहीं करना पड़ सकता है। यह सिर्फ उनमें से एक को मारना है। या, के लिए, LINQ-to-Entities, कहते हैं, उत्पन्न SQL IF COIS (...) के बजाय SELECT COUNT ... या यहां तक ​​कि SELECT * होगा ...।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.