ค้นหาเลขจำนวนเต็มสองตัวที่ใหญ่ที่สุดในห้าตัวโดยเร็วที่สุด


9

ฉันใช้รูปแบบของตัวกรองมัธยฐาน 5 ข้ามบนข้อมูลภาพบนระบบฝังตัวขนาดเล็กเช่น

    x
  x x x
    x

อัลกอริทึมนั้นง่ายมาก: อ่านค่าจำนวนเต็ม 5 ค่าที่ได้รับ 2 ได้ทำการคำนวณสูงสุดแล้วเขียนผลจำนวนเต็มที่ไม่ได้ลงนาม

สิ่งที่ดีคือค่าอินพุตจำนวนเต็ม 5 ค่าทั้งหมดอยู่ในช่วง 0-20 ค่าจำนวนเต็มจากการคำนวณยังอยู่ในช่วง 0-20!

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

อัลกอริทึมปัจจุบันใช้มาสก์ 32 บิตที่มี 1 ในตำแหน่งที่กำหนดโดยตัวเลข 5 ตัวและฟังก์ชั่น CLZ ที่รองรับ HW
ฉันควรจะบอกว่าซีพียูเป็นกรรมสิทธิ์ไม่สามารถใช้งานได้นอก บริษัท ของฉัน คอมไพเลอร์ของฉันคือ GCC แต่ปรับแต่งสำหรับ CPU นี้

ฉันลองคิดดูว่าฉันสามารถใช้ตารางการค้นหาได้หรือไม่ แต่ฉันไม่สามารถสร้างรหัสที่ฉันสามารถใช้ได้

ฉันมีรวมกันสำหรับการป้อนข้อมูลการสั่งซื้อ แต่ไม่ได้เป็นสิ่งที่สำคัญคือเป็นเช่นเดียวกับ215[5,0,0,0,5][5,5,0,0,0]

มันเกิดขึ้นว่าฟังก์ชั่นแฮชด้านล่างสร้างแฮชที่สมบูรณ์แบบโดยไม่มีการชน!

def hash(x):
    h = 0
    for i in x:
        h = 33*h+i
    return h

แต่แฮชมีขนาดใหญ่และมีหน่วยความจำไม่เพียงพอที่จะใช้

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


1
คุณใช้อัลกอริทึมใดในปัจจุบัน การเปรียบเทียบจำนวนเต็มเจ็ดอย่างเพียงพอแล้วนั่นช้าเกินไปหรือไม่ คุณhashทำการดำเนินการเพิ่มเติมแล้ว การโทรตามลำดับไปยังเมธอดเกี่ยวข้องหรือไม่เช่นศูนย์กลางxเคลื่อนที่ผ่านเมทริกซ์ทีละแถวหรือไม่?
Raphael

ตัวกรองถูกโน้มน้าวผ่านแถวรูปภาพทีละแถว คือรับค่า 5 และทำการคำนวณจากนั้นย้ายทุกอย่างในขั้นตอนเดียวไปทางขวาและทำซ้ำ แฮชเป็นเพียงตัวอย่างเท่านั้น ฉันเปรียบเทียบมาตรฐานโซลูชันหน้าต่างแบบเลื่อนหลายครั้งเพื่อลดการอ่านข้อมูล แต่ทั้งหมดก็ลดลงเพื่อค้นหาค่าสูงสุด 2 ค่า
Fredrik Pihl

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

@jkff ขึ้นอยู่กับเมทริกซ์ขนาดแคชและฟังก์ชั่นการทำแผนที่ (แคช) ค่าทั้งหมดอาจต้องโหลดเพียงครั้งเดียว การดำเนินการส่วนใหญ่ควรทำงานกับการลงทะเบียนหรือแคช L1 แล้ว แม้ว่าการวางท่อจะเป็นอีกปัญหาหนึ่ง
กราฟิลส์

1
โดยวิธีการที่คุณทำนี้ในแบบคู่ขนานแล้ว? ดูเหมือนจะเหมาะสมอย่างยิ่งสำหรับการขนานเวกเตอร์หรือ SIMD (เช่นบน GPU) เส้นทางนั้นจะช่วยได้มากกว่าการบันทึกสองสามเปอร์เซ็นต์ต่อเซลล์
กราฟิลส์

คำตอบ:


11

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

แน่นอนการเรียงลำดับอาจใช้งานมากเกินไป เราต้องการเพียงตัวเลขสองตัวที่ใหญ่ที่สุดเท่านั้น โชคดีสำหรับเราเครือข่ายคัดเลือกยังได้รับการศึกษา Knuth บอกเราว่าการหาตัวเลขสองตัวที่เล็กที่สุดจากห้า²นั้นสามารถทำได้ ยู^2(5)=6 การเปรียบเทียบ [1, 5.3.4 อดีต 19] (และสลับกันมากที่สุด)

