strlen จะถูกคำนวณหลายครั้งหากใช้ในเงื่อนไขการวนซ้ำ?


109

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

for (int i = 0; i < strlen(ss); ++i)
{
    // blabla
}

จะstrlen()คำนวณทุกครั้งเมื่อiเพิ่มขึ้น?


14
ฉันจะเดาว่าหากไม่มีการเพิ่มประสิทธิภาพที่ซับซ้อนซึ่งสามารถตรวจจับได้ว่า 'ss' ไม่เคยเปลี่ยนแปลงในลูปใช่แล้ว รวบรวมได้ดีที่สุดและดูที่การประกอบเพื่อดู
MerickOWA

6
ขึ้นอยู่กับคอมไพเลอร์ในระดับการเพิ่มประสิทธิภาพและสิ่งที่คุณ (อาจ) ทำssในลูป
Hristo Iliev

4
หากคอมไพลเลอร์สามารถพิสูจน์ได้ว่าssไม่มีการแก้ไขก็สามารถดึงการคำนวณออกจากลูปได้
Daniel Fischer

10
@ ไมค์: "ต้องการการวิเคราะห์เวลาคอมไพล์ของสิ่งที่ strlen ทำ" - strlen น่าจะเป็นสิ่งที่อยู่ภายในซึ่งในกรณีนี้เครื่องมือเพิ่มประสิทธิภาพจะรู้ว่ามันทำอะไร
Steve Jessop

3
@MikeSeymour: ไม่มีบางทีอาจจะไม่ strlen ถูกกำหนดโดยมาตรฐานภาษา C และชื่อของมันถูกสงวนไว้สำหรับการใช้งานที่กำหนดโดยภาษาดังนั้นโปรแกรมจึงไม่มีอิสระที่จะให้คำจำกัดความอื่น คอมไพลเลอร์และเครื่องมือเพิ่มประสิทธิภาพมีสิทธิ์ที่จะถือว่า strlen ขึ้นอยู่กับอินพุตของมันเท่านั้นและไม่ได้แก้ไขมันหรือสถานะส่วนกลางใด ๆ ความท้าทายในการปรับให้เหมาะสมที่นี่คือการกำหนดว่าหน่วยความจำที่ชี้โดย ss จะไม่ถูกเปลี่ยนแปลงโดยรหัสใด ๆ ภายในลูป ซึ่งเป็นไปได้ทั้งหมดกับคอมไพเลอร์ปัจจุบันขึ้นอยู่กับรหัสเฉพาะ
Eric Postpischil

คำตอบ:


138

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

ฉันจะทำสิ่งที่ชอบ

for (int i = 0, n = strlen(ss); i < n; ++i)

หรืออาจเป็นไปได้

for (int i = 0; ss[i]; ++i)

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


14
หากคุณรู้ว่าคุณไม่ได้จัดการสตริงข้อที่สองนั้นดีกว่ามากเนื่องจากเป็นห่วงที่จะดำเนินการstrlenต่อไป
mlibby

26
@alk: หากสตริงอาจสั้นลงแสดงว่าทั้งสองอย่างนี้ผิด
Mike Seymour

3
@alk: หากคุณกำลังเปลี่ยนสตริง a for loop อาจไม่ใช่วิธีที่ดีที่สุดในการวนซ้ำอักขระ ฉันคิดว่า while loop นั้นตรงและง่ายกว่าในการจัดการตัวนับดัชนี
mlibby

2
สถานการณ์ในอุดมคติได้แก่ การคอมไพล์ด้วย GCC ภายใต้ linux ซึ่งstrlenถูกทำเครื่องหมายว่า__attribute__((pure))อนุญาตให้คอมไพเลอร์สามารถกำหนดสายได้หลายสาย คุณสมบัติ GCC
David Rodríguez - dribeas

