งบล็อคแพงแค่ไหน?


111

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

private object mutex = new object();

public void Count(int amount)
{
 lock(mutex)
 {
  done += amount;
 }
}

แต่ฉันสงสัยว่า ... การล็อคตัวแปรแพงแค่ไหน? ผลเสียต่อประสิทธิภาพคืออะไร?


10
การล็อคตัวแปรนั้นไม่ได้แพงขนาดนั้น เป็นการรอตัวแปรที่ถูกล็อกที่คุณต้องการหลีกเลี่ยง
Gabe

53
มันแพงกว่าการใช้เวลาหลายชั่วโมงในการติดตามสภาพการแข่งขันอื่น ๆ ;-)
BrokenGlass

2
ถ้าล็อคมีราคาแพงคุณอาจต้องการหลีกเลี่ยงโดยการเปลี่ยนการเขียนโปรแกรมเพื่อให้ต้องการล็อคน้อยลง ฉันสามารถใช้การซิงโครไนซ์บางประเภทได้
Kees C.Bakker

1
ฉันมีการปรับปรุงประสิทธิภาพอย่างมาก (ตอนนี้หลังจากอ่านความคิดเห็นของ @Gabe) เพียงแค่ย้ายรหัสจำนวนมากออกจากบล็อกล็อคของฉัน Bottomline: จากนี้ไปฉันจะเหลือเพียงการเข้าถึงตัวแปร (โดยปกติคือหนึ่งบรรทัด) ในบล็อกล็อกโดยเรียงเป็น "การล็อกเวลาเท่านั้น" มันเข้าท่าไหม?
heltonbiker

2
@heltonbiker แน่นอนว่ามันสมเหตุสมผล ควรเป็นหลักการทางสถาปัตยกรรมด้วยเช่นกันคุณควรทำให้ล็อคสั้นง่ายและเร็วที่สุด เฉพาะข้อมูลที่จำเป็นจริงๆที่ต้องซิงโครไนซ์ ในกล่องเซิร์ฟเวอร์คุณควรคำนึงถึงลักษณะของการล็อกแบบไฮบริดด้วย ข้อโต้แย้งแม้ว่าจะไม่สำคัญสำหรับรหัสของคุณเนื่องจากลักษณะของการล็อกแบบไฮบริดทำให้คอร์หมุนระหว่างการเข้าถึงแต่ละครั้งหากมีผู้อื่นล็อกไว้ คุณกำลังกลืนกินทรัพยากร cpu จากบริการอื่น ๆ บนเซิร์ฟเวอร์เป็นระยะเวลาหนึ่งก่อนที่เธรดของคุณจะถูกระงับ
ipavlu

คำตอบ:


86

นี่คือบทความที่เกี่ยวข้องกับต้นทุน คำตอบสั้น ๆ คือ 50ns


39
คำตอบสั้น ๆ ที่ดีกว่า: 50ns + เวลาที่ใช้ในการรอหากเธรดอื่นกำลังล็อค
Herman

4
ยิ่งเธรดเข้าและออกจากล็อกมากเท่าไหร่ก็ยิ่งมีราคาแพงมากขึ้นเท่านั้น ค่าใช้จ่ายเพิ่มขึ้นอย่างทวีคูณด้วยจำนวนเธรด
Arsen Zahray

16
บางบริบท: หารตัวเลขสองตัวบน 3Ghz x 86 ใช้เวลาประมาณ 10ns (ไม่รวมเวลาที่ใช้ในการดึงข้อมูล / ถอดรหัสการเรียนการสอน) ; และการโหลดตัวแปรเดียวจากหน่วยความจำ (ไม่แคช) ลงในรีจิสเตอร์จะใช้เวลาประมาณ 40ns ดังนั้น 50ns จึงรวดเร็วอย่างไม่น่าเชื่อคุณไม่ควรกังวลกับค่าใช้จ่ายในการใช้งานlockมากกว่าที่คุณต้องกังวลเกี่ยวกับค่าใช้จ่ายในการใช้ตัวแปร
BlueRaja - Danny Pflughoeft

3
นอกจากนี้บทความนั้นเก่าเมื่อมีการถามคำถามนี้
Otis

