Delta sıkıştırması ağ üzerinden gönderilen veri miktarını nasıl azaltır?


26

Birçok oyun gönderilen veri yükünü azaltmak için delta sıkıştırma tekniğini kullanır. Bu tekniğin gerçekte veri yükünü nasıl düşürdüğünü anlayamıyorum?

Örneğin, bir konum göndermek istediğimi varsayalım. Delta sıkıştırması olmadan vector3, örneğin, varlığın tam konumunu bir ile gönderirim (30, 2, 19). Delta sıkıştırma vector3ile küçük sayılarla bir gönderirim (0.2, 0.1, 0.04).

Mesajların her ikisi de vector3- her bir kayan nokta için 32 bitse - veri yükünü nasıl düşürdüğünü anlamıyorum. 32 * 3 = 96 bit!

Her bir kayan noktayı bir bayta dönüştürebileceğinizi ve daha sonra bir bayttan kayan noktaya dönüştürebileceğinizi biliyorum, ancak bu görülebilir hassas hatalara neden oluyor.


Herhangi bir delta sıkıştırma biçimini kullanan bir oyunla ağ kurduğunuzda, tamamen deterministik olmalısınız (başarısız olunur ve zaman ayırırsınız). "Bayttan kayan noktaya dönüştürme" nin (veya her ne olursa olsun) hassas hatalara yol açıp açmadığı önemli değildir - gerekli şart, tüm senkronize makinelerde / oyunlarda her şeyin tamamen aynı olmasıdır. (Tam yüzer kullanılan bile, kaçınılmaz - CPU benim CPU ile aynı yüzer kullanmaz) "hassas hataları" varsa, hepsi makinelerde aynı olması gerekir - ve bu nedenle değil görünür. Görünür etkilerden kaçınmak için türleri seçtiniz.
Luaan

2
@Luaan veya sık sık kısmi bir mutlak durum ekleyebilir, örneğin her sıklıkta birkaç varlık seçebilir ve mutlak konumu geçebilir, oyuncuya yakın objeleri seçmeyi tercih edebilirsiniz.
cırcır ucube

Her nasılsa, bu soruyu rsync'in bir akrabası hakkında bekliyordum ...
SamB

Huffman Kodlaması.
Ben Voigt

Sadece ortadaki adamı kullanın
John Demetriou

Yanıtlar:


41

Oyun durumunu tam olarak göndermekten kaçınamadığınız zamanlar vardır - kaydedilmiş bir çok oyunculu oyunun yükü gibi ya da resync gerektiğinde. Ancak, tam durum göndermek genellikle önlenebilir niteliktedir ve delta kodlamanın girdiği yer burasıdır . Genellikle, delta sıkıştırmanın tek nedeni budur; Örneğiniz gerçekten bu durumu tarif etmiyor. Delta sıkıştırmasının bile belirtilmesinin nedeni, naif uygulamaların genellikle deltas yerine durum göndermesidir , çünkü durum genellikle naif bir oyun uygulamasının zaten depoladığı şeydir. Deltalar daha sonra bir optimizasyondur.

Delta'larla, hiç hareket etmeyen birimlerin pozisyonlarını asla göndermezsiniz . Bu onun ruhu.

Yıllarca görüştüğümüzü hayal edin ve hafızamı kaybettim (ve tüm mektuplarınızı okuduktan sonra atmıştım). Normal olarak bir dizi mektuba devam etmek yerine, hayatımın bütün tarihini bir kez daha tekrarlayabilmem için bana büyük bir harfle yazmanız gerekir.

Verilen durumda, (kod tabanınıza bağlı olarak) tam durumu göndermek için gereken büyük bit aralıklarının aksine deltaları kodlamak için daha az sayıda bit kullanmak mümkün olabilir. Dünyanın birçok kilometre boyunca olduğunu söyleyin, belirli bir eksendeki santimetreyi tam olarak konumlandırmak için 32 bitlik bir yüzeye ihtiyacınız olabilir. Bununla birlikte, kene başına sadece birkaç metre olabilen varlıklar için geçerli olan maksimum hız göz önüne alındığında, sadece 8 bit (2 ^ 8 = 256, maksimum 200 cm depolayacak kadar yeterli) yapılabilir. Elbette, bu, sabit nokta sorunlarını istemiyorsanız, kayan nokta kullanımı yerine ... veya OpenGL'deki gibi bir çeşit yarım / çeyrek yüzerlik yerine sabit sayılır.


