Teknik Makale

HotXLS Deep Recalc ile Excel Formül Önbelleği Denetimi

HotXLS, her spreadsheet işlem hattının er ya da son sormak zorunda kaldığı soruyu cevaplar: çalışma kitabında saklanan sayılar, onları üreten formüllerle hâlâ örtüşüyor mu? CalculateAndVerify, bütün bağımlılık grafiğini izole bir overlay'e yeniden hesaplar, her sonucu hücrede duran önbellek değeriyle karşılaştırır ve uyuşmazlıkları bildirir. Varsayılan olarak hiçbir şeyi değiştirmez

Bunun önem taşımasının nedeni, bir spreadsheet dosyasının her formül hücresi için iki şey saklamasıdır: formül ve biri onun için en son hesapladığı değer. Excel ikisini senkron tutar. Dünyanın geri kalanı tutmayabilir. Eski bir kütüphaneden geçmiş, kısmi yeniden hesaplanmış, elle düzenlenmiş bir XML parçası taşıyan ya da değerleri yeniden hesaplamadan yazan bir aracın elinden çıkmış bir dosya, girdilerinden artık türemeyen bir toplamı gönül rahatlığıyla sunar ve dosya formatında bunu işaretleyen hiçbir şey yoktur

Formülüyle uyuşmayan bir önbellek değeri neden bu kadar tehlikelidir?

Çünkü her sıradan okuma yolunda görünmezdir. Dosyayı bir görüntüleyicide açın, hücreyi bir API üzerinden okuyun, CSV'ye ya da PDF'e aktarın; aldığınız önbellekteki sayıdır. Formül aynı hücrenin içinde sağda durur ve kimse ikisini karşılaştırmaz. Uyuşmazlık, ancak biri çalışma kitabını Excel'de açtığında (çoğu ayarda yüklemede yeniden hesaplar) ortaya çıkar ve geçen çeyrek onaylanmış bir rapor bir anda farklı toplamlar gösterir

Denetim, o karşılaştırmayı bir kaza olmaktan çıkarıp kasıtlı, planlanmış bir işlem yapmak için vardır. Spreadsheet dünyasının checksum doğrulamasıdır: girdi işlem hattında çalışacak kadar ucuzdur ve sessiz bir veri bütünlüğü sorununu üzerine hareket edebileceğiniz bir rapora çeviren tek şeydir

var
  Book: TXLSWorkbook;
  Options: TXLSRecalcAuditOptions;
  Report: TXLSCalculationAuditReport;
  I: Integer;
begin
  Book := TXLSWorkbook.Create(nil);
  try
    Book.LoadFromFile('quarterly-close.xls');
    Options := TXLSRecalcAuditOptions.Default;
    Options.MaxIssues := 500;
    Report := Book.CalculateAndVerify(Options);
    try
      for I := 0 to Report.Count - 1 do
        if Report[I].Kind = xlcaiCacheMismatch then
          Writeln(Report[I].SheetName, '!',
                  Report[I].Row, ':', Report[I].Col, '  ',
                  Report[I].Formula,
                  '  cached=', VarToStr(Report[I].Actual),
                  '  recomputed=', VarToStr(Report[I].Expected));
      if Report.Truncated then
        Writeln('issue budget reached, raise MaxIssues');
    finally
      Report.Free;
    end;
  finally
    Book.Free;
  end;
end;

Üç overload vardır ve üç farklı soruya cevap verirler. Parametresiz CalculateAndVerify bir uyuşmazlık sayısı döndürür; sağlık kontrolü için gereken de budur. Uyuşmazlıkları out dizisiyle alan overload hücreleri verir. TXLSRecalcAuditOptions alan overload ise tam bir TXLSCalculationAuditReport döndürür; yalnızca bir değerin uyuşmadığını değil, denetimin bir şeyi neden değerlendiremediğini bilmeniz gerektiğinde uzanacağınız da odur

Overlay ve denetimin neden yazmadığı

Yeniden hesaplanan her değer hücre önbelleğine değil bir overlay'e düşer ve overlay, her iki çalışma kitabı motorunda da hücre okuma callback'inin en başına enjekte edilir. Denetimi kendi içinde tutarlı yapan yerleşim budur: B1 yeniden hesaplandığında ve C1, B1'e bağımlıysa C1, bayat önbellek değerini değil bu denetim geçişinin değerini görür. Bu olmadan tek bir yukarı akış hatası bir kez bildirilip sonra yutulur ve her aşağı akış hücresi yanlış bir girdiyle hemfikir görünürdür

Yeniden hesaplanan değeri önbellekle eşleşen hücreler overlay'e hiç girmez. Bu mikro optimizasyon değil, denetimi makul maliyetli tutan şeydir. Yüz bin formüllü temiz bir çalışma kitabı sıfır overlay yazması yapar ve geçiş, tam yeniden hesaplamaya göre 1.35x bütçenin içinde kalır; bu, her girdide çalıştırabileceğiniz bir şeyle çeyrekte bir çalıştırdığınız bir şey arasındaki farktır