3
เมตริกที่ยอดเยี่ยมจริงๆ "แทบไม่มีค่าใช้จ่าย" ไม่ต้องพูดถึงไม่ถูกต้อง พวกคุณอย่าคำนึงว่ามันสั้นและเร็วเท่านั้นและเฉพาะในกรณีที่ไม่มีการโต้แย้งเลยแม้แต่เธรดเดียว ในกรณีนี้คุณไม่จำเป็นต้องล็อคเลย ประเด็นที่สองการล็อกไม่ใช่การล็อก แต่เป็นการล็อกแบบไฮบริดจะตรวจพบภายใน CLR ซึ่งทุกคนไม่ได้ล็อคโดยอาศัยการทำงานของอะตอมและในกรณีเช่นนี้จะหลีกเลี่ยงการเรียกไปยังแกนหลักของระบบปฏิบัติการซึ่งเป็นวงแหวนที่แตกต่างกันซึ่งไม่ได้วัดโดยสิ่งเหล่านี้ การทดสอบ สิ่งที่วัดเป็น 25ns ถึง 50ns เป็นรหัสคำแนะนำที่เชื่อมต่อกันในระดับแอปพลิเคชันหากไม่ได้ทำการล็อค
ipavlu

50

คำตอบทางเทคนิคคือเป็นไปไม่ได้ที่จะหาปริมาณมันขึ้นอยู่กับสถานะของบัฟเฟอร์การเขียนกลับของหน่วยความจำ CPU และปริมาณข้อมูลที่รวบรวมไว้ล่วงหน้าจะต้องถูกทิ้งและอ่านใหม่ ซึ่งมีทั้งแบบไม่กำหนดปัจจัย ฉันใช้ 150 CPU Cycle เป็นการประมาณด้านหลังของซองจดหมายที่หลีกเลี่ยงความผิดหวังครั้งใหญ่

คำตอบในทางปฏิบัติคือว่ามันเป็นwaaaayราคาถูกกว่าระยะเวลาที่คุณจะเผาผลาญในการแก้จุดบกพร่องรหัสของคุณเมื่อคุณคิดว่าคุณสามารถข้ามล็อค

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


1
จริงๆแล้วไม่สามารถวัดปริมาณและวัดได้ มันไม่ง่ายเหมือนการเขียนล็อคเหล่านั้นรอบ ๆ รหัสแล้วระบุว่ามันเป็นเพียง 50ns ซึ่งเป็นตำนานที่วัดได้จากการเข้าถึงล็อคแบบเธรดเดียว
ipavlu

8
"คิดว่าคุณสามารถข้ามล็อกได้" ... ฉันคิดว่านั่นเป็นสิ่งที่ผู้คนจำนวนมากอยู่ในขณะที่อ่านคำถามนี้ ...
นูป

30

อ่านเพิ่มเติม:

ฉันต้องการนำเสนอบทความบางส่วนของฉันซึ่งมีความสนใจในการทำข้อมูลเบื้องต้นเกี่ยวกับการซิงโครไนซ์ทั่วไปและพวกเขากำลังขุดลงใน Monitor พฤติกรรมคำสั่งล็อค C # คุณสมบัติและค่าใช้จ่ายขึ้นอยู่กับสถานการณ์ที่แตกต่างกันและจำนวนเธรด มีความสนใจเป็นพิเศษเกี่ยวกับการสิ้นเปลืองของ CPU และระยะเวลาทรูพุตเพื่อทำความเข้าใจว่าสามารถผลักดันงานผ่านสถานการณ์ต่างๆได้มากเพียงใด:

https://www.codeproject.com/Articles/1236238/Unified-Concurrency-I-Introduction https://www.codeproject.com/Articles/1237518/Unified-Concurrency-II-benchmarking-methodologies https: // www. codeproject.com/Articles/1242156/Unified-Concurrency-III-cross-benchmarking

คำตอบเดิม:

โอ้ที่รัก!

ดูเหมือนว่าคำตอบที่ถูกต้องถูกตั้งค่าสถานะไว้ที่นี่เนื่องจากคำตอบนั้นไม่ถูกต้องโดยเนื้อแท้! ฉันอยากจะขอให้ผู้เขียนตอบด้วยความเคารพที่จะอ่านบทความที่เชื่อมโยงจนจบ บทความ