6
เวอร์ชันที่สองเป็นรูปแบบที่เหมาะและเป็นสำนวนมากที่สุด ช่วยให้คุณส่งผ่านสตริงได้เพียงครั้งเดียวแทนที่จะเป็นสองครั้งซึ่งจะมีประสิทธิภาพที่ดีกว่ามาก (โดยเฉพาะการเชื่อมโยงกันของแคช) สำหรับสตริงที่ยาว
R .. GitHub STOP HELPING ICE

14

ใช่ทุกครั้งที่คุณใช้ลูป จากนั้นทุกครั้งจะคำนวณความยาวของสตริง ดังนั้นใช้มันดังนี้:

char str[30];
for ( int i = 0; str[i] != '\0'; i++)
{
//Something;
}

ในโค้ดด้านบนstr[i]จะตรวจสอบเฉพาะอักขระหนึ่งตัวในสตริงที่ตำแหน่งiแต่ละครั้งที่ลูปเริ่มรอบดังนั้นจึงใช้หน่วยความจำน้อยลงและมีประสิทธิภาพมากขึ้น

ดูลิงค์นี้สำหรับข้อมูลเพิ่มเติม

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

char str[];
for ( int i = 0; i < strlen(str); i++)
{
//Something;
}

3
ฉันเห็นด้วยกับ "[มัน] มีประสิทธิภาพมากกว่า" แต่ใช้หน่วยความจำน้อยลง? ความแตกต่างในการใช้หน่วยความจำเพียงอย่างเดียวที่ฉันคิดได้คือใน call stack ระหว่างการstrlenโทรและถ้าคุณใช้งานแน่นขนาดนั้นคุณอาจกำลังคิดที่จะหลีกเลี่ยงการเรียกใช้ฟังก์ชันอื่น ๆ ด้วย ...
CVn

@ MichaelKjörlingถ้าคุณใช้ "strlen" ในการวนซ้ำจะต้องสแกนสตริงทั้งหมดทุกครั้งที่ลูปทำงานในขณะที่โค้ดด้านบน "str [ix]" จะสแกนเพียงองค์ประกอบเดียวในแต่ละรอบของ ลูปซึ่งแสดงตำแหน่งด้วย "ix" ดังนั้นจึงใช้หน่วยความจำน้อยกว่า "strlen"
codeDEXTER

1
ฉันไม่แน่ใจว่ามันสมเหตุสมผลมากที่จริง การใช้งาน strlen ที่ไร้เดียงสามากจะเป็นสิ่งint strlen(char *s) { int len = 0; while(s[len] != '\0') len++; return len; }ที่ค่อนข้างตรงกับสิ่งที่คุณทำในโค้ดในคำตอบของคุณ ฉันไม่ได้เถียงว่าการวนซ้ำสตริงหนึ่งครั้งมากกว่าสองครั้งนั้นมีประสิทธิภาพในการใช้เวลามากกว่าแต่ฉันไม่เห็นว่าอย่างใดอย่างหนึ่งใช้หน่วยความจำมากหรือน้อย หรือคุณกำลังอ้างถึงตัวแปรที่ใช้ในการเก็บความยาวสตริง?
CVn

@ MichaelKjörlingโปรดดูรหัสที่แก้ไขด้านบนและลิงค์ และสำหรับหน่วยความจำ - ทุกครั้งที่ลูปทำงานแต่ละค่าที่วนซ้ำจะถูกเก็บไว้ในหน่วยความจำและในกรณีของ "strlen" เมื่อนับสตริงทั้งหมดซ้ำแล้วซ้ำอีกก็ต้องใช้หน่วยความจำมากขึ้นในการจัดเก็บ และเนื่องจากไม่เหมือนกับ Java, C ++ ไม่มี "Garbage Collector" แล้วฉันก็ผิดได้เช่นกัน ดูลิงก์เกี่ยวกับการไม่มี "Garbage Collector" ใน C ++
codeDEXTER

