วิธีเพิ่มประสิทธิภาพฟังก์ชั่นระยะทาง?


23

ในขณะที่พัฒนาเกมคล้าย RTS ที่เรียบง่ายพอสมควรฉันสังเกตว่าการคำนวณระยะทางของฉันทำให้เกิดผลกระทบต่อประสิทธิภาพ

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

คำถามของฉันเกี่ยวกับเรื่องนั้นอย่างแน่นอน ฉันต้องการทราบว่าผู้พัฒนาเกมทางเลือกมีอะไรบ้างในการตรวจสอบระยะทางนอกเหนือจากวิธี sqrt (x * x + y * y) ตามปกติซึ่งใช้เวลาค่อนข้างนานหากเราทำการแสดงหลายพันครั้งต่อเฟรม

ฉันต้องการที่จะชี้ให้เห็นว่าฉันทราบระยะทางแมนฮัตตันและการเปรียบเทียบระยะทางกำลังสอง (โดยข้ามคอขวด sqrt) มีอะไรอีกไหม



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

คำตอบ:


26

TL; DR; ปัญหาของคุณไม่ได้เกิดจากฟังก์ชั่นระยะทาง ปัญหาของคุณกำลังทำฟังก์ชั่นระยะทางหลายครั้ง กล่าวอีกนัยหนึ่งคุณต้องมีการเพิ่มประสิทธิภาพอัลกอริทึมมากกว่าคณิตศาสตร์

[แก้ไข] ฉันลบส่วนแรกของคำตอบของฉันเพราะคนเกลียดมัน ชื่อคำถามได้ขอฟังก์ชั่นระยะทางอื่นก่อนการแก้ไข

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

ระยะทาง ^ 2 = x * x + y * y;

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

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

ในกรณีของคุณโชคไม่ดีที่เราอยู่ในคอมพิวเตอร์กราฟิกเกือบจะเกี่ยวข้องกับอวกาศแบบยุคลิดเท่านั้นและระยะทางนั้นถูกนิยามเหมือนกับSqrt of Vector dot itselfในอวกาศแบบยุคลิด

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

ดังนั้นคุณบอกว่าฉันไม่สามารถปรับฟังก์ชั่นระยะทางได้อย่างเหมาะสมฉันควรทำอย่างไร

ปัญหาของคุณไม่ได้เกิดจากฟังก์ชั่นระยะทาง ปัญหาของคุณกำลังทำฟังก์ชั่นระยะทางหลายครั้ง กล่าวอีกนัยหนึ่งคุณต้องมีการเพิ่มประสิทธิภาพอัลกอริทึมมากกว่าคณิตศาสตร์

จุดคือแทนที่จะตรวจสอบจุดตัดของผู้เล่นกับแต่ละวัตถุในฉากแต่ละเฟรม คุณสามารถใช้การเชื่อมโยงเชิงพื้นที่กับข้อได้เปรียบของคุณได้อย่างง่ายดายและตรวจสอบเฉพาะวัตถุที่อยู่ใกล้ผู้เล่น (ซึ่งมีแนวโน้มที่จะตี / ตัดกัน)

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

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

วิธีที่ซับซ้อนกว่านี้คือการใช้ BSP หรือ Octrees


2
ฉันเชื่อว่าประโยคสุดท้ายของคำถามที่ว่า OP กำลังมองหาทางเลือกอื่น ๆ (พวกเขารู้เกี่ยวกับการใช้ระยะทางกำลังสอง)
MichaelHouse

@ Byte56 ใช่คุณถูกต้องฉันไม่ได้อ่าน
concept3d

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

@Grimshaw ฉันแก้ไขคำตอบเพื่อแก้ไขปัญหาเดิม
concept3d

@ Byte56 ขอบคุณสำหรับการชี้ให้เห็น ฉันแก้ไขคำตอบ
concept3d

29

หากคุณต้องการบางสิ่งบางอย่างที่ยังคงเป็นเส้นตรงในระยะทางใด ๆ (ไม่เหมือนdistance^2) และยังปรากฏเป็นวงกลมอย่างชัดเจน (ไม่เหมือนกับ Chebyshev และระยะทางเหมือนแมนฮัตตันเพชร) คุณสามารถเฉลี่ยสองเทคนิคหลังเพื่อให้ได้ระยะทางประมาณรูปแปดเหลี่ยม:

dx = abs(x1 - x0)
dy = abs(y1 - y0)

dist = 0.5 * (dx + dy + max(dx, dy))

นี่คือการสร้างภาพ (โครงร่างของเส้นโค้ง) ของฟังก์ชันขอบคุณWolfram Alpha :

Contour Contour

และนี่คือพล็อตของฟังก์ชั่นข้อผิดพลาดของมันเมื่อเปรียบเทียบกับระยะทางแบบยุคลิด (เรเดียน, จตุภาคแรกเท่านั้น):

เกิดข้อผิดพลาด

