MSVC สร้างไบนารีแปลก/ช้าสำหรับการคูณและการหารบางอย่าง

ฉันใช้ MSVC 2010 SP1 และฉันมีรหัส C++ บรรทัดต่อไปนี้:

int32_t c = (int64_t(a)*int64_t(b))>>2;

เมื่อ a และ b ไม่ใช่ค่าคงที่ MSVC จะสร้างคำสั่ง 32 บิต imul และ shrd อย่างถูกต้อง แต่เมื่อ a หรือ b เป็นค่าคงที่ มันจะทำให้เกิดการเรียกไปที่ _allmull แทนที่จะเป็นคำสั่ง imul มีเหตุผลอะไรบ้างสำหรับเรื่องนี้? ฉันจะบังคับ/ชี้แนะให้สร้างโค้ดที่ดีอยู่เสมอได้อย่างไร สิ่งที่กวนใจฉันคือเหตุใดจึงสร้างโค้ดที่แย่ลงเมื่อมีข้อมูลเวลาในการคอมไพล์มากกว่า ฉันพบว่าฟังก์ชัน _allmull ทำการคูณ 64 บิต แต่ฉันคิดว่ามันไม่จำเป็นในกรณีนี้

ฉันสังเกตเห็นด้วยว่าสำหรับบรรทัด int32_t c = (int64_t(a)*int64_t(b))/4; มันยังสร้าง _alldiv สำหรับการหารด้วย 4 ด้วยซ้ำ

แก้ไข: ดูเหมือนว่าจะเป็นข้อผิดพลาดของคอมไพเลอร์ ฉันได้กรอกข้อผิดพลาด รายงาน.


person Juraj Blaho    schedule 06.04.2011    source แหล่งที่มา
comment
ทำไมคุณถึงใช้ int64_t ถ้าคุณรู้ว่ามันไม่จำเป็น   -  person Erik    schedule 06.04.2011
comment
ความหมายของจำนวนเต็มที่ลงนามและไม่ได้ลงนามแตกต่างกัน จะเกิดอะไรขึ้นถ้าคุณใช้ uint32_t และ uint64_t?   -  person Alexandre C.    schedule 06.04.2011
comment
@Erik: บนโปรเซสเซอร์ที่เข้ากันได้กับ Intel (และอาจเป็นตัวอื่น ๆ ส่วนใหญ่) imul พร้อมอาร์กิวเมนต์ 32b สองตัวสร้างผลลัพธ์ 64b และฉันต้องเปลี่ยนผลลัพธ์นี้ก่อนที่จะถูกกำหนดให้กับตัวแปร 32 บิต ไม่จำเป็นต้องคูณ 64 บิต   -  person Juraj Blaho    schedule 06.04.2011
comment
@Alexandre C. : สำหรับค่าที่ไม่ได้ลงนามชุดประกอบที่สร้างขึ้นนั้นถูกต้องในทั้งสามกรณีที่กล่าวถึง แต่ฉันต้องการให้มันใช้งานได้กับค่าที่เซ็นชื่อ มีเคล็ดลับอะไรบ้าง?   -  person Juraj Blaho    schedule 06.04.2011
comment
@Juraj: ไม่ขอโทษ ฉันสงสัยว่าปัญหานี้ แต่ฉันไม่ใช่ผู้เชี่ยวชาญในด้านนี้ อย่างน้อยคุณก็รู้ว่าปัญหาของคุณอยู่ที่ไหน   -  person Alexandre C.    schedule 06.04.2011
comment
คุณควรระบุส่วนของโค้ดที่สมบูรณ์ยิ่งขึ้นพร้อมกับรายงานข้อบกพร่องของคุณ MS จะใช้ทุกโอกาสเพื่อปิดจุดบกพร่องบน Connect และการไม่ให้โค้ดที่คอมไพล์ได้หรือตัวเลือกคอมไพเลอร์ที่คุณใช้จะทำให้มีโอกาสมากขึ้น   -  person Will Dean    schedule 06.04.2011


คำตอบ (3)


ที่เกี่ยวข้องบางส่วน: หากคุณต้องการแน่ใจว่าจะใช้ประโยชน์จากความสามารถ imul ของการคูณ 32x32=›64 บิต คุณสามารถใช้ Int32x32To64 API ปลอม (จริงๆ แล้วเป็นมาโคร):

คูณจำนวนเต็ม 32 บิตแบบมีเครื่องหมายสองตัว โดยส่งคืนผลลัพธ์จำนวนเต็ม 64 บิตแบบมีเครื่องหมาย ฟังก์ชั่นนี้ทำงานอย่างเหมาะสมบน Windows 32 บิต

ฟังก์ชันนี้ถูกนำไปใช้บนทุกแพลตฟอร์มด้วยโค้ดอินไลน์ที่ดีที่สุด: คำสั่งการคูณเดี่ยวที่ส่งคืนผลลัพธ์ 64 บิต

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


แก้ไข:

