Mümkün olan en kısa sürede beş küçük tam sayının en büyük ikisini bulmak


9

Küçük bir gömülü sistemdeki görüntü verileri üzerinde 5 çapraz medyan filtrenin bir varyasyonunu kullanıyorum, yani

    x
  x x x
    x

Algoritma gerçekten basittir: 5 işaretsiz tam sayı değeri okuyun, en yüksek 2 değerini alın, bunlar üzerinde bazı hesaplamalar yapın ve işaretsiz tam sayı sonucunu yazın.

Güzel olan, 5 tamsayı giriş değerinin hepsinin 0-20 aralığında olmasıdır. Hesaplanan tamsayı değeri de 0-20 aralığındadır!

Profilleme yoluyla, en büyük iki sayıyı elde etmenin darboğaz olduğunu anladım, bu yüzden bu kısmı hızlandırmak istiyorum. Bu seçimi yapmanın en hızlı yolu nedir?

Mevcut algoritma, 5 sayı ile verilen konumda 1 ve HW destekli bir CLZ fonksiyonu ile 32 bit maske kullanır.
CPU'nun tescilli olduğunu ve şirketimin dışında bulunmadığını söylemeliyim. Derleyicim GCC ama bu CPU için özel olarak üretildi.

Bir arama tablosu kullanabilir miyim anlamaya çalıştım ama kullanabileceğim bir anahtar üretmek için başarısız oldu.

Giriş için kombinasyon var ama sipariş önemli değil, yani aynı .215[5,0,0,0,5][5,5,0,0,0]

Aşağıdaki karma işlev, çarpışma olmadan mükemmel bir karma üretir!

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

Ancak karma çok büyük ve bunu kullanmak için yeterli bellek yok.

Kullanabileceğim daha iyi bir algoritma var mı? Sorunumu bir arama tablosu kullanarak ve bir anahtar üreterek çözmek mümkün mü?


1
Şu anda hangi algoritmayı kullanıyorsunuz? Yedi tamsayı karşılaştırması yeterlidir, bu çok yavaş mı? Sizin hashzaten fazla işlemlerini gerçekleştirir. Yönteme sonraki çağrılar ilişkili mi, örneğin merkezi xmatris boyunca satır satır ilerliyor mu?
Raphael

Filtre görüntü boyunca satır satır kıvrılır. 5 değeri alıp hesaplamaları yaparız, sonra her şeyi bir adım sağa hareket ettirir ve tekrarlar. Karma sadece bir örnektir. Verilerin okunmasını en aza indirmek için birkaç kayan pencere çözümünü karşılaştırdım, ancak hepsi en yüksek 2 değeri bulmak için kaynar.
Fredrik Pihl

3
Büyük olasılıkla algoritmanız, doğru şekilde uygulanırsa, hesaplama tarafından değil, bellek erişimi ile sınırlı olacaktır. Bir hashtable kullanmak yalnızca bellek erişim miktarını artıracak ve işleri yavaşlatacaktır. Nasıl geliştirilebildiğini görebilmemiz için lütfen mevcut kodunuzu gönderin - Yalnızca mikro optimizasyonun mümkün olduğuna inanıyorum. Düşünebileceğim en fazla şey: belki komşu pencereler arasında 2 değerin ortak olmasından yararlanabilir miyiz?
jkff

@jkff Matris, önbellek boyutları ve (önbellek) eşleme işlevine bağlı olarak, her değerin yalnızca bir kez yüklenmesi gerekebilir; çoğu işlem o zaman kayıtlar veya L1 önbellek üzerinde çalışmalıdır. Bununla birlikte, boruhattı başka bir konudur.
Raphael