ผู้เขียนบทความจากปี 2003 บทความได้รับการวัดบนเครื่อง Dual Core เท่านั้นและในกรณีที่วัดแรกเขาวัดล็อคกับหัวข้อเดียวเท่านั้นและผลที่ได้คือประมาณ 50ns ต่อการเข้าถึงล็อค

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

ดังนั้นผู้เขียนจึงกล่าวว่าด้วยสองเธรดบน Dual Core การล็อคจะมีราคา 120ns และ 3 เธรดจะไปที่ 180ns ดังนั้นดูเหมือนว่าจะขึ้นอยู่กับจำนวนเธรดที่เข้าถึงล็อกพร้อมกันอย่างชัดเจน

ดังนั้นจึงเป็นเรื่องง่ายไม่ใช่ 50 ns เว้นแต่จะเป็นเธรดเดียวซึ่งการล็อคจะไร้ประโยชน์

อีกประเด็นที่ต้องพิจารณาคือวัดเป็นเวลาเฉลี่ย !

หากจะวัดเวลาในการทำซ้ำจะมีช่วงเวลาระหว่าง 1ms ถึง 20ms ด้วยซ้ำเพียงเพราะส่วนใหญ่เร็ว แต่มีเธรดเพียงไม่กี่เธรดที่รอเวลาโปรเซสเซอร์และอาจเกิดความล่าช้าเป็นเวลานานถึงมิลลิวินาที

นี่เป็นข่าวร้ายสำหรับแอปพลิเคชันทุกประเภทที่ต้องการปริมาณงานสูงเวลาแฝงต่ำ

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

โปรดพิจารณาว่าเวลาผ่านไปกว่าหนึ่งทศวรรษแล้วนับจากปี 2546 ซึ่งเป็นโปรเซสเซอร์เพียงไม่กี่รุ่นที่ออกแบบมาโดยเฉพาะเพื่อให้ทำงานพร้อมกันได้อย่างสมบูรณ์และการล็อกก็ส่งผลเสียต่อประสิทธิภาพอย่างมาก


1
เพื่อชี้แจงบทความนี้ไม่ได้บอกว่าประสิทธิภาพการล็อกลดลงตามจำนวนเธรดในแอปพลิเคชัน ประสิทธิภาพจะลดลงตามจำนวนเธรดที่ขัดแย้งกับการล็อก (นั่นคือโดยนัย แต่ไม่ได้ระบุไว้อย่างชัดเจนในคำตอบด้านบน)
มะเฟือง

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

20

นี่ไม่ได้ตอบคำถามของคุณเกี่ยวกับประสิทธิภาพ แต่ฉันสามารถพูดได้ว่า. NET Framework มีInterlocked.Addวิธีการที่จะช่วยให้คุณสามารถเพิ่มของคุณamountไปยังdoneสมาชิกของคุณได้โดยไม่ต้องล็อกวัตถุอื่นด้วยตนเอง


1
ใช่นี่อาจเป็นคำตอบที่ดีที่สุด แต่ส่วนใหญ่เป็นเหตุผลของรหัสที่สั้นและสะอาดกว่า ความแตกต่างของความเร็วไม่น่าจะสังเกตเห็นได้
Henk Holterman

ขอบคุณสำหรับคำตอบนี้ ฉันกำลังทำหลายอย่างด้วยการล็อค ints ที่เพิ่มเข้ามาเป็นหนึ่งในหลาย ๆ ชอบข้อเสนอแนะจะใช้ต่อจากนี้
Kees C.Bakker

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

10

lock (Monitor.Enter / Exit) มีราคาถูกมากถูกกว่าทางเลือกอื่นเช่น Waithandle หรือ Mutex

แต่ถ้ามันช้า (เล็กน้อย) คุณอยากจะมีโปรแกรมที่รวดเร็วพร้อมผลลัพธ์ที่ไม่ถูกต้องหรือไม่?


5
ฮ่าฮ่า ... ฉันไปตามโปรแกรมที่รวดเร็วและผลลัพธ์ที่ดี
Kees C.Bakker