7
Cevabınız bana açık değil. Hareket etmeyen bir nesnenin bilgisini göndermemenin delta kodlamanın sadece bir yan etkisi olduğunu ve “onun ruhu” değil gibi hissettiğini hissediyorum. Delta kodlamasını kullanmanın asıl sebebi cırcır ucunun cevabında daha iyi vurgulanmış gibi görünüyor.
Etsitpab Nioliv

14
@EtsitpabNioliv "Ruh" basitçe "sahip olmadığın şeyi gönderme" dir. Bu bit seviyesine düşürülebilir - "sadece kablo üzerinden gerekli mesajı almak için ihtiyaç duyduğunuz kadar bant genişliği kullanın". Bu cevap açıkça herkes için yeterince açık görünüyor. Teşekkürler.
Mühendis

4
@EtsitpabNioliv SVN'nin sunucu tarafındaki dosyaları nasıl depoladığını hiç öğrendiniz mi? Her bir işlenişin tamamını saklamıyor. Her işin içerdiği değişiklikleri, deltaları depolar . "Delta" terimi genellikle matematik ve programlamada iki değer arasındaki farkı ifade etmek için kullanılır . Ben bir oyun programcısı değilim, ama eğer oyundaki kullanım bu kadar farklıysa şaşırırdım. Ardından fikir mantıklı: Her şeyi yerine sadece farklılıkları göndererek göndermeniz gereken veri miktarını "sıkıştırıyorsunuz". (Bu iste bana kafa karıştırıcı ise, "kompres." Kelime)
jpmc26

1
Ayrıca, küçük sayılar daha yüksek sıfır bit sayısına sahiptir ve gönderilen bilgiyi kodlamak / kod çözmek için uygun sıkıştırma algoritmasını kullanmak, ağ üzerinden gönderilmek üzere daha küçük bir veri yüküne yol açabilir.
liggiorgio

22

Sende yanlış delta var. Tek tek elementlerin deltasına bakıyorsunuz. Tüm sahnenin deltasını düşünmelisin.

Sizin sahnenizde 100 element bulunduğunu, ancak sadece 1 tanesinin hareket ettiğini varsayalım. 100 element vektörü gönderirseniz, 99 tanesi israf edilir. Gerçekten sadece 1 göndermeniz gerekiyor.

Şimdi tüm öğe vektörlerinizi saklayan bir JSON nesneniz olduğunu varsayalım. Bu nesne, sunucunuz ve istemciniz arasında senkronize edilir. Karar vermek yerine "öyle mi, öyle mi hareket etti?" Sadece bir sonraki oyun onayınızı bir JSON nesnesinde oluşturabilir, bir diff tick100.json tick101.jsonfark yaratabilir ve gönderebilirsiniz. İstemci tarafında, farklı çizgiyi geçerli kene vektörünüze uygularsınız ve hepiniz hazırsınızdır.

Bunu yaparak, metinlerdeki (veya ikili!) Farklılıkları tespit etmede onlarca yıllık uzmanlıktan yararlanırsınız ve kendiniz bir şeyleri kaçırmak için endişelenmenize gerek yoktur. Şimdi ideal olarak, geliştirici olarak sizi daha da kolaylaştırmak için perdenin arkasında bunu yapan bir kütüphane kullanıyorsunuz.


2
diffVerimsiz bir hack gibi sesler kullanarak . JSON stringini alıcı uçta tutmak, her defasında yama yapmak ve seri hale getirmek gereksizdir. İki anahtar-değer sözlüğünün farkını hesaplamak karmaşık bir iş değildir, temel olarak tüm anahtarların üzerinden geçip değerlerin eşit olup olmadığını kontrol edersiniz. Değilse, onları sonuçta ortaya çıkan anahtar-değer dikgesine eklersiniz ve son olarak JSON'a seri hale getirilen dikt'i gönderirsiniz. Basit, yıllarca uzmanlığa gerek yok. Farklılıkların aksine, bu yöntem: 1) eski (değiştirilmiş) verileri içermez; 2) UDP ile daha iyi oynuyor; 3) yeni
hatlara