1
@ aashis2s การไม่มีตัวเก็บขยะจะมีบทบาทเฉพาะเมื่อสร้างวัตถุบนฮีป วัตถุบนสแต็กจะถูกทำลายทันทีที่ขอบเขตและสิ้นสุดลง
Ikke

9

คอมไพเลอร์ที่ดีอาจไม่คำนวณทุกครั้ง แต่ฉันไม่คิดว่าคุณจะแน่ใจได้ว่าคอมไพเลอร์ทุกตัวทำอย่างนั้น

นอกเหนือจากนั้นคอมไพเลอร์ต้องรู้ว่าstrlen(ss)ไม่เปลี่ยนแปลง นี่จะเป็นจริงssก็ต่อเมื่อไม่มีการเปลี่ยนforลูป

ตัวอย่างเช่นถ้าคุณใช้ฟังก์ชันแบบอ่านอย่างเดียวssในforลูป แต่ไม่ประกาศss-parameter เป็นconstคอมไพลเลอร์จะไม่รู้ด้วยซ้ำว่าssไม่มีการเปลี่ยนแปลงในลูปและต้องคำนวณstrlen(ss)ในการวนซ้ำทุกครั้ง


3
+1: ไม่เพียง แต่ต้องssไม่เปลี่ยนแปลงในforลูปเท่านั้น ต้องไม่สามารถเข้าถึงและเปลี่ยนแปลงโดยฟังก์ชันใด ๆ ที่เรียกว่าในลูป (ไม่ว่าจะเป็นเพราะส่งผ่านเป็นอาร์กิวเมนต์หรือเนื่องจากเป็นตัวแปรส่วนกลางหรือตัวแปรขอบเขตไฟล์) Const-qualification อาจเป็นปัจจัยด้วยเช่นกัน
Jonathan Leffler

4
ฉันคิดว่าไม่น่าเป็นไปได้มากที่คอมไพเลอร์จะรู้ว่า 'ss' ไม่เปลี่ยนแปลง อาจมีพอยน์เตอร์หลงทางที่ชี้ไปที่หน่วยความจำภายใน 'ss' ซึ่งคอมไพเลอร์ไม่รู้ว่าจะเปลี่ยน 'ss' ได้
MerickOWA

โจนาธานพูดถูกสตริง const ในเครื่องอาจเป็นวิธีเดียวที่คอมไพเลอร์จะมั่นใจได้ว่าไม่มีทางที่ 'ss' จะเปลี่ยนแปลงได้
MerickOWA

2
@MerickOWA: นั่นคือหนึ่งในสิ่งที่restrictมีไว้สำหรับ C99
Steve Jessop

4
เกี่ยวกับ para สุดท้ายของคุณ: ถ้าคุณเรียกใช้ฟังก์ชันแบบอ่านอย่างเดียวssใน for-loop แม้ว่าจะมีการประกาศพารามิเตอร์const char*แต่คอมไพเลอร์ก็ยังคงต้องคำนวณความยาวใหม่เว้นแต่ว่า (a) จะรู้ว่าssชี้ไปยังวัตถุ const ซึ่งตรงข้ามกับการเป็นเพียงตัวชี้ไปยัง const หรือ (b) มันสามารถแทรกในฟังก์ชันหรือเห็นว่าเป็นแบบอ่านอย่างเดียว การใช้const char*พารามิเตอร์ไม่ได้เป็นการสัญญาว่าจะไม่แก้ไขข้อมูลที่ชี้ไปเนื่องจากสามารถส่งchar*และแก้ไขได้โดยที่อ็อบเจ็กต์ที่แก้ไขไม่ใช่ const และไม่ใช่สตริงลิเทอรัล
Steve Jessop

4

หากssเป็นประเภทconst char *และคุณไม่ได้ส่งconstness ออกไปภายในลูปคอมไพลเลอร์อาจเรียกใช้เพียงstrlenครั้งเดียวหากเปิดการเพิ่มประสิทธิภาพไว้ แต่นี่ไม่ใช่พฤติกรรมที่สามารถนับได้อย่างแน่นอน

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

