มันจะดีกว่าที่จะเรียก ToList () หรือ ToArray () ในการสืบค้น LINQ?


519

ฉันมักจะทำงานในกรณีที่ฉันต้องการที่จะประเมินการสอบถามที่ฉันประกาศ นี่เป็นเพราะฉันต้องทำซ้ำหลาย ๆ ครั้งและมันมีราคาแพงในการคำนวณ ตัวอย่างเช่น:

string raw = "...";
var lines = (from l in raw.Split('\n')
             let ll = l.Trim()
             where !string.IsNullOrEmpty(ll)
             select ll).ToList();

ใช้งานได้ดี แต่ถ้าฉันจะไม่แก้ไขผลลัพธ์ฉันก็อาจจะโทรหาคุณToArray()แทนToList()ก็ได้

แต่ฉันสงสัยว่าToArray()จะดำเนินการโดยการโทรครั้งแรกและเป็นหน่วยความจำจึงมีประสิทธิภาพน้อยกว่าเพียงแค่โทรToList()ToList()

ฉันบ้าเหรอ? ฉันควรจะโทรToArray()- ปลอดภัยในความรู้ที่หน่วยความจำจะไม่ถูกจัดสรรสองครั้ง?


10
ถ้าคุณเคยต้องการที่จะหาสิ่งที่เกิดขึ้นหลังม่านใน .NET ที่ผมขอแนะนำให้.NET สะท้อน
เดวิด Hedlund

32
@DavidHedlund ผมขอแนะนำให้รหัสที่มา .net
Gqqnbig

1
ฉันไม่เห็นด้วยที่stackoverflow.com/questions/6750447/c-toarray-performanceซ้ำซ้อนกับคำถามนี้แม้ว่าจะมีความสัมพันธ์ที่สำคัญก็ตาม ทั้งการใช้หน่วยความจำ (คำถามนี้) และประสิทธิภาพ (คำถามอื่น ๆ ) และมีการพิจารณาที่น่าสนใจและไม่สำคัญ สามารถอธิบายแยกกันได้ แต่ทั้งคู่ควรคำนึงถึงการตัดสินใจเลือกหนึ่งรายการด้วยกัน ฉันไม่สามารถแนะนำหนึ่งในคำตอบของคำถามนี้หรือคำถามอื่นใดที่ครอบคลุมได้ มีหลายคำตอบที่เมื่อนำมารวมกันจะให้การอภิปรายที่ค่อนข้างสมบูรณ์เกี่ยวกับวิธีการเลือกหนึ่งมากกว่าอีก
สตีฟ

1
@Gqqnbig - ความคิดเห็นที่มีประโยชน์ที่สุดเท่าที่เคยมีมา! ขอบคุณ :-)
Mark Cooper

คำตอบ:


366

เว้นแต่คุณก็ต้องอาร์เรย์เพื่อตอบสนองข้อ จำกัด อื่น ๆ ToListที่คุณควรใช้ ในส่วนของสถานการณ์จะจัดสรรหน่วยความจำมากกว่า ToArrayToList

ทั้งสองใช้อาร์เรย์สำหรับเก็บข้อมูล แต่ToListมีข้อ จำกัด ที่ยืดหยุ่นมากกว่า จำเป็นต้องมีอาร์เรย์อย่างน้อยใหญ่เท่ากับจำนวนองค์ประกอบในคอลเลกชัน หากอาร์เรย์มีขนาดใหญ่แสดงว่าไม่ใช่ปัญหา อย่างไรก็ตามToArrayความต้องการอาร์เรย์ต้องมีขนาดเท่ากับจำนวนองค์ประกอบ

เพื่อให้เป็นไปตามข้อ จำกัด นี้ToArrayมักจะจัดสรรมากกว่าToListหนึ่งครั้ง เมื่อมันมีอาร์เรย์ที่มีขนาดใหญ่พอที่จะจัดสรรอาร์เรย์ซึ่งเป็นขนาดที่ถูกต้องและคัดลอกองค์ประกอบกลับเข้าไปในอาร์เรย์นั้น เวลาเดียวที่สามารถหลีกเลี่ยงปัญหานี้ได้ก็คือเมื่ออัลกอริธึมการเติบโตของอาเรย์นั้นเกิดขึ้นพร้อมกับจำนวนองค์ประกอบที่ต้องจัดเก็บ (แน่นอนในชนกลุ่มน้อย)

แก้ไข

มีคนสองคนถามฉันเกี่ยวกับผลของการมีหน่วยความจำที่ไม่ได้ใช้เป็นพิเศษในList<T>ค่า

นี่เป็นข้อกังวลที่ถูกต้อง หากคอลเลกชันที่สร้างมีอายุการใช้งานนานจะไม่ถูกแก้ไขหลังจากถูกสร้างขึ้นและมีโอกาสสูงในการลงจอดในฮีป Gen2 ดังนั้นคุณอาจจะดีกว่าที่จะทำการจัดสรรToArrayล่วงหน้า

โดยทั่วไปแล้วฉันพบว่านี่เป็นเคสที่หายากกว่า เป็นเรื่องปกติมากขึ้นที่จะเห็นการToArrayโทรจำนวนมากที่ส่งผ่านไปยังการใช้งานหน่วยความจำระยะสั้นในทันทีซึ่งToListเป็นกรณีที่ดีกว่า

กุญแจสำคัญในที่นี้คือเพื่อทำโปรไฟล์โปรไฟล์และโปรไฟล์อื่น ๆ


14
ในทางกลับกันหน่วยความจำเพิ่มเติมที่จัดสรรให้กับงานสร้างอาร์เรย์จะไม่มีสิทธิ์ได้รับการรวบรวมขยะในขณะที่ค่าใช้จ่ายเพิ่มเติมสำหรับรายการจะยังคงอยู่หรือไม่ ฉันบอกว่าทำให้มันง่ายขึ้น หากคุณต้องการเพิ่มหรือลบองค์ประกอบมีเครื่องมือสำหรับการที่ ถ้าคุณทำไม่ได้มีเครื่องมือที่แตกต่างกัน ใช้สิ่งที่สมเหตุสมผล หากในภายหลังคุณค้นพบปัญหาเกี่ยวกับหน่วยความจำและประสิทธิภาพและนี่คือปัญหาให้เปลี่ยน
Anthony Pegram

1
@AnthonyPegram ใช่ว่าเป็นการพิจารณาที่ถูกต้องที่จะทำ หากมีการใช้ค่าในที่จัดเก็บข้อมูลระยะยาวจะไม่ได้รับการแก้ไขและอาจทำให้กลายเป็น Gen 2 ดังนั้นคุณอาจจ่ายการจัดสรรพิเศษได้ดีขึ้นในขณะนี้เทียบกับการสร้างมลพิษ Gen 2 ฮีป IME แม้ว่าฉันจะไม่ค่อยเห็นสิ่งนี้ เป็นเรื่องปกติมากที่จะเห็น ToArray ถูกส่งผ่านทันทีไปยังอีกแบบสอบถาม LINQ อายุสั้น
JaredPar

2
@AnthonyPegram ฉันอัปเดตคำตอบของฉันเพื่อรวมด้านการสนทนานี้
JaredPar

8
@ JaredPar ฉันไม่เข้าใจว่าToArrayจะจัดสรรหน่วยความจำเพิ่มเติมได้อย่างไรหากต้องการขนาดตำแหน่งที่ToList<>แน่นอน (autoincrease)
Royi Namir

5
@RoyiNamir เพราะ ToArray ก่อนทำการจัดสรร ToList-style ด้วยโอเวอร์เฮดจากนั้นทำการจัดสรรขนาดที่แน่นอนเพิ่มเติม
Timbo

169

ความแตกต่างของประสิทธิภาพจะไม่มีนัยสำคัญเนื่องจากList<T>จะถูกนำมาใช้เป็นอาร์เรย์ที่มีขนาดแบบไดนามิก การเรียกอย่างใดอย่างหนึ่งToArray()(ซึ่งใช้Buffer<T>คลาสภายในเพื่อสร้างอาเรย์) หรือToList()(ซึ่งเรียกว่าList<T>(IEnumerable<T>)นวกรรมิก) จะจบลงด้วยการเป็นเรื่องของการวางมันลงในอาเรย์และเพิ่มอาเรย์จนกระทั่งมันเหมาะกับพวกมันทั้งหมด

หากคุณต้องการการยืนยันที่เป็นรูปธรรมเกี่ยวกับข้อเท็จจริงนี้ลองดูการใช้งานวิธีการที่เป็นปัญหาในตัวสะท้อนสัญญาณ - คุณจะเห็นว่าพวกเขาต้มจนเกือบเป็นรหัสเดียวกัน


2
ข้อเท็จจริงที่น่าสนใจที่ฉันเจอคือแบบสอบถามที่สัมพันธ์กันที่เกิดจากการใช้กลุ่มที่กำหนดผ่านการเข้าร่วมกลุ่มในโปรเจคของคุณทำให้ Linq ไปยัง SQL เพื่อเพิ่มคิวรีย่อยอื่นเพื่อดึงจำนวนของกลุ่มนั้น ฉันสมมติว่านี่หมายความว่าในกรณีเหล่านี้ขนาดของคอลเลกชันจะเป็นที่รู้จักก่อนที่จะดึงรายการและอาร์เรย์ขนาดที่แน่นอนสามารถสร้างได้โดยตรงซึ่งจะช่วยประหยัดการประมวลผลและทรัพยากรหน่วยความจำในขณะที่ผลลัพธ์เป็นจริง
jpierson

133
หากทราบการนับล่วงหน้าประสิทธิภาพจะเหมือนกัน อย่างไรก็ตามหากไม่มีการนับจำนวนล่วงหน้าความแตกต่างเพียงอย่างเดียวระหว่างToArray()และToList()คืออดีตต้องตัดส่วนเกินซึ่งเกี่ยวข้องกับการคัดลอกอาร์เรย์ทั้งหมดในขณะที่หลังไม่ตัดส่วนเกิน แต่ใช้ค่าเฉลี่ย 25 หน่วยความจำเพิ่มขึ้น% structนี้จะมีเพียงผลกระทบถ้าชนิดข้อมูลที่มีขนาดใหญ่ แค่คิดอาหาร
Scott Rippey

9
@EldritchConundrum 25% มาจากตรรกะนี้: หากไม่ทราบจำนวนรายการให้ทำการโทรToListหรือToArrayจะเริ่มด้วยการสร้างบัฟเฟอร์เล็ก ๆ เมื่อเติมบัฟเฟอร์นั้นจะเพิ่มความจุของบัฟเฟอร์สองเท่าและดำเนินการต่อ เนื่องจากความสามารถเพิ่มเป็นสองเท่าเสมอบัฟเฟอร์ที่ไม่ได้ใช้จะอยู่ระหว่าง 0% ถึง 50%
Scott Rippey

2
@ScottRippey ฉันค้นหาแหล่งรายการใหม่จากแหล่ง IEnumerable และตรวจสอบว่า IEnumerable เป็น ICollection หรือไม่ถ้าเป็นเช่นนั้นจะเริ่มต้นด้วยการจัดสรรหนึ่งอาร์เรย์ด้วยขนาดที่แน่นอนที่ต้องการจากคุณสมบัติ Count ดังนั้นนี่จึงเป็น จะเป็นกรณีที่ ToList () จะเร็วขึ้นอย่างแน่นอน คำตอบที่สมบูรณ์อาจรวมถึงข้อเท็จจริงนั้นแม้ว่าฉันจะไม่คิดว่ามันเป็นกรณีที่พบบ่อยที่สุด
AndyClaw

3
@AndyClaw ทั้งคู่ListและBufferจะตรวจสอบICollectionซึ่งในกรณีนี้ประสิทธิภาพจะเหมือนกัน
Scott Rippey

54

(เจ็ดปีต่อมา ... )

อีกสองคำตอบ (ดี) มีสมาธิกับความแตกต่างของประสิทธิภาพของกล้องจุลทรรศน์ที่จะเกิดขึ้น

โพสต์นี้เป็นเพียงอาหารเสริมที่จะพูดถึงที่แตกต่างความหมายที่มีอยู่ระหว่างIEnumerator<T>ผลิตโดยอาร์เรย์ ( T[]) List<T>เมื่อเทียบกับที่ส่งกลับโดย