8
@gronostaj Bu noktaya gelmek için bir örnekti. Aslında JSON için diff kullanarak savunuculuk yapmıyorum - bu yüzden "varsayalım" deyin.
corsiKa

2
“Bunu yaparak, metinlerdeki (veya ikili!) Farkları tespit etmede onlarca yıllık uzmanlıktan yararlanırsınız ve kendiniz bir şeyleri kaçırmaktan endişe duymanıza gerek kalmaz. geliştirici olarak size daha kolay. " Bu bölüm kesinlikle, hiç kimse böyle bir şeyi makul bir şekilde yapamadığı zaman, bir diff kullanmasını veya bir diff kullanarak bir kütüphaneyi kullanmayı önerdiğiniz gibi ses çıkarır. Delta sıkıştırma "diffing" demezdim, sadece delta sıkıştırma, benzerlikler yüzeysel
Selali Adobor 16:17

Optimal bir fark ve optimal bir delta sıkıştırma aynıdır. Komut satırındaki diff yardımcı programı metin dosyaları için tasarlanmış ve size en uygun sonucu vermeyecek olsa da, delta sıkıştırmasını sizin için yapan kütüphaneleri araştırmanızı tavsiye ederim. Delta-diff kelimesi ile ilgili hiçbir şey fantezi değildir, bu anlamda, kelimenin tam anlamıyla aynı anlama gelir. Bu yıllar içinde kaybolmuş gibi görünüyor.
corsiKa

9

Çok sık başka bir sıkıştırma mekanizması, örneğin aritmetik sıkıştırma gibi delta kodlaması ile birlikte olacaktır.

Bu sıkıştırma şemaları, olası değerler öngörülebilir şekilde birlikte gruplandırıldığında daha iyi çalışır. Delta kodlaması, değerleri 0 civarında gruplayacaktır.


1
Başka bir örnek: eğer her biri kendi konumlarına sahip ancak aynı hız vektöründe seyahat eden 100 uzay geminiz varsa, hızı yalnızca bir kez göndermeniz gerekir (veya en azından gerçekten iyi sıkıştırması ); Aksi takdirde, 100 pozisyon göndermeniz gerekir. Başkalarının ekleme yapmasına izin verin. Paylaşımlı durum kilit adımı simülasyonlarını agresif bir delta sıkıştırma şekli olarak görürseniz, hızları bile göndermezsiniz - yalnızca oynatıcıdan gelen komutları. Yine, herkes kendi eklemesini yapsın.
Luaan

Katılıyorum. Sıkıştırma cevapta önemlidir.
Leopoldo Sanczyk

8

Tamamen haklısın ama önemli bir noktayı kaçırıyorsun.

Bir oyunda varlıklar, sadece bir tanesi olan birçok özellik ile tanımlanır. .

Ne tür nitelikler? Çok fazla düşünmek zorunda kalmadan, ağa bağlı bir oyunda bunlar şunları içerebilir:

  • Pozisyonu.
  • Oryantasyon.
  • Geçerli kare numarası.
  • Renk / aydınlatma bilgisi.
  • Şeffaflık.
  • Kullanılacak model.
  • Kullanılacak doku
  • Özel efektler.
  • Vb.

Bunların her birini ayrı ayrı seçtiyseniz, belirli bir çerçevede değişmesi gerektiğinde, tam olarak yeniden iletilmesi gerektiği koşulunu kesinlikle yapabilirsiniz.

Ancak, bu özelliklerin tümü aynı oranda değişmez .

Model değişmiyor mu? Delta sıkıştırması olmadan, yine de yeniden iletilmelidir. Delta sıkıştırma ile olması gerekmez.

Konum ve yönlendirme, daha ilginç olan ve genellikle her biri 3 yüzerden oluşan iki durumdur. Verilen iki çerçeve arasında, her 3 yüzer kümenin yalnızca 1 veya 2'sinin değişebilme olasılığı vardır. Düz bir çizgide mi hareket ediyorsunuz? Dönmüyor musun? Zıplamak yok mu? Bunlar, delta sıkıştırma olmadan tam olarak yeniden iletmeniz gereken durumlar, ancak delta sıkıştırma ile yalnızca değişen şeyi yeniden iletmeniz gereken durumlar.


