Porovnání rychlosti - C@ARM@HP-50g vs. UserRPL


Úvod

  

Egan Ford ve své práci Extend your 50g with C popisuje řadu výhod, které vytváření programů v jazyku C pro HP-50g přináší. Zároveň však s touto ideou na řadě míst polemizuje. Hned zkraje úvodní kapitoly je odstavec nadepsaný Why not C?. O něj se otírá předchozí materiál citací druhého bodu z výčtu důvodů proč v "céčku" programy pro HP-50g nepsat. Nápadným důvodem PROČ NE je citovaná skutečnost, že "Small UserRPL routines may be good enough". To by mělo být pro pacienty trpící simplifikační obsesí obzvláště důležitým mementem. A právě fakt, že krátké programy v UserRPL mohou být dost dobré, je leitmotivem tohoto textu.

V závěru první kapitoly Fordova textu je odstavec (nebo spíše podkapitola) s nadpisem, který se čertvíproč shoduje s názvem celého dokumentu: Extend your 50g with C. Funkce pro výpočet aritmeticko-geometrického průměru je zde ve dvou vzájemně konfrontovaných provedeních lišících se použitým programovacím jazykem: C a UserRPL. V textu autor uvádí, že the UserRPL version is as fast as the C version. Poněkud překvapivé, že? A právě proto je lákavé se tím zbývat.


Zdrojové texty C

  

Výchozí zdrojový text agm.c je pro účely provádění jednoduchých změn při různých typech měření mírně upraven. Spolu s úpravami získal nové jméno agm0.c (nepříliš invenční, přesto postačující :-).

/*
* agm0.c
*/
#include <hpgcc49.h>


#define  sys_slowOff()
#define  sys_slowOn()


int main(void)
{
    double a, b, c;

    sys_slowOff();

    a = sat_pop_real();
    b = sat_pop_real();

    if(CHECK_ARGS(a, b))
    {
        sat_push_real(a);
        sat_push_real(b);
        PUSH_EXIT_STATUS(1);
        sys_slowOn();
        return(0);
    }

    while(fabs(a - b) > 1e-10)
    {
        c = a;
        a = (a + b) / 2;
        b = sqrt(b * c);
        if(keyb_isAnyKeyPressed())
            break;
    }

    sat_push_real((a + b) / 2);
    PUSH_EXIT_STATUS(0);

    sys_slowOn();
    return(0);
}

Důležitá poznámka: Funkce změny rychlosti taktování procesoru sys_slowOff() a sys_slowOn() jsou vyřazeny. Z nezjištěných důvodů "nedělaly dobrotu" - program při opakovaném spouštění dával různé výsledky!

Modul agm0.c nejenže změnil jméno, on především ztratil vlastnost "samostatné přeložitelnosti". Vůbec to ale nevadí. Vše "poztrácené" lze snadno doplnit na úrovni preprocessoru. A toho právě využívají krátké moduly, které slouží k vytvoření tří různých variant binárního kódu.

/*
* agmck.c
*/
#define  PUSH_EXIT_STATUS   sat_push_real
#define  CHECK_ARGS(a, b)   (a < 0  ||  b < 0)


#include "agm0.c"
/*
* agmnc.c
*/
#define  PUSH_EXIT_STATUS   sat_push_real
#define  CHECK_ARGS(a, b)   0


#include "agm0.c"
/*
* AGM.c
*/
#define  PUSH_EXIT_STATUS(v)
#define  CHECK_ARGS(a, b)   0


#include "agm0.c"

Zdrojové texty RPL

  

Egan Ford staví do opozice vůči agm.c (zde agm0.c) zdrojový text v UserRPL, který je pod nadpisem AGM UserRPL již zmíněné podkapitololy Extend your 50g with C. RPL objekt AGMORI je jeho identickým dvojčetem. Ačkoliv je to čistý RPL kód neobsahující ani špetku algebraické notace, je úmyslně napsán tak, aby v něm byla jasně viditelná podobnost s programem v C. Lokální proměnné mají stejné významy i jména. Regulérní RPL program by však v praxi, zejména při takovéto jednoduchosti, vypadal jinak. Možnou variantu takové jinakosti ukazuje objekt AGMSTK. Lokální proměnné jsou zde vynechány - mezivýsledky jsou uloženy na RPL stack. Je zajímavé, jak dramaticky se tato úprava projeví v rychlosti provádění (viz Závěr).