เครือข่ายที่เขามอบให้ในโซลูชัน (เขียนใหม่ไปยังอาร์เรย์ที่ไม่มีศูนย์) คือ

[0:4][1:4][0:3][1:3][0:2][1:2]

ซึ่งดำเนินการ - หลังจากปรับทิศทางของการเปรียบเทียบ - เป็นรหัสเทียม

def selMax2(a : int[])
  a.swap(0,4) if a[0] < a[4]
  a.swap(1,4) if a[1] < a[4]
  a.swap(0,3) if a[0] < a[3]
  a.swap(1,3) if a[1] < a[3]
  a.swap(0,2) if a[0] < a[2]
  a.swap(1,2) if a[1] < a[2]
  return (a[0], a[1])
end

ตอนนี้การใช้งานแบบไร้เดียงสายังมีการข้ามแบบมีเงื่อนไข (ข้ามรหัสการสลับ) ขึ้นอยู่กับเครื่องของคุณคุณสามารถ cirumvent พวกเขาด้วยคำแนะนำตามเงื่อนไขแม้ว่า x86 ดูเหมือนจะเป็นตัวเร่ง ARM ดูมีแนวโน้มมากขึ้นเนื่องจากการดำเนินการส่วนใหญ่มีเงื่อนไข ในตัวเอง หากฉันเข้าใจคำแนะนำ อย่างถูกต้องการสลับครั้งแรกจะแปลสิ่งนี้โดยถือว่าค่าอาร์เรย์ของเราถูกโหลดไปยังการลงทะเบียนR0ผ่านR4:

CMP     R0,R4
MOVLT   R5 = R0
MOVLT   R0 = R4
MOVLT   R4 = R6

ใช่แน่นอนคุณสามารถใช้XOR แลก กับEOR

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

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


  1. จัดเรียงและค้นหา โดย Donald E. Knuth; ศิลปะการเขียนโปรแกรมคอมพิวเตอร์ปีที่ 5 3 (2nd ed, 1998)
  2. โปรดทราบว่าสิ่งนี้จะทำให้องค์ประกอบที่เลือกไว้ทั้งสองไม่มีการเรียงลำดับ การสั่งซื้อสินค้านั้นต้องการการเปรียบเทียบพิเศษนั่นคือW^2(5)=7 รวมเป็นจำนวนมาก [1, p234 ตารางที่ 1]

ฉันยอมรับสิ่งนี้ ฉันได้รับแนวคิดใหม่มากมายที่ฉันต้องใช้ในการวัดประสิทธิภาพก่อนที่จะดำเนินการต่อ การอ้างอิงถึง Knuth นั้นใช้ได้กับฉันเสมอ :-) ขอบคุณสำหรับความพยายามและเวลาของคุณ!
Fredrik Pihl

@FredrikPihl เยี่ยมโปรดแจ้งให้เราทราบว่ามันจะออกมาในท้ายที่สุด!
Raphael

ฉันจะ! อ่านบทที่ 5.3.3 ทันที รักจุดเริ่มต้นของมันด้วยการอ้างอิงถึง Lewis Carroll และการแข่งขันเทนนิส :-)
Fredrik Pihl

2
ขึ้นอยู่กับชุดคำสั่งการใช้ 2 * max (a, b) = a + b + abs (ab) พร้อมกับเครือข่ายการเลือกอาจมีประโยชน์ มันอาจมีค่าใช้จ่ายน้อยกว่าการกระโดดตามเงื่อนไขที่คาดเดาไม่ได้ (แม้จะไม่มีการเคลื่อนไหวแบบมีเงื่อนไขหรือไม่มีเงื่อนไขสำหรับ abs: gcc อย่างน้อยสำหรับ x86 ให้สร้างลำดับ jumpless ซึ่งดูเหมือนจะไม่ขึ้นอยู่กับ x86) การมีลำดับ jumpless นั้นมีประโยชน์เมื่อรวมกับ SIMD หรือ GPU
AProgrammer

1
โปรดทราบว่าเครือข่ายการเลือก (เช่นเครือข่ายการเรียงลำดับ) สามารถรองรับการทำงานแบบขนานได้ โดยเฉพาะในเครือข่ายการเลือกที่ระบุการเปรียบเทียบ 1: 4 และ 0: 3 สามารถดำเนินการแบบขนาน (หากตัวประมวลผลคอมไพเลอร์ ฯลฯ สนับสนุนอย่างมีประสิทธิภาพ) และการเปรียบเทียบ 1: 3 และ 0: 2 สามารถดำเนินการควบคู่กัน
Bruce Lilly