1
Bu arada, bunu paralel olarak zaten yapıyor musunuz? Bu özellikle vektör paralelleştirmesi veya SIMD (örn. GPU'da) için uygundur. Bu yol, hücre başına yüzde birkaç tasarruf etmekten çok daha fazla yardımcı olacaktır.
Raphael

Yanıtlar:


11

Benim içinde diğer cevap ben koşullu atlayışlar verimlilik için en büyük engeli olabileceğini düşündürmektedir. Sonuç olarak, sıralama ağları akla gelir: veri agnostiktir, yani girdi ne olursa olsun aynı karşılaştırma dizisi yürütülür, sadece swaplar koşulludur.

Elbette, sıralama çok fazla iş olabilir; sadece en büyük iki sayıya ihtiyacımız var. Bizim için şanslı olan seçim ağları da incelenmiştir. Knuth bize beş²'den en küçük iki sayıyı bulmanın karşılaştırması [1, 5.3.4 ex 19] (ve en fazla takas) ile yapılabileceğini söylüyor .U^2(5)=6

Çözümlerde verdiği ağ (sıfır tabanlı dizilere yeniden yazılır)

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

karşılaştırmaların yönünü ayarladıktan sonra - sözde kod olarak

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

Şimdi, saf uygulamalar hala koşullu sıçramalara sahiptir (takas kodu boyunca). Makinenize bağlı olarak, koşullu talimatlarla bunları atlatabilirsiniz. x86 her zamanki çamurluk benliği gibi görünüyor; Görünüşe göre çoğu operasyon kendi içinde şartlı olduğundan ARM daha umut verici görünüyor . Ben anlamak talimatları doğru, ilk takas eden dizi değerleri sicillere yüklenmiş olduğu varsayılarak, bu çevirir R0aracılığıyla R4:

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

Evet, evet, elbette XOR takasını EOR ile kullanabilirsiniz .

Umarım işlemcinizde buna benzer bir şey vardır. Tabii ki, eğer bu amaç için bir şey inşa ederseniz , belki ağ orada sabit kablolu alabilirsiniz?

Bu muhtemelen (muhtemelen?) Klasik alanda yapabileceğiniz en iyisidir, yani sınırlı alandan faydalanmadan ve kötü kelime içi sihirbazlar yapmadan.


  1. Donald E. Knuth'a Göre Sıralama ve Arama ; Bilgisayar Programlama Sanatı Vol. 3 (2. baskı, 1998)
  2. Bunun seçilen iki öğeyi sırasız bıraktığını unutmayın. Bunları sipariş etmek için ekstra bir karşılaştırma gerekir, yani toplam kişi [1, p234 Tablo 1].W^2(5)=7

Bunu kabul ediyorum. Devam etmeden önce karşılaştırmam gereken birçok yeni fikir aldım. Knuth'a başvurmak her zaman benim için işe yarar :-) Çaba ve zaman için teşekkürler!
Fredrik Pihl

@FredrikPihl Cool, lütfen sonunda nasıl ortaya çıktığını bize bildirin!
Raphael

Yapacağım! Bölüm 5.3.3 okunuyor. Lewis Carroll ve tenis turnuvasına referanslarla başlamasını
Fredrik Pihl

2
Komut setine bağlı olarak, seçim ağı ile birlikte 2 * max (a, b) = a + b + abs (ab) kullanmak faydalı olabilir; öngörülemeyen koşullu sıçramalardan daha az maliyetli olabilir (abs: gcc için en azından x86 için içsel veya koşullu bir hareket olmasa bile, x86'ya bağımlı görünmeyen bir atlama dizisi oluşturur). Atlamasız bir sekansa sahip olmak, SIMD veya GPU ile birleştirildiğinde de yararlıdır.
AProgrammer

1
Seçim ağlarının (sıralama ağları gibi) paralel işlemlere uygun olduğunu unutmayın; özellikle belirtilen seçim ağında, karşılaştırmalar 1: 4 ve 0: 3 paralel olarak yapılabilir (işlemci, derleyici vb. verimli bir şekilde destekliyorsa) ve karşılaştırmalar 1: 3 ve 0: 2 de paralel olarak yapılabilir.
Bruce Lilly

4

Sadece masada olması için, doğrudan bir algoritma:

// 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

Akıllıca uygulanarak, if ... elsedoğrudan çevirinin sahip olabileceği bazı koşulsuz sıçramalardan kurtulabilir.

Bu çirkin ama sadece

  • beş veya altı karşılaştırma (yani koşullu atlama),
  • dokuz ila on ödev (11 değişkenli, hepsi kayıtlarda) ve
  • ek bellek erişimi yok.

Aslında, [1] bölüm 5.3.3'teki Teorem S'nin gösterdiği gibi, altı karşılaştırma bu sorun için en uygunudur; burada ihtiyacımız var .W2(5)

Bununla birlikte, boru hattı olan makinelerde bunun hızlı olması beklenemez; koşullu sıçramaların yüksek yüzdesi göz önüne alındığında, çoğu zaman muhtemelen durakta geçirilirdi.

Daha basit bir varyantın - sıralayın x1ve x2ardından diğer değerleri daha sonra ekleyin - dört ila yedi karşılaştırmayı ve yalnızca beş ila altı atamayı aldığını unutmayın . Atlamaların burada daha pahalı olmasını beklediğimden, bununla sıkıştım.


  1. Donald E. Knuth'a Göre Sıralama ve Arama ; Bilgisayar Programlama Sanatı Vol. 3 (2. baskı, 1998)

Bir optimizasyon derleyicisinin bunlarla neler yapabileceğini merak ediyorum.
Raphael

Bunu uygulayacağım ve mevcut CLZ tabanlı çözümle kıyaslayacağım. Zaman ayırdığınız için teşekkürler!
Fredrik Pihl

1
@FredrikPihl Kriterlerinizin sonucu neydi?
Raphael

1
SWAP tabanlı yaklaşım CLZ'yi yeniyor! Şimdi mobil cihazlarda. Başka bir zaman daha fazla veri gönderebilir, şimdi
mobilde

@FredrikPihl Havalı! İyi eski teori yaklaşımının (hala) pratik kullanımda olabileceği için mutluyum. :)
Raphael

4

Bu , Souper projesi için harika bir uygulama ve test örneği olabilir . Souper, süper bir kodlayıcıdır - giriş olarak kısa bir kod dizisi alan ve mümkün olduğunca optimize etmeye çalışan (daha hızlı olacak eşdeğer bir kod dizisi bulmaya çalışır) bir araçtır.

Souper açık kaynak. Daha iyi olup olmadığını görmek için kod snippet'inizde Souper'i çalıştırmayı deneyebilirsiniz.

Ayrıca bkz. John Regehr'un 16 adet 4 bitlik değeri sıralamak için hızlı kod yazma yarışması ; buradaki bazı tekniklerin yararlı olması mümkündür.


OP'nin denediği programlarda bunun ne yapabileceğini merak ediyorum.
Raphael

3

Üç tamsayı alan ve en büyük ikisini çıkaran tablo kullanabilirsiniz . Daha sonra üç tablo araması kullanabilirsiniz:213

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

Benzer şekilde, tablo kullanarak, tabloyu iki tablo aramasına azaltabilirsiniz, ancak bunun daha hızlı olacağı açık değildir.214

Gerçekten küçük bir tablo istiyorsanız, iki sayıyı "sıralamak" için iki tablo ve ardından bir sıralama ağı kullanabilirsiniz. Wikipedia'ya göre , bu en fazla 18 tablo araması gerektirir (9 karşılaştırıcı); (1) sadece en büyük iki unsuru bilmek istediğinizden ve (2) bazı karşılaştırma kapıları için daha azıyla başa çıkabilirsiniz, sadece maksimum değerle ilgilenebilirsiniz.212

Ayrıca tek bir tablosu da kullanabilirsiniz . Daha sonra bir sıralama ağı uygulamak daha az bellek erişimi, ancak daha fazla aritmetik kullanır. Bu şekilde en fazla 9 tablo araması elde edersiniz.212

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.