AGMORI
« → a b
  «
    WHILE a b - ABS .0000000001 >
    REPEAT
      a b + 2. /
      a b * √
      'b' STO
      'a' STO
    END
    a b + 2. /
  »
»
AGMSTK
«
  WHILE DUP2 - ABS .0000000001 >
  REPEAT
    DUP2 + 2. /
    UNROT * √
  END
  + 2. /
»

Druhou skupinou zdrojových textů RPL je nezajímavý balast sloužící k vlatnímu měření. Objekty AGM.CK a AGM.NC spolu s WRAPPER "obalují" binární kódy kvůli interakci s RPL. Objekt CYCLE0 obstará opakované provádění funkcí AGM, aby se všeobjímajícímu RUN snáze měřila rychlost provádění pro každý program v aktuálním adresáři se jménem začínajícím "AGM".

AGM.CK
« 'agmck.hp' WRAPPER
»
AGM.NC
« 'agmnc.hp' WRAPPER
»
CYCLE0
«
  → fn lo hi
    « 0. lo hi
      FOR it
        hi 1. + it - it fn EVAL +
      NEXT
    »
»
RUN
«
  IF DEPTH 2. <
  THEN 1. 99.
  END
  2. →LIST → li
  «
    VARS 1.
    «
      IF DUP →STR "AGM" POS 2. ==
      THEN li +
      ELSE DROP
      END
    » DOLIST
  »
  1.
  « OBJ→ DROP PICK3 UNROT 'CYCLE0' TEVAL NIP SWAP →TAG »
  DOLIST OBJ→ DROP
»
WRAPPER
«
  IF EVAL
  THEN DOERR "Error"
  END
»

Pozn.: Součet výsledků jednotlivých iterací/výpočtů aritmeticko-geometrického průměru by v podprogramu CYCLE0 samozřejmě být nemusel. Je tam pouze pro parádu a možná trochu z důvodů čistě diagnostických...


Závěr

  

Program RUN byl spuštěn bez argumentů (prázdný RPL stack). Počet opakování a od něj odvozené hodnoty proměnných a a b si stanovil sám. Výsledky měření rychlosti provádění ukazuje kopie displeje kalkulátoru.

Největší "dravec" je podle očekávání AGM.hp, binární kód sestavený z modulu AGM.c (žádné kontroly argumentů, žádný exit-status, nepotřebuje wrapper).

Druhým nejrychlejším je AGMSTK. Z porovnání s AGMORI je vidět, že aparát lokálních proměnných znamená pro kalkulátor nezanedbatelnou zátěž. Tuctové operace se stackem (DUP2, UNROT) zjevně probíhají mnohem rychleji. Zejména UNROT musí být pro procesor Saturn hračkou, když se jedná pouze o záměny ukazatelů do paměti (pointer).

Pokud konečně dojde na porovnání AGMSTK s binárními kódy požadující wrapper, výsledek potvrzuje výrok Egana Forda: the UserRPL version is dovopraudy as fast as the C version.

Pozn.: Ze zobrazených výsledků je patrné, že rozdíl mezi agmck.hp a agmnc.hp je zanedbatelný. Dokonce je - překvapivě - agmck.hp rychlejší. Naměřené hodnoty fluktuují v určitém rozmezí. Při pořizování "snímku" naměřených hodnot se sešly právě takhle.

Seznam souborů v adresáři kalkulátoru je zde pro výstrahu. Pětimístné hodnoty délek souborů s binárními kódy v kombinaci s minimálním nárůstem rychlosti jejich provádění jasně říkají, že tudy cesta nevede. Jak pravil Mistr: "Small UserRPL routines may be good enough". Howgh.