for( auto i = strlen(s); i > 0; --i ) {
  // do whatever
  // remember value of s[strlen(s)] is the terminating NULL character
}

1
เป็นความผิดพลาดในการโทรstrlenเลย เพียงแค่วนซ้ำจนกว่าคุณจะถึงจุดสิ้นสุด
R .. GitHub STOP HELPING ICE

i > 0เหรอ? นั่นควรจะไม่อยู่i >= 0ที่นี่หรือ? โดยส่วนตัวแล้วฉันจะเริ่มต้นที่strlen(s) - 1หากทำซ้ำข้ามสตริงไปข้างหลังการยกเลิก\0ไม่จำเป็นต้องพิจารณาเป็นพิเศษ
CVn

2
@ MichaelKjörlingจะใช้i >= 0งานได้ก็ต่อเมื่อคุณเริ่มต้นstrlen(s) - 1แต่ถ้าคุณมีสตริงที่มีความยาวเป็นศูนย์ค่าเริ่มต้นจะต่ำกว่า
Praetorian

@ Prætorianจุดที่ดีในสตริงความยาวเป็นศูนย์ ฉันไม่ได้พิจารณากรณีนั้นเมื่อฉันเขียนความคิดเห็นของฉัน C ++ ประเมินi > 0นิพจน์ในรายการลูปเริ่มต้นหรือไม่ ถ้าไม่เป็นเช่นนั้นคุณพูดถูกกรณีความยาวเป็นศูนย์จะทำลายลูปแน่นอน ถ้ามันไม่คุณ "เพียงแค่" ได้รับการลงนามi== -1 <0 i >= 0จึงไม่มีรายการห่วงถ้ามีเงื่อนไข
CVn

@ MichaelKjörlingใช่เงื่อนไขการออกจะได้รับการประเมินก่อนที่จะดำเนินการวนซ้ำเป็นครั้งแรก strlenประเภทการส่งคืนของไม่ได้ลงนามดังนั้นจึง(strlen(s)-1) >= 0ประเมินเป็นจริงสำหรับสตริงที่มีความยาวเป็นศูนย์
Praetorian

3

ใช่อย่างเป็นทางการstrlen()คาดว่าจะถูกเรียกให้ทำซ้ำทุกครั้ง

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


3

รหัสเพรดิเคตทั้งหมดจะถูกเรียกใช้งานทุกครั้งที่forวนซ้ำ เพื่อที่จะจดจำผลลัพธ์ของการstrlen(ss)เรียกคอมไพเลอร์จะต้องรู้อย่างน้อยที่สุด

  1. ฟังก์ชั่นstrlenนี้ไม่มีผลข้างเคียง
  2. หน่วยความจำที่ชี้โดยssจะไม่เปลี่ยนแปลงตลอดระยะเวลาของการวนซ้ำ

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


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

@GManNickG พิสูจน์ได้แน่นอน # 1 แต่ # 2 ยากกว่า สำหรับเธรดเดียวใช่มันสามารถพิสูจน์ได้อย่างแน่นอน แต่ไม่ใช่สำหรับสภาพแวดล้อมแบบมัลติเธรด
JaredPar

1
บางทีฉันอาจจะดื้อ แต่ฉันคิดว่าหมายเลขสองเป็นไปได้ในสภาพแวดล้อมแบบมัลติเธรดด้วยเช่นกัน แต่ก็ไม่แน่นอนหากไม่มีระบบการอนุมานที่แข็งแกร่ง แค่นึกถึงที่นี่; เกินขอบเขตของคอมไพเลอร์ C ++ ปัจจุบันแน่นอน
GManNickG