HotXLS deep recalc denetim işlem hattı: çalışma kitabı önbelleklerine dokunulmadan yüklenir, her bağımlılık nodeu kirli işaretlenip topolojik sırada bir kez değerlendirilir, yeniden hesaplanan değerler her iki motorda da hücre okuma callbacki tarafından önce danışılan izole bir overlaye düşer, sonuçlar önbellek değerleriyle karşılaştırılır, CalculateAndVerify üzerinden TXLSCalculationAuditReport olarak sınıflandırılır ve diske hiçbir şey yazılmaz
Yeniden hesaplanan değerler, hücre okuma callback'inden önce bir overlay'e düşer; eşleşen hücreler ona hiç dokunmaz ve diskteki çalışma kitabı, ApplyResults tümüyle temiz bir geçişi commit edene kadar dokunulmadan kalır

Değerlendirme, bağımlılık grafiğinden türetilen seriyel bir topolojik sırayı izler; önce her node kirli işaretlenir, böylece her hücre girdilerinden sonra tam bir kez hesaplanır. Saklanmış bir çalışma kitabını denetlemek yerine canlı bir kitabı güncel tutan artımlı mekanizmayı istiyorsanız, o ayrı bir mekanizmadır ve artımlı yeniden hesaplama ve bağımlılık grafiği makalesinde anlatılır

Başarısızlıklar yığın hâlinde değil sınıflandırılmış gelir

Denetimin değerlendiremediği bir hücre, değeri uyuşmayan bir hücreyle aynı bulgu değildir ve TXLSCalculationAuditIssueKind kategorileri ayrı tutar. xlcaiCacheMismatch değer uyuşmazlığıdır. xlcaiMissingFunction ile xlcaiMissingName, değerlendiricinin uygulatmadığı ya da çözümleyemediği bir şeyle karşılaştığını söyler. xlcaiUnsupportedArguments, desteklenen alt küme dışındaki argüman biçimlerini kapsar. xlcaiExternalReferenceDenied ile xlcaiExternalReferenceMissing, politika reddini olmayan bir çalışma kitabından ayırır. xlcaiCircularReference, xlcaiDataTableSkipped, xlcaiParseFailure, xlcaiCancelled ve xlcaiInternalFailure seti tamamlar

HotXLS denetim bulgu sınıflandırması: TXLSCalculationAuditIssueKind, xlcaiCacheMismatch olarak bildirilen değer uyuşmazlığını xlcaiMissingFunction, xlcaiMissingName, xlcaiUnsupportedArguments gibi değerlendirme başarısızlığı türlerinden, xlcaiExternalReferenceDenied ile xlcaiExternalReferenceMissing çiftinden ve xlcaiCircularReferenceten ayırır; pozitif bir Excel hata kodu ise başarısızlık değil sonuç sayılır
Bir tür değer uyuşmazlığını, geri kalanları değerlendiricinin bir hücreye hükmedememe nedenini bildirir; Excel hata değeri hesaplanmış bir sonuçtur, bu yüzden kasıtlı hata hücreleri sıfır bulgu üretir

Bir ayrım söylemeye değer; çünkü yaygın bir varsayımı tersine çevirir. Pozitif bir Excel hata kodu bir sonuçtur, başarısızlık değil. Meşru biçimde #DIV/0! değerine varan bir hücre doğru hesaplanmıştır; bu yüzden denetim o hatayı overlay'e yazar ve önbellekle herhangi bir değer gibi karşılaştırır. Kasıtlı hata hücreleriyle dolu bir çalışma kitabı sıfır bulgu üretir, değerler önbelleğe alındığından beri bir hatanın belirdiği ya da kaybolduğu bir çalışma kitabı ise tam istediğiniz bulguları üretir

Döngüsel referanslar ayrı bir muamele görür. Döngüdeki node'lar topolojik sıraya hiç giremez; her biri ayrı ayrı xlcaiCircularReference olarak bildirilir ve denetim, iteratif çözücüyü çalıştırmaz. Bu kasıtlı bir salt okunur sözleşmedir: iterasyonun açık olup olmaması, sonuç kodunun nasıl yorumlanacağını etkiler, denetimin ne yaptığını değil. İteratif değerlendirmenin mekaniği ayrıca iteratif hesaplama ve döngüsel referanslar makalesinde ele alınır

Bir başarısızlık zincirini okuma

Bir formül değerlendirilemediğinde hangi hücrenin kaldığını bilmek nadiren yeterlidir; çünkü başarısızlık genellikle bir referans zincirinde üç seviye aşağıdadır. Bu yüzden her bulgu, en dış çerçeveden içe doğru dizilmiş bir Stack dizgesi taşır (Sheet1!A1 > Sheet1!B2 > Data!C7 biçiminde); rapor, tesadüfen baktığınız hücreyi değil gerçekten kıran hücreyi işaret eder