ตัวอย่างที่ดีที่สุดจากตัวอย่าง:

IList<int> source = Enumerable.Range(1, 10).ToArray();  // try changing to .ToList()

foreach (var x in source)
{
  if (x == 5)
    source[8] *= 100;
  Console.WriteLine(x);
}

โค้ดข้างต้นจะทำงานโดยไม่มีข้อยกเว้นและสร้างผลลัพธ์:

1
2
3
4
5
6
7
8
900
10

สิ่งนี้แสดงให้เห็นว่าการIEnumarator<int>คืนค่าโดย an int[]ไม่ได้ติดตามว่าอาร์เรย์นั้นได้รับการแก้ไขตั้งแต่การสร้างตัวแจงนับหรือไม่

โปรดทราบว่าฉันประกาศตัวแปรท้องถิ่นในฐานะที่เป็นsource IList<int>ด้วยวิธีนี้ฉันต้องแน่ใจว่าคอมไพเลอร์ C # ไม่ปรับforeachคำสั่งให้เป็นสิ่งที่เทียบเท่ากับfor (var idx = 0; idx < source.Length; idx++) { /* ... */ }ลูป นี่คือสิ่งที่คอมไพเลอร์ C # อาจทำถ้าฉันใช้var source = ...;แทน ใน. NET Framework ปัจจุบันของฉันตัวแจงนับจริงที่ใช้ที่นี่เป็นแบบอ้างอิงที่ไม่ใช่แบบสาธารณะSystem.SZArrayHelper+SZGenericArrayEnumerator`1[System.Int32]แต่แน่นอนว่านี่คือรายละเอียดการใช้งาน

ตอนนี้ถ้าฉันเปลี่ยน.ToArray()เป็น.ToList()ฉันได้รับเท่านั้น:

1
2
3
4
5

ตามด้วยSystem.InvalidOperationExceptionคำพูดระเบิด:

การสะสมถูกแก้ไข; การดำเนินการแจงนับอาจไม่ทำงาน