น่าสนใจพอ โดยมองหา Int32x32To64 ใน winnt.h คุณพบสำหรับ x86:

//
// The x86 C compiler understands inline assembler. Therefore, inline functions
// that employ inline assembler are used for shifts of 0..31.  The multiplies
// rely on the compiler recognizing the cast of the multiplicand to int64 to
// generate the optimal code inline.
//

#define Int32x32To64( a, b ) (LONGLONG)((LONGLONG)(LONG)(a) * (LONG)(b))
#define UInt32x32To64( a, b ) (ULONGLONG)((ULONGLONG)(DWORD)(a) * (DWORD)(b))

ดังนั้นจึงควรสร้าง imul อย่างแน่นอน หากแม้แต่ Platform SDK ยังเชื่อถือคอมไพเลอร์เพื่อทำสิ่งที่ถูกต้อง


แก้ไขอีกครั้ง:

หากคุณต้องการให้แน่ใจว่าได้รับ imul คุณสามารถใช้ __emul คอมไพเลอร์ภายใน

person Matteo Italia    schedule 06.04.2011
comment
ดูเหมือนว่ามาโครนั้นจะไม่ทำงานในกรณีของฉันเช่นกัน หากไม่มีการดำเนินการ >> ก็จะทำงานได้ดี แต่การเปลี่ยนแปลงนี้ทำให้คอมไพเลอร์เข้าใจผิดในการสร้าง call _allmul - person Juraj Blaho; 06.04.2011
comment
@Juraj: แล้ว __emul ที่อยู่ภายในล่ะ? - person Matteo Italia; 06.04.2011
comment
ฉันเพิ่งลองใช้ __emul และดูเหมือนว่าจะได้ผล ฉันไม่ชอบมันมากนักเพราะมันเป็นคอมไพเลอร์ที่เฉพาะเจาะจงมากและไม่สามารถพกพาได้ แต่ฉันสามารถใช้มันเป็นวิธีแก้ปัญหาเมื่อใช้ MSVC ขอบคุณ. - person Juraj Blaho; 06.04.2011
comment
@Juraj: ฉันก็ไม่ชอบสิ่งเฉพาะของคอมไพเลอร์เช่นกัน แต่ฉันไม่เห็นตัวเลือกอื่นมากมาย ในทางกลับกัน มันจะน่าสนใจที่จะเปรียบเทียบโค้ดที่สร้างขึ้นโดยมีและไม่มีสิ่งที่อยู่ภายใน คงจะเป็นเรื่องที่น่าประหลาดใจหากปรากฏว่าคอมไพเลอร์รู้จริงๆ ว่ากำลังทำอะไรอยู่ และโค้ดที่ดูเหมือนช้ากว่าในกรณีนี้คือ เร็วขึ้นจริงๆ - person Matteo Italia; 06.04.2011
comment
@Matteo Italia: ดูเหมือนว่าคอมไพเลอร์จะผิดที่นี่ จากการทดสอบอย่างรวดเร็ว เวอร์ชัน _allmull จะช้าลง 2 เท่า ถ้าฉันใช้การหารด้วย 4 แทนที่จะเป็น >>2 คอมไพเลอร์จะสร้าง _alldiv และโค้ดจะช้ากว่า __emul และ >> ถึง 10 เท่า - person Juraj Blaho; 06.04.2011
comment
@Juraj: อาจมีเนื้อหาสำหรับรายงานข้อผิดพลาด - person Matteo Italia; 06.04.2011

ฉันเห็น allmul หากฉันรันคอมไพเลอร์โดยไม่ปรับให้เหมาะสม แต่ด้วย /Ox ฉันเห็นการรวมกันของการเปลี่ยนแปลงและเพิ่มที่ขึ้นอยู่กับค่าของส่วนที่คงที่

ฉันคิดว่าคุณต้องระบุโค้ดบางส่วนและตัวเลือกคอมไพเลอร์ที่คุณใช้

person Will Dean    schedule 06.04.2011
comment
คุณได้ลองใช้ค่าคงที่ที่ไม่ใช่กำลังสองที่ใหญ่กว่านี้ (เช่น 150) แล้วหรือยัง? สำหรับค่าคงที่เล็กน้อย ดูเหมือนว่าจะมีการปรับปรุงเพิ่มเติมบางอย่าง ฉันเปิดการเพิ่มประสิทธิภาพแล้ว (/Ox) - person Juraj Blaho; 06.04.2011

คุณได้ลองใช้วิธีแก้ปัญหาแล้วหรือยัง:

int32_t c = (int64_t(int32_t(a))*int64_t(int32_t(b)))>>2;
person Mark Ransom    schedule 06.04.2011
comment
ใช่ และดูเหมือนว่าจะไม่ได้ช่วยอะไร ซึ่งเทียบเท่ากับ Int32x32To64() ที่แนะนำโดย Matteo Italia - person Juraj Blaho; 06.04.2011
comment
เฮ้! ยินดีที่ได้พบคุณที่ SO Meetup! - person jjnguy; 07.04.2011