8

Naif bir delta hesaplamasını kendi başınıza yapmakta haklısınız, sonuç işlenenlerle aynı büyüklükte veri yapısında saklanır ve başka bir işlem yapılmadan iletilirse trafikten tasarruf etmeyeceksiniz.

Bununla birlikte, deltalara dayalı iyi tasarlanmış bir sistemin trafiği kurtarabilmesi için iki yol vardır.

İlk olarak, birçok durumda delta sıfır olacaktır. Protokolünüzü delta sıfır olduğunda hiç göndermeyeceğiniz şekilde tasarlayabilirsiniz. Açıkçası, ne gönderdiğinizi veya göndermeyeceğinizi belirtmek zorunda olduğunuz için bazı ek yükler var, ancak genel olarak büyük bir kazanç olması muhtemel.

İkincisi, deltalar genellikle orijinal numaralardan çok daha küçük bir değer aralığına sahip olacaktır. ve bu aralık sıfır merkezli olacaktır. Bu, çoğu delta için daha küçük bir veri türü kullanarak veya tüm veri akışını genel amaçlı bir sıkıştırma algoritmasından geçirerek kullanılabilir.


6

Çoğu cevap, delta kodlamanın yalnızca değişiklikleri bir bütün olarak duruma göndermekle ilgili olduğu hakkında konuşsa da, tam durumda sıkıştırmanız gereken veri miktarını azaltmak için filtre olarak kullanılabilecek "delta kodlama" adı verilen başka bir şey daha vardır. Ayrıca, sorulan sorudaki karışıklığın nereden geldiği de olabilir.

Bir sayı vektörünü kodlarken, bazı durumlarda (örneğin tam sayılar, sayılar vb.) Tek tek öğeler için değişken baytlı bir kodlama kullanabilir ve bu durumlarda bazılarında her öğenin ihtiyaç duyduğu veri miktarını daha da azaltabilirsiniz. Bunu, çalışan toplamlar olarak ya da minimum değer ve her değer ile bu minimum arasındaki fark olarak saklarsanız.

Örneğin, vektörü kodlamak istiyorsanız, {68923, 69012, 69013, 69015}o zaman delta kodlayabilirsiniz {68923, 89, 1, 2}. Bayt başına 7 bit veri depoladığınız ve başka bir bayt geldiğini belirtmek için bir bit kullanan önemsiz bir değişken baytlık kodlama kullanmak, dizideki öğelerin her birinin iletmek için 3 bayt gerektirmesi gerekir, ancak delta kodlu sürüm sadece ilk eleman için 3 bayt, kalan elemanlar için 1 bayt gerektirecektir; Serileştirdiğiniz veri türlerine bağlı olarak bu, oldukça etkileyici tasarruflara yol açabilir.

Bununla birlikte, bu bir seri hale getirme optimizasyonundan daha fazlasıdır ve genel olarak oyun durumunun (veya videonun veya benzerlerinin) bir parçası olarak rasgele veri akışı söz konusu olduğunda "delta kodlaması" hakkında konuştuğumuzda ne anlama geldiğini; diğer cevaplar da bunu açıklamak için yeterli bir işi yapmıştır.


4

Sıkıştırma algoritmalarının, işlerini farklı alanlarda daha iyi yaptığını belirtmek de önemlidir. Diğer cevaplardan bahsedildiği gibi, elementlerinizin çoğu 2 durum arasında aynı kalır veya değerler küçük bir kesirle değişir. Her iki durumda da, sayı vektörünüzün farkına bir sıkıştırma algoritması uygulamak size önemli tasarruflar sağlar. Eğer bile yok 0 unsurları kaldırarak gibi vektör için herhangi bir ekstra mantığı geçerlidir.

İşte Python'da bir örnek:

import numpy as np
import zlib
import json
import array


state1 = np.random.random(int(1e6))

diff12 = np.r_[np.random.random(int(0.1e6)), np.zeros(int(0.9e6))]
np.random.shuffle(diff12) # shuffle so we don't cheat by having all 0s one after another
state2 = state1 + diff12

state3 = state2 + np.random.random(int(1e6)) * 1e-6
diff23 = state3 - state2

