HotPDF, birçok PDF sayfasını iki giriş noktası aracılığıyla eşzamanlı olarak render eder: bir bit eşlem dizisi döndüren RenderLoadedPagesParallel ve her sayfayı hazır olur olmaz giriş sırasına göre bir geri çağrıya teslim eden RenderLoadedPagesParallelOrdered. Ölçeklenen biçim sıralı olandır, çünkü kaç sayfa istediğinizden bağımsız olarak bellekte her zaman sınırlı sayıda bit eşlem tutar
Bu ayrım yazının tamamıdır. 10.000 sayfayı 300 DPI'de bir diziye render etmek, 10.000 canlı bit eşlem demektir; bu bir aktarım hızı sorunu değil, bellek yetersizliği çökmesidir. Bunları kuyruk derinliği iki olan sıralı bir geri çağrı üzerinden render etmek iki canlı bit eşlem demektir ve iş, belge ne kadar uzun olursa olsun sabit bir ayak izinde çalışır
Paralel Render Etme Neden İki Aşamaya İhtiyaç Duyar?
Bir PDF sayfasını render etmek, tek bir ad taşıyan iki farklı iştir. Önce içerik akışı belirteçlere ayrılır ve bir görüntü listesine derlenir; bu CPU bağımlıdır ve paylaşılan önbelleklere dokunur. Ardından görüntü listesi bir bit eşleme geri oynatılır; bu da CPU bağımlıdır ama yoğun şekilde bellek ayırır. Bunları bölünemez tek bir birim olarak ele almak, önbelleğe dokunan yarıyı sıralı hale getirmekle işi yinelemek arasında bir seçim yapmaya zorlar
HotPDF bunları ayırır. Görüntü listesi önbellek kilidi yalnızca sıklık muhasebesini, LRU sırasını, kullanım sayaçlarını ve kabul kararlarını korur; derlemenin kendisi kilidin dışında çalışır. Derlemeden sonra, çalışan işlem önbelleği yeniden sorgular: eğer bu sırada başka bir çalışan işlem aynı sayfayı yayımladıysa, kendi kopyasını serbest bırakır ve yayımlanan girdi üzerinde bir kullanım sayacı alır. Bu, yinelenen işi sınırlı tutar ve aynı sayfa için iki girdi oluşmasını önler; sayfalar arasında derlemeyi hiçbir zaman sıralı hale getirmeden
32 sayfalık karmaşık bir test düzeneğinde ölçülen etki, akılda kalması gereken kısımdır. Win64'te toplam süre, tek çalışan işlemle 1.565 ms'den dört çalışan işlemle 696 ms'ye, yaklaşık 2,25 kata düştü. İlk sonuca ulaşma süresi 1.255 ms'den 63 ms'ye, yaklaşık yüzde 95 düştü; çünkü çalışan işlemler, tam bir derleme bariyerinde beklemek yerine diğerleri hâlâ derlerken tamamlanmış görüntü listelerini geri oynatmaya başlıyor
Sıralı Teslimat ve Ayrılmış Yuva
Sıralı API, çalışan işlemler hangi sırayla bitirirse bitirsin, geri çağrınızın sayfaları istediğiniz sırada görmesini garanti eder. Tamamlanan bit eşlemler, giriş konumuna göre indekslenen bir çıktı yuvasına yayımlanır ve geri çağrı, çağıran iş parçacığında sıralı olarak çalışır
İki kural, bunu yalnızca sıralı değil, sınırlı da yapar. Çıktı kuyruğu sayısı, o anda geri çağrı tarafından kullanılan bit eşlemleri de içerir, bu yüzden yavaş bir geri çağrı, üreticilerin kuyruk derinliğini aşmasına izin veremez. Ve sonraki sayfalar en fazla queueDepth - 1 yuva işgal edebilir, çünkü bir yuva her zaman teslim sırası gelen bir sonraki sayfa için ayrılmıştır. Bu ayırma olmadan, yavaş bir ilk sayfa, kuyruğu dolduran tamamlanmış sonraki sayfalar tarafından kilitlenebilir ve boru hattı, tüm çalışan işlemler boşta beklerken sıranın başında kilitlenmeye girer
uses
HPDFDoc;
procedure TExportJob.PageReady(Sender: TObject; InputIndex,
PageIndex: Integer; Bitmap: TBitmap; var Cancel: boolean);
begin
// Bit eşlem ödünç alınmıştır: yalnızca bu çağrı için geçerlidir. Onu burada
// tüketin (diske yazın, kodlayın, özetleyin) ve referansı saklamayın
Bitmap.SaveToFile(Format('page-%.4d.bmp', [InputIndex + 1]));
Cancel := FUserCancelled;
end;
procedure TExportJob.Run;
var
Pdf: THotPDF;
Pages: array of Integer;
Info: THPDFParallelRenderPipelineInfo;
I: Integer;
begin
Pdf := THotPDF.Create(nil);
try
if Pdf.LoadFromFile('manual.pdf') <= 0 then
Exit;
SetLength(Pages, Pdf.LoadedPageCount);
for I := 0 to High(Pages) do
Pages[I] := I;
Pdf.RenderLoadedPagesParallelOrdered(Pages, 200, 4, 2,
PageReady, Info);
Writeln(Format('workers=%d peak queued=%d peak bytes=%d',
[Info.WorkerCount, Info.PeakQueuedPageCount, Info.PeakQueuedBytes]));
Writeln(Format('first result=%d ms total=%d ms backpressure waits=%d',
[Info.FirstResultMilliseconds, Info.TotalMilliseconds,
Info.BackpressureWaitCount]));
finally
Pdf.Free;
end;
end;
Ödünç bit eşlem sözleşmesi, ayak izinin sabit kalmasının nedenidir. Kitaplık, geri çağrınız döner dönmez bit eşlemi serbest bırakır, bu yüzden kuyruk derinliği 2 olan 10.000 sayfalık bir test düzeneği, hem Win32'de hem de Win64'te tam olarak iki bit eşlemde tepe yapar. Bit eşleme geri çağrıdan sonra ihtiyacınız varsa onu kopyalayın; o zaman belleğin ve muhasebenin sahibi siz olursunuz
Bellek Bütçesi Çalışan İşlem Sayısını Nasıl Seçer
Daha fazla çalışan işlem her zaman daha hızlı değildir ve çoğu zaman ölümcüldür. A3 sayfalarını 600 DPI'de render eden altı çalışan işlem, kaç çekirdeğin mevcut olduğundan bağımsız olarak birkaç yüz megabayt canlı bit eşleme ihtiyaç duyar ve sekiz çekirdeğe sahip bir makinede o kadar boş adres alanı bulunmayabilir
Bu yüzden HotPDF, istenen çalışan işlem sayısını istek boyutuna, sayfa sayısına ve sabit bir üst sınıra göre normalleştirir, ardından o anda kullanılabilir genel bütçeyi istekteki en büyük muhafazakâr tek sayfa tahminine böler. En az bir çalışan işlem her zaman hayatta kalır, bu yüzden aşırı büyük bir sayfa yine de tek başına render edilir. Referans ölçümde, 512 MiB bütçe altındaki altı sayfalık 600 DPI'lik bir iş, altı yerine üç çalışan işlemle çalıştı ve rezervasyon tepe noktası 504.583.296 bayt oldu
Her çalışan işlem bir sayfayı üstlendikten sonra ve derlemeden önce bir bellek rezervasyonu alır ve bu rezervasyon derleme ile geri oynatmayı birlikte kapsar. Yalnızca rasterleştirme aşaması çevresinde rezervasyon yapmak, grafik ağırlıklı sayfalarda daha büyük yarı olan görüntü listesini ve içerik akışı çalışma kümesini gözden kaçırırdı. İki API bu noktada farklılaşır: dizi biçimi, bit eşlem tamamlanır tamamlanmaz rezervasyonunu serbest bırakır, çünkü sonucun sahibi çağırandır ve zamanlayıcı artık onu hesaba katamaz; sıralı biçim ise rezervasyonu bit eşlemle birlikte çıktı yuvasına aktarır ve geri çağrı dönene kadar tutar. Bu, çıktı kümesi büyük olduğunda sıralı API'yi tercih etmenin bir başka nedenidir
Kilitlenme Olmadan İptal
Geri çağrıda Cancel'ı ayarlamak yeni işi durdurur ve geri basınç üzerinde bekleyen her çalışan işlemi uyandırır. Zaten render motorunun içinde olan sayfalar terk edilmek yerine tamamlanır ve temizlenir; yalnızca ilk başarısızlık tanılaması saklanır ve her çalışan işlem birleştikten sonra yükseltilir
Buradaki sıralama taşıyıcıdır. Önce iptal durumu ayarlanır, ikinci olarak yayımlanmamış çıktı rezervasyonları serbest bırakılır ve ancak ondan sonra çalışan işlemler birleştirilir. Son iki adımı tersine çevirmek kapalı bir döngü yaratır: bir çalışan işlem, tüketici bitirene kadar serbest bırakılmayacak bir bütçeye kabul edilmeyi beklerken, tüketici de o çalışan işlemin çıkmasını bekler
Etkileşimli uygulamaların ön plan önceliklendirmesi hakkında da bilgi sahibi olması gerekir. Sayfa önden getirme kendi iptal belirteçleri üzerinde çalışır ve ön plan render çağrıları, kendi işlerini yapmadan önce önden getirme çalışan işlemlerini iptal edip birleştirir; böylece arka plan aktarım hızı, bir kullanıcının beklediği sayfaya hiçbir zaman gecikme eklemez. İptal, 4 KiB'lik bayt taramasıyla veya 256 belirteçle sınırlı aralıklarla kontrol edilir; ancak atomik bir codec veya işletim sistemi çağrısı çalışırken kesilemez — yanıt verebilirlik garantisi kitaplığa ait döngüleri kapsar, üçüncü taraf kod çözücüleri değil. Etkileşimli render etmeye istek kuyruğu tabanlı yaklaşım istek kuyruğuyla arka planda render etme başlığında anlatılmıştır
Boru Hattı İstatistiklerini Okumak
THPDFParallelRenderPipelineInfo, kolayca birbirine karıştırılabilecek sayıları ayırır. RequestedWorkerCount'un WorkerCount ve MemoryBudgetWorkerLimit ile karşılaştırılması, bütçenin eşzamanlılığınızı azaltıp azaltmadığını gösterir. CompiledPageCount'un CacheHitPageCount ile karşılaştırılması, görüntü listesi önbelleğinin ne kadar tasarruf sağladığını gösterir. CompiledPagesAtFirstResult, boru hattının derleme ile geri oynatmayı gerçekten örtüştürüp örtüştürmediğini yoksa bir bariyere mi dönüştüğünü gösterir
Ayarlama için en yararlı iki değer BackpressureWaitCount ve MemorySchedulerWaitMilliseconds'tur. Yüksek geri basınç bekleyişleri, geri çağrınızın darboğaz olduğu anlamına gelir; bu yüzden ya onu hızlandırın ya da bellek izin veriyorsa kuyruk derinliğini artırın. Yüksek zamanlayıcı bekleyişleri, bütçenin darboğaz olduğu anlamına gelir; bu yüzden DPI'yi azaltın, eşzamanlılığı azaltın veya bütçeyi artırın. Her iki durumda da çalışan işlem sayısını artırmak aktarım hızını kötüleştirir; sezgiye aykırı olan kısım da budur
Toplu dışa aktarma için pratik bir başlangıç noktası: fiziksel çekirdek sayısı eksi bir kadar eşzamanlılık, iki veya üç kuyruk derinliği ve alışkanlıktan değil gerçek çıktı gereksiniminden seçilen DPI. Ardından gerçek bir belgeden istatistikleri okuyun ve iki kez tahmin etmek yerine bir kez ayarlayın. Belgenin kendisinin bellekte tutulamayacak kadar büyük olduğu işler için bunu büyük PDF iş akışları için doğrudan dosya API'si başlığındaki akış tabanlı erişim modeliyle birleştirin
Paralel render etme, önden getirme ve görüntü listesi önbelleği, Delphi ve C++Builder için aynı render yığınının parçalarıdır; eksiksiz özellik listesi HotPDF Delphi PDF bileşeni sayfasında bulunur