Roslyn SyntaxNodes ถูกใช้ซ้ำหรือไม่


124

ฉันได้ดูRoslyn CTPแล้วและในขณะที่มันแก้ปัญหาที่คล้ายกันกับExpression tree APIทั้งสองไม่เปลี่ยนรูป แต่ Roslyn ทำในลักษณะที่แตกต่างกันมาก:

  • Expression โหนดไม่มีการอ้างอิงไปยังโหนดหลักถูกแก้ไขโดยใช้ไฟล์ ExpressionVisitorและนั่นคือเหตุผลที่สามารถใช้ชิ้นส่วนขนาดใหญ่ซ้ำได้

  • SyntaxNodeอีกด้านหนึ่งของ Roslyn มีการอ้างอิงถึงพาเรนต์ดังนั้นโหนดทั้งหมดจึงกลายเป็นบล็อกที่ไม่สามารถใช้ซ้ำได้อย่างมีประสิทธิภาพ วิธีการเช่นUpdate, ReplaceNodeฯลฯ มีไว้เพื่อทำการแก้ไข

เรื่องนี้จบลงที่ไหน? Document? Project? ISolution? API ส่งเสริมการเปลี่ยนแปลงทีละขั้นตอนของทรี (แทนที่จะเป็นปุ่มขึ้น) แต่แต่ละขั้นตอนทำสำเนาทั้งหมดหรือไม่

ทำไมพวกเขาถึงเลือกเช่นนั้น? มีเคล็ดลับที่น่าสนใจบางอย่างที่ฉันขาดหายไปหรือไม่?

คำตอบ:


181

UPDATE: คำถามนี้เป็นเรื่องของบล็อกของฉันใน 8 มิถุนายน 2012 ขอบคุณสำหรับคำถามดีๆ!


คำถามที่ดี เราถกเถียงกันในประเด็นที่คุณยกมาเป็นเวลานาน

เราต้องการโครงสร้างข้อมูลที่มีลักษณะดังต่อไปนี้:

  • ไม่เปลี่ยนรูป
  • รูปแบบของต้นไม้
  • การเข้าถึงโหนดหลักราคาถูกจากโหนดลูก
  • เป็นไปได้ที่จะแมปจากโหนดในทรีกับอักขระที่ออฟเซ็ตในข้อความ
  • หมั่น

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

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

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

แต่ในทีม Roslyn เรามักจะทำสิ่งที่เป็นไปไม่ได้ เราทำสิ่งที่เป็นไปไม่ได้จริง ๆ โดยการแยกต้นไม้สองต้น ทรี "สีเขียว" ไม่เปลี่ยนรูปถาวรไม่มีการอ้างอิงพาเรนต์สร้างขึ้น "จากล่างขึ้นบน" และทุกโหนดติดตามความกว้างแต่ไม่ใช่ตำแหน่งที่แน่นอนตำแหน่งที่แน่นอนเมื่อการแก้ไขเกิดขึ้นเราจะสร้างเฉพาะส่วนของต้นไม้สีเขียวที่ได้รับผลกระทบจากการแก้ไขซึ่งโดยทั่วไปจะเกี่ยวกับ O (log n) ของโหนดแยกวิเคราะห์ทั้งหมดในทรี

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

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

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

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


3
และเพื่อตอบคำถามของคุณเกี่ยวกับ IProjects และ IDocuments: เราใช้โมเดลที่คล้ายกันในเลเยอร์บริการ ภายในมีประเภท "DocumentState" และ "ProjectState" ที่มีศีลธรรมเทียบเท่ากับโหนดสีเขียวของโครงสร้างไวยากรณ์ อ็อบเจ็กต์ IProject / IDocument ที่คุณได้รับคือส่วนหน้าโหนดสีแดงสำหรับสิ่งเหล่านี้ หากคุณดูการใช้งาน Roslyn.Services.Project ใน decompiler คุณจะเห็นว่าการเรียกเกือบทั้งหมดส่งต่อไปยังออบเจ็กต์สถานะภายใน
Jason Malinowski

@ เอริกขอโทษสำหรับคำพูด แต่คุณกำลังขัดแย้งกับตัวเอง The expense and difficulty of building a complex persistent data structure doesn't pay for itself.ref: stackoverflow.com/questions/6742923/…หากคุณมีเป้าหมายด้านประสิทธิภาพสูงทำไมคุณถึงทำให้ไม่เปลี่ยนรูปตั้งแต่แรก? มีเหตุผลอื่นนอกเหนือจากเหตุผลที่ชัดเจนหรือไม่? เช่นทำให้เธรดปลอดภัยง่ายขึ้นเหตุผลเกี่ยวกับ ฯลฯ
Lukasz Madon

2
@lukas คุณกำลังนำคำพูดนั้นออกจากบริบท ประโยคก่อนหน้านี้คือ "เนื่องจากเมื่อคุณดูการดำเนินการที่มักจะทำกับสตริงในโปรแกรม. NET มันมีความเกี่ยวข้องกันแทบจะไม่แย่ไปกว่าเดิมเลยที่จะสร้างสตริงใหม่ทั้งหมด" OTOH เมื่อคุณดูการดำเนินการที่มักจะทำบนแผนภูมินิพจน์เช่นการพิมพ์อักขระสองสามตัวลงในไฟล์ต้นฉบับการสร้างแผนภูมินิพจน์ใหม่จะแย่กว่ามาก ดังนั้นพวกเขาจึงสร้างมันขึ้นมาเพียงครึ่งเดียว
Timbo

1
@lukas ฉันเดา: เนื่องจาก Roslyn ควรจะทำงานบนเธรดพื้นหลังความไม่เปลี่ยนรูปทำให้หลายเธรดสามารถวิเคราะห์ซอร์สโค้ดเดียวกันได้ในเวลาเดียวกันโดยไม่ต้องกังวลว่าจะมีการเปลี่ยนแปลงเมื่อผู้ใช้กดปุ่ม เพื่อตอบสนองต่อการป้อนข้อมูลของผู้ใช้ต้นไม้ที่ไม่เปลี่ยนรูปสามารถอัปเดตได้โดยไม่ต้องหยุดงานวิเคราะห์ที่กำลังทำงานอยู่ ดังนั้นฉันจึงจินตนาการว่าเป้าหมายหลักของการไม่เปลี่ยนรูปคือการทำให้ Roslyn เขียนได้ง่ายขึ้น (และอาจจะง่ายกว่าสำหรับลูกค้าที่จะใช้)
Qwertie

3
@lukas โครงสร้างข้อมูลแบบต่อเนื่องมีประสิทธิภาพมากกว่าการคัดลอกเมื่อโครงสร้างข้อมูลโดยทั่วไปมีขนาดใหญ่กว่าการเปลี่ยนแปลงมาก ประเด็นของคุณถ้าคุณมีจะหายไปกับฉัน
Qwertie
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.