def compressed_size(nrs):
    serialized = zlib.compress(array.array("d", nrs).tostring())
    return len(serialized)/(1024**2)


print("Only 10% of elements change for state2")
print("Compressed size of diff12: {:.2f}MB".format(compressed_size(diff12)))
print("Compressed size of state2: {:.2f}MB".format(compressed_size(state2)))

print("All elements change by a small value for state3")
print("Compressed size of diff23: {:.2f}MB".format(compressed_size(diff23)))
print("Compressed size of state3: {:.2f}MB".format(compressed_size(state3)))

Bu bana verir:

Only 10% of elements change for state2
Compressed size of diff12: 0.90MB
Compressed size of state2: 7.20MB
All elements change by a small value for state3
Compressed size of diff23: 5.28MB
Compressed size of state3: 7.20MB

Güzel örnek Sıkıştırma burada bir rol oynar.
Leopoldo Sanczyk

0

Konumunuz vector3'te saklanırsa, ancak gerçek varlık bir anda yalnızca birkaç tam sayı taşıyabilir. O zaman onu bayt cinsinden bir delta göndererek, tamsayı olarak göndermekten daha iyi olurdu.

Şu anki pozisyon: 23009.0, 1.0, 2342.0 (3 şamandıra)
Yeni pozisyon: 23010.0, 1.0, 2341.0 (3 şamandıra)
Delta: 1, 0, -1 (3 bayt)

Her zaman kesin pozisyonu göndermek yerine, deltayı göndeririz.


0

Delta sıkıştırma, delta kodlu değerlerin sıkıştırılmasıdır. Delta kodlaması, sayıların farklı istatistiksel dağılımlarını üreten bir dönüşümdür. Dağıtım, seçilen sıkıştırma algoritmasına uygunsa, veri miktarını düşürür. Varlıkların iki güncelleme arasında sadece biraz hareket ettiği bir oyun gibi bir sistemde çok iyi çalışır.

Diyelim ki 2B'de 100 varlığınız var. Büyük bir ızgarada, 512 x 512. Yalnızca örnek olarak tamsayılar göz önünde bulundurulur. Bu, varlık başına iki tam sayı veya 200 sayıdır.

İki güncelleme arasında tüm konumlarımız 0, 1, -1, 2 veya -2 olarak değişir. 100 0, 33 1 ve -1 örneği ve sadece 17 ve 2 ve -2 örnekleri olmuştur. Bu oldukça yaygındır. Huffman'ı sıkıştırma için kodlamayı seçiyoruz.

Bunun için Huffman ağacı:

 0  0
-1  100
 1  101
 2  110
-2  1110

Tüm 0'larınız tek bir bit olarak kodlanacaktır. Bu sadece 100 bit. 66 değer 3 bit ve sadece 34 değer 4 bit olarak kodlanacaktır. Bu 434 bit veya 55 bayt. Ayrıca ağaç küçüldükçe, haritalama ağacımızı kurtarmak için küçük bir ek yük. 5 sayıyı kodlamak için 3 bit gerektiğini unutmayın. Burada '-2' için 4 bit kullanma gereksinimi için '0' için 1 bit kullanma yeteneğini sattık.

Şimdi bunu 200 rastgele sayı göndermek için karşılaştırın. Eğer varlıklarınız aynı döşemede olamazsa, neredeyse kötü bir istatistiksel dağılım elde edeceğiniz garanti edilir. En iyi durum 100 benzersiz sayı olacaktır (hepsi farklı Y ile aynı X'te). Bu sayı başına en az 7 bit (175 bayt) ve herhangi bir sıkıştırma algoritması için çok zor.

Delta sıkıştırma, varlıklarınız yalnızca biraz değiştiğinde özel durumda çalışır. Çok fazla benzersiz değişikliğiniz varsa, delta kodlaması yardımcı olmaz.


Delta kodlamanın ve sıkıştırmanın, diğer dönüşümlerde de diğer durumlarda kullanıldığını unutmayın.

MPEG resmi küçük kareler halinde böler ve resmin bir kısmı hareket ederse sadece hareket ve değişim parlaklık olur. 25fps'lik bir filmde, kareler arasındaki birçok değişiklik çok küçük. Yine, delta kodlama + sıkıştırma. Statik sahneler için en iyi şekilde çalışır.

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.