นับตั้งแต่ชั้นเรียนการเขียนโปรแกรมครั้งแรกของฉันในโรงเรียนมัธยมฉันได้ยินมาว่าการทำงานของสายอักขระนั้นช้ากว่า - มีค่าใช้จ่ายสูงกว่า "การปฏิบัติการโดยเฉลี่ย" ในตำนาน ทำไมจึงทำให้ช้า (คำถามนี้ปล่อยให้กว้างโดยเจตนา)
นับตั้งแต่ชั้นเรียนการเขียนโปรแกรมครั้งแรกของฉันในโรงเรียนมัธยมฉันได้ยินมาว่าการทำงานของสายอักขระนั้นช้ากว่า - มีค่าใช้จ่ายสูงกว่า "การปฏิบัติการโดยเฉลี่ย" ในตำนาน ทำไมจึงทำให้ช้า (คำถามนี้ปล่อยให้กว้างโดยเจตนา)
คำตอบ:
"การดำเนินการเฉลี่ย" เกิดขึ้นกับพื้นฐาน แต่แม้ในภาษาที่มีการใช้งานสตริงเป็นพื้นฐานพวกเขายังคงอยู่ภายใต้ประทุนและการทำทุกอย่างที่เกี่ยวข้องกับสตริงทั้งหมดต้องใช้เวลา O (N) โดยที่ N คือความยาวของสตริง
ตัวอย่างเช่นการเพิ่มตัวเลขสองหลักโดยทั่วไปจะใช้คำสั่ง 2-4 ASM การต่อสองสตริง ("เพิ่ม") จำเป็นต้องมีการจัดสรรหน่วยความจำใหม่และสำเนาหนึ่งหรือสองสตริงที่เกี่ยวข้องกับสตริงทั้งหมด
ปัจจัยทางภาษาบางอย่างสามารถทำให้แย่ลงได้ ใน C ตัวอย่างเช่นสตริงเป็นเพียงตัวชี้ไปยังอาร์เรย์ของอักขระที่สิ้นสุดด้วยค่า null ซึ่งหมายความว่าคุณไม่รู้ว่ามันอยู่นานแค่ไหนดังนั้นจึงไม่มีวิธีที่จะปรับห่วงการคัดลอกสตริงด้วยการดำเนินการย้ายอย่างรวดเร็ว คุณต้องคัดลอกทีละตัวเพื่อให้คุณสามารถทดสอบแต่ละไบต์สำหรับ null terminator
char*
, ไม่ใช่strbuf
, และคุณกลับไปที่ตาราง 1 มีเพียงคุณเท่านั้น สามารถทำเมื่อการออกแบบที่ไม่ดีถูกอบเป็นภาษา
buf
ตัวชี้อยู่ที่นั่น ฉันไม่เคยตั้งใจจะบอกเป็นนัยว่ามันไม่สามารถใช้ได้ ค่อนข้างว่ามันจำเป็น โค้ดใด ๆ ที่ไม่ทราบเกี่ยวกับประเภทสตริงที่ได้รับการปรับปรุง แต่ไม่ได้มาตรฐานรวมถึงสิ่งที่เป็นพื้นฐานของไลบรารี่มาตรฐานยังคงต้องถอยกลับช้าและไม่ปลอดภัย char*
คุณสามารถเรียก FUD นั้นได้ถ้าคุณต้องการ แต่นั่นก็ไม่ได้ทำให้มันไม่เป็นความจริง
นี่เป็นหัวข้อเก่าและฉันคิดว่าคำตอบอื่น ๆ นั้นยอดเยี่ยม แต่มองข้ามบางสิ่งดังนั้นนี่คือ 2 เซ็นต์ของฉัน
ปัญหาเกี่ยวกับสตริงคือพวกเขาเป็นพลเมืองชั้นสองในภาษาส่วนใหญ่และในความเป็นจริงส่วนใหญ่ไม่ได้เป็นส่วนหนึ่งของสเปคภาษาเอง: พวกมันเป็นสิ่งก่อสร้างที่ดำเนินการโดยห้องสมุดที่มีการเคลือบน้ำตาล syntactic เป็นครั้งคราว เพื่อลดความเจ็บปวดที่จะใช้
ผลที่ตามมาโดยตรงจากสิ่งนี้คือภาษาซ่อนความซับซ้อนส่วนใหญ่ไว้ห่างจากสายตาของคุณและคุณจ่ายผลข้างเคียงลับๆล่อๆเพราะคุณโตจนติดเป็นนิสัยในการพิจารณาพวกมันเหมือนเอนทิตี้อะตอมระดับต่ำเช่นเดียวกับ ประเภทดั้งเดิมอื่น ๆ (ตามที่อธิบายโดยคำตอบที่ได้รับคะแนนสูงสุดและอื่น ๆ )
หนึ่งในองค์ประกอบของ "ความซับซ้อน" พื้นฐานนี้คือการใช้งานสตริงส่วนใหญ่จะหันไปใช้โครงสร้างข้อมูลอย่างง่ายที่มีพื้นที่หน่วยความจำต่อเนื่องบางส่วนเพื่อเป็นตัวแทนสตริง: อาร์เรย์ ol ดีของคุณ
นี่เป็นเหตุผลที่ดีที่คุณต้องการให้คุณเข้าถึงสตริงโดยรวมได้อย่างรวดเร็ว แต่นั่นก็หมายถึงค่าใช้จ่ายที่น่ากลัวเมื่อคุณต้องการจัดการกับสตริงนี้ การเข้าถึงองค์ประกอบที่อยู่ตรงกลางอาจเร็วถ้าคุณรู้ว่าคุณเป็นดัชนีใด แต่การค้นหาองค์ประกอบตามเงื่อนไขนั้นไม่ใช่
แม้การส่งคืนขนาดของสตริงอาจมีค่าใช้จ่ายสูงหากภาษาของคุณไม่ได้เก็บข้อมูลความยาวของสตริงและจำเป็นต้องใช้มันเพื่อนับจำนวนอักขระ
ด้วยเหตุผลที่คล้ายคลึงกันการเพิ่มองค์ประกอบในสตริงของคุณจะพิสูจน์ได้ว่ามีค่าใช้จ่ายสูงเนื่องจากคุณอาจต้องจัดสรรหน่วยความจำใหม่เพื่อให้การดำเนินการนี้เกิดขึ้นอีก
ดังนั้นภาษาที่ต่างกันใช้แนวทางที่แตกต่างกันสำหรับปัญหาเหล่านี้ ตัวอย่างเช่น Java ใช้เสรีภาพในการสร้างสตริงที่ไม่เปลี่ยนรูปด้วยเหตุผลที่ถูกต้อง (ความยาวแคช, ความปลอดภัยของเธรด) และสำหรับคู่ที่ไม่แน่นอน (StringBuffer และ StringBuilder) จะเลือกจัดสรรขนาดโดยใช้ชิ้นขนาดใหญ่ที่ไม่จำเป็นต้องจัดสรร ทุกครั้ง แต่หวังว่าจะดีที่สุดสำหรับสถานการณ์กรณี โดยทั่วไปแล้วใช้งานได้ดี แต่ข้อเสียคือการจ่ายผลกระทบหน่วยความจำบางครั้ง
นอกจากนี้และอีกครั้งนี้เกิดจากความจริงที่ว่าการเคลือบน้ำตาล syntactic ของภาษาของคุณซ่อนไว้จากคุณที่จะเล่นที่ดีคุณมักจะไม่คิดว่ามันเป็นเงื่อนไขของการสนับสนุน Unicode (โดยเฉพาะตราบใดที่คุณไม่ต้องการมันจริงๆ และชนกำแพงนั้น) และบางภาษากำลังคิดไปข้างหน้าอย่าใช้สายอักขระกับอาเรย์พื้นฐาน 8 บิตแบบดั้งเดิม พวกเขาอบใน UTF-8 หรือ UTF-16 หรือสิ่งที่คุณมีให้การสนับสนุนและผลที่ตามมาคือการใช้หน่วยความจำขนาดใหญ่มากซึ่งมักจะไม่จำเป็นต้องใช้เวลาและเวลาประมวลผลขนาดใหญ่เพื่อจัดสรรหน่วยความจำประมวลผลสตริง และใช้ตรรกะทั้งหมดที่เกิดขึ้นควบคู่กับการจัดการจุดรหัส
ผลลัพธ์ของสิ่งนี้คือเมื่อคุณทำสิ่งที่เทียบเท่าในรหัสหลอกไปที่:
hello = "hello,"
world = " world!"
str = hello + world
มันอาจจะไม่ใช่ - แม้จะมีความพยายามอย่างเต็มที่ที่นักพัฒนาภาษาจะต้องทำตามที่คุณต้องการยกเว้น - เป็นเรื่องง่าย:
a = 1;
b = 2;
shouldBeThree = a + b
ในการติดตามคุณอาจต้องการอ่าน:
วลีที่ว่า "การดำเนินงานค่าเฉลี่ย" น่าจะเป็นชวเลขสำหรับการทำงานครั้งเดียวของทฤษฎีสุ่มการเข้าถึงที่จัดเก็บ-โปรแกรมเครื่อง นี่เป็นเครื่องจักรตามทฤษฎีที่ใช้ในการวิเคราะห์เวลาทำงานของอัลกอริทึมต่างๆ
โดยทั่วไปการดำเนินการทั่วไปจะถูกดำเนินการเพื่อโหลดเพิ่มลบจัดเก็บสาขา อาจจะอ่านพิมพ์และหยุด
แต่การดำเนินการสตริงส่วนใหญ่ต้องการการดำเนินงานพื้นฐานหลายอย่าง ตัวอย่างเช่นการทำสำเนาสตริงโดยปกติต้องใช้การคัดลอกและด้วยเหตุนี้จำนวนของการดำเนินการซึ่งเป็นสัดส่วนกับความยาวของสตริง (นั่นคือมัน "เชิงเส้น") การค้นหาสตริงย่อยภายในสตริงอื่นยังมีความซับซ้อนเชิงเส้น
มันขึ้นอยู่กับการดำเนินการทั้งหมดวิธีการแสดงสตริงและการเพิ่มประสิทธิภาพที่มีอยู่ หากสตริงมีความยาว 4 หรือ 8 ไบต์ (และจัดตำแหน่ง) พวกเขาจะไม่จำเป็นต้องช้าลง - การดำเนินการหลายอย่างจะเร็วพอ ๆ กัน หรือถ้าสตริงทั้งหมดมีแฮชแบบ 32- บิตหรือ 64- บิตการดำเนินการจำนวนมากก็จะเร็วเหมือนกัน (แม้ว่าคุณจะจ่ายค่าแฮชราคาล่วงหน้า)
นอกจากนี้ยังขึ้นอยู่กับสิ่งที่คุณหมายถึงโดย "ช้า" โปรแกรมส่วนใหญ่จะประมวลผลสตริงอย่างรวดเร็วสำหรับสิ่งที่จำเป็น การเปรียบเทียบสตริงอาจไม่เร็วเท่ากับการเปรียบเทียบสอง ints แต่การทำโปรไฟล์จะเปิดเผยว่า "ช้า" หมายถึงโปรแกรมของคุณ
ให้ฉันตอบคำถามของคุณด้วยคำถาม เหตุใดการพูดชุดคำศัพท์จึงใช้เวลานานกว่าคำเดียว