ตัวแจงนับพื้นฐานในกรณีนี้คือชนิดค่าที่ไม่แน่นอนสาธารณะSystem.Collections.Generic.List`1+Enumerator[System.Int32](กล่องภายในIEnumerator<int>กล่องในกรณีนี้เพราะฉันใช้IList<int>)

โดยสรุปตัวแจงนับที่ผลิตโดยList<T>ติดตามจะติดตามว่ารายการเปลี่ยนแปลงในระหว่างการแจงนับหรือไม่ในขณะที่ตัวแจงนับที่ผลิตโดยT[]ไม่มี เพื่อพิจารณาความแตกต่างนี้เมื่อเลือกระหว่างและ.ToList().ToArray()

คนมักจะเพิ่มหนึ่งพิเศษ .ToArray()หรือ.ToList()เพื่อหลีกเลี่ยงการสะสมที่ติดตามว่ามีการปรับเปลี่ยนในช่วงชีวิตของตัวแจงนับ

(ถ้าใครอยากจะรู้ว่าวิธีList<>ติดตามว่าคอลเลกชันที่ถูกปรับเปลี่ยนมีข้อมูลส่วนตัว_versionในชั้นนี้ซึ่งมีการเปลี่ยนแปลงทุกList<>มีการปรับปรุง.)


28

ฉันเห็นด้วยกับ @mander ว่าความแตกต่างของประสิทธิภาพควรไม่มีนัยสำคัญ อย่างไรก็ตามฉันต้องการเปรียบเทียบเพื่อให้แน่ใจว่าฉันได้ทำ - และมันก็ไม่มีนัยสำคัญ

Testing with List<T> source:
ToArray time: 1934 ms (0.01934 ms/call), memory used: 4021 bytes/array
ToList  time: 1902 ms (0.01902 ms/call), memory used: 4045 bytes/List

Testing with array source:
ToArray time: 1957 ms (0.01957 ms/call), memory used: 4021 bytes/array
ToList  time: 2022 ms (0.02022 ms/call), memory used: 4045 bytes/List

อาร์เรย์ / รายการต้นทางแต่ละรายการมีองค์ประกอบ 1000 รายการ ดังนั้นคุณจะเห็นได้ว่าทั้งเวลาและความแตกต่างของหน่วยความจำนั้นเล็กน้อย

ข้อสรุปของฉัน: คุณอาจใช้ToList ()เนื่องจากList<T>มีฟังก์ชันการทำงานมากกว่าอาร์เรย์ยกเว้นว่ามีหน่วยความจำไม่กี่ไบต์ที่สำคัญกับคุณ


1
ฉันสงสัยว่าผลลัพธ์นี้จะแตกต่างกันหรือไม่หากคุณใช้ขนาดใหญ่structแทนประเภทดั้งเดิมหรือคลาส
Scott Rippey

12
รายการ <T> .ToList ???? รู้สึกอะไร คุณควรพยายามให้ IEnumerable เข้าไปด้วยซึ่งจะไม่ใช้อินเทอร์เฟซ ICollection
กริกอ

8
ผมอยากจะให้แน่ใจว่าฉันวัดเพียงครั้งของToListหรือการโทรและไม่นับใด ๆToArray IEnumerableรายการ <T> .ToList () ยังคงสร้างรายการใหม่ <T> - ไม่ใช่แค่ "คืนค่านี้"
EMP

23
-1 พฤติกรรมของToArray()และToList()แตกต่างกันมากเกินไปเมื่อพวกเขาได้รับICollection<T>พารามิเตอร์ - พวกเขาทำการจัดสรรเพียงครั้งเดียวและการคัดลอกเดียว ทั้งList<T>และArrayนำมาใช้ICollection<T>ดังนั้นการวัดประสิทธิภาพของคุณจึงไม่ถูกต้องเลย
Mohammad Dehghan

1
สำหรับทุกคนที่สนใจผมโพสต์มาตรฐานของตัวเองเป็นคำตอบที่แยกต่างหาก มันใช้.Select(i => i)เพื่อหลีกเลี่ยงICollection<T>ปัญหาการใช้งานและรวมถึงกลุ่มควบคุมเพื่อดูว่าเวลาส่วนใหญ่จะวนซ้ำในแหล่งที่มาIEnumerable<>ในตอนแรก
StriplingWarrior

19

ToList()เป็นที่ต้องการโดยทั่วไปหากคุณใช้งานบนIEnumerable<T>(จาก ORM เป็นต้น) หากไม่ทราบความยาวของลำดับในตอนต้นให้ToArray()สร้างคอลเลกชันความยาวแบบไดนามิกเช่นรายการแล้วแปลงเป็นอาร์เรย์ซึ่งใช้เวลาเพิ่ม


26
ฉันได้ตัดสินใจว่าความสามารถในการอ่านสูงกว่าประสิทธิภาพในกรณีนี้ ตอนนี้ฉันใช้ ToList ก็ต่อเมื่อฉันคาดว่าจะเพิ่มองค์ประกอบต่อไป ในกรณีอื่น ๆ (กรณีส่วนใหญ่) ฉันใช้ ToArray แต่ขอบคุณสำหรับอินพุต!
Frank Krueger

5
มองใน ILSpy, โทรEnumerable.ToArray() new Buffer<TSource>(source).ToArray()ในตัวสร้างบัฟเฟอร์ถ้าแหล่งที่มาใช้ ICollection มันจะเรียก source.CopyTo (items, 0), และ. ToArray () จะส่งคืนอาร์เรย์ไอเท็มภายในโดยตรง ดังนั้นจึงไม่มีการแปลงที่ต้องใช้เวลาเพิ่มในกรณีนั้น หากแหล่งที่มาไม่ได้ใช้ ICollection ดังนั้น ToArray จะส่งผลให้มีการคัดลอกอาร์เรย์เพื่อตัดตำแหน่งที่ไม่ได้ใช้งานเพิ่มเติมจากจุดสิ้นสุดของอาร์เรย์ตามที่อธิบายโดยข้อคิดเห็นของ Scott Rippey ด้านบน
BrandonAGr

19

หน่วยความจำจะได้รับการจัดสรรสองครั้งเสมอ - หรือสิ่งที่อยู่ใกล้กับที่ เนื่องจากคุณไม่สามารถปรับขนาดอาร์เรย์ได้ทั้งสองวิธีจะใช้กลไกบางอย่างในการรวบรวมข้อมูลในชุดสะสมที่กำลังเพิ่มขึ้น (คือรายการคือชุดสะสมที่เพิ่มขึ้นในตัวเอง)

รายการใช้อาร์เรย์เป็นที่เก็บข้อมูลภายในและเพิ่มความจุเป็นสองเท่าเมื่อต้องการ ซึ่งหมายความว่าโดยเฉลี่ย 2/3 ของรายการได้รับการจัดสรรอย่างน้อยหนึ่งครั้งครึ่งหนึ่งของรายการที่ถูกจัดสรรใหม่อย่างน้อยสองครั้งครึ่งหนึ่งของรายการที่ได้รับการจัดสรรอย่างน้อยสามครั้งเป็นต้น ซึ่งหมายความว่าแต่ละรายการได้รับการจัดสรรใหม่โดยเฉลี่ย 1.3 ครั้งซึ่งไม่ได้มีค่าใช้จ่ายมากนัก

โปรดจำไว้ว่าหากคุณกำลังรวบรวมสตริงการรวบรวมนั้นมีเพียงการอ้างอิงไปยังสตริงสตริงนั้นจะไม่ถูกจัดสรรใหม่


นี่อาจเป็นสิ่งที่ไม่รู้ที่จะถาม แต่ตรรกะ 2/3, 1/3, 1/6 ไม่คุณคิดว่าอาร์เรย์ของรายการสามารถขยายได้หรือไม่ นั่นคือมีพื้นที่ว่างในตอนท้ายของอาร์เรย์เพื่อให้ไม่ต้องย้ายการจัดสรรที่มีอยู่

@JonofAllTrades: ไม่อาเรย์จะไม่ขยายอีกต่อไปการจัดการหน่วยความจำใน. NET ก็ไม่ได้ทำเช่นนั้น ถ้ามันจะขยายในสถานที่ก็จะไม่จำเป็นต้องมีการจัดสรรใหม่ของรายการ
Guffa

อาฉันเห็น: รายการที่ไม่ได้ถูกจัดสรรใหม่ไม่จำเป็นต้องทำเช่นนั้นเพราะมันอยู่ในการจัดสรรขั้นสุดท้าย รายการทั้งหมดที่จัดสรรในการจัดสรรก่อนหน้านี้จะถูกย้าย แต่เนื่องจากการเพิ่มลอการิทึมในความยาวของอาเรย์นี่เป็นส่วนที่คำนวณได้ ขอบคุณสำหรับการชี้แจง!

19

มันอยู่ข้างนอกในปี 2020 และทุกคนใช้. NET Core 3.1 ดังนั้นฉันตัดสินใจที่จะใช้การวัดประสิทธิภาพด้วย Benchmark.NET

TL; DR: ToArray () นั้นฉลาดกว่าประสิทธิภาพและทำงานได้ดีขึ้นหากคุณไม่ได้วางแผนที่จะกลายพันธุ์คอลเลกชัน


    [MemoryDiagnoser]
    public class Benchmarks
    {
        [Params(0, 1, 6, 10, 39, 100, 666, 1000, 1337, 10000)]
        public int Count { get; set; }

        public IEnumerable<int> Items => Enumerable.Range(0, Count);

        [Benchmark(Description = "ToArray()", Baseline = true)]
        public int[] ToArray() => Items.ToArray();

        [Benchmark(Description = "ToList()")]
        public List<int> ToList() => Items.ToList();

        public static void Main() => BenchmarkRunner.Run<Benchmarks>();
    }

ผลลัพธ์ที่ได้คือ:


    BenchmarkDotNet=v0.12.0, OS=Windows 10.0.14393.3443 (1607/AnniversaryUpdate/Redstone1)
    Intel Core i5-4460 CPU 3.20GHz (Haswell), 1 CPU, 4 logical and 4 physical cores
    Frequency=3124994 Hz, Resolution=320.0006 ns, Timer=TSC
    .NET Core SDK=3.1.100
      [Host]     : .NET Core 3.1.0 (CoreCLR 4.700.19.56402, CoreFX 4.700.19.56404), X64 RyuJIT
      DefaultJob : .NET Core 3.1.0 (CoreCLR 4.700.19.56402, CoreFX 4.700.19.56404), X64 RyuJIT


    |    Method | Count |          Mean |       Error |      StdDev |        Median | Ratio | RatioSD |   Gen 0 | Gen 1 | Gen 2 | Allocated |
    |---------- |------ |--------------:|------------:|------------:|--------------:|------:|--------:|--------:|------:|------:|----------:|
    | ToArray() |     0 |      7.357 ns |   0.2096 ns |   0.1960 ns |      7.323 ns |  1.00 |    0.00 |       - |     - |     - |         - |
    |  ToList() |     0 |     13.174 ns |   0.2094 ns |   0.1958 ns |     13.084 ns |  1.79 |    0.05 |  0.0102 |     - |     - |      32 B |
    |           |       |               |             |             |               |       |         |         |       |       |           |
    | ToArray() |     1 |     23.917 ns |   0.4999 ns |   0.4676 ns |     23.954 ns |  1.00 |    0.00 |  0.0229 |     - |     - |      72 B |
    |  ToList() |     1 |     33.867 ns |   0.7350 ns |   0.6876 ns |     34.013 ns |  1.42 |    0.04 |  0.0331 |     - |     - |     104 B |
    |           |       |               |             |             |               |       |         |         |       |       |           |
    | ToArray() |     6 |     28.242 ns |   0.5071 ns |   0.4234 ns |     28.196 ns |  1.00 |    0.00 |  0.0280 |     - |     - |      88 B |
    |  ToList() |     6 |     43.516 ns |   0.9448 ns |   1.1949 ns |     42.896 ns |  1.56 |    0.06 |  0.0382 |     - |     - |     120 B |
    |           |       |               |             |             |               |       |         |         |       |       |           |
    | ToArray() |    10 |     31.636 ns |   0.5408 ns |   0.4516 ns |     31.657 ns |  1.00 |    0.00 |  0.0331 |     - |     - |     104 B |
    |  ToList() |    10 |     53.870 ns |   1.2988 ns |   2.2403 ns |     53.415 ns |  1.77 |    0.07 |  0.0433 |     - |     - |     136 B |
    |           |       |               |             |             |               |       |         |         |       |       |           |
    | ToArray() |    39 |     58.896 ns |   0.9441 ns |   0.8369 ns |     58.548 ns |  1.00 |    0.00 |  0.0713 |     - |     - |     224 B |
    |  ToList() |    39 |    138.054 ns |   2.8185 ns |   3.2458 ns |    138.937 ns |  2.35 |    0.08 |  0.0815 |     - |     - |     256 B |
    |           |       |               |             |             |               |       |         |         |       |       |           |
    | ToArray() |   100 |    119.167 ns |   1.6195 ns |   1.4357 ns |    119.120 ns |  1.00 |    0.00 |  0.1478 |     - |     - |     464 B |
    |  ToList() |   100 |    274.053 ns |   5.1073 ns |   4.7774 ns |    272.242 ns |  2.30 |    0.06 |  0.1578 |     - |     - |     496 B |
    |           |       |               |             |             |               |       |         |         |       |       |           |
    | ToArray() |   666 |    569.920 ns |  11.4496 ns |  11.2450 ns |    571.647 ns |  1.00 |    0.00 |  0.8688 |     - |     - |    2728 B |
    |  ToList() |   666 |  1,621.752 ns |  17.1176 ns |  16.0118 ns |  1,623.566 ns |  2.85 |    0.05 |  0.8793 |     - |     - |    2760 B |
    |           |       |               |             |             |               |       |         |         |       |       |           |
    | ToArray() |  1000 |    796.705 ns |  16.7091 ns |  19.8910 ns |    796.610 ns |  1.00 |    0.00 |  1.2951 |     - |     - |    4064 B |
    |  ToList() |  1000 |  2,453.110 ns |  48.1121 ns |  65.8563 ns |  2,460.190 ns |  3.09 |    0.10 |  1.3046 |     - |     - |    4096 B |
    |           |       |               |             |             |               |       |         |         |       |       |           |
    | ToArray() |  1337 |  1,057.983 ns |  20.9810 ns |  41.4145 ns |  1,041.028 ns |  1.00 |    0.00 |  1.7223 |     - |     - |    5416 B |
    |  ToList() |  1337 |  3,217.550 ns |  62.3777 ns |  61.2633 ns |  3,203.928 ns |  2.98 |    0.13 |  1.7357 |     - |     - |    5448 B |
    |           |       |               |             |             |               |       |         |         |       |       |           |
    | ToArray() | 10000 |  7,309.844 ns | 160.0343 ns | 141.8662 ns |  7,279.387 ns |  1.00 |    0.00 | 12.6572 |     - |     - |   40064 B |
    |  ToList() | 10000 | 23,858.032 ns | 389.6592 ns | 364.4874 ns | 23,759.001 ns |  3.26 |    0.08 | 12.6343 |     - |     - |   40096 B |

    // * Hints *
    Outliers
      Benchmarks.ToArray(): Default -> 2 outliers were removed (35.20 ns, 35.29 ns)
      Benchmarks.ToArray(): Default -> 2 outliers were removed (38.51 ns, 38.88 ns)
      Benchmarks.ToList(): Default  -> 1 outlier  was  removed (64.69 ns)
      Benchmarks.ToArray(): Default -> 1 outlier  was  removed (67.02 ns)
      Benchmarks.ToArray(): Default -> 1 outlier  was  removed (130.08 ns)
      Benchmarks.ToArray(): Default -> 1 outlier  was  detected (541.82 ns)
      Benchmarks.ToArray(): Default -> 1 outlier  was  removed (7.82 us)

    // * Legends *
      Count     : Value of the 'Count' parameter
      Mean      : Arithmetic mean of all measurements
      Error     : Half of 99.9% confidence interval
      StdDev    : Standard deviation of all measurements
      Median    : Value separating the higher half of all measurements (50th percentile)
      Ratio     : Mean of the ratio distribution ([Current]/[Baseline])
      RatioSD   : Standard deviation of the ratio distribution ([Current]/[Baseline])
      Gen 0     : GC Generation 0 collects per 1000 operations
      Gen 1     : GC Generation 1 collects per 1000 operations
      Gen 2     : GC Generation 2 collects per 1000 operations
      Allocated : Allocated memory per single operation (managed only, inclusive, 1KB = 1024B)
      1 ns      : 1 Nanosecond (0.000000001 sec)

1
หากคุณไม่ได้วางแผนที่จะกลายพันธุ์คอลเลกชันฉันคิดว่าความตั้งใจสามารถแสดงได้ดีกว่าด้วยToImmutableArray()(จาก System.Collections แพ็คเกจที่ไม่น่าเชื่อถือ) 😉
Arturo Torres Sánchez

@ ArturoTorresSánchezจริง แต่ถ้าคอลเลกชันไม่ได้เปิดเผยนอกวิธีการฉันจะใช้อาร์เรย์
Tyrrrz

2
ขอบคุณสำหรับสิ่งนี้. คำตอบที่เลือกนั้นเป็นเพียงข้อโต้แย้งและสมมติว่าผลลัพธ์เป็นไปตามอาร์กิวเมนต์นั้น ในการทำสิ่งนี้ทางวิทยาศาสตร์และในฐานะที่เป็นโบนัสรู้ว่ามีความแตกต่างกันมากเพียงใดมีวิธีการรู้อย่างแท้จริง
Jonas

15

แก้ไข : ส่วนสุดท้ายของคำตอบนี้ไม่ถูกต้อง อย่างไรก็ตามส่วนที่เหลือยังเป็นข้อมูลที่มีประโยชน์ดังนั้นฉันจะทิ้งมันไว้

ฉันรู้ว่านี่เป็นโพสต์เก่า แต่หลังจากมีคำถามเดียวกันและทำการวิจัยบางอย่างฉันได้พบสิ่งที่น่าสนใจที่อาจคุ้มค่าที่จะแบ่งปัน

ก่อนอื่นฉันเห็นด้วยกับ @mquander และคำตอบของเขา เขาพูดถูกว่าการแสดงฉลาดทั้งสองเหมือนกัน

อย่างไรก็ตามฉันได้ใช้ Reflector เพื่อดูวิธีการในSystem.Linq.Enumerablenamespace ส่วนขยายและฉันได้สังเกตเห็นการเพิ่มประสิทธิภาพที่พบบ่อยมาก
เมื่อใดก็ตามที่เป็นไปได้IEnumerable<T>แหล่งสัญญาณจะถูกส่งไปยังIList<T>หรือICollection<T>เพื่อปรับวิธีการให้เหมาะสม ElementAt(int)ยกตัวอย่างให้ดูที่

ที่น่าสนใจไมโครซอฟท์เลือกที่จะเพิ่มประสิทธิภาพเฉพาะแต่ไม่ได้IList<T> IListดูเหมือนว่า Microsoft ต้องการใช้IList<T>ส่วนต่อประสาน

System.Arrayใช้งานเท่านั้นIListดังนั้นจะไม่ได้รับประโยชน์จากการปรับแต่งส่วนขยายเหล่านี้
ดังนั้นฉันขอเสนอว่าวิธีปฏิบัติที่ดีที่สุดคือการใช้.ToList()วิธีนี้
หากคุณใช้ใด ๆ IList<T>ของวิธีการขยายหรือผ่านรายการเพื่อวิธีอื่นมีโอกาสที่มันอาจจะเหมาะสำหรับการอื่น


16
ฉันทำการทดสอบและพบสิ่งที่น่าแปลกใจ อาร์เรย์ใช้ระบบ IList <T>! การใช้ตัวสะท้อนแสงเพื่อวิเคราะห์ System.Array จะเผยให้เห็นห่วงโซ่การสืบทอดของ IList, ICollection, IEnumerable แต่ใช้การสะท้อนแบบรันไทม์ฉันพบว่า string [] มีห่วงโซ่การสืบทอดของ IList, ICollection, IEnumerable, string ILollection >, IEnumerable <string> ดังนั้นฉันไม่มีคำตอบที่ดีไปกว่า @mquander!
Scott Rippey

@ScottRippey ใช่ การสังเกตที่แปลก ๆ ที่คุณสังเกตเห็นนั้นเป็นส่วนหนึ่งของ "แฮ็ค" - และมันก็มีความหมายที่ค่อนข้างแปลก ๆ เช่นกันเกี่ยวกับ "ขนาดคงที่" และคุณสมบัติที่คล้ายคลึงกัน มีบางความคิดเห็นที่ค่อนข้างใหญ่ที่สัมผัสกับหัวข้อนี้ในซอร์สโค้ด. net ขออภัยที่ไม่ได้เชื่อมโยง แต่ถ้าฉันจำได้อย่างถูกต้องมันค่อนข้างหาง่าย (ภายในอาเรย์คลาส) (และยังมีคำถาม SO ขนาดใหญ่ที่พูดถึงความไม่สอดคล้องกัน .... ที่ใดที่หนึ่ง ... > __>)
AnorZaken

@ScottRippey เพียง FYI ฉันพบคำตอบที่เกี่ยวข้องกับความคิดเห็นของคุณ: stackoverflow.com/a/4482567/2063755
David Klempfner

14

ฉันพบว่าเกณฑ์มาตรฐานอื่น ๆ ที่ผู้คนทำกันที่นี่ยังขาดอยู่ แจ้งให้เราทราบหากคุณพบสิ่งผิดปกติกับวิธีการของฉัน

/* This is a benchmarking template I use in LINQPad when I want to do a
 * quick performance test. Just give it a couple of actions to test and
 * it will give you a pretty good idea of how long they take compared
 * to one another. It's not perfect: You can expect a 3% error margin
 * under ideal circumstances. But if you're not going to improve
 * performance by more than 3%, you probably don't care anyway.*/
void Main()
{
    // Enter setup code here
    var values = Enumerable.Range(1, 100000)
        .Select(i => i.ToString())
        .ToArray()
        .Select(i => i);
    values.GetType().Dump();
    var actions = new[]
    {
        new TimedAction("ToList", () =>
        {
            values.ToList();
        }),
        new TimedAction("ToArray", () =>
        {
            values.ToArray();
        }),
        new TimedAction("Control", () =>
        {
            foreach (var element in values)
            {
                // do nothing
            }
        }),
        // Add tests as desired
    };
    const int TimesToRun = 1000; // Tweak this as necessary
    TimeActions(TimesToRun, actions);
}


#region timer helper methods
// Define other methods and classes here
public void TimeActions(int iterations, params TimedAction[] actions)
{
    Stopwatch s = new Stopwatch();
    int length = actions.Length;
    var results = new ActionResult[actions.Length];
    // Perform the actions in their initial order.
    for (int i = 0; i < length; i++)
    {
        var action = actions[i];
        var result = results[i] = new ActionResult { Message = action.Message };
        // Do a dry run to get things ramped up/cached
        result.DryRun1 = s.Time(action.Action, 10);
        result.FullRun1 = s.Time(action.Action, iterations);
    }
    // Perform the actions in reverse order.
    for (int i = length - 1; i >= 0; i--)
    {
        var action = actions[i];
        var result = results[i];
        // Do a dry run to get things ramped up/cached
        result.DryRun2 = s.Time(action.Action, 10);
        result.FullRun2 = s.Time(action.Action, iterations);
    }
    results.Dump();
}

public class ActionResult
{
    public string Message { get; set; }
    public double DryRun1 { get; set; }
    public double DryRun2 { get; set; }
    public double FullRun1 { get; set; }
    public double FullRun2 { get; set; }
}

public class TimedAction
{
    public TimedAction(string message, Action action)
    {
        Message = message;
        Action = action;
    }
    public string Message { get; private set; }
    public Action Action { get; private set; }
}

public static class StopwatchExtensions
{
    public static double Time(this Stopwatch sw, Action action, int iterations)
    {
        sw.Restart();
        for (int i = 0; i < iterations; i++)
        {
            action();
        }
        sw.Stop();

        return sw.Elapsed.TotalMilliseconds;
    }
}
#endregion

คุณสามารถดาวน์โหลดสคริปต์ LinqPad ที่นี่

ผล: ประสิทธิภาพ ToArray vs ToList

การปรับเปลี่ยนโค้ดด้านบนคุณจะพบว่า:

  1. ความแตกต่างอย่างมีนัยสำคัญน้อยลงเมื่อจัดการกับอาร์เรย์ที่มีขนาดเล็ก มีการทำซ้ำมากกว่า แต่มีอาร์เรย์ที่เล็กกว่า
  2. ความแตกต่างนั้นมีนัยสำคัญน้อยกว่าเมื่อจัดการกับints มากกว่าstrings
  3. การใช้structs ขนาดใหญ่แทนที่จะstringใช้เวลามากกว่าปกติ แต่ไม่ได้เปลี่ยนอัตราส่วนมากนัก

สิ่งนี้เห็นด้วยกับข้อสรุปของคำตอบที่ได้รับคะแนนสูงสุด:

  1. คุณไม่น่าจะสังเกตเห็นความแตกต่างด้านประสิทธิภาพเว้นแต่ว่ารหัสของคุณมักจะผลิตรายการข้อมูลจำนวนมาก (มีความแตกต่างเพียง 200ms เมื่อสร้างรายการ 1,000 รายการของ 100K สายอักขระ)
  2. ToList() ทำงานได้เร็วขึ้นอย่างต่อเนื่องและเป็นตัวเลือกที่ดีกว่าหากคุณไม่ได้วางแผนที่จะใช้ผลลัพธ์เป็นเวลานาน

ปรับปรุง

@ จอนฮันนาชี้ให้เห็นว่าขึ้นอยู่กับการนำไปปฏิบัติของSelectมันเป็นไปได้สำหรับการดำเนินการToList()หรือToArray()การคาดการณ์ขนาดของคอลเลกชันที่เกิดขึ้นก่อนเวลา การแทนที่.Select(i => i)ในโค้ดข้างต้นด้วยWhere(i => true) ผลลัพธ์ที่คล้ายกันมากในขณะนี้และมีแนวโน้มที่จะทำเช่นนั้นโดยไม่คำนึงถึงการใช้. NET

การวัดประสิทธิภาพโดยใช้ที่แทนการเลือก


ใน. NET Core ทั้งสองกรณีควรจะดีกว่าที่นี่บน netfx เพราะมันจะรู้ว่าขนาดกำลังจะเป็นไปได้100000และใช้มันเพื่อเพิ่มประสิทธิภาพทั้งสองToList()และToArray()ด้วยToArray()ความที่เบากว่าเล็กน้อยมากเพราะมันไม่ต้องการการดำเนินการย่อขนาด มิฉะนั้นซึ่งเป็นสถานที่แห่งหนึ่งToList()มีข้อได้เปรียบ ตัวอย่างในคำถามจะยังคงหายไปเนื่องจากWhereวิธีการทำนายขนาดดังกล่าวไม่สามารถทำได้
Jon Hanna

@ Jonanna: ขอบคุณสำหรับข้อเสนอแนะอย่างรวดเร็ว ฉันไม่รู้ว่า. NET Core กำลังทำการเพิ่มประสิทธิภาพนั้น มันเท่ห์มาก ในรหัสของฉัน.Select(i => i)สามารถถูกแทนที่ด้วย.Where(i => true)เพื่อแก้ไขให้ถูกต้อง
StriplingWarrior

ใช่ว่าจะหยุดการเพิ่มประสิทธิภาพที่มีผลต่อมันใน corefx อาจเป็นเรื่องที่น่าสนใจที่จะมีทั้งขนาดที่เป็นกำลังสอง (ซึ่งควรให้ToArray()ข้อได้เปรียบ) และขนาดที่ไม่เหมือนด้านบนและเปรียบเทียบผลลัพธ์
Jon Hanna

@ จอนฮันนา: ที่น่าสนใจToArray() ยังคงแพ้ในสถานการณ์ที่ดีที่สุด ด้วยMath.Pow(2, 15)องค์ประกอบมัน (ToList: 700ms, ToArray: 900ms) การเพิ่มองค์ประกอบอีกหนึ่งกระแทกไปที่ (ToList: 925, ToArray: 1350) ฉันสงสัยว่าToArrayจะยังคงคัดลอกอาเรย์อยู่หรือไม่แม้ว่าจะเป็นขนาดที่สมบูรณ์แล้ว? พวกเขาอาจจะคิดว่ามันเป็นเหตุการณ์ที่เกิดขึ้นน้อยมากที่มันไม่คุ้มกับเงื่อนไขพิเศษ
StriplingWarrior

มันไม่ได้คัดลอกในการจับคู่ขนาดที่แน่นอนก่อนที่เราจะเริ่มปรับให้เหมาะสมใน corefx ดังนั้นจึงเป็นกรณีที่ได้รับการแบ่งมากที่สุด
Jon Hanna

12

คุณควรตัดสินใจบนพื้นฐานของการตัดสินใจToListหรือToArrayตั้งอยู่บนพื้นฐานของตัวเลือกการออกแบบ ToArrayถ้าคุณต้องการที่คอลเลกชันที่สามารถซ้ำและเข้าถึงได้โดยดัชนีให้เลือก หากคุณต้องการความสามารถเพิ่มเติมของการเพิ่มและลบออกจากคอลเลกชันในภายหลังโดยไม่ต้องยุ่งยากมากให้ทำToList(ไม่ใช่ที่คุณไม่สามารถเพิ่มในอาร์เรย์ได้ แต่ไม่ใช่เครื่องมือที่เหมาะสมสำหรับมัน)

หากประสิทธิภาพการทำงานมีความสำคัญคุณควรพิจารณาว่าจะใช้งานอะไรได้เร็วขึ้น ตามความเป็นจริงคุณจะไม่โทรToListหรือToArrayล้านครั้ง แต่อาจใช้ได้กับการสะสมที่ได้รับล้านครั้ง ในแง่ที่[]ดีกว่าเนื่องจากList<>อยู่[]กับค่าใช้จ่ายบางส่วน ดูหัวข้อนี้สำหรับการเปรียบเทียบประสิทธิภาพ: ข้อใดมีประสิทธิภาพมากขึ้น: รายการ <int> หรือ int []

ในการทดสอบของฉันเองเมื่อไม่นานมานี้ฉันพบว่าToArrayเร็วขึ้น และฉันไม่แน่ใจว่าวิธีการทดสอบเบ้อย่างไร ความแตกต่างด้านประสิทธิภาพนั้นไม่มีความสำคัญมากนักซึ่งสามารถสังเกตได้ก็ต่อเมื่อคุณเรียกใช้คิวรีเหล่านี้ในรอบหลายล้านครั้ง


2
ใช่ - หากคอมไพเลอร์รู้ว่าคุณกำลังวนซ้ำอาร์เรย์ (แทนที่จะเป็น IEnumerable <>) มันสามารถเพิ่มประสิทธิภาพการทำซ้ำได้อย่างมีนัยสำคัญ
RobSiklos

12

คำตอบที่ช้ามาก แต่ฉันคิดว่ามันจะเป็นประโยชน์สำหรับชาว Google

พวกเขาทั้งสองดูดเมื่อพวกเขาสร้างโดยใช้ linq พวกเขาทั้งสองใช้รหัสเดียวกันในการปรับขนาดบัฟเฟอร์ในกรณีที่จำเป็น ToArrayภายในใช้คลาสเพื่อแปลงIEnumerable<>เป็นอาร์เรย์โดยการจัดสรรอาร์เรย์ขององค์ประกอบที่ 4 หากยังไม่เพียงพอที่จะเพิ่มขนาดเป็นสองเท่าด้วยการสร้างอาร์เรย์ใหม่เป็นสองเท่าของขนาดปัจจุบันและคัดลอกอาร์เรย์ปัจจุบันไปยังอาร์เรย์นั้น ในตอนท้ายระบบจะจัดสรรจำนวนรายการใหม่ให้คุณ หากแบบสอบถามของคุณส่งคืนองค์ประกอบ 129 รายการ ToArray จะทำการจัดสรร 6 รายการและดำเนินการคัดลอกหน่วยความจำเพื่อสร้างอาร์เรย์องค์ประกอบ 256 รายการและมากกว่าอาร์เรย์อีก 129 รายการที่จะส่งคืน มากสำหรับประสิทธิภาพของหน่วยความจำ

ToList ทำสิ่งเดียวกัน แต่ข้ามการจัดสรรครั้งล่าสุดเนื่องจากคุณสามารถเพิ่มรายการได้ในอนาคต รายการไม่สนใจว่ามันถูกสร้างขึ้นจากแบบสอบถาม linq หรือสร้างขึ้นด้วยตนเอง

สำหรับการสร้างรายการดีกว่าด้วยหน่วยความจำ แต่แย่กว่ากับ cpu เนื่องจากรายการเป็นโซลูชันทั่วไปทุกการกระทำต้องใช้การตรวจสอบช่วงเพิ่มเติมเพื่อตรวจสอบช่วงภายในของ. net

ดังนั้นถ้าคุณจะวนซ้ำชุดผลลัพธ์ของคุณหลายครั้งเกินไปอาร์เรย์ก็ดีเพราะมันหมายถึงการตรวจสอบช่วงน้อยกว่ารายการ

การจัดสรรการกำหนดค่าเริ่มต้นของรายการจะดีกว่าหากคุณระบุพารามิเตอร์ความจุเมื่อคุณสร้าง ในกรณีนี้มันจะจัดสรรอาร์เรย์เพียงครั้งเดียวสมมติว่าคุณรู้ขนาดผลลัพธ์ ToListของ LINQ List<>.AddRangeไม่ได้ระบุเกินที่จะให้มันดังนั้นเราต้องสร้างวิธีขยายของเราที่สร้างรายชื่อที่มีความจุได้รับและการใช้งานแล้ว

เพื่อให้ตอบคำถามนี้ฉันต้องเขียนประโยคต่อไปนี้

  1. ในตอนท้ายคุณสามารถใช้ ToArray หรือ ToList ประสิทธิภาพจะไม่แตกต่างกัน (ดูคำตอบของ @EMP)
  2. คุณกำลังใช้ C # หากคุณต้องการประสิทธิภาพไม่ต้องกังวลกับการเขียนเกี่ยวกับรหัสประสิทธิภาพสูง แต่ต้องกังวลเกี่ยวกับการไม่เขียนโค้ดประสิทธิภาพที่ไม่ดี
  3. กำหนดเป้าหมาย x64 เสมอสำหรับรหัสประสิทธิภาพสูง AFAIK, x64 JIT ขึ้นอยู่กับคอมไพเลอร์ C ++ และทำบางสิ่งที่ตลกเช่นการปรับการเรียกกลับหางให้เหมาะสม
  4. ด้วย 4.5 คุณสามารถเพลิดเพลินกับการเพิ่มประสิทธิภาพที่แนะนำของโปรไฟล์และ Multi-Core JIT
  5. ในที่สุดคุณสามารถใช้รูปแบบ async / await เพื่อประมวลผลได้เร็วขึ้น

พวกเขาทั้งสองดูด? คุณมีแนวคิดอื่นที่ไม่ต้องการการจัดสรรหน่วยความจำสำรองหรือไม่
nawfal

ในบริบทของคำถามใช่พวกเขาทั้งสองดูด แต่เนื่องจากการจัดสรรซ้ำซ้อนและไม่มีอะไรอื่น เพื่อลดการจัดสรรที่ซ้ำซ้อนคุณสามารถใช้รายการที่ลิงก์ได้โดยเสียค่าใช้จ่ายหน่วยความจำและความเร็วการทำซ้ำ ในตอนท้ายของวันนี่คือสิ่งที่เราทำเราทำการแลกเปลี่ยน แนวคิดอื่นหากจะสร้างรายการที่มีความจุ 200 (ตัวอย่าง) จากนั้นโหลดรายการ สิ่งนี้จะลดความซ้ำซ้อน แต่อาร์เรย์เร็วกว่าเสมอดังนั้นจึงเป็นการแลกเปลี่ยนอื่น
Erdogan Kurtur

สร้างรายการ200 ? นั่นอาจหลีกเลี่ยงการปรับขนาด แต่ฉันกำลังพูดถึงหน่วยความจำที่ใช้ซ้ำซ้อน คุณไม่สามารถช่วยได้เพราะไม่มีความรู้เบื้องต้นเกี่ยวกับขนาดที่สามารถเป็นได้ คุณสามารถระบุความจุในตัวสร้างของ a List<T>แล้ว แต่เมื่อคุณทำไม่ได้หรือเมื่อคุณทำไม่ได้คุณไม่สามารถช่วยได้
nawfal

2
ข้อมูลซ้ำซ้อนในหน่วยความจำเท่านั้นคือเนื้อหาของอาเรย์ซึ่งเป็นรายการของพอยน์เตอร์ (ในกรณีนี้) หนึ่งพอยน์เตอร์ 64 ล้านบิตใช้หน่วยความจำได้มากถึง 8 MB ซึ่งไม่มีอะไรเทียบได้กับหนึ่งล้านวัตถุที่ชี้ไป 200 เป็นเพียงตัวเลขและมีโอกาสลดจำนวนการปรับขนาดการโทรได้สูงสุด 5 ครั้ง และใช่เราไม่สามารถช่วยได้ เราไม่มีตัวเลือกที่ดีกว่า ฉันไม่มีทางออกที่ดีกว่า แต่นั่นไม่ได้หมายความว่าฉันไม่ได้รับอนุญาตให้พูดว่าปัญหาอยู่ที่ใด
Erdogan Kurtur

1
hmm ในที่สุดมันเป็นที่ที่คุณวาดเส้น ฉันชอบการนำไปใช้ในปัจจุบัน เสียงคำตอบของคุณทำให้ฉันคิดว่ามันเป็นคำวิจารณ์มากกว่าที่เป็นปัญหา :)
Nawfal

7

นี่เป็นคำถามเก่า - แต่เพื่อประโยชน์ของผู้ใช้ที่สะดุดมันนอกจากนี้ยังมีอีกทางเลือกของ 'Memoizing' the Enumerable - ซึ่งมีผลของการแคชและหยุดการแจงนับ Linq ซึ่งเป็นสิ่งที่ ToArray () และ ToList () มีการใช้งานเป็นจำนวนมากแม้ว่าจะไม่เคยใช้คุณสมบัติการรวบรวมของรายการหรืออาร์เรย์

Memoize มีอยู่ใน RX / System.Interactive lib และอธิบายไว้ที่นี่: LINQ เพิ่มเติมพร้อม System.Interactive

(จากบล็อกของ Bart De'Smetซึ่งเป็นที่แนะนำอย่างสูงหากคุณกำลังทำงานกับ Linq ไปยัง Objects มาก)


4

ทางเลือกหนึ่งคือการเพิ่มวิธีการขยายของคุณเองที่ส่งกลับแบบอ่าน ICollection<T>อย่างเดียว สิ่งนี้อาจดีกว่าการใช้ToListหรือToArrayเมื่อคุณไม่ต้องการใช้คุณสมบัติการทำดัชนีของอาร์เรย์ / รายการหรือเพิ่ม / ลบออกจากรายการ

public static class EnumerableExtension
{
    /// <summary>
    /// Causes immediate evaluation of the linq but only if required.
    /// As it returns a readonly ICollection, is better than using ToList or ToArray
    /// when you do not want to use the indexing properties of an IList, or add to the collection.
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="enumerable"></param>
    /// <returns>Readonly collection</returns>
    public static ICollection<T> Evaluate<T>(this IEnumerable<T> enumerable)
    {
        //if it's already a readonly collection, use it
        var collection = enumerable as ICollection<T>;
        if ((collection != null) && collection.IsReadOnly)
        {
            return collection;
        }
        //or make a new collection
        return enumerable.ToList().AsReadOnly();
    }
}

การทดสอบหน่วย:

[TestClass]
public sealed class EvaluateLinqTests
{
    [TestMethod]
    public void EvalTest()
    {
        var list = new List<int> {1, 2, 3};
        var linqResult = list.Select(i => i);
        var linqResultEvaluated = list.Select(i => i).Evaluate();
        list.Clear();
        Assert.AreEqual(0, linqResult.Count());
        //even though we have cleared the underlying list, the evaluated list does not change
        Assert.AreEqual(3, linqResultEvaluated.Count());
    }

    [TestMethod]
    public void DoesNotSaveCreatingListWhenHasListTest()
    {
        var list = new List<int> {1, 2, 3};
        var linqResultEvaluated = list.Evaluate();
        //list is not readonly, so we expect a new list
        Assert.AreNotSame(list, linqResultEvaluated);
    }

    [TestMethod]
    public void SavesCreatingListWhenHasReadonlyListTest()
    {
        var list = new List<int> {1, 2, 3}.AsReadOnly();
        var linqResultEvaluated = list.Evaluate();
        //list is readonly, so we don't expect a new list
        Assert.AreSame(list, linqResultEvaluated);
    }

    [TestMethod]
    public void SavesCreatingListWhenHasArrayTest()
    {
        var list = new[] {1, 2, 3};
        var linqResultEvaluated = list.Evaluate();
        //arrays are readonly (wrt ICollection<T> interface), so we don't expect a new object
        Assert.AreSame(list, linqResultEvaluated);
    }

    [TestMethod]
    [ExpectedException(typeof (NotSupportedException))]
    public void CantAddToResultTest()
    {
        var list = new List<int> {1, 2, 3};
        var linqResultEvaluated = list.Evaluate();
        Assert.AreNotSame(list, linqResultEvaluated);
        linqResultEvaluated.Add(4);
    }

    [TestMethod]
    [ExpectedException(typeof (NotSupportedException))]
    public void CantRemoveFromResultTest()
    {
        var list = new List<int> {1, 2, 3};
        var linqResultEvaluated = list.Evaluate();
        Assert.AreNotSame(list, linqResultEvaluated);
        linqResultEvaluated.Remove(1);
    }
}

เป็นที่น่าสังเกตว่าสัญญาการรวบรวมแบบอ่านอย่างเดียวเท่านั้นกำหนดว่าผู้ใช้ของวัตถุอาจไม่แก้ไข แต่เจ้าของอาจยังคงทำเช่นนั้นหากมันยังคงอ้างอิงถึงมันที่มีอินเตอร์เฟซที่ไม่แน่นอน สำหรับอินเทอร์เฟซที่รับประกันว่าโครงสร้างพื้นฐานจะไม่เปลี่ยนแปลงให้ดูคอลเลกชันที่ไม่เปลี่ยนรูป สำหรับสาเหตุที่คอลเล็กชันการอ่าน - เขียนที่ไม่เปลี่ยนรูปหรืออ่านอย่างเดียวหรือธรรมดาดีกว่าหรือแย่กว่านั้นเราต้องการจุดอ้างอิงสำหรับการเปรียบเทียบ ไม่มีคำตอบที่ดีที่สุด (เราไม่ต้องเลือก)
tne

@tne หมายเหตุ: ฉันต้องยอมรับ Tolist ก่อน AsReadOnly ดังนั้นจึงไม่มีการอ้างอิงถึงรากฐานที่ไม่แน่นอน
weston

คุณพูดถูกและเป็นวิธีที่ดีที่สุดในการทำสิ่งต่าง ๆ ก่อนที่คอลเลคชั่นไม่เปลี่ยนรูปจะมาถึง BCL (ฉันเห็นเบต้าแรกออกมาหนึ่งเดือนหลังจากคำตอบของคุณ)
tne

คอลเลกชันที่ไม่เปลี่ยนรูปมีอยู่เพื่อความปลอดภัยของเธรดโดยที่เธรดสามารถสันนิษฐานได้ว่ามันจะไม่เปลี่ยนแปลงและหากเป็นเช่นนั้นจะมีการสร้างเวอร์ชันใหม่แทนที่จะแข่งกับผู้อ่านและเปลี่ยนมันในขณะที่ใช้ วิธีนี้ไม่มีใครต้องการได้รับการล็อค
doug65536

4

ToListAsync<T>() เป็นที่ต้องการ

ใน Entity Framework 6 ทั้งสองวิธีจะเรียกใช้วิธีการภายในเดียวกันในที่สุด แต่การToArrayAsync<T>()เรียกlist.ToArray()ที่ท้ายซึ่งถูกนำมาใช้เป็น

T[] array = new T[_size];
Array.Copy(_items, 0, array, 0, _size);
return array;

ดังนั้นToArrayAsync<T>()มีค่าโสหุ้ยบางอย่างจึงToListAsync<T>()เป็นที่ต้องการ


1
นั่นคือคำตอบที่ฉันกำลังมองหาว่า EF ทำอย่างไร ฉันอยากรู้ว่ามันเป็นอย่างไรใน EF Core
Shimmy Weitzhandler

3

คำถามเก่า แต่ผู้ถามใหม่ตลอดเวลา

ตามแหล่งที่มาของSystem.Linq.Enumerable , ToListเพียงแค่ส่งกลับnew List(source)ในขณะที่ToArrayการใช้งานกลับnew Buffer<T>(source).ToArray()T[]

เกี่ยวกับการจัดสรรหน่วยความจำ:

ในขณะที่ทำงานบนIEnumerable<T>เพียงวัตถุไม่จัดสรรหน่วยความจำอีกครั้งหนึ่งกว่าToArray ToListแต่คุณไม่ต้องสนใจมันในกรณีส่วนใหญ่เพราะ GC จะทำการเก็บขยะเมื่อจำเป็น

เกี่ยวกับรันไทม์ที่มีประสิทธิภาพ:

ผู้ที่ถามคำถามนี้สามารถเรียกใช้รหัสต่อไปนี้บนเครื่องของคุณและคุณจะได้รับคำตอบ

class PersonC
{
    public Guid uuid;
    public string name;
    public int age;
    public bool sex;
    public DateTime BirthDay;
    public double weight;
}

struct PersonS
{
    public Guid uuid;
    public string name;
    public int age;
    public bool sex;
    public DateTime BirthDay;
    public double weight;
}

class PersonT<T> : IEnumerable<T>
{
    private List<T> items;
    public PersonT(IEnumerable<T> init)
    {
        items = new List<T>(init);
    }

    public IEnumerator<T> GetEnumerator() => items.GetEnumerator();
    IEnumerator IEnumerable.GetEnumerator() => items.GetEnumerator();
}

private IEnumerable<PersonC> C(int count)
{
    for (var i = 0; i < count; ++i)
    {
        var guid = Guid.NewGuid();
        var guidBytes = guid.ToByteArray(); //16 bytes
        yield return new PersonC
        {
            uuid = guid,
            name = guid.ToString(),
            age = guidBytes[0] ^ guidBytes[7],
            sex = guidBytes[14] % 2 == 0,
            BirthDay = DateTime.Now.AddDays(-guidBytes[11] * 18),
            weight = guidBytes[12] * 100
        };
    }
}

private IEnumerable<PersonS> S(int count)
{
    for (var i = 0; i < count; ++i)
    {
        var guid = Guid.NewGuid();
        var guidBytes = guid.ToByteArray(); //16 bytes
        yield return new PersonS
        {
            uuid = guid,
            name = guid.ToString(),
            age = guidBytes[0] ^ guidBytes[7],
            sex = guidBytes[14] % 2 == 0,
            BirthDay = DateTime.Now.AddDays(-guidBytes[11] * 18),
            weight = guidBytes[12] * 100
        };
    }
}

private void MakeLog(string test, List<long> log) =>
    Console.WriteLine("{0} {1} ms -> [{2}]",
        test,
        log.Average(),
        string.Join(", ", log)
    );

private void Test1(int times, int count)
{
    var test = Enumerable.Range(1, times).ToArray();

    MakeLog("C.ToList", test.Select(o =>
    {
        var sw = new Stopwatch();
        GC.Collect();
        sw.Start();
        var ret = C(count).ToList();
        sw.Stop();
        return sw.ElapsedMilliseconds;
    }).ToList());

    MakeLog("C.ToArray", test.Select(o =>
    {
        var sw = new Stopwatch();
        GC.Collect();
        sw.Start();
        var ret = C(count).ToArray();
        sw.Stop();
        return sw.ElapsedMilliseconds;
    }).ToList());

    MakeLog("S.ToList", test.Select(o =>
    {
        var sw = new Stopwatch();
        GC.Collect();
        sw.Start();
        var ret = S(count).ToList();
        sw.Stop();
        return sw.ElapsedMilliseconds;
    }).ToList());

    MakeLog("S.ToArray", test.Select(o =>
    {
        var sw = new Stopwatch();
        GC.Collect();
        sw.Start();
        var ret = S(count).ToArray();
        sw.Stop();
        return sw.ElapsedMilliseconds;
    }).ToList());
}

private void Test2(int times, int count)
{
    var test = Enumerable.Range(1, times).ToArray();

    var dataC1 = new PersonT<PersonC>(C(count));
    var dataS1 = new PersonT<PersonS>(S(count));

    MakeLog("C1.ToList", test.Select(o =>
    {
        var sw = new Stopwatch();
        GC.Collect();
        sw.Start();
        var ret = dataC1.ToList();
        sw.Stop();
        return sw.ElapsedMilliseconds;
    }).ToList());

    MakeLog("C1.ToArray", test.Select(o =>
    {
        var sw = new Stopwatch();
        GC.Collect();
        sw.Start();
        var ret = dataC1.ToArray();
        sw.Stop();
        return sw.ElapsedMilliseconds;
    }).ToList());

    MakeLog("S1.ToList", test.Select(o =>
    {
        var sw = new Stopwatch();
        GC.Collect();
        sw.Start();
        var ret = dataS1.ToList();
        sw.Stop();
        return sw.ElapsedMilliseconds;
    }).ToList());

    MakeLog("S1.ToArray", test.Select(o =>
    {
        var sw = new Stopwatch();
        GC.Collect();
        sw.Start();
        var ret = dataS1.ToArray();
        sw.Stop();
        return sw.ElapsedMilliseconds;
    }).ToList());
}

private void Test3(int times, int count)
{
    var test = Enumerable.Range(1, times).ToArray();

    var dataC2 = (ICollection<PersonC>) new List<PersonC>(C(count));
    var dataS2 = (ICollection<PersonS>) new List<PersonS>(S(count));

    MakeLog("C2.ToList", test.Select(o =>
    {
        var sw = new Stopwatch();
        GC.Collect();
        sw.Start();
        var ret = dataC2.ToList();
        sw.Stop();
        return sw.ElapsedMilliseconds;
    }).ToList());

    MakeLog("C2.ToArray", test.Select(o =>
    {
        var sw = new Stopwatch();
        GC.Collect();
        sw.Start();
        var ret = dataC2.ToArray();
        sw.Stop();
        return sw.ElapsedMilliseconds;
    }).ToList());

    MakeLog("S2.ToList", test.Select(o =>
    {
        var sw = new Stopwatch();
        GC.Collect();
        sw.Start();
        var ret = dataS2.ToList();
        sw.Stop();
        return sw.ElapsedMilliseconds;
    }).ToList());

    MakeLog("S2.ToArray", test.Select(o =>
    {
        var sw = new Stopwatch();
        GC.Collect();
        sw.Start();
        var ret = dataS2.ToArray();
        sw.Stop();
        return sw.ElapsedMilliseconds;
    }).ToList());
}

private void TestMain()
{
    const int times = 100;
    const int count = 1_000_000 + 1;
    Test1(times, count);
    Test2(times, count);
    Test3(times, count);
}

ฉันได้ผลลัพธ์เหล่านี้ในเครื่องของฉัน:

กลุ่มที่ 1:

C.ToList 761.79 ms -> [775, 755, 759, 759, 756, 759, 765, 750, 757, 762, 759, 754, 757, 753, 763, 753, 759, 756, 768, 754, 763, 757, 757, 777, 780, 758, 754, 758, 762, 754, 758, 757, 763, 758, 760, 754, 761, 755, 764, 847, 952, 755, 747, 763, 760, 758, 754, 763, 761, 758, 750, 764, 757, 763, 762, 756, 753, 759, 759, 757, 758, 779, 765, 760, 760, 756, 760, 756, 755, 764, 759, 753, 757, 760, 752, 764, 758, 760, 758, 760, 755, 761, 751, 753, 761, 762, 761, 758, 759, 752, 765, 756, 760, 755, 757, 753, 760, 751, 755, 779]
C.ToArray 782.56 ms -> [783, 774, 771, 771, 773, 774, 775, 775, 772, 770, 771, 774, 771, 1023, 975, 772, 767, 776, 771, 779, 772, 779, 775, 771, 775, 773, 775, 771, 765, 774, 770, 781, 772, 771, 781, 762, 817, 770, 775, 779, 769, 774, 763, 775, 777, 769, 777, 772, 775, 778, 775, 771, 770, 774, 772, 769, 772, 769, 774, 775, 768, 775, 769, 774, 771, 776, 774, 773, 778, 769, 778, 767, 770, 787, 783, 779, 771, 768, 805, 780, 779, 767, 773, 771, 773, 785, 1044, 853, 775, 774, 775, 771, 770, 769, 770, 776, 770, 780, 821, 770]
S.ToList 704.2 ms -> [687, 702, 709, 691, 694, 710, 696, 698, 700, 694, 701, 719, 706, 694, 702, 699, 699, 703, 704, 701, 703, 705, 697, 707, 691, 697, 707, 692, 721, 698, 695, 700, 704, 700, 701, 710, 700, 705, 697, 711, 694, 700, 695, 698, 701, 692, 696, 702, 690, 699, 708, 700, 703, 714, 701, 697, 700, 699, 694, 701, 697, 696, 699, 694, 709, 1068, 690, 706, 699, 699, 695, 708, 695, 704, 704, 700, 695, 704, 695, 696, 702, 700, 710, 708, 693, 697, 702, 694, 700, 706, 699, 695, 706, 714, 704, 700, 695, 697, 707, 704]
S.ToArray 742.5 ms -> [742, 743, 733, 745, 741, 724, 738, 745, 728, 732, 740, 727, 739, 740, 726, 744, 758, 732, 744, 745, 730, 739, 738, 723, 745, 757, 729, 741, 736, 724, 744, 756, 739, 766, 737, 725, 741, 742, 736, 748, 742, 721, 746, 1043, 806, 747, 731, 727, 742, 742, 726, 738, 746, 727, 739, 743, 730, 744, 753, 741, 739, 746, 728, 740, 744, 734, 734, 738, 731, 747, 736, 731, 765, 735, 726, 740, 743, 730, 746, 742, 725, 731, 757, 734, 738, 741, 732, 747, 744, 721, 742, 741, 727, 745, 740, 730, 747, 760, 737, 740]

C1.ToList 32.34 ms -> [35, 31, 31, 31, 32, 31, 31, 31, 31, 31, 31, 31, 31, 31, 33, 32, 31, 31, 31, 31, 30, 32, 31, 31, 31, 31, 32, 30, 31, 31, 31, 30, 32, 31, 31, 31, 36, 31, 31, 31, 32, 30, 31, 32, 31, 31, 31, 31, 31, 32, 31, 31, 31, 31, 33, 32, 31, 32, 31, 31, 33, 31, 31, 31, 31, 31, 32, 31, 32, 31, 34, 38, 68, 42, 79, 33, 31, 31, 31, 31, 31, 30, 30, 30, 30, 31, 31, 31, 31, 32, 31, 32, 31, 31, 31, 32, 33, 33, 31, 31]
C1.ToArray 56.32 ms -> [57, 56, 59, 54, 54, 55, 56, 57, 54, 54, 55, 55, 57, 56, 59, 57, 56, 58, 56, 56, 54, 56, 57, 55, 55, 55, 57, 58, 57, 58, 55, 55, 56, 55, 57, 56, 56, 59, 56, 56, 56, 56, 58, 56, 57, 56, 56, 57, 56, 55, 56, 56, 56, 59, 56, 56, 56, 55, 55, 54, 55, 54, 57, 56, 56, 56, 55, 55, 56, 56, 56, 59, 56, 56, 57, 56, 57, 56, 56, 56, 56, 62, 55, 56, 56, 56, 69, 57, 58, 56, 57, 58, 56, 57, 56, 56, 56, 56, 56, 56]
S1.ToList 88.69 ms -> [96, 90, 90, 89, 91, 88, 89, 90, 96, 89, 89, 89, 90, 90, 90, 89, 90, 90, 89, 90, 89, 91, 89, 91, 89, 91, 89, 90, 90, 89, 87, 88, 87, 88, 87, 87, 87, 87, 88, 88, 87, 87, 89, 87, 87, 87, 91, 88, 87, 86, 89, 87, 90, 89, 89, 90, 89, 87, 87, 87, 86, 87, 88, 90, 88, 87, 87, 92, 87, 87, 88, 88, 88, 86, 86, 87, 88, 87, 87, 87, 89, 87, 89, 87, 90, 89, 89, 89, 91, 89, 90, 89, 90, 88, 90, 90, 90, 88, 89, 89]
S1.ToArray 143.26 ms -> [130, 129, 130, 131, 133, 130, 131, 130, 135, 137, 130, 136, 132, 131, 130, 131, 132, 130, 132, 136, 130, 131, 157, 153, 194, 364, 176, 189, 203, 194, 189, 192, 183, 140, 142, 147, 145, 134, 159, 158, 142, 167, 130, 143, 145, 144, 160, 154, 156, 153, 153, 164, 142, 145, 137, 134, 145, 143, 142, 135, 133, 133, 135, 134, 134, 139, 139, 133, 134, 141, 133, 132, 133, 132, 133, 131, 135, 132, 133, 132, 128, 128, 130, 132, 129, 129, 129, 129, 129, 128, 134, 129, 129, 129, 129, 128, 128, 137, 130, 131]

C2.ToList 3.25 ms -> [5, 3, 3, 3, 3, 4, 3, 4, 3, 4, 3, 3, 3, 4, 4, 3, 3, 3, 4, 3, 3, 3, 3, 4, 3, 4, 3, 4, 3, 3, 4, 4, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 3, 4, 3, 3, 3, 3, 3, 4, 3, 4, 3, 3, 3, 4, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 3, 4, 3, 4, 3, 3, 3, 4, 3, 3, 4, 3, 3, 3, 4, 3, 3, 3, 3, 3]
C2.ToArray 3.37 ms -> [4, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 3, 4, 3, 5, 4, 9, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 3, 3, 3, 3, 3, 4, 4, 3, 3, 4, 4, 3, 3, 3, 4, 3, 3, 3, 3, 4, 3, 3, 3, 3, 3, 4, 4, 3, 4, 4, 3, 3, 4, 3, 3, 4, 3, 4, 4, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 3, 3, 4, 3, 3, 3, 3, 3, 4, 4, 3, 3, 3, 3, 3, 4, 3, 4, 3, 3, 3]
S2.ToList 37.72 ms -> [38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 40, 38, 38, 39, 39, 38, 38, 38, 38, 37, 37, 37, 37, 39, 37, 37, 39, 38, 37, 37, 37, 37, 39, 38, 37, 37, 38, 37, 38, 37, 37, 38, 37, 37, 37, 38, 37, 37, 36, 37, 38, 37, 39, 37, 39, 38, 37, 38, 38, 38, 38, 38, 38, 37, 38, 38, 38, 38, 38, 37, 38, 37, 37, 38, 37, 37, 39, 41, 37, 38, 38, 37, 37, 37, 37, 38, 37, 37, 37, 40, 37, 37, 37, 37, 39, 38]
S2.ToArray 38.86 ms -> [39, 37, 39, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 39, 38, 38, 38, 38, 38, 38, 38, 38, 39, 39, 38, 38, 38, 39, 37, 38, 38, 38, 38, 38, 37, 37, 38, 37, 37, 38, 38, 40, 38, 38, 38, 38, 38, 39, 38, 38, 39, 38, 38, 39, 38, 38, 40, 38, 39, 38, 38, 39, 38, 38, 38, 38, 38, 39, 38, 38, 38, 39, 39, 37, 38, 38, 39, 71, 78, 37, 37, 37, 39, 38, 38, 39, 38, 38, 38, 38, 38, 39, 38, 38, 38, 39, 38, 38, 38]

Group2:

C.ToList 756.81 ms
C.ToArray 774.21 ms
S.ToList 709.7 ms
S.ToArray 753.51 ms

C1.ToList 32.06 ms
C1.ToArray 56.58 ms
S1.ToList 89.43 ms
S1.ToArray 132.85 ms

C2.ToList 3.45 ms
C2.ToArray 3.36 ms
S2.ToList 41.43 ms
S2.ToArray 40.84 ms

Group3:

C.ToList 756.64 ms
C.ToArray 771.56 ms
S.ToList 705.42 ms
S.ToArray 749.59 ms

C1.ToList 31.45 ms
C1.ToArray 57.03 ms
S1.ToList 91.26 ms
S1.ToArray 129.77 ms

C2.ToList 3.26 ms
C2.ToArray 3.29 ms
S2.ToList 41.57 ms
S2.ToArray 40.69 ms

Group4:

C.ToList 729.65 ms -> [749, 730, 721, 719, 723, 743, 721, 724, 727, 722, 716, 725, 723, 726, 718, 722, 731, 722, 723, 725, 723, 722, 728, 726, 728, 718, 726, 1088, 788, 737, 729, 710, 730, 728, 717, 723, 728, 721, 722, 728, 722, 736, 723, 729, 732, 724, 726, 727, 728, 728, 726, 726, 725, 727, 725, 728, 728, 718, 724, 725, 726, 724, 726, 729, 727, 722, 722, 725, 725, 728, 724, 727, 738, 717, 726, 723, 725, 725, 727, 724, 720, 726, 726, 723, 727, 730, 723, 721, 725, 727, 727, 733, 720, 722, 722, 725, 722, 725, 728, 726]
C.ToArray 788.36 ms -> [748, 740, 742, 797, 1090, 774, 781, 787, 784, 786, 786, 782, 781, 781, 784, 783, 783, 781, 783, 787, 783, 784, 775, 789, 784, 785, 778, 774, 781, 783, 786, 781, 780, 788, 778, 785, 777, 781, 786, 782, 781, 787, 782, 787, 784, 773, 783, 782, 781, 777, 783, 781, 785, 788, 777, 776, 784, 784, 783, 789, 778, 781, 791, 768, 779, 783, 781, 787, 786, 781, 784, 781, 785, 781, 780, 809, 1155, 780, 790, 789, 783, 776, 785, 783, 786, 787, 782, 782, 787, 777, 779, 784, 783, 776, 786, 775, 782, 779, 784, 784]
S.ToList 705.54 ms -> [690, 705, 709, 708, 702, 707, 703, 696, 703, 702, 700, 703, 700, 707, 705, 699, 697, 703, 695, 698, 707, 697, 711, 710, 699, 700, 708, 707, 693, 710, 704, 691, 702, 700, 703, 700, 705, 700, 703, 695, 709, 705, 698, 699, 709, 700, 699, 704, 691, 705, 703, 700, 708, 1048, 710, 706, 706, 692, 702, 705, 695, 701, 710, 697, 698, 706, 705, 707, 707, 695, 698, 704, 698, 699, 705, 698, 703, 702, 701, 697, 702, 702, 704, 703, 699, 707, 703, 705, 701, 717, 698, 695, 713, 696, 708, 705, 697, 699, 700, 698]
S.ToArray 745.01 ms -> [751, 743, 727, 734, 736, 745, 739, 750, 739, 750, 758, 739, 744, 738, 730, 744, 745, 739, 744, 750, 733, 735, 743, 731, 749, 748, 727, 746, 749, 731, 737, 803, 1059, 756, 769, 748, 740, 745, 741, 746, 749, 732, 741, 742, 732, 744, 746, 737, 742, 739, 733, 744, 741, 729, 746, 760, 725, 741, 764, 739, 750, 751, 727, 745, 738, 727, 735, 741, 720, 736, 740, 733, 741, 746, 731, 749, 756, 740, 738, 736, 732, 741, 741, 733, 741, 744, 736, 742, 742, 735, 743, 746, 729, 748, 765, 743, 734, 742, 728, 749]

C1.ToList 32.27 ms -> [36, 31, 31, 32, 31, 32, 31, 30, 32, 30, 30, 30, 34, 32, 31, 31, 31, 31, 31, 31, 31, 32, 38, 51, 68, 57, 35, 30, 31, 31, 30, 30, 33, 30, 31, 34, 31, 34, 32, 31, 31, 31, 31, 32, 30, 30, 31, 30, 31, 31, 32, 31, 31, 31, 32, 31, 31, 31, 32, 31, 33, 31, 31, 32, 30, 30, 30, 30, 30, 33, 30, 33, 32, 31, 30, 31, 31, 32, 32, 31, 35, 31, 34, 31, 31, 32, 31, 31, 32, 31, 32, 31, 31, 35, 31, 31, 31, 31, 31, 32]
C1.ToArray 56.72 ms -> [58, 56, 57, 57, 59, 58, 58, 57, 56, 59, 57, 55, 55, 54, 56, 55, 56, 56, 57, 59, 56, 55, 58, 56, 55, 55, 55, 55, 58, 58, 55, 57, 57, 56, 57, 57, 57, 57, 59, 59, 56, 57, 56, 57, 57, 56, 57, 59, 58, 56, 57, 57, 57, 58, 56, 56, 59, 56, 59, 57, 57, 57, 57, 59, 57, 56, 57, 56, 58, 56, 57, 56, 57, 59, 55, 58, 55, 55, 56, 56, 56, 56, 56, 56, 56, 56, 56, 57, 56, 56, 57, 56, 56, 57, 58, 57, 57, 57, 57, 57]
S1.ToList 90.72 ms -> [95, 90, 90, 89, 89, 89, 91, 89, 89, 87, 91, 89, 89, 89, 91, 89, 89, 89, 90, 89, 89, 90, 88, 89, 88, 90, 89, 90, 89, 89, 90, 90, 89, 89, 90, 91, 89, 91, 89, 90, 89, 89, 90, 91, 89, 89, 89, 89, 89, 89, 90, 89, 89, 89, 90, 89, 90, 89, 91, 89, 90, 89, 90, 89, 90, 89, 96, 89, 90, 89, 89, 89, 89, 89, 90, 89, 89, 89, 90, 87, 89, 90, 90, 91, 89, 91, 89, 89, 90, 91, 90, 89, 93, 144, 149, 90, 90, 89, 89, 89]
S1.ToArray 131.4 ms -> [130, 128, 127, 134, 129, 129, 130, 136, 131, 130, 132, 132, 133, 131, 132, 131, 133, 132, 130, 131, 132, 131, 130, 133, 133, 130, 130, 131, 131, 131, 132, 134, 131, 131, 132, 131, 132, 131, 134, 131, 131, 130, 131, 131, 130, 132, 129, 131, 131, 131, 132, 131, 133, 134, 131, 131, 132, 132, 131, 133, 131, 131, 130, 133, 131, 130, 134, 132, 131, 132, 132, 131, 131, 134, 131, 131, 132, 132, 131, 130, 138, 130, 130, 131, 132, 132, 130, 134, 131, 131, 132, 131, 130, 132, 133, 131, 131, 131, 130, 131]

C2.ToList 3.21 ms -> [4, 3, 3, 3, 4, 3, 4, 4, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 3, 3, 3, 3, 4, 3, 3, 3, 3, 3, 4, 3, 4, 3, 3, 3, 3, 3, 4, 3, 3, 4, 3, 3, 3, 3, 3, 3, 3, 4, 3, 3, 4, 3, 3, 3, 3, 3, 3, 4, 3, 4, 3, 3, 4, 4, 4, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 3, 3, 3, 4, 4, 3, 3, 3, 3, 3, 4, 3, 3, 3]
C2.ToArray 3.22 ms -> [4, 3, 3, 3, 3, 3, 3, 3, 3, 3, 5, 4, 3, 3, 3, 4, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 3, 3, 3, 3, 3, 3, 4, 3, 4, 3, 3, 3, 4, 3, 4, 3, 3, 3, 4, 4, 3, 3, 3, 3, 3, 3, 3, 3, 4, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 3, 3, 3, 4, 3, 4, 3, 3, 3, 3, 3, 4, 3, 3, 3, 4, 3, 4, 3, 3, 3, 3, 4]
S2.ToList 41.46 ms -> [42, 40, 41, 40, 42, 40, 40, 40, 40, 40, 40, 40, 40, 41, 40, 40, 41, 40, 40, 40, 39, 41, 41, 39, 40, 40, 43, 40, 39, 40, 40, 40, 40, 40, 40, 41, 40, 40, 40, 43, 40, 43, 75, 76, 47, 39, 40, 40, 40, 40, 42, 40, 41, 40, 40, 40, 44, 41, 40, 42, 42, 40, 41, 41, 41, 41, 41, 40, 41, 41, 41, 41, 42, 41, 40, 41, 41, 42, 42, 41, 40, 41, 41, 41, 41, 41, 40, 42, 40, 42, 41, 41, 41, 43, 41, 41, 41, 41, 42, 41]
S2.ToArray 41.14 ms -> [42, 41, 41, 40, 40, 40, 40, 41, 41, 42, 41, 42, 41, 41, 41, 42, 41, 41, 42, 41, 41, 41, 41, 41, 42, 40, 41, 40, 42, 40, 42, 41, 40, 42, 41, 41, 43, 42, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 40, 40, 41, 41, 41, 40, 42, 41, 41, 41, 41, 41, 40, 41, 41, 42, 41, 41, 41, 42, 41, 41, 41, 41, 41, 41, 42, 42, 42, 41, 45, 46, 41, 40, 41, 41, 42, 41, 41, 41, 41, 41, 41, 40, 41, 43, 40, 40, 40, 40, 43, 41]

Group5:

C.ToList 757.06 ms -> [770, 752, 752, 751, 778, 763, 761, 763, 747, 758, 748, 747, 754, 749, 752, 753, 756, 762, 750, 753, 756, 749, 755, 757, 755, 756, 755, 744, 753, 758, 747, 751, 759, 751, 761, 755, 746, 752, 752, 749, 746, 752, 753, 755, 752, 755, 754, 754, 966, 937, 749, 759, 748, 747, 754, 749, 755, 750, 746, 754, 757, 752, 753, 745, 758, 755, 761, 753, 751, 755, 755, 752, 746, 756, 755, 746, 742, 751, 751, 749, 752, 751, 756, 756, 755, 742, 749, 754, 749, 756, 753, 751, 754, 752, 751, 754, 753, 749, 755, 756]
C.ToArray 772.8 ms -> [766, 772, 755, 763, 758, 767, 763, 762, 761, 768, 769, 763, 770, 757, 765, 760, 766, 759, 764, 761, 760, 777, 1102, 881, 759, 765, 758, 762, 772, 761, 758, 757, 765, 769, 769, 761, 762, 762, 763, 760, 770, 764, 760, 768, 758, 766, 763, 770, 769, 761, 764, 761, 761, 767, 761, 762, 764, 757, 765, 766, 767, 771, 753, 762, 769, 768, 759, 764, 764, 760, 763, 763, 763, 763, 763, 767, 761, 771, 760, 765, 760, 758, 768, 770, 751, 771, 767, 771, 765, 763, 760, 765, 765, 769, 767, 767, 1193, 774, 767, 764]
S.ToList 704.73 ms -> [682, 708, 705, 699, 705, 704, 695, 703, 702, 699, 701, 708, 699, 702, 703, 701, 701, 699, 701, 707, 707, 700, 701, 705, 700, 697, 706, 702, 701, 706, 699, 692, 702, 697, 707, 704, 697, 698, 699, 699, 702, 703, 698, 697, 702, 703, 702, 704, 694, 697, 707, 695, 711, 710, 700, 693, 703, 699, 699, 706, 698, 701, 703, 704, 698, 706, 700, 704, 701, 699, 702, 705, 694, 698, 709, 736, 1053, 704, 694, 700, 698, 696, 701, 700, 700, 706, 706, 692, 698, 707, 703, 695, 703, 699, 694, 708, 695, 694, 706, 695]
S.ToArray 744.17 ms -> [746, 740, 725, 740, 739, 731, 746, 760, 735, 738, 740, 734, 744, 748, 737, 744, 745, 727, 736, 738, 728, 743, 745, 735, 748, 760, 739, 748, 762, 742, 741, 747, 733, 746, 758, 742, 742, 741, 724, 744, 747, 727, 740, 740, 729, 742, 757, 741, 740, 742, 726, 739, 746, 1133, 749, 737, 730, 740, 747, 733, 747, 752, 731, 747, 742, 730, 741, 749, 731, 749, 743, 730, 747, 742, 731, 737, 745, 734, 739, 735, 727, 743, 752, 731, 744, 742, 729, 740, 746, 731, 739, 746, 733, 745, 743, 733, 739, 742, 727, 737]

C1.ToList 31.71 ms -> [35, 32, 32, 30, 31, 33, 31, 32, 32, 31, 31, 32, 32, 33, 32, 31, 31, 32, 31, 32, 32, 32, 31, 32, 33, 32, 31, 31, 31, 32, 31, 34, 31, 31, 32, 33, 32, 32, 31, 32, 34, 32, 31, 32, 33, 31, 32, 32, 31, 32, 32, 32, 32, 32, 32, 31, 31, 32, 31, 33, 30, 31, 32, 30, 30, 33, 32, 32, 34, 31, 31, 31, 31, 32, 31, 31, 31, 31, 32, 31, 31, 33, 31, 32, 32, 32, 33, 32, 31, 31, 31, 31, 31, 32, 32, 33, 32, 31, 31, 32]
C1.ToArray 59.53 ms -> [63, 57, 58, 58, 57, 59, 59, 57, 60, 131, 127, 67, 58, 56, 59, 56, 57, 58, 58, 58, 57, 59, 60, 57, 57, 59, 60, 57, 57, 57, 58, 58, 58, 58, 57, 57, 61, 57, 58, 57, 57, 57, 57, 57, 58, 58, 58, 58, 57, 58, 59, 57, 58, 57, 57, 59, 58, 58, 59, 57, 59, 57, 56, 56, 59, 56, 56, 59, 57, 58, 58, 58, 57, 58, 59, 59, 58, 57, 58, 62, 65, 57, 57, 57, 58, 60, 59, 58, 59, 57, 58, 57, 58, 59, 58, 58, 58, 59, 60, 58]
S1.ToList 82.78 ms -> [87, 82, 83, 83, 82, 82, 83, 84, 82, 83, 84, 84, 84, 82, 82, 84, 82, 84, 83, 84, 82, 82, 82, 81, 83, 83, 83, 84, 84, 82, 82, 83, 83, 83, 82, 83, 85, 83, 82, 82, 84, 82, 82, 83, 83, 83, 82, 82, 82, 83, 82, 83, 82, 84, 82, 83, 82, 83, 82, 82, 82, 84, 82, 83, 82, 82, 86, 83, 83, 82, 83, 83, 83, 82, 84, 82, 83, 81, 82, 82, 82, 82, 83, 83, 83, 82, 83, 84, 83, 82, 83, 83, 83, 82, 83, 84, 82, 82, 83, 83]
S1.ToArray 122.3 ms -> [122, 119, 119, 120, 119, 120, 120, 121, 119, 119, 122, 120, 120, 120, 122, 120, 123, 120, 120, 120, 121, 123, 120, 120, 120, 121, 120, 121, 122, 120, 123, 119, 121, 118, 121, 120, 120, 120, 119, 124, 119, 121, 119, 120, 120, 120, 120, 120, 122, 121, 123, 230, 203, 123, 119, 119, 122, 119, 120, 120, 120, 122, 120, 121, 120, 121, 120, 121, 120, 121, 120, 120, 120, 121, 122, 121, 123, 119, 119, 119, 119, 121, 120, 120, 120, 122, 121, 122, 119, 120, 120, 121, 121, 120, 121, 120, 121, 118, 118, 118]

C2.ToList 3.43 ms -> [5, 3, 4, 4, 4, 3, 4, 4, 4, 4, 4, 3, 3, 3, 4, 3, 3, 3, 4, 3, 4, 3, 4, 3, 4, 4, 3, 3, 3, 3, 4, 3, 3, 3, 4, 3, 4, 3, 3, 3, 4, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 3, 3, 6, 4, 4, 3, 3, 4, 3, 3, 4, 3, 3, 3, 3, 3, 3, 3, 4, 3, 3, 3, 3, 3, 3, 4, 3, 4, 4, 4, 3, 4, 4, 3, 4, 4, 4, 4, 4, 4, 3, 3, 3, 4, 4, 3, 3, 3, 3]
C2.ToArray 3.48 ms -> [3, 3, 3, 3, 4, 4, 3, 4, 4, 4, 3, 4, 3, 3, 4, 3, 3, 4, 3, 4, 3, 3, 3, 4, 3, 3, 3, 4, 3, 3, 3, 3, 4, 3, 4, 3, 3, 4, 3, 4, 3, 3, 3, 3, 4, 3, 3, 3, 3, 4, 3, 4, 3, 3, 4, 3, 3, 4, 3, 3, 4, 4, 3, 3, 4, 3, 4, 4, 3, 4, 4, 4, 4, 4, 3, 3, 3, 4, 4, 3, 4, 4, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 3, 4, 4, 4, 4, 3]
S2.ToList 41.47 ms -> [41, 41, 49, 67, 82, 41, 41, 40, 40, 40, 40, 40, 41, 40, 40, 40, 40, 40, 41, 40, 42, 42, 40, 40, 41, 41, 41, 40, 41, 40, 41, 40, 41, 40, 42, 41, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 41, 41, 41, 41, 41, 42, 41, 41, 41, 42, 40, 41, 40, 40, 40, 42, 40, 41, 42, 41, 42, 41, 42, 40, 41, 41, 41, 41, 41, 41, 41, 41, 40, 41, 40, 41, 41, 41, 40, 41, 41, 40, 40, 41, 41, 41, 41, 41, 43, 40, 40, 41, 42, 41]
S2.ToArray 40.62 ms -> [42, 41, 44, 40, 40, 40, 40, 41, 41, 40, 41, 41, 41, 40, 41, 41, 40, 41, 41, 40, 41, 40, 40, 41, 42, 41, 41, 41, 40, 40, 40, 40, 40, 41, 41, 42, 40, 41, 41, 41, 41, 41, 40, 42, 40, 40, 41, 41, 41, 40, 41, 40, 40, 40, 40, 40, 41, 40, 40, 41, 40, 40, 40, 40, 41, 40, 41, 41, 41, 40, 41, 41, 40, 41, 40, 41, 42, 40, 41, 41, 42, 41, 41, 40, 41, 40, 41, 40, 41, 41, 40, 40, 40, 41, 41, 40, 40, 40, 40, 40]

เนื่องจากข้อ จำกัด ของ stackoverflow ถึงจำนวนอักขระของคำตอบรายการตัวอย่างของ Group2 และ Group3 จะถูกละเว้น

อย่างที่คุณเห็นมันไม่สำคัญที่จะใช้งานToListหรือToArryในกรณีส่วนใหญ่

ในขณะที่การประมวลผลIEnumerable<T>วัตถุที่คำนวณจากรันไทม์หากการโหลดที่นำมาจากการคำนวณหนักกว่าการจัดสรรหน่วยความจำและการดำเนินการคัดลอกของToListและToArrayความไม่เท่าเทียมกันนั้นไม่มีนัยสำคัญ ( C.ToList vs C.ToArrayและS.ToList vs S.ToArray)

ความแตกต่างสามารถสังเกตได้เฉพาะกับอIEnumerable<T>อบเจ็กต์ที่ไม่ได้คำนวณด้วย runtime ( C1.ToList vs C1.ToArrayและS1.ToList vs S1.ToArray) เท่านั้น แต่แตกต่างสัมบูรณ์ (<60ms) IEnumerable<T>ยังคงได้รับการยอมรับในวัตถุขนาดเล็กหนึ่งล้าน ในความเป็นจริงความแตกต่างจะตัดสินใจโดยการดำเนินการของEnumerator<T> IEnumerable<T>ดังนั้นหากโปรแกรมของคุณมีความอ่อนไหวต่อเรื่องนี้จริง ๆ คุณต้องทำโปรไฟล์โปรไฟล์โปรไฟล์ ! ที่สุดท้ายที่คุณอาจจะพบว่าคอขวดไม่ได้อยู่ในToListหรือToArrayแต่รายละเอียดของ enumerators

และผลลัพธ์ของC2.ToList vs C2.ToArrayและS2.ToList vs S2.ToArrayแสดงให้เห็นว่าคุณไม่จำเป็นต้องสนใจToListหรือวัตถุที่ToArrayคำนวณICollection<T>โดยไม่ใช้งานจริง

แน่นอนนี่เป็นเพียงผลลัพธ์บนเครื่องของฉันเวลาที่ใช้จริงของการดำเนินการเหล่านี้ในเครื่องที่แตกต่างกันจะไม่เหมือนกันคุณสามารถค้นหาบนเครื่องของคุณโดยใช้รหัสด้านบน

เหตุผลเดียวที่คุณจะต้องให้ทางเลือกก็คือว่าคุณมีความต้องการที่เฉพาะเจาะจงในList<T>หรือT[]ตามที่อธิบายคำตอบของ@Jeppe เพอร์นีลเซ่น


1

สำหรับผู้ที่สนใจใช้ผลลัพธ์นี้ใน Linq-to-sql อีกเช่น

from q in context.MyTable
where myListOrArray.Contains(q.someID)
select q;

ดังนั้น SQL ที่สร้างขึ้นจะเหมือนกันไม่ว่าคุณจะใช้ List หรือ Array สำหรับ myListOrArray ตอนนี้ฉันรู้ว่าบางคนอาจถามว่าทำไมถึงระบุก่อนคำสั่งนี้ แต่มีความแตกต่างระหว่าง SQL ที่สร้างขึ้นจาก IQueryable vs (List หรือ Array)

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.