@GManNickG ฉันไม่คิดว่าจะเป็นไปได้แม้ว่าใน C / C ++ ฉันสามารถซ่อนที่อยู่ของssเป็น a size_tหรือแบ่งออกเป็นหลายbyteค่าได้อย่างง่ายดาย ssด้ายคดเคี้ยวของฉันทำได้แล้วก็เขียนไบต์ลงในที่อยู่ที่และคอมไพเลอร์จะมีวิธีรู้ของการทำความเข้าใจว่ามันเกี่ยวข้องกับ
JaredPar

1
@JaredPar: ขออภัยที่ต้องดำเนินการคุณสามารถอ้างว่าint a = 0; do_something(); printf("%d",a);ไม่สามารถปรับให้เหมาะสมได้บนพื้นฐานที่do_something()สามารถทำสิ่ง int ที่ไม่ได้เริ่มต้นของคุณหรืออาจรวบรวมข้อมูลสำรองสแต็กและแก้ไขaโดยเจตนา ในความเป็นจริง gcc 4.5 ปรับให้เหมาะสมdo_something(); printf("%d",0);กับ -O3
Steve Jessop

2

ครับ . strlen จะคำนวณทุกครั้งเมื่อฉันเพิ่มขึ้น

หากคุณไม่ได้เปลี่ยน ssด้วยในลูปหมายความว่าจะไม่มีผลต่อตรรกะมิฉะนั้นจะมีผล

การใช้รหัสต่อไปนี้ปลอดภัยกว่า

int length = strlen(ss);

for ( int i = 0; i < length ; ++ i )
{
 // blabla
}

2

ใช่strlen(ss)จะคำนวณความยาวในการวนซ้ำแต่ละครั้ง หากคุณเพิ่มขึ้นssโดยวิธีใดวิธีหนึ่งและยังเพิ่มi; จะมีการวนซ้ำไม่สิ้นสุด


2

ใช่strlen()ฟังก์ชันจะถูกเรียกทุกครั้งที่มีการประเมินลูป

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

คุณสามารถใช้รหัสดังต่อไปนี้:

String str="ss";
int l = strlen(str);

for ( int i = 0; i < l ; i++ )
{
    // blablabla
}


2

ไม่ธรรมดาในปัจจุบัน แต่เมื่อ 20 ปีที่แล้วบนแพลตฟอร์ม 16 บิตฉันขอแนะนำสิ่งนี้:

for ( char* p = str; *p; p++ ) { /* ... */ }

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


1

ใช่. การทดสอบไม่ทราบว่า ss ไม่ได้รับการเปลี่ยนแปลงภายในลูป ถ้าคุณรู้ว่ามันจะไม่เปลี่ยนแปลงฉันจะเขียนว่า:

int stringLength = strlen (ss); 
for ( int i = 0; i < stringLength; ++ i ) 
{
  // blabla 
} 

1

อ๊ะมันจะแม้ภายใต้สถานการณ์ที่ดีเยี่ยม!

ณ วันนี้ (มกราคม 2018) และ gcc 7.3 และ clang 5.0 หากคุณรวบรวม:

#include <string.h>

void bar(char c);

void foo(const char* __restrict__ ss) 
{
    for (int i = 0; i < strlen(ss); ++i) 
    {
        bar(*ss);
    }
}    

ดังนั้นเรามี:

  • ss เป็นตัวชี้ค่าคงที่
  • ss ถูกทำเครื่องหมาย __restrict__
  • เนื้อลูปไม่สามารถสัมผัสกับหน่วยความจำที่ชี้ไปด้วยวิธีใด ๆss(ดีเว้นแต่จะละเมิด__restrict__)

และยังคงมีทั้งคอมไพเลอร์รันทุกย้ำเดียวของวงว่าstrlen() น่าอัศจรรย์.

นอกจากนี้ยังหมายถึงการพาดพิง / ความคิดปรารถนาของ @Praetorian และ @JaredPar ไม่ได้หลุดออกไป