อย่างที่คุณเห็นข้อผิดพลาดอยู่ในช่วงตั้งแต่ 0% ของแกนจนถึงประมาณ + 12% ในกลีบ ด้วยการปรับเปลี่ยนค่าสัมประสิทธิ์เล็กน้อยเราสามารถทำให้มันเป็น +/- 4%:

dist = 0.4 * (dx + dy) + 0.56 * max(dx, dy)

ป้อนคำอธิบายรูปภาพที่นี่

ปรับปรุง

เมื่อใช้ค่าสัมประสิทธิ์ข้างต้นความผิดพลาดสูงสุดจะอยู่ภายใน +/- 4% แต่ข้อผิดพลาดเฉลี่ยจะยังคงอยู่ที่ + 1.3% ปรับให้เหมาะสมสำหรับข้อผิดพลาดเฉลี่ยที่เป็นศูนย์คุณสามารถใช้:

dist = 0.394 * (dx + dy) + 0.554 * max(dx, dy)

ซึ่งให้ข้อผิดพลาดระหว่าง -5% ถึง + 3% และข้อผิดพลาดเฉลี่ยที่ + 0.043%


ขณะค้นหาชื่ออัลกอริทึมนี้บนเว็บฉันพบการประมาณแปดเหลี่ยมที่คล้ายกันนี้ :

dist = 1007/1024 * max(dx, dy) + 441/1024 * min(dx, dy)

โปรดทราบว่านี้จะเทียบเท่าหลัก (แม้ว่าเลขยกกำลังที่มีแตกต่างกัน - คนเหล่านี้ให้ความผิดพลาด -1.5% ถึง 7.5% แต่ก็สามารถจะนวดเพื่อ +/- 4%) max(dx, dy) + min(dx, dy) == dx + dyเพราะ การใช้แบบฟอร์มนี้minและการmaxโทรสามารถถูกแยกออกจากกันโดยชอบ:

if (dy > dx)
    swap(dx, dy)

dist = 1007/1024 * dx + 441/1024 * dy

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


3
น่าสนใจยังไม่เคยเห็นมาก่อน! มันมีชื่อหรือเพียงแค่ "ค่าเฉลี่ยของ Chebyshev และแมนฮัตตัน"?
congusbongus

@congusbongus มันอาจมีชื่อ แต่ฉันไม่รู้ว่ามันคืออะไร ถ้าไม่ได้บางทีวันหนึ่งมันจะถูกเรียกว่าศาสนจักรระยะทาง (ฮะ ... อาจจะไม่ได้)
bcrist

1
โปรดทราบว่าการคูณทศนิยมนั้นไม่มีประสิทธิภาพมาก นั่นเป็นเหตุผลที่การประมาณแบบอื่นใช้ 1007/1024 (ซึ่งจะนำมาใช้เป็นการคูณจำนวนเต็มแล้วตามด้วยการเลื่อนบิต)
MSalters

@MSalters ใช่การดำเนินการจุดลอยมักจะช้ากว่าการดำเนินการจำนวนเต็ม แต่นั่นไม่เกี่ยวข้อง - 0.4 และ 0.56 สามารถแปลงได้อย่างง่ายดายเพื่อใช้การดำเนินการจำนวนเต็ม นอกจากนี้บนฮาร์ดแวร์ x86 ที่ทันสมัยที่สุดในการดำเนินงานจุดลอย (นอกเหนือจากFDIV, FSQRTและฟังก์ชั่นยอดเยี่ยมอื่น ๆ ) ค่าใช้จ่ายหลักเช่นเดียวกับรุ่นจำนวนเต็มของพวกเขา: 1 หรือ 2 รอบต่อการเรียนการสอน
bcrist

1
สิ่งนี้ดูคล้ายกับ Alpha max + เบต้าขั้นต่ำ: en.wikipedia.org/wiki/Alpha_max_plus_beta_min_algorithm
drake7707

21

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

ในขนาดใหญ่โลกของเกมที่มีหลายนักแสดงก็คือเข้าไปไม่ได้ที่จะให้การตรวจสอบระยะทางระหว่างหนึ่งในนักแสดงและคนอื่น ๆ ทั้งหมด ในฐานะที่เป็นผู้เล่นมากขึ้น, NPCs และขีปนาวุธเข้าสู่โลกจำนวนรถที่จะต้องมีการทำจะเติบโตquadraticallyO(N^2)กับ

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

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

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

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

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

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

(ฉันรู้ว่านี่ไม่ใช่คำตอบที่ตรงกับคำถาม แต่อาจเป็นประโยชน์สำหรับผู้อ่านบางคนขอโทษถ้าฉันเสียเวลา!)


7
นี่คือคำตอบที่ดีที่สุด ไม่มีสิ่งใดที่จะปรับให้เหมาะสมในฟังก์ชั่นระยะทาง หนึ่งก็ต้องใช้มันบ่อยครั้ง
sam hocevar