Kaydedicinin sınırları vardır. MaxStackFrames varsayılanı 64'tür, tabanı 8'dir ve saklanan, en derindeki başarısız zinciridir: başarısızlık orada kök saldığında iç çerçeve zinciri kaydeder, ardından sökülen dış çerçeveler onun üzerine yazmaz. Herhangi bir zincir bütçeyi aştıysa Report.StackTruncated kurulur; bu, kısa bir zincirle tamamını görmediğiniz bir zincir arasındaki farkı söyler

HotXLS denetim başarısızlık zinciri: üç referans aşağıdaki bir formül kaldığında Stack en dış çerçeveden içe doğru Sheet1!A1, sonra Sheet1!B2, sonra Data!C7 olarak dizilir; en iç çerçeve zinciri kaydeder, sökülen dış çerçeveler üzerine yazmaz, MaxStackFrames varsayılanı 64 tabanı 8dir ve Report.StackTruncated tamamını görmediğiniz zinciri işaretler
Stack en dış çerçeveden içe doğru render eder; böylece rapor gerçekte kıran hücreyi işaret eder, saklanan en derin başarısız zinciridir ve StackTruncated kısa zincirleri kırpmış olanlardan ayırır
// Varsayılan davranış salt okunur. ApplyResults, overlay'i yalnızca
// tümüyle başarılı bir denetimden sonra uygular; yazma guardı, denetim
// sürerken çalışma kitabı yapısı değişirse commit'i reddeder
Options := TXLSRecalcAuditOptions.Default;
Options.ApplyResults := True;
Options.AbsoluteTolerance := 0;     // kesin karşılaştırma, sürüklenmeyi görünür kılar
Options.RelativeTolerance := 0;
Options.OnProgress := HandleProgress;

Report := Book.CalculateAndVerify(Options);
try
  if Report.Applied then
    Book.SaveToFile('quarterly-close-repaired.xls')
  else
    Writeln('not applied: ', Report.Count, ' issues blocked the commit');
finally
  Report.Free;
end;

procedure THarness.HandleProgress(ASender: TObject;
  ACurrent, ATotal: Integer; var ACancel: Boolean);
begin
  ACancel := FUserRequestedStop;   // denetim bir sonraki node sınırında durur
end;

Denetime çalışma kitabını ne zaman onartmalısınız?

Yalnızca denetim, başarısızlık sınıfı bulgulardan tümüyle temiz döndüğünde; bu da tam olarak ApplyResults'un sizin adınıza dayattığı koşuldur. Commit, tümüyle başarılı bir geçişten sonra, iptal edilmemişken ve yapısal bir guard'dan geçtikten sonra gerçekleşir: binary motor bir çalışma kitabı değişiklik tanımlayıcısını izler, OOXML motoru sayfa başına yapı neslinin anlık görüntüsünü alır. Denetim sürerken herhangi bir şey kaydıysa sonuçlar artık var olmayan bir çalışma kitabını tarif eder ve commit reddedilir

Kasıtlı asimetriye dikkat. Önbellek uyuşmazlıkları uygulamayı engellemez; çünkü commit'in onarmak için var olduğu şey tam da onlardır. Başarısızlık sınıfı bulgular engeller; çünkü bazı formülleri değerlendirilememiş bir çalışma kitabı yarım onarılmış olur ve yarım onarılmış bir kitap, güvenmemesi gerektiğini bildiğiniz onarılmamış bir kitaptan kötüdür

Tolerans bir varsayılan değil, politika kararıdır

Varsayılan karşılaştırma, göreli tolerans kapalıyken 1E-6 mutlak toleranstır; klasik davranışı korur ve 4E-7'lik bir sürüklenmeyi sessizce kabul eder. Bu genellikle doğrudur: dosyayı üreten her neyse onunla güncel değerlendirici arasındaki kayan nokta değerlendirme sırası farkları, uzun toplamlarda bu büyüklükte farklar üretir ve bunları bütünlük bulgusu olarak bildirmek gürültüdür

Soru farklıysa her iki toleransı da sıfıra çekin: bir değerlendiricinin sürümler arasında davranış değiştirip değiştirmediğini ya da üçüncü taraf bir aracın değerleri sinsi biçimde farklı yeniden yazıp yazmadığını öğrenmeye çalışıyorsunuzdur. Sıfırda aynı 4E-7'lik sürüklenme görünür olur; gerisi de öyle. Toleransı sorduğunuz soruya göre seçin ve tercihi raporun yanına yazın; toleransı olmayan bir rapor yorumlanamaz

Komşu iki yetenek tabloyu tamamlar. Tek bir formülün neden o değeri ürettiğini bilmek istediğinizde doğru araç, formül değerlendirme izleyicisindeki adım adım görünümdür. Önbellek değerlerinin hiç yeniden hesaplanmadan sayılmasını kasıtlı olarak istediğinizde (örneğin dosyayı geldiği gibi birebir üretmek zorunda olan bir girdi yolunda) o mod, önbellekteki formül değerlerini yeniden hesaplamadan okuma makalesinde anlatılır. Denetim, ikisinin arasında duran şeydir: önbelleğe güvenmenin güvenli olup olmadığını söyler. Hem binary hem OOXML motorları için HotXLS Delphi spreadsheet bileşeni ile birlikte gelir