@ henk-holterman มีปัญหาหลายประการเกี่ยวกับข้อความของคุณ: ประการแรกเนื่องจากคำถามและคำตอบนี้แสดงให้เห็นอย่างชัดเจนมีความเข้าใจน้อยเกี่ยวกับผลกระทบของการล็อกต่อประสิทธิภาพโดยรวมแม้แต่คนที่ระบุตำนานเกี่ยวกับ 50ns ซึ่งใช้ได้กับสภาพแวดล้อมแบบเธรดเดียวเท่านั้น ประการที่สองคำแถลงของคุณอยู่ที่นี่และจะอยู่ที่นี่เป็นเวลาหลายปีและในเวลานั้นโปรเซสเซอร์ที่เติบโตในคอร์ แต่ความเร็วของคอร์ไม่มากนัก ** แอพพลิเคชั่น Thrid ** มีความซับซ้อนมากขึ้นเมื่อเวลาผ่านไปและจากนั้นก็เป็นเลเยอร์ตามเลเยอร์ของ การล็อคในสภาพแวดล้อมของคอร์จำนวนมากและจำนวนเพิ่มขึ้น 2,4,8,10,20,16,32
ipavlu

วิธีการปกติของฉันคือการสร้างการซิงโครไนซ์ควบคู่ไปกับการโต้ตอบให้น้อยที่สุด นั่นเป็นไปอย่างรวดเร็วมากในการล็อคโครงสร้างข้อมูล ฉันสร้างสำหรับห่อรหัสของฉันรอบ ๆ spinlock เพื่อลดความซับซ้อนในการพัฒนาและแม้ว่า TPL จะมีคอลเลกชันพิเศษพร้อมกันฉันได้พัฒนาคอลเลกชันที่ถูกล็อคด้วยการหมุนรอบรายการอาร์เรย์พจนานุกรมและคิวของฉันเองเนื่องจากฉันต้องการการควบคุมเพิ่มขึ้นเล็กน้อยและบางครั้งบางรหัสก็ทำงานภายใต้ Spinlock. ฉันบอกคุณได้ว่ามันเป็นไปได้และช่วยให้สามารถแก้ปัญหาหลาย ๆ สถานการณ์ที่คอลเลกชัน TPL ไม่สามารถทำได้และด้วยประสิทธิภาพที่ยอดเยี่ยม / อัตราการรับส่งข้อมูลที่ยอดเยี่ยม
ipavlu

7

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

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace LockPerformanceConsoleApplication
{
    class Program
    {
        static void Main(string[] args)
        {
            var stopwatch = new Stopwatch();
            const int LoopCount = (int) (100 * 1e6);
            int counter = 0;

            for (int repetition = 0; repetition < 5; repetition++)
            {
                stopwatch.Reset();
                stopwatch.Start();
                for (int i = 0; i < LoopCount; i++)
                    lock (stopwatch)
                        counter = i;
                stopwatch.Stop();
                Console.WriteLine("With lock: {0}", stopwatch.ElapsedMilliseconds);

                stopwatch.Reset();
                stopwatch.Start();
                for (int i = 0; i < LoopCount; i++)
                    counter = i;
                stopwatch.Stop();
                Console.WriteLine("Without lock: {0}", stopwatch.ElapsedMilliseconds);
            }

            Console.ReadKey();
        }
    }
}

เอาท์พุต:

With lock: 2013
Without lock: 211
With lock: 2002
Without lock: 210
With lock: 1989
Without lock: 210
With lock: 1987
Without lock: 207
With lock: 1988
Without lock: 208

4
นี่อาจเป็นตัวอย่างที่ไม่ดีเพราะลูปของคุณไม่ได้ทำอะไรเลยนอกจากการกำหนดตัวแปรเดียวและการล็อกคือการเรียกใช้ฟังก์ชันอย่างน้อย 2 ครั้ง นอกจากนี้ 20ns ต่อล็อคที่คุณได้รับก็ไม่ได้แย่ขนาดนั้น
Zar Shardan

5

มีหลายวิธีในการกำหนด "ต้นทุน" มีค่าใช้จ่ายที่แท้จริงในการรับและปลดล็อค ดังที่เจคเขียนนั่นเป็นเรื่องเล็กน้อยเว้นแต่การดำเนินการนี้จะดำเนินการหลายล้านครั้ง

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

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