0

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


0

ใช่.

strlen()คำนวณทุกครั้งเมื่อiเพิ่มขึ้นและไม่ได้รับการปรับให้เหมาะสม

strlen()ด้านล่างนี้แสดงให้เห็นว่าทำไมรหัสคอมไพเลอร์ไม่ควรเพิ่มประสิทธิภาพ

for ( int i = 0; i < strlen(ss); ++i )
{
   // Change ss string.
   ss[i] = 'a'; // Compiler should not optimize strlen().
}

ฉันคิดว่าการทำการปรับเปลี่ยนเฉพาะนั้นไม่เคยเปลี่ยนความยาวของ ss เพียงแค่เนื้อหาดังนั้นคอมไพเลอร์ (ช่างฉลาดจริงๆ) ยังคงสามารถปรับให้เหมาะสมstrlenได้
Darren Cook

0

เราสามารถทดสอบได้อย่างง่ายดาย:

char nums[] = "0123456789";
size_t end;
int i;
for( i=0, end=strlen(nums); i<strlen(nums); i++ ) {
    putchar( nums[i] );
    num[--end] = 0;
}

เงื่อนไขการวนซ้ำจะประเมินหลังจากการทำซ้ำแต่ละครั้งก่อนที่จะเริ่มการวนซ้ำ

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


0

ฉันสังเกตเห็นว่ามีคนบอกว่ามันได้รับการปรับให้เหมาะสมโดยค่าเริ่มต้นโดยคอมไพเลอร์สมัยใหม่ที่ "ฉลาด" โดยดูที่ผลลัพธ์โดยไม่ต้องปรับให้เหมาะสม ฉันพยายาม:
รหัส C ขั้นต่ำ:

#include <stdio.h>
#include <string.h>

int main()
{
 char *s="aaaa";

 for (int i=0; i<strlen(s);i++)
  printf ("a");
 return 0;
}

คอมไพเลอร์ของฉัน: g ++ (Ubuntu / Linaro 4.6.3-1ubuntu5) 4.6.3
คำสั่งสำหรับการสร้างรหัสแอสเซมบลี: g ++ -S -masm = intel test.cpp

Gotten assembly code at the output:
    ...
    L3:
mov DWORD PTR [esp], 97
call    putchar
add DWORD PTR [esp+40], 1
    .L2:
     THIS LOOP IS HERE
    **<b>mov    ebx, DWORD PTR [esp+40]
mov eax, DWORD PTR [esp+44]
mov DWORD PTR [esp+28], -1
mov edx, eax
mov eax, 0
mov ecx, DWORD PTR [esp+28]
mov edi, edx
repnz scasb</b>**
     AS YOU CAN SEE it's done every time
mov eax, ecx
not eax
sub eax, 1
cmp ebx, eax
setb    al
test    al, al
jne .L3
mov eax, 0
     .....

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

0

การอธิบายเกี่ยวกับคำตอบของPrætorianฉันขอแนะนำสิ่งต่อไปนี้:

for( auto i = strlen(s)-1; i > 0; --i ) {foo(s[i-1];}
  • autoเพราะคุณไม่ต้องการสนใจว่า strlen จะส่งคืนแบบไหน คอมไพเลอร์ C ++ 11 (เช่นgcc -std=c++0xC ++ 11 ไม่สมบูรณ์ แต่ประเภทอัตโนมัติทำงานได้) จะทำเช่นนั้นให้คุณ
  • i = strlen(s)เพราะคุณต้องการเปรียบเทียบกับ0(ดูด้านล่าง)
  • i > 0 เนื่องจากการเปรียบเทียบกับ 0 นั้นเร็วกว่า (เล็กน้อย) เมื่อเปรียบเทียบกับตัวเลขอื่น ๆ

ข้อเสียคือคุณต้องใช้i-1เพื่อเข้าถึงอักขระสตริง

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