3
คำตอบที่ได้รับการยอมรับยังครอบคลุมถึงการแบ่งเชิงพื้นที่มิฉะนั้นคำตอบของคุณเหมาะสมที่สุด ขอขอบคุณ.
Grimshaw

เวลาของฉันใช้เวลาในการอ่านคำตอบของคุณเป็นอย่างดี ขอบคุณ Joey
Patrick M

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

คุณช่วยอธิบายได้ไหมว่าทำไมจำนวนการเปรียบเทียบจึงเป็นเลขยกกำลัง ฉันแม้ว่ามันจะเป็นกำลังสองการเปรียบเทียบนักแสดงแต่ละคนด้วยกันในแต่ละช่วงเวลา
Petr Pudlák

4

วิธีการเกี่ยวกับระยะทาง Chebyshev สำหรับคะแนน p, q จะถูกกำหนดดังนี้:

ระยะทาง

ดังนั้นสำหรับคะแนน (2, 4) และ (8, 5) ระยะทาง Chebyshev คือ 6, เป็น | 2-8 | > | 4-5 |

นอกจากนี้ให้ E เป็นระยะ Euclidean และ C เป็นระยะทาง Chebyshev แล้ว:

distance2

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

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


1
นอกจากนี้คุณยังสามารถใช้ระยะทางแมนฮัตตันเป็นส่วนบนได้
congusbongus

1
จริงพอ ฉันคิดว่าจากที่นั่นเพียงกระโดดข้ามและกระโดดไปที่ "ค่าเฉลี่ยของ Chebyshev และแมนฮัตตัน" ตามที่แนะนำโดย bcrist
Tetrinity

2

การปรับให้เหมาะสมที่สุดในท้องถิ่นง่ายมากคือการตรวจสอบเพียงมิติเดียวก่อน

นั่นคือ :

distance ( x1, y1 , x1, y2) > fabs (x2 - x1)

ดังนั้นเพียงแค่ตรวจสอบ fabs (x2 - x1)เป็นตัวกรองแรกอาจให้ผลกำไรที่ประเมินได้ จะขึ้นอยู่กับขนาดของโลกเมื่อเทียบกับช่วงที่เกี่ยวข้อง

นอกจากนี้คุณสามารถใช้สิ่งนี้เป็นทางเลือกแทนโครงสร้างข้อมูลการแบ่งพาร์ติชันเชิงพื้นที่

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


2

sqrtความพยายามที่ได้ทำในอดีตที่ผ่านมาเพื่อเพิ่มประสิทธิภาพ แม้ว่าจะไม่ได้ใช้กับเครื่องของวันนี้อีกต่อไป แต่นี่คือตัวอย่างจากซอร์สโค้ด Quake ซึ่งใช้หมายเลขเวทย์มนตร์ 0x5f3759df :

float Q_rsqrt( float number )
{
  long i;
  float x2, y;
  const float threehalfs = 1.5F;

  x2 = number * 0.5F;
  y  = number;
  i  = * ( long * ) &y;  // evil floating point bit level hacking
  i  = 0x5f3759df - ( i >> 1 ); // what the hell?
  y  = * ( float * ) &i;
  y  = y * ( threehalfs - ( x2 * y * y ) ); // 1st iteration
  // y  = y * ( threehalfs - ( x2 * y * y ) ); // 2nd iteration (optional)
  // ...
  return y;
}

คำอธิบายรายละเอียดของสิ่งที่เกิดขึ้นที่นี่สามารถพบได้ในวิกิพีเดีย

กล่าวโดยสรุปคือวิธีการทำซ้ำสองสามครั้งของนิวตัน (อัลกอริธึมเชิงตัวเลขที่ปรับปรุงการประมาณการซ้ำ ๆ ) ด้วยหมายเลขเวทย์มนตร์ที่ใช้ในการประเมินเบื้องต้นอย่างสมเหตุสมผล

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


2
นี่ไม่ใช่การเพิ่มประสิทธิภาพที่คุ้มค่าอีกต่อไป สถาปัตยกรรมพีซีระดับผู้บริโภคเกือบทั้งหมดที่คุณสามารถซื้อได้ในปัจจุบันมีคำแนะนำ sqrt ที่ปรับแต่งด้วยฮาร์ดแวร์ซึ่งทำรากที่สองในรอบสัญญาณนาฬิกาหรือน้อยกว่า หากคุณต้องการ sqrt ที่เร็วที่สุดเท่าที่จะเป็นไปได้คุณใช้คำสั่ง x86 simd floating point sqrt: en.wikipedia.org/wiki/ ...... สำหรับสิ่งต่าง ๆ เช่น shaders บน GPU การเรียก sqrt จะส่งผลในการสอนโดยอัตโนมัติ บน CPU ผมคิดว่าคอมไพเลอร์หลาย ๆ ตัวใช้ sqrt ผ่าน SIMD sqrt ถ้ามี
TravisG

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