4

เพื่อให้มันอยู่บนโต๊ะนี่เป็นอัลกอริทึมโดยตรง:

// Sort x1, x2
if x1 < x2
  M1 = x2
  m1 = x1
else
  M1 = x1
  m1 = x2
end

// Sort x3, x4
if x3 < x4
  M2 = x4
  m2 = x3
else
  M2 = x3
  m2 = x4
end

// Pick largest two
if M1 > M2
  M3 = M1
  if m1 > M2
    m3 = m1
  else
    m3 = M2
  end
else
  M3 = M2
  if m2 > M1
    m3 = m2
  else
    m3 = M1
  end
end

// Insert x4
if x4 > M3
  m3 = M3
  M3 = x4
else if x4 > m3
  m3 = x4
end

ด้วยการดำเนินการอย่างชาญฉลาดของif ... elseหนึ่งสามารถกำจัดการกระโดดที่ไม่มีเงื่อนไขบางอย่างการแปลโดยตรงจะมี

นี้น่าเกลียด แต่ใช้เวลาเท่านั้น

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

ในความเป็นจริงการเปรียบเทียบหกรายการดีที่สุดสำหรับปัญหานี้เนื่องจากทฤษฎีบท S ในส่วน 5.3.3 ของ [1] แสดง; ที่นี่เราต้องการW2(5).

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

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


  1. จัดเรียงและค้นหาโดย Donald E. Knuth; ศิลปะการเขียนโปรแกรมคอมพิวเตอร์ปีที่ 5 3 (2nd ed, 1998)

ฉันสงสัยว่าคอมไพเลอร์การเพิ่มประสิทธิภาพสามารถทำอะไรกับสิ่งเหล่านี้
กราฟิลส์

ฉันจะใช้สิ่งนี้และเปรียบเทียบกับโซลูชันที่อิงกับ CLZ ปัจจุบัน ขอบคุณที่สละเวลา!
Fredrik Pihl

1
@FredrikPihl อะไรคือผลลัพธ์ของการวัดประสิทธิภาพของคุณ?
Raphael

1
วิธีการตาม SWAP เต้น CLZ! บนมือถือตอนนี้ สามารถโพสต์ข้อมูลเพิ่มเติมได้อีกครั้งบนมือถือตอนนี้
Fredrik Pihl

@ FredrikPihl เจ๋ง! ฉันมีความสุขที่วิธีการทางทฤษฎีแบบเก่าที่ดี (ยัง) สามารถใช้งานได้จริง :)
Raphael

4

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

Souper เป็นโอเพ่นซอร์ส คุณอาจลองใช้ Souper ในข้อมูลโค้ดของคุณเพื่อดูว่าสามารถทำได้ดีกว่านี้หรือไม่

ดูการประกวดของ John Regehr เกี่ยวกับการเขียนโค้ดอย่างรวดเร็วเพื่อจัดเรียงค่า 16 4 บิต ; เป็นไปได้ว่าเทคนิคบางอย่างอาจมีประโยชน์


ฉันสนใจที่จะทำสิ่งนี้ในโปรแกรมที่ OP พยายามทำ
กราฟิลส์

3

คุณสามารถใช้ 213ตารางที่รับจำนวนเต็มสามจำนวนและส่งออกค่าที่ใหญ่ที่สุด จากนั้นคุณสามารถใช้การค้นหาสามตาราง:

T[T[T[441*a+21*b+c]*21+d]*21+e]

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

หากคุณต้องการโต๊ะเล็กจริงๆคุณสามารถใช้สองโต๊ะได้ 212ตารางเพื่อ "เรียงลำดับ" สองตัวเลขจากนั้นใช้เครือข่ายการเรียงลำดับ ตามWikipediaสิ่งนี้ต้องมีการค้นหาบนโต๊ะมากที่สุด 18 ตัว (9 ตัวเปรียบเทียบ); คุณอาจจะทำได้น้อยลงเนื่องจาก (1) คุณต้องการเพียงแค่รู้องค์ประกอบที่ใหญ่ที่สุดสองรายการและ (2) สำหรับประตูเปรียบเทียบบางตัวคุณอาจสนใจเพียงสูงสุดเท่านั้น

คุณยังสามารถใช้ซิงเกิ้ลได้ 212โต๊ะ. การใช้เครือข่ายการเรียงลำดับจากนั้นใช้การเข้าถึงหน่วยความจำน้อยลง แต่ใช้คณิตศาสตร์มากกว่า วิธีนี้คุณจะได้รับการค้นหาสูงสุด 9 ตาราง

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