Studie av utvecklingsverktyg med inriktning mot PLC-system (HS-IDA-EA-99-103) Christoffer Brax ([email protected]) Institutionen för datavetenskap Högskolan i Skövde, Box 408 S-54128 Skövde, SWEDEN Examensarbete på program för systemprogrammering under vårterminen 1999. Handledare: Joakim Eriksson Studie av utvecklingsverktyg med inriktning mot PLC-system Examensrapport inlämnad av Christoffer Brax till Högskolan i Skövde, för Kandidatexamen (B.Sc.) vid Institutionen för Datavetenskap. 99-06-18 Härmed intygas att allt material i denna rapport, vilket inte är mitt eget, har blivit tydligt identifierat och att inget material är inkluderat som tidigare använts för erhållande av annan examen. Signerat: _______________________________________________ Studie av utvecklingsverktyg med inriktning mot PLC-system Christoffer Brax ([email protected]) Sammanfattning Datoranvändningen i samhället växer för varje dag. Det är då viktigt att programvara håller hög kvalité, då vissa programvaror styr kritiska maskiner som exempelvis flygplan. Ett sätt att få kvalitativa programvaror är att använda bra utvecklingsverktyg. I detta arbete utvärderas fem olika utvecklingsverktyg: GNAT (Ada), Microsoft Visual C++, Microsoft J++, Borland Delphi och Active Perl. Inriktningen på utvärderingen är mot utveckling av programvara för PLC-system. Det som utvärderats är effektivitet av genererad kod, kompileringstid, storlek på genererad kod, kodportabilitet och utbud av färdiga komponenter. Undersökningen har genomförts med hjälp av praktiska tester. Nyckelord: Kompilering, Realtidssystem PLC-system, Benchmark, Utvecklingsverktyg, Innehållsförteckning 1 2 Introduktion ......................................................................................1 Bakgrund och begrepp.....................................................................2 2.1 Utvecklingsprocessen vid programvaruutveckling ......................................... 2 2.2 Allmänt om utvecklingsmiljöer....................................................................... 2 2.3 Allmänt om kompilatorer ................................................................................ 2 2.4 Programmeringsspråk som ingår i de utvärderade utvecklingsverktygen....... 5 2.4.1 C/C++ ......................................................................................................... 5 2.4.2 Pascal .......................................................................................................... 6 2.4.3 Java ............................................................................................................. 6 2.4.4 Ada/Ada95.................................................................................................. 7 2.4.5 Perl.............................................................................................................. 7 3 2.5 Realtidssystem................................................................................................. 7 2.6 PLC-system ..................................................................................................... 8 Problemprecisering ..........................................................................9 3.1 Problemformulering ........................................................................................ 9 3.1.1 Kvantitativa kriterier................................................................................... 9 3.1.2 Kvalitativa kriterier..................................................................................... 9 3.1.3 Undersökningskriterier ............................................................................. 10 3.1.4 Val av programmeringsspråk för undersökningen ................................... 10 3.2 4 Avgränsning .................................................................................................. 11 Metoder............................................................................................13 4.1 Jämförelse av metoder................................................................................... 13 4.1.1 Kvantitativa kriterier................................................................................. 13 4.1.2 Kvalitativa kriterier................................................................................... 15 4.2 Vald metod .................................................................................................... 15 4.3 Precisering av utvärderingskriterier för undersökningen .............................. 15 4.4 Benchmarks ................................................................................................... 17 4.4.1 Språkfunktionstester ................................................................................. 17 4.4.2 Testfall för kodeffektivitet........................................................................ 18 4.4.3 Testfall för kompileringshastighet............................................................ 19 5 Genomförande ................................................................................20 I 5.1 Förutsättningar för tester ............................................................................... 20 5.2 Effektivitet hos genererad kod ...................................................................... 21 5.2.1 Loop overhead .......................................................................................... 21 5.2.2 Lokal variabeltilldelning........................................................................... 22 5.2.3 Global variabeltilldelning ......................................................................... 22 5.2.4 Matristilldelning ....................................................................................... 23 5.2.5 Uppräkning av 8-bitarsvariabel ................................................................ 23 5.2.6 Uppräkning av 16-bitarsvariabel .............................................................. 24 5.2.7 Uppräkning av 32-bitarsvariabel .............................................................. 24 5.2.8 Uppräkning av 64-bitarsvariabel .............................................................. 25 5.2.9 Uppräkning av 64-bitars flyttalsvariabel .................................................. 25 5.2.10 Matrisallokering ..................................................................................... 26 5.2.11 Funktionsanrop....................................................................................... 27 5.2.12 Anrop av matematisk funktion 1 ............................................................ 27 5.2.13 Anrop av matematisk funktion 2 ............................................................ 28 5.2.14 Villkorstest ............................................................................................. 29 5.2.15 Strängallokering ..................................................................................... 29 5.2.16 Typecasting ............................................................................................ 30 5.2.17 Rekursivt anrop ...................................................................................... 31 5.2.18 Lintest..................................................................................................... 31 5.3 Kompileringshastighet................................................................................... 32 5.4 Storlek på kompilerad kod ............................................................................ 33 5.4.1 Filstorlekar vid kodeffektivitetstester ....................................................... 33 5.4.2 Filstorlekar vid kompileringshastighetstester ........................................... 34 6 5.5 Kodportabilitet............................................................................................... 35 5.6 Utbud av färdiga komponenter...................................................................... 36 Analys av resultat ...........................................................................37 6.1 Slutsatser av kvantitativa undersökningar..................................................... 37 6.1.1 Kodeffektivitet.......................................................................................... 37 6.1.2 Kompileringshastighet.............................................................................. 38 6.1.3 Storlek på kompilerad och länkad kod ..................................................... 38 6.2 Slutsatser av kvalitativa undersökningen ...................................................... 39 6.2.1 Kodportabilitet.......................................................................................... 39 6.2.2 Utbud av färdiga komponenter ................................................................. 40 II 7 Sammanfattning .............................................................................41 7.1 Framtida arbete.............................................................................................. 42 Referenser................................................................................................43 Bilaga A. Källkod för benchmarkprogram............................................1 Källkod för Ada-program ......................................................................................... 1 Källkod för C++-program......................................................................................... 2 Källkod för Javaprogram .......................................................................................... 6 Källkod för Delphiprogram .................................................................................... 12 Källkod för Perlprogram ......................................................................................... 16 Bilaga B. Poäng vid betygsbedömningar................................................1 III 1 Introduktion 1 Introduktion I takt med att samhället blir mer och mer datoriserat så ökar kraven på tillförlitlighet hos produkter som innehåller mikroprocessorer. Dessa produkter är numera inte bara PC-datorer utan även mobiltelefoner, mikrovågsugnar, bilar, industriella styrsystem, TV-apparater etc. Detta leder till att större krav ställs på programvarorna. En bil får till exempel inte börja accelerera okontrollerat för att programvaran är felaktig. Kravet på tillförlitliga programvaror för dessa system ökar. Det finns många aspekter som måste beaktas när ett system med krav på tillförlitlighet ska utvecklas. Dels så måste själva hårdvaran vara av bra kvalité, om hårdvaran går sönder leder detta ofta till att systemet ”går ner”. Idag är det vanligare att det är något fel på mjukvaran än på hårdvaran i ett system. Detta beror på att mjukvaran ofta blir väldigt komplex och är svårare att kontrollera än hårdvaran. Ett sätt att få en programvara som är tillförlitlig är att använda ett utvecklingsverktyg som underlättar för programmeraren. Med tillförlitlighet (eng. reliability) menas att en programvara uppför sig på det sätt den är avsedd att göra i alla situationer (även i onormala situationer). Vid val av utvecklingsverktyg finns det många aspekter att ta hänsyn till. Dels själva utvecklingsmiljön, vilka hjälpmedel det finns som förenklar arbetet och dels själva kompileringsdelen som innehåller kompilatorn, bibliotekshanterare, länkare och avlusare. Det är också viktigt att utvecklingsverktyget är anpassad till den typ av applikation som ska skrivas. Varje program har speciella krav på funktionalitet, och vissa program kan till exempel ha krav på att utföra sina uppgifter inom en förutbestämd tid. Dessa kallas realtidssystem, på denna typ av system ställs speciella krav på kompilatorn för att systemet ska uppfylla vissa kriterier, som exempelvis förutsägbarhet och punktlighet. I andra system är dessa krav inte alls lika viktiga och där kanske andra aspekter hos kompilatorn är mer betydande som exempelvis kompileringshastighet, avlusningsmöjligheter och inbyggda funktioner. Utveckling av programvara är ofta en itererande process. Detta innebär bland annat att själva kompileringen måste utföras många gånger under utvecklingsarbetet. Det är då en fördel om kompileringstiden är kort, vilket leder till kortare väntetider för utvecklaren. Andra detaljer som är viktiga är hur väl de olika delarna i utvecklingsmiljön är integrerade med varandra, hur felsökning kan göras och hur bra stöd för tredjepartsbibliotek som finns. I detta arbete utvärderas ett antal utvecklingsmiljöer. Dessa är baserade på ett antal olika programmeringsspråk. De språk som används är C/C++, Pascal, Java, Ada och Perl. Inriktningen på utvärderingen kommer att vara mot PLC-system (Programable Logic Controller). PLC-system är mycket vanliga inom industrin där de används för att styra allt ifrån dörrar och fönster till stora maskiner. Vid utvärderingen används ett antal utvärderingskriterier. Dessa kriterier är prestanda vid kompilering, prestanda vid exekvering av kompilerad kod, storlek på genererad kod, kodportabilitet och utbud av färdiga komponenter. 1 2 Bakgrund och begrepp 2 Bakgrund och begrepp Detta avsnitt beskriver centrala begrepp som används i detta arbete. Här beskrivs även de fem olika programmeringsspråken som ingår i de utvärderade utvecklingsverktygen. 2.1 Utvecklingsprocessen vid programvaruutveckling Enligt Pressman (1997) finns det ett antal olika modeller som beskriver utvecklingsprocessen vid programvaruutveckling. En vanlig modell är den som Pressman kallar för spiralmodellen. Spiralmodellen är en iterativ modell som består av ett antal olika faser, vanligtvis mellan tre och sex. Under utvecklingen växlar arbetet mellan dessa olika faser ett antal varv tills det att den utvecklade programvaran är klar. De faser som Pressman tar upp är: kundkontakt, planering, riskanalys, utveckling, konstruktion & testning, och kundutvärdering. Var och en av dessa faser består av ett antal olika arbetsmoment. Beroende på projektets storlek varierar antalet arbetsmoment. Vid större projekt finns en del paraplyaktiviteter som till exempel kvalitetsförsäkran och konfigurationshantering. Var och en av de ovanstående faserna är själva itererande. I faserna konstruktion och testning är det en vanlig itererande process att programmeraren gör ändringar för att sedan testa programmet för att se om programmet uppför sig på rätt sätt. 2.2 Allmänt om utvecklingsmiljöer En utvecklingsmiljö består ofta av ett antal olika delar. Dessa delar kan till exempel vara en editor där programmen skrivs, kompilatorn och länkaren som skapar körbara filer, avlusaren som används för felsökning och som ofta är integrerad med editorn, hjälpfunktioner och bibliotekshanterare. Utöver dessa kan utvecklingsmiljön även innehålla verktyg för att till exempel utforma gränssnitt och skapa databaskopplingar. Vissa utvecklingsmiljöer innehåller även funktioner för att grafiskt rita upp programmet och skapar sedan kod efter den grafiska representationen. 2.3 Allmänt om kompilatorer En kompilator är enligt Aho, Sethi och Ullman (1986) ett program som läser in ett program skrivet i ett speciellt språk (källspråk) och översätter denna till ett annat språk (målspråk). Vid översättningen rapporterar kompilatorn eventuella fel i det översatta programmet till användaren. En kompilator består i huvudsak av åtta olika delar eller faser (Aho, Sethi och Ullman, 1986), och trots att det finns många olika programmeringsspråk så är de flesta kompilatorer uppbyggda på ungefär samma sätt. De åtta faserna besktriver Aho, Sethi och Ullman på följande sätt: 2 2 Bakgrund och begrepp Lexisk analys Lexisk analys innebär att varje programrad analyseras och alla element identifieras. Exempel: position := ursprunglig + förändring * 60 Den ovanstående raden skulle i den lexiska analysen delas upp i sju olika delar. 1. Identifieraren position 2. Tilldelningssymbolen := 3. Identifieraren ursprunglig 4. Plustecknet 5. Identifieraren förändring 6. Multiplikationstecknet 7. Värdet 60 Syntaxanalys Detta steg kallas ibland även ”parsing” och innebär att de olika elementen på en rad grupperas hierarkiskt i till exempel ett träd i form av grammatiska fraser som används av kompilatorn. I detta steg kontrolleras så att programmeraren inte har gjort några syntaxfel. Uppdelningen görs efter ett antal förutbestämda regler. Exempelvis så skulle uttrycket ursprunglig + förändring * 60 delas upp i två delar. Dels ursprunglig och dels förändring * 60. Detta på grund av de aritmetiska reglerna som säger att multiplikation går före addition. Semantisk analys Under den semantiska analysen undersöks om källprogramkoden har några semantiska fel. Vidare så samlas information om vilka olika datatyper som används. Denna information används bland annat för så kallad datatyp-kontroll vilket innebär att en undersökning görs om några otillåtna datatyper används. Exempelvis så är det i en del språk förbjudet att använda variabler av datatypen flyttal vid indexering av listor. Andra saker som undersöks är operationer mellan två olika datatyper som till exempel en addition av ett heltal och ett flyttal. Om en sådan operation inte kan utföras utan extra omvandlingar, så kan kompilatorn se till att lämpliga omvandlingar görs. Mellanliggande kodgeneration I denna fas genereras en abstrakt programkod. Syftet med detta är att på ett så bra sätt som möjligt binda ihop de tre första faserna med de två sista. Den abstrakta programkoden bör ha två egenskaper: den ska vara lätt att producera och lätt att översätta till målprogrammet. Genereringen av abstrakt kod kan ske på många olika sätt; ett exempel är ”three-adress code”, där varje minnesadress ses som ett register. ”Three-adress code” är uppbyggt av en sekvens med operationer. Dessa operationer 3 2 Bakgrund och begrepp får maximalt innehålla tre operander. Den tidigare exempelkoden skulle se ut på följande sätt: temp1 := id3 * 60 temp2 := id2 + temp1 id1 := temp2 Kodoptimering Kodoptimeringen försöker förbättra koden som genererats i den mellanliggande kodgenerationen, för att få en så snabb kod som möjligt. Även detta kan göras på många olika sätt med olika avancerade algoritmer. En enkel optimering av ovanstående kod skulle kunna vara: temp1 := id3 * 60 id1 := id2 + temp1 Vid detta steg måste en avvägning göras. Antingen kan koden optimeras så bra som möjligt vilket tar lång tid och ger snabba och små program eller också kan koden optimeras på ett enklare sätt som går snabbare men resulterar i större och långsammare program. Kodgenerering Är den sista fasen i kompileringen. Här översätts den mellanliggande koden till maskinkod eller assemblerkod. Symboltabell I symboltabellen lagras information om de identifierare som används i programmet. Här samlas även olika attribut för identifierarna. De attribut som lagas kan vara minnesadressen till identifieraren, identifierarens typ och var i programmet den gäller. Om identifieraren är en procedur så lagras procedurens namn, dess argument och typ hos eventuell returvariabel. Felhanterare I var och en av de ovan beskrivna faserna kan fel uppstå. Om ett fel uppstår i en av faserna så ska inte kompileringen stanna utan fortsätta så att fel längre fram i programmet kan detekteras. Felhanterarens uppgift är att ta hand om de fel som uppstår och lösa dessa så att kompileringen kan fortsätta. 4 2 Bakgrund och begrepp Källprogram Lexisk analys Syntaxanalys Semantisk analys Symboltabell hanterare Felhanterare Mellanliggande kodgeneration Kodoptimerare Kodgenerator Målprogram Bild 2.1: Olika faser i en kompilator 2.4 Programmeringsspråk utvecklingsverktygen som ingår i de utvärderade I detta arbete utvärderas utvecklingsverktyg baserade på fem olika programmeringsspråk. I detta avsnitt kommer en kort beskrivning av dessa fem språk. 2.4.1 C/C++ C utvecklades av Bell Laboratories 1972 och användes i början mest som programmeringsspråk för UNIX (som också är skrivet i C) (Houston, 1989). Numera har C flyttats till ett antal olika maskiner och finns nu till de flesta olika plattformar, från små så kallade en-chipsdatorer till stordatorer. C använder en två-pass kompilator. Med detta menas att kompilatorn gör två genomgångar av programkoden. Vid den första översätts alla funktionsnamn, variabler och anrop till minnesadresser som används vid det andra passet. Fördelen med detta är till exempel att en funktion eller variabel som anropas inte behöver vara deklarerad tidigare i koden utan kan vara deklarerad efter den punkt där den anropas. C++ är en vidareutveckling av C och utvecklades av Bjarne Stroustrup på AT&T Laboratories i början av 1980-talet. C++ skiljer sig ifrån C på tre viktiga punkter (Lippman, 1995): 1. Stöd för att skapa och använda dataabstraktioner 5 2 Bakgrund och begrepp 2. Stöd för objektorienterad design och programmering 3. Förbättringar av många av de befintliga C-funktionerna Trots detta behöll C++ många av de fördelar som fanns i C, som snabb exekvering och stor uttryckbarhet (Lippman, 1995). Även C++ finns portat till många olika plattformar och är numera också standardiserad av ANSI-kommittén (ANSI C++). C/C++ är ett av de mest använda språken och används för att utveckla många olika typer av applikationer. Det finns en mängd färdiga komponenter och bibliotek att köpa ifrån tredjepartstillverkare. 2.4.2 Pascal Pascal utvecklades av Nicklaus Wirth 1971 (Koffman, 1985). Syntaxen är relativt enkel att lära sig och programkoden är lätt att läsa och förstå, varför Pascal ofta används som programmeringsspråk vid programmeringskurser. Pascal är nu en ISO/ANSI-standard och det som skiljer Pascal-kompilatorer mest ifrån de som finns för C/C++ är att de är ”single pass”. ”Single pass” innebär att kompilatorn bara går igenom koden en gång. Detta medför att alla funktioner och variabler som används måste vara deklarerade tidigare i koden. Dessutom har Pascal hårdare ”typning” än C/C++. Med hårdare typning menas bland annat att omvandlingar mellan olika datatyper inte sker implicit som i C/C++ utan måste utföras explicit av programmeraren. 2.4.3 Java Java är utvecklat av Sun Microsystems och var från början tänkt att användas som ett plattformsoberoende operativsystem för hemelektronik (Cohn, 1996). Idag består Java av två delar. En del är själva programmeringsspråket och en del är programkörningsmiljön. Java skiljer sig ifrån traditionella programmeringsspråk som C++ och Pascal på det sättet att istället för att generera maskinkod för en speciell plattform så genererar Java en så kallad ”byte-kod” som kan interpreteras på vilken plattform som helst förutsatt att det finns en så kallad virtuell Javamaskin (Java Virtual Machine). Med kod som interpreteras menas att koden inte kompileras till maskinkod vid utvecklingsarbetet utan tolkas av en programtolk varje gång programmet körs. Syntaxen hos Java påminner om den som finns i C++ och har en del funktioner som inte finns i C++. Till exempel har Java automatisk minneshantering och support för multitrådade applikationer på språknivå (Cohn, 1996). Java är ett rent objektorienterat programmeringspråk och är enligt Cohn i många avseenden lättare att använda än C++ då det saknar multipla arv, pekare och goto-satser. Eftersom Java är ett interpreterande språk så är prestandan beroende av hur bra implementationen av den virtuella Javamaskinen är. Java är starkt på frammarsch inom många olika applikationsområden, även när det gäller PLC-system där speciella varianter för inbäddade och realtidssystem håller på 6 2 Bakgrund och begrepp att utvecklas. I denna undersökning kommer dock inte någon av dessa varianter att undersökas på grund av att de fortfarande är under utveckling. Det som gör Java speciellt intressant i detta arbete är för att se hur det står sig i prestandatesterna gentemot C/C++ och Pascal. 2.4.4 Ada/Ada95 Ada är ett hårt ”typat”, modulärt uppbyggt programmeringsspråk som påminner om Pascal och Modula (Burns, 1985). Ada utvecklades för den amerikanska militären där det användes som standardspråk för i stort sett all programmering. Ada är designat för att skapa feltoleranta program och innehåller funktioner för undantagshantering och modularitet. Den första ANSI-standarden för Ada kom 1983 och 1995 kom nästa standard, Ada95, som bland annat innehåller stöd för objektorientering och trådhantering (Kempe, 1996). 2.4.5 Perl Perl står för ”Practical Extraction Report Language” och utvecklades 1986 av Larry Wall som ville underlätta bearbetning av textfiler på UNIX (Husain, 1996). Perl är ett interpreterande programmeringsspråk som syntaxmässigt påminner om C. Perl innehåller bland annat mönstermatchning, modularisering, objektorientering och POSIX-kompatibilitet (Husain, 1996). Perl används i detta arbete för att se om ett skriptspråk med interpreterande kod, prestandamässigt går att använda i PLC-system. Interpreterande kod är intressant i ett PLC-system då det skulle vara möjligt att ändra i programkoden direkt i minnet på PLC-systemet, vilket eliminerar nerladdningstiden av programmet ifrån värddatorn där programmen skrivs till måldatorn där programmen körs. 2.5 Realtidssystem Ett realtidssystem är ett system som reagerar på en utomstående signal inom en förbestämd tid (Burns och Wellings, 1997), och kan delas upp i två grupper: hårda och mjuka realtidssystem. Ett hårt realtidssystem måste garantera att de tidskrav som är uppsatta hålls. Exempel på ett hårt realtidssystem är styrsystem i flygplan och om dessa inte håller tidskraven kan det resultera i en katastrof. I mjuka realtidssystem är tidskraven viktiga men systemet fungerar även om de inte uppfylls. Exempel på ett mjukt realtidssystem är bankomater som i värsta fall kan låta kunden vänta en längre tid på att få ut sina pengar. Enligt Burns och Wellings är många realtidssystem både hårda och mjuka, det vill säga de har mjuka tidskrav som ibland kan överskridas och hårda tidskrav som inte får överskridas. 7 2 Bakgrund och begrepp 2.6 PLC-system PLC står för ”Programable Logic Controller”, och är ett programmerbart styrsystem som används till många olika applikationer. Ett PLC-system är uppbyggt kring en central processor, in/ut-gränssnitt och ett minne. Själva styrprogrammet ligger i ett minne som kan programmeras av användaren. Detta styrprogram innehåller funktioner för in/ut-hantering, aritmetiska operationer och reglering. Till in/ut-gränssnittet kopplas bland annat olika sensorer som läser in data ifrån omvärlden. Beroende på vilken data som kommer in utför systemet olika uppgifter. I ett PLC-system läggs liten vikt på presentation av information till användaren då de flesta PLC-system saknar någon typ av bildskärm. PLC-system utför i allmänhet ingen eller mycket liten filhantering och saknar i många fall hårddiskar. Programvaran ligger ofta i ett ROM, (Read Only Memory) vilket gör att laddningstider blir mycket korta jämfört med ett hårddiskbaserat system. PLC-system utför ofta någon typ av datainsamling, exempelvis ifrån sensorer, den insamlade datan bearbetas och sedan skapas ofta någon typ av utdata. Insamlingen och bearbetningen av data sker ofta periodiskt, men ett PLC-system kan även arbeta avbrottsstyrt. Programmering av ett PLC-system sker ibland bara vid utvecklingen men ofta så måste programändringar göras även när systemet är installerat. Ett exempel på en applikation för ett PLCsystem är ett system som styr en svetsrobot. Beroende på hur omgivningen ser ut så kan ett PLC-system både vara ett hårt och mjukt realtidssystem. Vissa PLC-system använder sig inte av några signaler från omgivningen och räknas därför inte som realtidssystem. 8 3 Problemprecisering 3 Problemprecisering I detta avsnitt formuleras själva problemet och vissa avgränsningar identifieras. 3.1 Problemformulering Vid utvärdering av utvecklingsverktyg för realtidssystem finns det ett stort antal aspekter att ta hänsyn till. Dessa aspekter är vid kompilering, prestanda vid exekvering av den kompilerade koden, kostnad vid inköp och vid utbildning av personal, kvalité på dokumentation, support från tillverkaren, avlusningsfunktioner, medföljande funktionsbibliotek och kvalité på medföljande verktyg (till exempel för att rita gränssnitt). I detta arbete utvärderas ett antal utvecklingsverktyg för PLC-system baserade på fem olika språk (C/C++, Pascal, Java, Ada och Perl) utifrån både kvantitativa och kvalitativa kriterier. Exempel på kvantitativa kriterier är effektivitet som kan mätas i kompileringshastighet, kodeffektivitet, funktionalitet och inköpskostnad. Kvalitativa kriterier kan vara användarvänlighet, kvalité på dokumentation och support från tillverkaren. 3.1.1 Kvantitativa kriterier Enligt Weiderman (1989) är det som karaktäriserar kvantitativa kriterier att de ofta kan kontrolleras med någon typ av test. De tester som används för att mäta prestanda kallas för benchmarktester. Ett exempel på ett prestandakriterium är kompileringshastighet, vilken går att mäta genom att kompilera olika testprogram under utvecklingsverktygen. Det finns dock enligt Weiderman en hel del nackdelar med benchmarks, de påverkas av många olika variabler vilket gör det svårt att få exakta mätningar utan att göra en avancerad analys av systemet som testerna körs på. Det är inte bara själva systemet som påverkar resultaten av tester. Beroende på vilka inställningar som görs i utvecklingsverktyget kan till exempel kompileringshastigheten ändras drastiskt. Vidare säger Weiderman att en annan svår avvägning när det gäller konstruktion av benchmarks är att skriva själva testprogrammen. Hur ska valet av vilka funktioner som ska utföras göras? Mer om dessa problem kommer att tas upp senare i detta arbete. 3.1.2 Kvalitativa kriterier Till kvalitativa kriterier hör kriterier som är svåra att mäta med hjälp av testning (Weiderman, 1989). Ett exempel på ett kvalitativt kriterium är användarvänlighet, alltså hur lätt det är att lära sig att behärska och använda ett utvecklingsverktyg. Denna typen av kriterium påverkas mycket av bakgrund och erfarenhet hos användaren. De flesta kvalitativa kriterier kan enligt Weiderman utvärderas genom att sätta upp ett antal checklistor där olika kvalitativa kriterier ställs upp i form av ja/nej-frågor. Till exempel: Stödjer utvecklingsverktyget några avlusningsfunktioner? Det är sedan 9 3 Problemprecisering relativt enkelt att undersöka de olika kriterierna. Det är enligt Weiderman alltid en avvägning mellan att använda ja/nej-frågor och mer subjektiva frågor. I många fall så räcker dock enligt Weiderman, ja/nej-frågorna långt när en bedömning ska göras. 3.1.3 Undersökningskriterier Weiderman (1989, s.25) räknar upp ett antal kriterier som är intressanta vid utvärdering av Ada-kompilatorer och vissa av dessa kriterier är även applicerbara på andra typer av kompilatorer. Bland de kriterier som Weiderman satt upp betraktas följande som relevanta för detta arbete: • effektivitet hos genererad kod • kompileringshastighet • storlek på kompilerad kod • kodportabilitet • utbud av färdiga komponenter (bibliotek, arkiv, paket, klasser) • support och ”recompilation avoidance” (Undvikande av omkompilering av hela programmet vid små ändringar). Andra kriterier som är intressanta i ett utvecklingsverktyg är bland annat: • avlusningsfunktioner • integration mellan de olika delarna i utvecklingsmiljön • kopplingar till andra komponenter som exempelvis databaser. De tre första kriterierna är kvantitativa och de övriga kvalitativa. Bland dessa kriterier kommer de fem första att undersökas. En utförligare förklaring och motivering av var och ett av kriterierna återfinns i kapitel 4.3. 3.1.4 Val av programmeringsspråk för undersökningen De programmeringsspråk som kommer att vara med i detta arbete är följande: • C/C++ • Pascal • Java • Ada • Perl. 10 3 Problemprecisering 3.2 Avgränsning På grund av den begränsade tiden för arbetet, så kommer vissa avgränsningar att göras rörande vilka kriterier som skall undersökas och vilka utvecklingsverktyg som kommer att vara med i undersökningen. Det applikationsområde utvärderingen riktar sig mot är begränsat till utveckling av styrprogram för PLC-system och alla tester kommer att göras med avseende på denna typ av system. De utvecklingsverktyg som kommer att utvärderas är följande: • Microsoft Visual C++ v6.0: Anledningen till att använda Microsofts utvecklingsverktyg i detta arbete är att det är den ledande utvecklingsverktyget för PC-baserade program idag. • Borland Delphi v4.0 (Pascal): Innehåller en ”Single pass”-kompilator. Delphi är ett populärt utvecklingsverktyg för många olika typer av applikationer. • Mircosoft Visual J++ v1.1: Visual J++ är en av de större kommersiella utvecklingsverktygen för Java. Det har är en mer komplett utvecklingsmiljö jämfört med Suns JDK (Java Development Kit) och fanns tillgängligt för detta arbete. • AdaIDE v2.5 for GNAT 3.09 (Ada 95): AdaIDE är ett utvecklingsverktyg som använder GNAT för att generera C-kod av Ada-kod. GCC (Gnu C Compiler) används sedan för kompilering. • Active Perl 5.15 med Perl Development Kit 1.14: Ett utvecklingsverktyg för Perl. Förutom dessa finns det ytterligare ett antal utvecklingsverktyg/programmeringsspråk som hade varit intressanta att ta med i utvärderingen. Bland dessa finns till exempel Diabs realtidskompilator för C (Diab Data). Anledningen till att inga realtidskompilatorer för C är med i utvärderingen är att de som finns tillgängliga för detta arbete inte kan köras under Windows utan bara under Solaris på Sun-hårdvara. Vissa av utvärderingskriterierna är svåra att jämföra om alla tester inte körs med samma förutsättningar. Vidare skulle det vara intressant att undersöka hur ett utvecklingsverktyg baserat på assembler hade stått sig mot utvecklingsverktyg baserade på högnivåspråk. Anledningen till att ingen assembler finns med är på grund av att assembler saknar många av de funktioner som finns i ”modernare” programmeringsspråk, till exempel funktioner med parameterlistor och abstrakta datatyper. Assembler används inte heller i större utsträckning när det gäller större system. När det gäller utvecklingsverktyg för C/C++ (ej med realtidskompilatorer) så finns många att välja mellan från olika tillverkare. Om en begränsning görs till att använda Windows95/98/NT så finns det fortfarande många att tillgå. Bland de som inte kommer att undersökas är Borland C++ Builder och Watcom C++, vilka är två av de större utvecklingsverktygen för C++. Anledningen till att dessa inte undersöks är dels att de inte finns tillgängliga för detta arbete och dels för att tiden är begränsad. 11 3 Problemprecisering Förutom dessa så finns det ett antal andra programmeringsspråk som kunde vara intressanta att ha med i jämförelsen. Bland dessa finns Modula, Basic och Fortran. Dessa valdes bort på grund av att de sällan används för realtidssystem idag. 12 4 Metoder 4 Metoder De flesta metoderna i en utvärdering går ut på att samla in information om det som skall utvärderas. Information kan inhämtas på flera olika sätt. Patel och Davidson (1994) beskriver ett antal tekniker som ofta används för att samla information om en frågeställning. Informationen kan fås via befintliga dokument, test och prov, olika typer av självrapporteringar, observationer, enkäter, intervjuer och attitydskalor. De informationskällorna som är mest relevanta för detta arbete är: • befintliga dokument: böcker, tekniska rapporter, information ifrån tillverkaren etc. • egna tester • enkäter och intervjuer av de som arbetar med det som skall utvärderas. I detta arbete används de tre ovanstående informationskällorna för att samla in information. Den insamlade informationen används för att utvärdera två typer av kriterier: • Kvantitativa kriterier: Vid utvärdering av kvantitativa kriterier är olika tester en bra metod. Det är en fördel om testerna görs med avseende på den typ av uppgift systemet är tänkt att utföra. Detta är dock inte alltid möjligt då sådana tester ibland blir väldigt omfattande. • Kvalitativa kriterier: När kvalitativa kriterier skall undersökas är det viktigt att resultatet inte blir alltför subjektivt, vilket kan vara fallet vid för detaljerade frågor. Den som utvärderar ett kriterium kan med sina egna åsikter påverka resultatet, både positivt och negativt. 4.1 Jämförelse av metoder Här utvärderas de olika metoderna för informationsinhämtning för att se vilka som passar bäst för att utvärdera kvantitativa och kvalitativa kriterier. 4.1.1 Kvantitativa kriterier Befintliga dokument: Det finns ofta mycket information att hämta från befintliga dokument. När det gäller benchmarks så kan information fås både från tillverkaren och från användare. Dock finns det en del problem med benchmarks. För att exempelvis utvärdera hastigheten hos en kompilator behövs ett mått på snabbhet (Weiderman, 1989). Många tillverkare använder ”rader kod per minut” som mått. Detta mått är inte speciellt bra definierat och är väldigt beroende av de givna förutsättningarna. Bland de faktorer som påverkar resultatet finns: • maskinen och operativsystemet testen körts på • vilka parametrar kompilatorn har fått (till exempel för optimering och avlusning) • antalet kommentarer och tomma rader • definitionen på en ”rad kod” (vanligtvis brukar en rad räknas för varje radmatningstecken som finns i programkoden) 13 4 Metoder • storleken på programmet som kompileras (små program har ofta lägre ”rader kod per minut” än stora program) • definition av tid (en vanlig klocka räknar med I/O-fördröjningar medan CPU-tid inte alltid gör det). Om hänsyn tas till detta så är benchmarkresultat från tillverkare inte speciellt relevanta i en jämförelse mellan olika programmeringsspråk och kompilatorer. För att en relevant jämförelse ska kunna göras så måste alla tester göras med samma förutsättningar, vilket inte är troligt då många tillverkare anpassar förutsättningarna så att deras produkter ger så bra resultat som möjligt. Samma problem finns med resultat från utomstående tester på en viss kompilator. Att använda befintlig information från prestandatester är alltså ingen bra metod i detta arbete. Egna tester: Fördelarna med att göra egna tester är att samma förutsättningar kan ges för alla utvecklingsverktyg som testas. Resultatet blir därför mer rättvist än information från tillverkare och användare. När testfall skapas är det många faktorer som måste tas med, både när det gäller plattformen testet skall göras på och själva testprogrammen. De faktorer som kan påverka resultatet är bland annat (Weiderman, 1989): • Processorn: Beroende på hur processorn är uppbyggd påverkas testresultat på olika sätt. De delar i processorn som påverkar resultatet är bland annat cacheminne, pipelines, register, klockfrekvens, minneshantering, bredd på bussar, intern arkitektur, interrupt och upplösningen på klockgeneratorn. • Operativsystemet: De faktorer i operativsystemet som påverkar tester är bland annat enanvändarsystem kontra fleranvändarsystem, en eller flera processer som körs parallellt, minneshantering och filsystem, schemaläggning i multiprocess system, olika typer av systemprocesser och ”garbage collection”. • Minne: Faktorer här som kan påverka resultaten är cacheminne, bandbredd till processorn, bredd på minnesbussen, klockfrekvens på minnesbussen, periferienheter som ”stjäl” minnesklockcyklar och grad av minnesfragmentering. • Arkitektur: Hur datorn är uppbyggd, hur många processorer datorn har och vilka databussar som finns och hur de används. • Tidmätning: Hur tiden mäts och vilken noggrannhet som används. • Kompilering: Vid kompilering kan till exempel vald grad av optimering påverka resultatet på benchmarks. Många av dessa faktorer påverkar resultatet av testerna i detta arbete. För att få så rättvisa tester som möjligt vidtas vissa åtgärder för att eliminera påverkan från så många som möjligt av ovanstående faktorer. Mer information om dessa åtgärder finns i kapitel 5.1. Enkäter och intervjuer: Med enkäter och intervjuer finns samma problem som vid information via befintliga dokument. En annan nackdel är att informationen från 14 4 Metoder denna typ av källa ofta är subjektiv. Denna informationskälla är därför inte speciellt intressant för benchmarkresultat och används inte i detta arbete. 4.1.2 Kvalitativa kriterier Befintliga dokument: Oavsett om de kvalitativa kriterierna är omskrivna på ja/nejform eller inte, så är befintliga dokument en utmärkt informationskälla för kvalitativa utvärderingar. Både information från tillverkaren och från andra utomstående källor kan användas i en utvärdering. Egna tester: Egna tester fungera som en informationskälla för kvalitativa kriterier och har samma för och nackdelar som befintliga dokument. I detta arbete har egna tester av kvalitativa kriterier fungera som ett komplement till befintliga dokument. Enkäter och intervjuer: En bra informationskälla om frågorna ställs på rätt sätt. Även här finns risk för att svaren blir subjektiva. På grund av den begränsade tidsramen för detta arbete genomförs inga enkäter eller intervjuer. 4.2 Vald metod De undersökningar som görs i detta arbete kan delas upp i två olika kategorier: undersökningar av kvantitativa kriterier och undersökningar av kvalitativa kriterier. För att undersöka de kvantitativa kriterierna har egna tester att gjorts då detta ger den mest rättvisa jämförelsen mellan de olika kompilatorerna och utvecklingsverktygen. För de kvalitativa kriterierna har en litteraturstudie av befintliga dokument att gjorts. 4.3 Precisering av utvärderingskriterier för undersökningen De kvantitativa kriterier som utvärderas i detta arbete är följande: • effektivitet hos genererad kod • kompileringshastighet • storlek på kompilerad kod. Effektivitet hos genererad kod: Med detta menas hur snabbt den genererade maskinkoden eller den interpreterade koden exekveras. Detta är viktigt i ett PLCsystem då olika tidskrav ofta är inblandade och ju effektivere koden är desto lättare är det att möta dessa tidskrav. Effektivitet hos genererad kod har utvärderas genom att ett antal testprogram skrivs och sedan testkörs. Testprogrammen redovisas längre fram. Kompileringshastighet: Kompileringshastigheten utvärderas i detta arbete genom att kompileringstiden för ett antal testprogram mäts. Kompileringstid innebär hur snabbt kompilatorn kompilerar ett visst program. Mer information om testprogrammen följer längre fram i rapporten. Vid utveckling av programvara för PLC-system är kompileringshastigheten viktig då många omkompileringar kan behövas när ett system skall anpassas till att fungera enligt specifikationen. 15 4 Metoder Storlek på kompilerad kod: Vid utveckling av program för persondatorer är storleken på den kompilerade programfilen inte speciellt viktig, då de datorer programmen kommer att köras på ofta har stora hårddiskar och ramminne. När det gäller PLC-system är dock både den eventuella hårddisken och ramminnet begränsade, men ofta är detta inget problem då det för det mesta går att utöka dessa minnen. Ännu viktigare är nerladdningstiden, det vill säga den tid det tar att ladda ner ett program ifrån utvecklingsplattformen till målplattformen. Denna nerladdning sker många gånger under utvecklingsarbetet och det är då viktigt att den går så fort som möjligt. Det är alltså positivt om en kompilator skapar så små programfiler som möjligt. Vid testerna av effektiviteten hos den kompilerade koden kommer även storleken på programfilen att mätas. När det gäller Java och Perl behövs det extra filer för att köra programmen, dels en programtolk och dels de bibliotek eller klasser som används. Vid storleksmätningarna kommer även dessa att räknas in. De två övriga kriterier som kommer att undersökas är kvalitativa och är följande: • kodportabilitet • utbud av färdiga komponenter (bibliotek, arkiv, klasser och moduler). Kodportabilitet: Med kodportabilitet menas dels hur lätt det är att flytta kod ifrån ett utvecklingsverktyg till ett annat och kompilera om den där, och dels hur lätt kod kan flyttas från ett målsystem till ett annat med annorlunda hårdvara. Hur god kodportabilitet ett visst utvecklingsverktyg har är viktigt att tänka på när man utvecklar programvara för PLC-system. Det beror på att målsystemets hårdvara ibland måste bytas ut för att till exempel få ett snabbare system. Om själva programkoden då är lätt att flytta mellan olika hårdvaruplattformar så leder detta till att kostnaden för ett hårdvarubyte kan hållas nere, då stora delar av den gamla programkoden kan användas. Kodportabiliteten undersöks med en subjektiv bedömning av den information som finns tillgänglig på tillverkarens hemsida på Internet samt delvis via egna tester. Alla utvecklingsverktyg bedöms och tilldelats ett av tre olika omdömen: • God kodportabilitet: Koden kan utan några som helst problem eller ändringar användas i ett annat utvecklingsverktyg eller på en annan hårdvara. • Medelgod kodportabilitet: Koden kan med vissa mindre ändringar användas i ett annat utvecklingsverktyg eller på en annan hårdvara. • Dålig kodportabilitet: Koden kan inte utan stora ändringar användas i ett annat utvecklingsverktyg eller på en annan hårdvara. 16 4 Metoder Utbud av färdiga komponenter (bibliotek, arkiv, klasser och moduler): Hur mycket färdiga komponenter som ingår i utvecklingsverktyget och som kan köpas ifrån tredjepartstillverkare. Exempel på färdiga komponenter är funktionsbibliotek för databaskoppling och TCP/IP (nätverksprotokoll). Utbudet av färdiga komponenter görs genom en subjektiv bedömning av information från tillverkaren och olika källor på Internet. Även här används tre olika omdömen. Dessa tre är: • God tillgång till färdiga komponenter: Många typer av färdiga komponenter finns att få tag på, antingen via tredjepartstillverkare eller direkt från tillverkaren. • Medelgod tillgång till färdiga komponenter: Många typer av färdiga komponenter finns att få tag på, antingen via tredjepartstillverkare eller direkt från tillverkaren, dock är utbudet av leverantörer begränsat. • Dålig tillgång till färdiga komponenter: Få färdiga komponenter finns att få tag på, via tredjepartstillverkare eller direkt av tillverkaren. Tom text för att word inte ska bugga 4.4 Benchmarks De benchmarktester som används i detta arbete kommer dels från Bolin (1997) och dels från idéer av Weiderman (1989) som vidareutvecklats. Test 1-16 kommer ifrån Bolin (1997). Dessa tester är anpassade till detta arbete. Anpassningen är att i detta arbete körs testprogrammen i en loop ett fast antal gånger, Bolin använder en loop som kör testprogrammen under en bestämd tid. Mätningarna görs med ett fast antal iterationer och tiden mäts med 4DOS (JP Software, 1999). Test 17 bygger på Weidermans förslag till tester av Ada-kompilatorer och test 18 är ett av deltesten i Linpacktesten, (Grace, 1996) som har anpassats för detta arbete. 4.4.1 Språkfunktionstester Enligt Weiderman (1989) finns det många olika typer av benchmarks. Weiderman beskriver fem olika typer: språkfunktionstester, kapacitetstester, komposittester, symmetriska tester samt applikationsspecifika tester. I detta arbete genomförs bara språkfunktionstester. Det innebär att testerna i detta arbete testar enstaka funktioner var för sig hos ett språk. De funktioner som testas är grundfunktioner. Anledningen till detta är att ett program ofta består av en kombination av dessa funktioner. Denna typ av tester visar svagheter hos olika delar i ett utvecklingsverktyg. Om den programvara som skall utvecklas använder vissa funktioner mycket kan utvecklingsverktyget väljas med stöd av denna typ av tester. Dock är det svårt att få den exakta prestandan utvärderad i denna typ av test. I detta arbete är språkfunktionstest intressanta för att de visar skillnaden i prestanda mellan olika utvecklingsverktyg. De övriga testtyperna ger inte heller ett exakt prestandavärde utan bara ett ungefärligt prestandavärde. Undantaget är applikationsspecifika tester. Problemet med dessa är att applikationen inte är skriven när utvecklingsverktyg skall väljas. 17 4 Metoder 4.4.2 Testfall för kodeffektivitet 1. Loopoverhead: Testerna körs i en loop ett antal gånger. Vid dessa tester är det inte bara själva testfallet som påverkar tiden utan också de anrop som behövs för själva loopen. För att eliminera de fel som kan uppstå körs detta test med en tom loop med fyra olika loopvärden: 100,000; 1,000,000; 10,000,000 och 100,000,000. Resultatet på detta test används för att justera resultaten på de andra testen. 2. Lokal variabeltilldelning: Testar hastigheten vid en lokal variabeltilldelning. Exempel: n:=i, där i är en ökande 32-bitars lokal heltalsvariabel. Under testet görs 10,000,000 tilldelningar i en loop. 3. Global variabeltilldelning: Testar hastigheten vid en global variabeltilldelning. Exempel: n:=i, där i är en ökande 32-bitars global heltalsvariabel. Under testet görs 10,000,000 tilldelningar i en loop. 4. Matristilldelning: Testar hastigheten vid tilldelning av värden till olika element i en matris. Matrisen består av fyra heltal som tilldelas stigande heltalsvärden. Under testet körs en loop 10,000,000 varv där alla fyra elementen tilldelas nya värden på varje varv i loopen. 5. Uppräkning av 8-bitarsvariabel: Detta test räknar upp en 8-bitars teckenlös heltalsvariabel ett antal gånger. Under testet så körs 10,000,000 uppräkningar där varje uppräkning ökar variabeln med 1. Variabeln tillåts ”slå runt” när den nått sitt maxvärde. 6. Uppräkning av 16-bitarsvariabel: Räknar upp en 16-bitars heltalsvariabel med tecken ett antal gånger. Under testet så körs 10,000,000 uppräkningar där varje uppräkning ökar variabeln med 1. Variabeln tillåts ”slå runt” när den nått sitt maxvärde. 7. Uppräkning av 32-bitarsvariabel: Detta test räknar upp en 32-bitars heltalsvariabel med tecken ett antal gånger. Under testet så körs 10,000,000 uppräkningar där varje uppräkning ökar variabeln med 1. Variabeln tillåts ”slå runt” när den nått sitt maxvärde. 8. Uppräkning av 64-bitarsvariabel: Detta test räknar upp en 64-bitars heltalsvariabel med tecken ett antal gånger. Under testet så körs 10,000,000 uppräkningar där varje uppräkning ökar variabeln med 1. Variabeln tillåts ”slå runt” när den nått sitt maxvärde. 9. Uppräkning av 64-bitars flyttalsvariabel: Detta test räknar upp en 64-bitars flyttalsvariabel med tecken ett antal gånger. Under testet så körs 10,000,000 uppräkningar där varje uppräkning ökar variabeln med 1. Variabeln tillåts ”slå runt” när den nått sitt maxvärde. 10. Matrisallokering: Detta test skapar en matris med 4 stycken 32-bitars heltal. Under testet skapas matrisen 10,000,000 gånger i en loop. De gamla matriserna 18 4 Metoder deallokeras direkt efter att de allokerats för att minnet inte skall ta slut under testen. 11. Funktionsanrop: Detta test anropar en parameterlös funktion som inte innehåller någon kod. Även detta test kommer att utföras 10,000,000 gånger i en loop. 12. Anrop av matematisk funktion 1: Detta test anropar funktionen för absolutvärde på en heltalsvariabel. Under testet körs 10,000,000 anrop av funktionen. 13. Anrop av matematisk funktion 2: Detta test anropar den inbyggda funktionen för cosinus på en 32 bitars flyttalsvariabel. Under testet körs 10,000,000 anrop av funktionen men fyra olika värden: 15.0, 30.0, 45.0 och 60.0. 14. Villkorstest: Detta test utför utvärderingar av villkor. Under testet körs 10,000,000 anrop av en if-else if-else-sats. 15. Strängallokering: Detta test allokerar upp en ny sträng på 50 tecken. Under testet skapas strängen 10,000,000 gånger i en loop. De gamla strängarna deallokeras direkt efter de allokerats för att minnet inte skall ta slut under testen. 16. Typecasting: Detta test gör en ”typecast” på en flyttals- till en heltalsvariabel. Under testet körs denna ”typecast” 10,000,000 gånger i en loop. Flyttalsvariabeln initieras till värdet 1000. 17. Rekursivt anrop: Detta test gör ett rekursivt anrop som resulterar i 100,000 iterationer. 18. Lintest: Detta test är en del av Linpacktesten (Grace, 1996) och testar hastigheten vid linjära funktioner. I detta arbete beräknas den linjära funktionen: 2/3n^3+2n^2. Detta görs 10,000,000 gånger i en loop. Istället för n^3 och n^2 så används loopräknarvariabeln/10 och loopräknarvariabeln/100 vid uträkningarna. 4.4.3 Testfall för kompileringshastighet För att testa kompileringshastighet hos de olika utvecklingsverktygen används speciella testprogram. Dessa testprogram består av alla de ovanstående testfallen i ett och samma program där var och ett av testfallen är duplicerade ett antal gånger för att få ett så stort program som möjligt. Anledningen till att de ovanstående testfallen används istället för att skriva ett helt nytt testprogram är att den begränsade tiden för detta arbete inte tillåter att ett helt nytt testprogram utvecklas. 19 5 Genomförande 5 Genomförande I detta avsnitt redovisas resultat av genomförandet av tester och undersökningar. Först redovisas resultatet av de kvantitativa testerna och sedan resultaten av de kvalitativa undersökningarna. 5.1 Förutsättningar för tester Alla tester i undersökningen körs på en 200 MHz Pentiumdator med 32Mbyte ramminne och 4.3Gbyte IDE hårddisk. Vid testerna används Windows 98 som operativsystem. För tidmätningarna används 4DOS inbyggda tidmätningsfunktioner. Dessa ger en noggrannhet på en hundradels sekund vilket är betydligt bättre än att använda ett stoppur. Ett bättre alternativ till 4DOS för tidmätning är att i varje testprogram läsa av systemtimern före och efter själva testen. Anledningen till att detta inte gjordes är att de nödvändiga funktionerna för detta inte finns tillgängliga i alla utvecklingsverktyg som testas och att inte finns tid att skapa den nödvändiga funktionaliteten under detta arbete. Ett problemet med att använda 4DOS för tidsmätning är att även programladdning ingår i den uppmätta tiden. För att förhindra att detta påverkar resultatet allt för mycket, så körs alla tester 12 gånger efter varandra där det högsta och lägsta resultatet tas bort och de övriga används för att beräkna ett medelvärde på tiden för testet. Denna metod förhindrar att laddningstiderna påverkar resultatet allt för mycket. Efter den första testkörningen så befinner sig programmet i disk-cachen och laddas mycket fort. Dock är det inte alltid säkert att det första testet tar längst tid. I ett PLC-system är programmets laddningstid inte speciellt intressant då programmen ofta laddas från ett snabbt ROM-minne och inte från en hårddisk. För att inte datorns cache ska påverka resultatet, har både den interna (i processorn) och den externa cachen i testdatorns varit avstängda. Under testkörningarna finns endast två processer på testdatorn: Explorer och 4DOS. Explorer måste vara igång för att kunna använda Windows och går inte att stänga av. Utöver dessa processer körs testprogrammet och dess eventuella tolk. Vid alla tester är all optimering i kompilatorerna avstängd. Alla tester kan inte genomföras med alla de fem utvecklingsmiljöerna, till exempel så saknar Perl vissa av de funktioner som krävs för dynamisk minneshantering. I dessa fall anpassas testprogrammen för att köras på ett liknande sätt. Hur denna anpassning gjordes beskrivs i kapitel 5.2. Vid testkörningar av Javaprogram användes Suns JRE 1.1.8 (Java Run-time Environment) med JIT (Just In Time compilation) påslaget. JIT innebär att delar av Javakoden kompileras till maskinkod vid exekvering i tolken. 20 5 Genomförande 5.2 Effektivitet hos genererad kod I dessa tester har 18 olika testprogram körts på var och ett av utvecklingsverktygen. Vissa av testerna har inte kunnat genomföras på alla verktygen på grund av att några av verktygen saknar vissa av de funktioner som används i testerna. I diagrammen för benchmark 2 till 18 finns två tider. En som kallas totaltid och är den uppmätta tiden för testet. Den andra tiden kallas för omräknad tid och består av totaltiden med loopoverheaden bortdragen. 5.2.1 Loop overhead Kör en tom loop mellan 10^5 och 10^8 gånger. Loop overhead - Loopvärde: 10^5 - 10^8 1000 100 sekunder 10^5 10^6 10^7 10^8 10 1 GNAT (Ada) Microsoft Visual C++ Mircosoft J++ Borland Delphi Active Perl 10^5 1,852 0,7012 7,675 0,835 11,246 10^6 3,746 2,169 8,209 1,306 94,019 10^7 20,737 13,603 13,546 6,463 922,107 10^8 187,789 125,535 66,87 52,198 9184,32 GNAT, Visual C++, J++ och Delphi har jämförbara prestanda i loopoverheadtesten. Värdena ifrån testerna med Active Perl är så höga att de inte kommer att redovisas i diagramform i resten av testerna, detta för att resultaten från de fyra första utvecklingsverktygen skall synas bättre. 21 5 Genomförande 5.2.2 Lokal variabeltilldelning Testar exekveringstiden vid tilldelning av värde till en lokal variabel. Lokal variabeltilldelning - Loopvärde: 10^7 80,00 70,00 60,00 sekunder 50,00 Totaltid (sek) 40,00 Omr. tid (sek) 30,00 20,00 10,00 0,00 5.2.3 GNAT (Ada) Visual C++ MS J++ Borland Delphi Totaltid (sek) 30,79 30,94 66,86 19,16 Omr. tid (sek) 10,06 17,34 53,31 6,49 Active Perl 1359,63 437,53 Global variabeltilldelning Testar exekveringstiden vid tilldelning av värde till en global variabel. Global variabeltilldelning - Loopvärde: 10^7 80,00 70,00 60,00 sekunder 50,00 Totaltid (sek) 40,00 Omr. tid (sek) 30,00 20,00 10,00 0,00 Active Perl GNAT (Ada) Visual C++ MS J++ Borland Delphi Totaltid (sek) 29,80 20,85 66,87 17,25 1358,44 Omr. tid (sek) 9,06 7,25 53,32 4,58 436,33 22 5 Genomförande 5.2.4 Matristilldelning Tilldelar värden till olika element i en matris. Matristilldelning - Loopvärde: 10^7 120,00 100,00 sekunder 80,00 Totaltid (sek) 60,00 Omr. tid (sek) 40,00 20,00 0,00 5.2.5 Active Perl GNAT (Ada) Visual C++ MS J++ Borland Delphi Totaltid (sek) 41,57 35,75 103,06 33,04 6083,47 Omr. tid (sek) 20,83 22,15 89,51 20,37 5161,37 Uppräkning av 8-bitarsvariabel Räknar upp en 8-bitarsvariabel och låter den ”slå runt” vid maxvärde. Uppräkning av 8-bitarsvariabel - Loopvärde: 10^7 80,00 70,00 60,00 sekunder 50,00 Totaltid (sek) 40,00 Omr. tid (sek) 30,00 20,00 10,00 0,00 Active Perl GNAT (Ada) Visual C++ MS J++ Borland Delphi Totaltid (sek) 29,08 21,02 66,83 16,35 1136,22 Omr. tid (sek) 8,35 7,42 53,29 3,67 214,12 I Perl finns bara en enda variabeltyp som kan innehålla allt ifrån heltal och flyttal till strängar. Perl finns med i alla av de fem uppräkningstesterna. Dock så används 23 5 Genomförande samma testprogram till alla heltalsuppräkningstesterna. Till flyttalsuppräkningen används en lite modifierad version. Mer om detta i avsnitt 5.2.9. 5.2.6 Uppräkning av 16-bitarsvariabel Räknar upp en 16-bitarsvariabel och låter den ”slå runt” vid maxvärde. Uppräkning av 16-bitarsvariabel - Loopvärde 10^7 80,00 70,00 60,00 sekunder 50,00 Totaltid (sek) 40,00 Omr. tid (sek) 30,00 20,00 10,00 0,00 Active Perl GNAT (Ada) Visual C++ MS J++ Borland Delphi Totaltid (sek) 31,20 21,98 66,85 16,33 1136,22 Omr. tid (sek) 10,46 8,38 53,31 3,66 214,12 Testprogrammet för Perl är samma som vid uppräkning av 8-bitarsvariabel. 5.2.7 Uppräkning av 32-bitarsvariabel Räknar upp en 32-bitarsvariabel och låter den ”slå runt” vid maxvärde. Uppräkning av 32-bitarsvariabel - Loopvärde: 10^7 80,00 70,00 60,00 sekunder 50,00 Totaltid (sek) 40,00 Omr. tid (sek) 30,00 20,00 10,00 0,00 Active Perl GNAT (Ada) Visual C++ MS J++ Borland Delphi Totaltid (sek) 29,93 21,02 66,92 16,36 1136,22 Omr. tid (sek) 9,19 7,41 53,37 3,69 214,12 24 5 Genomförande Testprogrammet för Perl är samma som vid uppräkning av 8-bitarsvariabel. 5.2.8 Uppräkning av 64-bitarsvariabel Räknar upp en 64-bitarsvariabel och låter den ”slå runt” vid maxvärde. Uppräkning av 64-bitarsvariabel - Loopvärde: 10^7 80,00 70,00 60,00 sekunder 50,00 Totaltid (sek) 40,00 Omr. tid (sek) 30,00 20,00 10,00 0,00 Active Perl GNAT (Ada) Visual C++ MS J++ Borland Delphi Totaltid (sek) 62,01 21,07 66,86 24,50 1136,22 Omr. tid (sek) 41,27 7,46 53,32 11,83 214,12 Testprogrammet för Perl är samma som vid uppräkning av 8-bitarsvariabel. 5.2.9 Uppräkning av 64-bitars flyttalsvariabel Räknar upp en 64-bitars flyttalsvariabel och låter den ”slå runt” vid maxvärde. Uppräkning av 64-bitars flyttalsvariabel - Loopvärde: 10^7 80,00 70,00 60,00 sekunder 50,00 Totaltid (sek) 40,00 Omr. tid (sek) 30,00 20,00 10,00 0,00 Active Perl GNAT (Ada) Visual C++ MS J++ Borland Delphi Totaltid (sek) 29,21 23,95 66,88 20,20 1184,21 Omr. tid (sek) 8,48 10,34 53,33 7,53 262,11 25 5 Genomförande Testprogrammet för Perl är samma som används vid uppräkning av 8-bitarsvariabel med en modifikation så att uppräkningsvariabeln initieras till ett flyttal och inte ett heltal. 5.2.10 Matrisallokering Allokerar upp en matris med fyra 32-bitars heltalsvärden. Matrisen deallokeras i varje varv i loopen. Matrisallokering - Loopvärde: 10^7 16000,00 14000,00 12000,00 sekunder 10000,00 Totaltid (sek) 8000,00 Omr. tid (sek) 6000,00 4000,00 2000,00 0,00 Active Perl GNAT (Ada) Visual C++ MS J++ Borland Delphi Totaltid (sek) 420,77 896,17 14869,96 427,51 N/A Omr. tid (sek) 400,03 882,57 14856,41 414,84 N/A Detta test gick inte att genomföra i Perl. Anledningen till detta är att funktioner för dynamisk allokering av variabler inte finns tillgängliga. Anledningen till att Java kommer så långt efter de övriga i denna test kan bero på att Java automatiskt genomför ”garbage collection”. Deallokering av ett dynamiskt allokerat element sker när alla referenser till det frigörs. I detta fallet frigörs referensen varje varv i loopen vilket kan medföra att den Virtuella Javamaskinen utför en ”garbage collection” vilket skulle förklara resultatet av detta test. 26 5 Genomförande 5.2.11 Funktionsanrop Anropar en tom, parameterlös funktion. Funktionsanrop - Loopvärde: 10^7 80,00 70,00 60,00 sekunder 50,00 Totaltid (sek) 40,00 Omr. tid (sek) 30,00 20,00 10,00 0,00 Active Perl GNAT (Ada) Visual C++ MS J++ Borland Delphi Totaltid (sek) 46,86 36,39 67,16 22,20 2710,45 Omr. tid (sek) 26,13 22,79 53,61 9,53 1788,34 5.2.12 Anrop av matematisk funktion 1 Beräknar absolutvärdet av –30. Matematisk funktion 1 (abs) - Loopvärde: 10^7 70,00 60,00 50,00 40,00 Totaltid (sek) Omr. tid (sek) 30,00 20,00 10,00 0,00 Active Perl GNAT (Ada) Visual C++ MS J++ Borland Delphi Totaltid (sek) 25,18 36,78 58,79 15,32 1319,95 Omr. tid (sek) 4,44 23,18 45,25 2,65 397,85 Nämnvärt i denna test är att Visual C++ kommer så långt efter GNAT och Borland Delphi. Detta kan bero på att abs-funktionen är implementerad på ett långsammare sätt än hos GNAT och Borland Delphi. 27 5 Genomförande 5.2.13 Anrop av matematisk funktion 2 Beräknar cosinus för 15.0; 30.0; 45.0 och 60.0. Matematisk funktion 2 (cos) - Loopvärde: 10^7 2000,00 1800,00 1600,00 sekunder 1400,00 1200,00 Totaltid (sek) 1000,00 Omr. tid (sek) 800,00 600,00 400,00 200,00 0,00 Active Perl GNAT (Ada) Visual C++ MS J++ Borland Delphi Totaltid (sek) 1753,63 121,63 67,27 36,91 1338,62 Omr. tid (sek) 1732,89 108,02 53,73 24,24 416,51 I detta test utfördes fyra deltester där olika värden på cosinus används. I diagrammet finns medelvärdet för dessa fyra testerna. GNAT får väldigt dåliga testresultat vilket kan bero på en långsam implemenation av cosinusfunktionen. Visual C++ tar dubbelt så lång tid på sig som Java i detta test men skillnaden är inte så stor som mellan GNAT och de övriga. Även detta kan bero på att implementationen av cosinus i Visual C++ inte är lika snabb som den i Java och Borland Delphi. 28 5 Genomförande 5.2.14 Villkorstest Utvärderar en ”if-else if-else” selektion. Villkorstest - Loopvärde: 10^7 80,00 70,00 60,00 sekunder 50,00 Totaltid (sek) 40,00 Omr. tid (sek) 30,00 20,00 10,00 0,00 Active Perl GNAT (Ada) Visual C++ MS J++ Borland Delphi Totaltid (sek) 29,64 15,47 67,21 25,51 1405,41 Omr. tid (sek) 8,91 1,87 53,67 12,84 483,31 Anmärkningsvärt i detta test är att Visual C++ får så väldigt bra resultat. Detta kan bero på en bra implementation av selektioner. 5.2.15 Strängallokering Allokerar upp minne för en 50 tecken lång sträng. Strängen deallokeras vid varje varv i loopen. Strängallokering - Loopvärde: 10^7 40000,00 35000,00 30000,00 sekunder 25000,00 Totaltid (sek) 20000,00 Omr. tid (sek) 15000,00 10000,00 5000,00 0,00 Active Perl GNAT (Ada) Visual C++ MS J++ Borland Delphi Totaltid (sek) 425,75 895,28 37050,22 426,89 N/A Omr. tid (sek) 405,02 881,68 37036,67 414,22 N/A 29 5 Genomförande Detta test gick inte att genomföra i Perl. Anledningen till detta är att funktioner för dynamisk allokering av variabler inte finns tillgängliga. Anledningen till att Java fick så dåliga värden är troligtvis samma som för matrisallokeringstesten. Axlarna är skalade så att skillnaden mellan GNAT, Visual C++ och Delphi ska synas. 5.2.16 Typecasting Utför en typecast från en flyttalsvariabel innehållande värdet 1000 till en 32-bitars heltalsvariabel. Typecasting - Loopvärde: 10^7 80,00 70,00 60,00 sekunder 50,00 Totaltid (sek) 40,00 Omr. tid (sek) 30,00 20,00 10,00 0,00 GNAT (Ada) Visual C++ MS J++ Borland Delphi Active Perl Totaltid (sek) 69,22 61,08 67,20 N/A N/A Omr. tid (sek) 48,48 47,48 53,66 N/A N/A Detta test har inte varit möjligt att köra i Delphi och Perl. Detta beror på att Delphi inte tillåter den typ av typecasting som testen gör. Perl har bara en datatyp vilket leder till att typecasting inte behövs. 30 5 Genomförande 5.2.17 Rekursivt anrop Utför en rekursiv loop. Rekursivt anrop - Loopvärde: 10^5 9,00 8,00 7,00 sekunder 6,00 5,00 Tid (sek) 4,00 3,00 2,00 1,00 0,00 Tid (sek) GNAT (Ada) Visual C++ MS J++ Borland Delphi 2,96 1,10 8,17 1,18 Active Perl N/A Perl stöder inte rekursiva anrop och kan därför inte vara med i denna test. 5.2.18 Lintest Utför en beräkning av en linjär ekvation. Lintest - Loopvärde: 10^7 60,00 50,00 sekunder 40,00 Totaltid (sek) 30,00 Omr. tid (sek) 20,00 10,00 0,00 Active Perl GNAT (Ada) Visual C++ MS J++ Borland Delphi Totaltid (sek) 55,70 38,23 36,17 39,82 4005,92 Omr. tid (sek) 34,96 24,63 22,62 27,15 3083,81 31 5 Genomförande 5.3 Kompileringshastighet I denna test har tre speciella testprogram kompilerats och tiden för kompileringen är uppmätt på samma sätt som i exekveringshastighetstesterna. Perlprogram behöver inte kompileras vilket leder till att kompileringstiden är noll. Kompileringstid 250,00 sekunder 200,00 150,00 100,00 50,00 0,00 K-Test 1 K-Test 2 K-Test 3 Ada 99,25 137,97 206,46 C++ 20,99 22,9 38,01 Java 19,99 44 109,03 Delphi 10,00 16,97 19,99 0 0 0 Perl Vid test har tre olika program kompilerats. Det som skiljer dessa är främst storleken. Testprogram ett (K-Test 1) är mellan 33 och 45 kb, testprogram två (K-Test 2) är mellan 66 och 92 kb och det tredje testprogrammet (K-Test 3) är mellan 134 och 180 kb. Alla olika språkversioner av varje testprogram utför samma sak, dock varierar programstorlekarna på grund av de enskilda språkens egenskaper. K-Test 1 består av 19 av benchmarktestprogrammen i 5.2, inlagda i ett och samma program, som var och ett är duplicerade 9 gånger med olika namn. I K-Test 2 har hela K-Test 1 duplicerats. Det samma gäller K-Test 3 där K-Test 2 har duplicerats. 32 5 Genomförande 5.4 Storlek på kompilerad kod I detta avsnitt redovisas storleken på den körbara koden som genererats av de olika utvecklingsverktygen. Först redovisas storlekar från kodeffektivitets-testerna och sedan från kompileringshastighetstesterna. 5.4.1 Filstorlekar vid kodeffektivitetstester När det gäller storleken på den kompilerade koden, varierar denna ganska mycket mellan de olika utvecklingsverktygen. Skillnaden mellan de olika testprogrammen i samma utvecklingsverktyg är däremot inte så stora och därför kommer ett medelvärde av alla testprogrammens storlek att redovisas istället för storleken på var och ett av testprogrammen. I de fall där ett test inte gick att genomföra i någon av utvecklingsmiljöerna, räknas resultatet för det testet inte med för någon av utvecklingsmiljöerna. Alltså kommer benchmarktest 10 och 15-17 inte att vara med i uträkningen. Medelstorlek på de kompilerade och länkade testprogrammen i 5.2 300000 250000 200000 150000 Medelstorlek (bytes): 100000 50000 0 Medelstorlek (bytes): GNAT (Ada) * Visual C++ * MS J++ Borland Delphi * Active Perl 259760 27034 276 40448 134 Följande diagram innehåller både storleken på den genererade programfilen samt storlek på de filer som behövs för att köra Java- och Perlprogrammen. Till dessa filer hör själva tolken, samt eventuella dll-filer (dynamiska länk bibliotek som används av Windows) som laddas när programmet körs. * Betyder att den kompilerade och länkade koden kan köras direkt utan några extra filer. 33 5 Genomförande Medelstorlek de på kompilerade och länkade testprogrammen i 5.2 inklusive nödvändiga filer 2500000 2000000 1500000 Total medelstorlek (bytes): 1000000 500000 0 GNAT (Ada) * Visual C++ * MS J++ Borland Delphi * Active Perl Total medelstorlek 259760 (bytes): 27034 2264449 40448 847494 5.4.2 Filstorlekar vid kompileringshastighetstester Siffrorna i nedanstående diagram är från programmen som används vid testen av kompileringshastighet. I diagrammet är inte programfiler som är nödvändiga för att köra Javaprogram inräknade. En anledningen till att Javafilerna blir stora är att alla funktioner i programfilen är definierade som klasser, vilket leder till en viss overhead. Storlek på kompilerade och länkade programfiler vid kompileringstidstest 400000 350000 300000 bytes 250000 K-Test 1 200000 K-Test 2 K-Test 3 150000 100000 50000 0 * GNAT (Ada) Visual C++ MS J++ Borland Delphi K-Test 1 293468 45056 54539 40448 K-Test 2 312684 53248 109268 40448 K-Test 3 351328 73728 219106 40448 Betyder att den kompilerade och länkade koden kan köras direkt utan några extra filer. 34 5 Genomförande 5.5 Kodportabilitet Den kod som skrivs i Microsoft Visual C++ kan i många fall överföras till andra utvecklingsverktyg för samma miljö. Om för mycket av de medföljande funktionsbiblioteken används, kan det i många fall bli svårt att göra en direkt överföring utan att behöva skriva om delar av koden. Om ett utvecklingsverktyg på en annan plattform som till exempel UNIX skall användas, måste de plattformsspecifika delarna skrivas om så att de passar för den nya plattformen. När det gäller Borland Delphi är det mycket svårt att föra över koden till en annan plattform. Detta beror dels på att Delphis grafiska uppbyggnad inte följer någon standard och dels på att den Pascal-dialekt som används inte heller följer någon standard, utan är en av Borland utvecklad dialekt på de Pascal-standarder som finns. Med Microsoft J++ däremot är det ofta lätt att flytta program mellan plattformar. Detta beror på att Java på många sätt är plattformsoberoende. Även om speciella funktionsbibliotek används går dessa ofta också att flytta till en annan utvecklingsmiljö och/eller plattform. Den ”byte-kod” som genereras vid kompilering av ett Javaprogram kan köras på vilken hårdvara som helst, kravet är dock att en Virtuell Javamaskin finns tillgänglig för den plattform som Javaprogrammet skall köras på. De flesta tillverkare av utvecklingsverktyg för Java följer Suns Javastandard. GNAT följer Ada95-standarden och gör det därför möjligt att på ett enkelt sätt flytta program mellan olika utvecklingsmiljöer och plattformar. Om målmiljöns hårdvara byts ut måste dock de hårdvarunära funktionerna i programvaran anpassas för den nya hårdvaran. När det gäller Perl är det inte aktuellt med portningar mellan olika utvecklingsmiljöer utan mer om att porta mellan olika tolkar. Detta fungerar ofta mycket bra om inte allt för många specialbibliotek används. Det är till exempel omöjligt att direkt porta ett Perlprogram som använder Win32-anrop till UNIX som saknar dessa anrop helt. I detta fall måste Win32-anropen bytas ut mot motsvarande UNIX-anrop. Microsoft Visual C++ Borland Delphi God Medelgod Dålig Microsoft J++ GNAT (Ada) Active Perl X X X X X 35 5 Genomförande 5.6 Utbud av färdiga komponenter Till Microsoft Visual C++ finns det gott om färdiga komponenter, både från Microsoft själva och från kommersiella tredjepartstillverkare. Det finns även mycket fria komponenter på Internet. Anledningen till den goda tillgången på färdiga komponenter är att Visual C++ är ett av de mest använda utvecklingsverktygen för PC-baserade program. Även till Borland Delphi finns det mycket färdiga komponenter, dels från Borland själva men det finns även här ett stort utbud av komponenter från tredjepartstillverkare. Dock är detta utbud inte riktigt lika stort som det för Visual C++. Mycket fria komponenter finns att få tag på ifrån Internet. När det gäller Microsoft J++ är utbudet lite mer begränsat. Det finns i och för sig mycket färdiga komponenter från Microsoft själva, från Sun och även från tredjepartstillverkare. Men eftersom Java är relativt nytt finns det inte lika mycket färdiga komponenter som för till exempel Visual C++ som har varit ute på marknaden mycket längre. Till Ada är utbudet av kommersiella komponenter lite större än till Java. Ada har funnits i många år och det finns kommersiella komponenter till det mesta. Dock finns det inte lika många tredjepartstillverkare att välja mellan, jämfört med till exempel Visual C++. Fria komponenter finns att få tag på men utbudet är inte alls lika stort som för till exempel Visual C++. Detta beror på att Ada inte används så mycket för att utveckla ”vanliga” program utan mest för att utveckla program för olika typer av styrsystem. Utbudet av komponenter till Perl är stort, dock är det mest fria komponenter och inte så mycket kommersiella vilket kan leda till att supporten är sämre än hos kommersiella komponenter. God Medelgod Microsoft Visual C++ Borland Delphi X X Microsoft J++ GNAT (Ada) Active Perl X X X Dålig 36 6 Analys av resultat 6 Analys av resultat I detta kapitel dras slutsatser från resultatet av de olika benchmarktesterna och undersökningarna i detta arbete. Resultatet av de kvantitativa undersökningarna kommer för effektivitet av kompilerad kod leda till ett betyg av vart och ett av utvecklingsverktygen. För kompileringshastighet och storlek på kompilerad och länkad kod kommer ett omdöme att ges till vart och ett av utvecklingsverktygen. Även vid resultatet av de kvalitativa undersökningarna; utbud av färdiga komponenter och kodportabilitet kommer ett omdöme att ges till vart och ett av utvecklingsverktygen. 6.1 Slutsatser av kvantitativa undersökningar Betygsättningen av utvecklingsverktygen i kodeffektivitetstesterna kommer att göras med betyg mellan 1 och 5, där 5 är bästa betyg. Om två utvecklingsverktyg ligger väldigt jämnt i ett test så kan båda få samma betyg. En komplett lista av betygsättningen finns i bilaga 2. När betygen beräknas används bara resultat från test 1-9, 11-14 och 18, anledningen till detta är att de övriga testerna inte är möjliga att köra på alla utvecklingsverktyg. 6.1.1 Kodeffektivitet I tabellen nedan har betygen för var och ett av utvecklingsverktygen sammanställts och räknats ihop till en totalpoäng. GNAT Visual C++ MS J++ Delphi Active Perl Antal 5:or 4 6 1 12 0 Antal 4:or 7 6 2 1 0 Antal 3:or 2 2 9 1 0 Antal 2:or 0 0 2 0 2 Antal 1:or 1 0 0 0 12 Totalpoäng: 55 60 44 67 14 Resultaten i tabellen visar att Delphi är det snabbaste utvecklingsverktyget. Därefter kommer Visual C++ följt av GNAT. Slutsatsen blir att både GNAT, Visual C++ och Delphi lämpar sig bättre än MS J++ och Active Perl för att utveckla programvara för tidskritiska PLC-system. MS J++ klarar sig förvånansvärt bra för att vara ett semi-interpreterande programmeringsspråk. Detta kan bero på att JRE, som används som tolk för att köra Javaprogrammen, utför en ”just in time compilation”. Det vill säga den kompilerar delar av byte-koden till maskinkod innan programmet börjar köras. Dock så har MS J++ dålig prestanda när det gäller dynamisk minnesallokering. GNAT, Visual C++ och Delphi är i genomsnitt tre gånger så snabba som MS J++. Därför kan det bli problem att få ett program tillräckligt snabbt på en given hårdvara 37 6 Analys av resultat vid tidskritiska system. Det finns dock lösningar på detta. Det skulle vara möjligt att använda en Javakompilator som skapar ren maskinkod för en specifik hårdvaruplattform. Då skulle Javaprogrammen kunna bli lika snabba som GNAT, Visual C++ och Delphiprogrammen, med biverkningen att portabiliteten mellan olika målmiljöer minskar. Detta är dock inget stort problem om programmen kompileras om för den nya målmiljön. Slutsatsen blir att Java kan användas till PLC-system. Om prestandan inte räcker så kan koden kompileras till maskinkod istället för byte-kod. Perl ligger långt efter de övriga i nästan alla tester och är därmed inte särskilt intressant som programmeringsspråk för PLC-system ur prestandasynpunkt. 6.1.2 Kompileringshastighet När det gäller kompileringshastigheten skiljer den sig mycket mellan de olika utvecklingsverktygen. Perl har den absolut kortaste ”kompileringstiden”. Detta beror på att Perlprogram inte behöver kompileras utan kan köras direkt via en tolk. Bland de utvecklingsverktyg där program måste kompileras har Delphi den kortaste kompileringstiden. Delphi tar i genomsnitt halva tiden på sig att kompilera ett program jämfört med Visual C++. Detta kan bero på att Delphi är en så kallad ”single-pass” kompilator och går bara igenom programkoden en gång till skillnad från de övriga (undantaget Perl) som går igenom programkoden två gånger. GNAT tar ungefär fem gånger så lång tid på sig att kompilera ett program som Visual C++. Detta beror på att GNATprogrammen först översätts till C-kod och sedan kompileras med GCC som i sig är en långsam C-kompilator jämfört med många kommersiella kompilatorer. Vid små program tar MS J++ lika lång tid på sig som Visual C++ vid kompilering. Vid större program ökar kompileringstiden för GNAT och MS J++ mycket snabbare än kompileringstiden för Visual C++ och Delphi. I fallet med MS J++ kan det som tidigare nämnts delvis bero på att varje funktion i testprogrammet är deklarerad som en egen klass och skapar därmed en egen fil, vilket kan leda till en viss ”overhead” då en mängd filer på hårddisken måste skapas. Denna ”overhead” leder också till att den totala storleken på de genererade filerna blir större. När det gäller GNAT så är det troligt att omvandlingen ifrån Ada till C bidrar till den snabbare ökningen av kompileringstiden. Om kompileringshastigheten är viktig är Delphi eller Visual C++ bättre alternativ än GNAT och MS J++. 6.1.3 Storlek på kompilerad och länkad kod När det gäller storleken på de körbara programfiler som genererats av utvecklingsverktygen så skiljer den sig mycket. Här går det att dela upp utvecklingsverktygen i två olika kategorier: De som skapar körbara filer och de som skapar filer som måste köras vi en tolk. Om storleken på tolken och de filer som används vid tolkning inte inräknas är dessa program tveklöst minst. Genomsnittsstorleken på programmen i benchmarktesterna är för Javaprogrammen 38 6 Analys av resultat 289 bytes och för Perlprogrammen bara 134 bytes. Om de filer som behövs för att köra programmen räknas in så ser det lite annorlunda ut. För Javaprogrammet blir då den totala storleken över 2 Mb och för Perlprogrammet över 800 kb. Om programvara för ett PLC-system utvecklas så är som tidigare nämnt programstorleken väldigt viktig då programmet måste laddas ner ifrån utvecklingsdatorn till en målmiljö. Denna nerladdning går ofta med låg hastighet och tar lång tid om programfilerna är stora. Därför är Java och Perl utmärkta språk i den aspekten då de ger små programfiler. Tolken och dess tillhörande filer förändras sällan och kan ligga i ett ROM på målsystemet och behöver då inte laddas ner varje gång en programändring har gjorts. Vid kompileringshastighetstestet ser det dock lite annorlunda ut. Här är Javaprogrammen ungefär lika stora som Visual C++- och Delphiprogrammen. Storleken på de kompilerade Javaprogrammen växer snabbt vid större program. Detta beror delvis som tidigare nämnts på att alla funktioner är klasser. Om källkodens storlek dubbleras så dubbleras även storleken på de genererade filerna. Visual C++ och Delphi genererar ungefär lika stora filer. Anmärkningsvärt är dock att filstorleken i Delphi är densamma i alla tester. Även om källkodens storlek fyrdubblas är den kompilerade och länkade filens storlek samma. Detta kan bero på att Delphi utför någon typ av optimering som inte går att stänga av. Visual C++- och GNATprogram däremot växer när källkodsstorleken ökar, dock inte lika snabbt som Javaprogrammen. Av de program som inte behöver någon tolk för att köras är det GNAT-programmen som är klart störst. Detta beror troligtvis på att fler standardfunktioner länkas med och att vissa av de speciella funktioner som Adaspråket har tar plats i programfilen. Om nerladdningstiden i ett PLC-system är viktig är GNAT inte det bästa alternativet, då är Visual C++ eller Delphi bättre alternativ med avseende på genererade programfilsstorlekar. Om systemet inte skall utföra uppgifter som är prestandakrävande så kan även MS J++ och Perl passa bra då dessas nerladdningstid ofta blir väldigt kort. MS J++ har även fördelen med klassuppdelning som följer med Javaspråket. Men hjälp av denna kan programmet delas upp i mindre delar på en enkelt sätt och vid uppdatering av programvara i en målmiljö kanske inte alla klasser behöver laddas över utan bara de som är ändrade. 6.2 Slutsatser av kvalitativa undersökningen I den kvalitativa undersökningen kommer inte utvecklingsmiljöerna att få några betyg utan får istället ett omdöme. 6.2.1 Kodportabilitet Vid kodportabilitet hamnar Visual C++ och Delphi bland de sämre utvecklingsverktygen. Inget av dem är dock direkt dåligt men om det är viktigt att den kod som skrivs skall kunna flyttas mellan olika hård- och mjukvaruplattformar, är GNAT, MS J++ och Perl bättre alternativ. Det bästa alternativet när det gäller 39 6 Analys av resultat kodportabilitet, är något av de interpreterande språken. Javaprogram kan köras på vilken hårdvara som helst, förutsatt att det finns en virtuell Javamaskin. Perlprogram kan också köras på de flesta plattformar om det finns en tolk tillgänglig. 6.2.2 Utbud av färdiga komponenter När det gäller utbudet av färdiga komponenter utmärker sig Delphi och Visual C++. Detta beror mest på att dessa två används mycket för att utveckla ”vanliga” PCprogram och har därigenom ett stort utbud av färdiga komponenter för de flesta olika användningsområden. Exempel på komponenter som kan vara till nytta i ett PLCsystem är komponenter för nätverkskommunikation, databashantering och matematiska beräkningar. Det går att få tag på denna typ av komponenter för GNAT, MS J++ och Perl men utbudet är inte riktigt lika stort. 40 7 Sammanfattning 7 Sammanfattning Vid val av utvecklingsverktyg för programvaruutveckling mot PLC-system finns det mycket att tänka på. I detta arbete har en del viktiga kriterier utvärderats. Det är dock inte alltid dessa kriterier är de som är viktigast för en specifik applikation. Resultaten i detta arbete kan användas som beslutshjälp vid val av utvecklingsverktyg. GNAT (Ada) Rent prestandamässigt ligger GNAT bra till även om Visual C++ och Delphi är snabbare. Prestandan räcker dock till de flesta typer av system och om prestandan inte skulle räcka till så går det alltid att skaffa snabbare hårdvara att köra programmen på. Om korta kompilerings- och nerladdningstider är viktiga så är GNAT ett dåligt alternativ. Kompileringshastigheten är låg och programfilerna blir stora jämfört med de övriga utvecklingsmiljöerna i utvärderingen. God portabilitet av kod och medelbra utbud av färdiga komponenter gör GNAT till ett intressant alternativ för många typer av PLC-system. Microsoft Visual C++ Microsoft Visual C++ har bra prestanda vid kodexekvering och kompilering och genererar små programfiler. Utbudet av färdiga komponenter är stort. Dock kan koden i vissa fall vara svår att porta mellan olika hård- och mjukvaruplattformar. Microsoft J++ Microsoft J++ är prestandamässigt inte i samma klass som exempelvis Visual C++ men borde räcka till vissa typer av applikationer. Om en kompilator som genererar ren maskinkod används blir prestandan jämförbar med Visual C++ vilket borde vara fullt tillräcklig även för tidskritiska system. Java ger små program på grund av att många funktioner finns i standardklasser och en bra moduluppdelning vilket leder till att nerladdningstider mellan utvecklingssystem och målmiljö går snabbt. Kompilering går relativt snabbt och kan snabbas upp ytterligare genom en smart moduluppdelning av programmet. Portabilitet av koden är en av Javas starka sidor. Utbudet av färdiga komponenter är inte lika stort som för Visual C++, men de viktigaste komponenterna går ofta att få tag på. Borland Delphi Borland Delphi är den snabbaste av de utvecklingsmiljöer som utvärderats i detta arbete, både när det gäller kompileringshastighet och kodeffektivitet. Delphi är därför ett intressant alternativ om låga kompileringstider och snabba program efterfrågas. Även storleksmässigt ligger Delphiprogram bra till, vilket leder till korta nerladdningstider vid uppdatering av programvaran i ett PLC-system. Utbudet av färdiga komponenter är stort. En nackdel med Delphi är att koden är svår att porta till andra hård- och mjukvaruplattformar. 41 7 Sammanfattning Active Perl Active Perl har väldigt dålig prestanda och kan därför inte användas till annat än system som inte är prestandakrävande. Dock finns ingen kompileringstid då programmen tolkas direkt av en tolk. Programfilerna blir små och möjliggör därför korta nerladdningstider. På grund av att koden inte behöver kompileras finns möjligheten att ändra i koden direkt på målmiljön utan att behöva ladda ner den ifrån utvecklingsdatorn. Perlprogram är enkla att porta mellan olika plattformar. Det enda som krävs är att en tolk för målmiljön finns tillgänglig. Utbudet av färdiga komponenter är dock ganska begränsat även om komponenter för nätverkskommunikation och databastillgång finns tillgängliga. 7.1 Framtida arbete Vid fortsatt arbete finns det mycket att göra. Till exempel kan fler av de kriterier som satts upp utvärderas och fler utvecklingsmiljöer undersökas. Undersökningen skulle kunna inriktas på andra typer av målsystem än PLC-system. Vid de kvantitativa testerna kan andra typer av testprogram användas. Istället för att bara testa språkfunktioner så kan större och mer komplexa testprogram skrivas. Förutsättningarna för testerna kan ändras. Det skulle till exempel vara intressant att se hur mycket de olika cache-minnena i datorn inverkar på prestandan. Tidmätningen skulle kunna genomföras genom att läsa av systemklockan före och efter själva testkoden utförts, vilket skulle leda till exaktare tider. Vid kompileringstidstester skulle ett mer varierande testprogram kunna användas där fler funktioner ifrån externa bibliotek används, detta för att se hur väl ett utvecklingsverktyg hanterar externa bibliotek. 42 Referenser Referenser Aho, A. V., Sethi, R. & Ullman, J. D., 1986, Compilers – Principles, Techniques and Tools, Addison-Wesley Publishing. Bolin, R. L., 1997, CSc882 Research Paper Java Optimization and Performance. [Online]. Tillgänglig från: http://www.mindspring.com/~rlb/csc882/index.html [Hämtad 1999-04-13]. Burns, A., 1985, Concurrent programming in Ada, University of Cambridge Burns, A. & Wellings, A., 1997, Real-Time Systems and Programming Languages, Tredje upplagan, Addison-Wesley Publishing. Cohn, M., Morgan, B., Nygard, M., Joshi, D. & Trinke, T., 1996, Java Developer´s Reference, Sams Publishing. Diab Data, 1999, Power Compiling Solutions, [Online]. Tillgänglig från: http://www.ddi.com/products/products.htm, [Hämtad 1999-04-15]. Grace, R., 1996, The Benchmark Book, Prentice Hall. Houston, 1989, Looking into C, Merrill Publishing. Husain K. & Breedlove R., 1996, Perl 5 Unleashed, Sams Publishing. Lippman, S. B., C++ Primer, Andra upplagan, Addison-Wesley Publishing. JP Software, 1999, JP Software – The Prompt Solution, [Online]. Tillgänglig från: http://www.jpsoft.com/index.htm, [Hämtad 1999-04-21]. Kempe, M., 1996, Ada 95 Reference Manual, [Online]. Tillgänglig från: http://www.adahome.com/rm95/ [Hämtad 1999-04-15]. Koffman, E. B., 1985, Pascal, Andra upplagan, Addison-Wesley Publishing. Pressman, S. R., 1997, Software Engineering – A Practitioner´s Approach, Fjärde upplagan, McGraw-Hill. Weidermanm, N. H., 1989, Ada Adoption Handbook: Compiler Evaluation and Selection, Carnegie Mellon University. 43 Bilaga A. Källkod för benchmarkprogram Bilaga A. Källkod för benchmarkprogram Källkod för Ada-program -Benchmark Loopoverhead. 1 test -- Loopvärde: 10^7 1: procedure ben2 is -- Loopvärde: 10^5 procedure loopen is procedure ben1 is j : integer; begin begin for I in 1..100000 loop for I in 1..10000000 loop null; j := I; end loop; end loop; end ben1; -Benchmark Loopoverhead. end loopen; 1 test 2: begin loopen; -- Loopvärde: 10^6 end ben2; procedure ben2 is begin -- Benchmark 3 test variabeltilldelning. for I in 1..1000000 loop null; 1: Global -- Loopvärde: 10^7 end loop; procedure ben3 is end ben2; j : integer; begin -Benchmark Loopoverhead. 1 test 3: for I in 1..10000000 loop j := I; -- Loopvärde: 10^7 end loop; procedure ben3 is end ben3; begin for I in 1..10000000 loop -Benchmark Matristilldelning null; end loop; 4 test 1: -- Loopvärde: 10^7 end ben3; procedure ben4 is j : array(1..4) of integer; -Benchmark Loopoverhead. 1 test 4: begin for I in 1..10000000 loop -- Loopvärde: 10^8 procedure ben4 is j(1) := I; begin j(2) := I; j(3) := I; for I in 1..100000000 loop j(4) := I; null; end loop; end loop; end ben4; end ben4; -- Benchmark 2 test variabeltilldelning. 1: -- Benchmark 5 test 1: Uppräkning av 8-bitarsvariabel Lokal 1 Bilaga A. Källkod för benchmarkprogram begin -- Loopvärde: 10^7 j:=0; procedure ben5 is for I in 1..10000000 loop type Byte is mod 256; j:=j+1; j : Byte; end loop; begin end ben8; j := 0; for I in 1..10000000 loop -- Benchmark 9 test 1: Uppräkning av 64-bitars flyttalsvariabel j := j + 1; end loop; -- Loopvärde: 10^7 end ben5; procedure ben9 is j : float; -- Benchmark 6 test 1: Uppräkning av 16-bitarsvariabel begin -- Loopvärde: 10^7 j := 0.0; procedure ben6 is for I in 1..10000000 loop j := j + 1.0; type Short is mod 65536; end loop; j : Short; end ben9; begin j := 0; -Benchmark Matrisallokering for I in 1..10000000 loop j := j + 1; 10 test 1: -- Loopvärde: 10^7 end loop; with Unchecked_Deallocation; end ben6; procedure ben10 is type MATRIS Integer; -- Benchmark 7 test 1: Uppräkning av 32-bitarsvariabel is array (1..4) of type Ptr is access MATRIS; -- Loopvärde: 10^7 procedure Free Unchecked_Deallocation Ptr); procedure ben7 is j : integer; is new (MATRIS, j : Ptr; begin j := 0; begin for I in 1..10000000 loop for I in 1..10000000 loop j := j + 1; j := new MATRIS; end loop; Free(j); end ben7; end loop; -- Benchmark 8 test 1: Uppräkning av 64-bitarsvariabel end ben10; -- Fungerar ej i Ada d† Ada saknar 64-bitars variabler -Benchmark Funktionsanrop -- Loopvärde: 10^7 -- Loopvärde: 10^7 procedure ben8 is type longint 2**63+1..+2**63-1; 11 procedure ben11 is is range - procedure loopen is begin j : longint; null; 2 test 1: Bilaga A. Källkod för benchmarkprogram j:=cos(30.0); end loopen; end loop; end ben13b; begin for I in 1..10000000 loop -- Benchmark 13 test 3: Matematisk funktion 2 loopen; end loop; -- Loopvärde: 10^7 end ben11; with Ada.Numerics.Elementary_Functions; -- Benchmark 12 test 1: Matematisk funktion 1 use Ada.Numerics.Elementary_Functions; -- Loopvärde: 10^7 procedure ben13c is procedure ben12 is j : float; j : integer; begin begin for I in 1..10000000 loop j:=cos(45.0); for I in 1..10000000 loop end loop; j:=abs(-30); end ben13c; end loop; end ben12; -- Benchmark 13 test 4: Matematisk funktion 2 -- Benchmark 13 test 1: Matematisk funktion 2 -- Loopvärde: 10^7 -- Loopvärde: 10^7 with Ada.Numerics.Elementary_Functions; with Ada.Numerics.Elementary_Functions; use Ada.Numerics.Elementary_Functions; use Ada.Numerics.Elementary_Functions; procedure ben13d is procedure ben13a is begin j : float; j : float; for I in 1..10000000 loop begin j:=cos(60.0); for I in 1..10000000 loop end loop; j:=cos(15.0); end ben13d; end loop; end ben13a; -Benchmark Villkorstest -- Benchmark 13 test 2: Matematisk funktion 2 -- Loopvärde: 10^7 14 test procedure ben14 is -- Loopvärde: 10^7 j : integer; with Ada.Numerics.Elementary_Functions; begin j:=1; use Ada.Numerics.Elementary_Functions; for I in 1..10000000 loop if j>0 then procedure ben13b is null; j : float; elsif j<0 then begin null; for I in 1..10000000 loop 3 1: Bilaga A. Källkod för benchmarkprogram end loop; else end ben16; null; end if; -Benchmark Funktionsanrop end loop; end ben14; 17 test 1: -- Loopvärde: 10^7 -Benchmark Strängallokering 15 test procedure ben17 is 1: j : integer; -- Loopvärde: 10^7 procedure rekloop is with Unchecked_Deallocation; begin if j<100000 then procedure ben15 is type MATRIS Character; is array (1..50) j:=j+1; of rekloop; type Ptr is access MATRIS; procedure Free Unchecked_Deallocation Ptr); is end if; new (MATRIS, end rekloop; j : Ptr; begin j:=0; begin rekloop; for I in 1..10000000 loop end ben17; j := new MATRIS; Free(j); -- Benchmark 18 test 1: Lintest end loop; -- Loopvärde: 10^7 end ben15; procedure ben18 is j : float; -- Benchmark 16 test 1: Typecasting begin -- Loopvärde: 10^7 for I in 1..10000000 loop procedure ben16 is j:=Float(3.0*(Float(I)/10.0))+Float (2.0*(Float(I)/100.0)); j : integer; k : float; j:=2.0/j; begin end loop; k:=1000.0; end ben18; for I in 1..10000000 loop j:=integer(k); 4 Bilaga A. Källkod för benchmarkprogram Källkod för C++-program * int i; Benchmark 1 test 1: for(i=0;i<100000000;i++){ Loopoverhead. Testar hur mycket } overhead en tom loop orsakar. } Loopvärde: 10^5 */ /* #include <stdio.h> Benchmark 2 test 1: Lokal void main(){ variabeltilldelning int i; Loopvärde: 10^7 for(i=0;i<100000;i++){ */ } #include <stdio.h> } void loop(){ /* int i; Benchmark 1 test 2: int j; Loopoverhead. Testar hur mycket for(i=0;i<10000000;i++){ overhead en tom loop orsakar. j=i; Loopvärde: 10^6 } */ } #include <stdio.h> void main(){ void main(){ int i; loop(); for(i=0;i<1000000;i++){ } } } /* Benchmark 3 test 1: Global /* variabeltilldelning Benchmark 1 test 3: Loopvärde: 10^7 Loopoverhead. Testar hur mycket */ overhead en tom loop orsakar. #include <stdio.h> Loopvärde: 10^7 int i; */ int j; #include <stdio.h> void main(){ void main(){ int i; for(i=0;i<10000000;i++){ for(i=0;i<10000000;i++){ j=i; } } } } /* Benchmark 1 test 4: /* Loopoverhead. Testar hur mycket Benchmark 4 test 1: overhead en tom loop orsakar. Matristilldelning Loopvärde: 10^8 Loopvärde: 10^7 */ */ #include <stdio.h> #include <stdio.h> void main(){ int i; 2 Bilaga A. Källkod för benchmarkprogram int j[4]; int i; int j = 0; void main(){ for(i=0;i<10000000;i++){ void main(){ j[0]=i; for(i=0;i<10000000;i++){ j[1]=i; j++; j[2]=i; } j[3]=i; } } } /* /* av 64-bitarsvariabel Benchmark 8 test 1: Uppräkning Benchmark 5 test 1: Uppräkning Loopvärde: 10^7 av 8-bitarsvariabel */ Loopvärde: 10^7 #include <stdio.h> */ int i; #include <stdio.h> long j = 0; int i; char j = 0; void main(){ for(i=0;i<10000000;i++){ void main(){ j++; for(i=0;i<10000000;i++){ } j++; } } } /* Benchmark 9 test 1: Uppräkning /* av 64-bitars flyttalsvariabel Benchmark 6 test 1: Uppräkning Loopvärde: 10^7 av 16-bitarsvariabel */ Loopvärde: 10^7 #include <stdio.h> */ int i; #include <stdio.h> float j = 0; int i; short j = 0; void main(){ for(i=0;i<10000000;i++){ void main(){ j++; for(i=0;i<10000000;i++){ } j++; } } } /* Benchmark 10 test 1: /* Matrisallokering Benchmark 7 test 1: Uppräkning Loopvärde: 10^7 av 32-bitarsvariabel */ Loopvärde: 10^7 #include <stdio.h> */ int i; #include <stdio.h> int* j; 3 Bilaga A. Källkod för benchmarkprogram float j; void main(){ for(i=0;i<10000000;i++){ void main(){ j = (int*)malloc(16); for(i=0;i<10000000;i++){ free(j); j = cos(15.0); } } } } /* /* Benchmark 13 test 2: Matematisk Benchmark 11 test 1: funktion 2 Funktionsanrop Loopvärde: 10^7 Loopvärde: 10^7 */ */ #include <stdio.h> #include <stdio.h> int i; int i; float j; void funk(){ void main(){ } for(i=0;i<10000000;i++){ void main(){ } j = cos(30.0); for(i=0;i<10000000;i++){ } funk(); } /* } Benchmark 13 test 3: Matematisk funktion 2 /* Loopvärde: 10^7 Benchmark 12 test 1: Matematisk */ funktion 1 #include <stdio.h> Loopvärde: 10^7 int i; */ float j; #include <stdio.h> int i; void main(){ int j; for(i=0;i<10000000;i++){ j = cos(45.0); void main(){ } for(i=0;i<10000000;i++){ } j = abs(-30); } /* } Benchmark 13 test 4: Matematisk funktion 2 /* Loopvärde: 10^7 Benchmark 13 test 1: Matematisk */ funktion 2 #include <stdio.h> Loopvärde: 10^7 int i; */ float j; #include <stdio.h> int i; void main(){ 4 Bilaga A. Källkod för benchmarkprogram for(i=0;i<10000000;i++){ int i; j = cos(60.0); int j; } double k = 1000; } void main(){ /* for(i=0;i<10000000;i++){ Benchmark 14 test 1: j = (int)k; Villkorstest } Loopvärde: 10^7 } */ #include <stdio.h> /* int i; Benchmark 17 test 1: Rekursivt int j; anrop. Loopvärde: 10^7 void main(){ */ j=1; #include <stdio.h> for(i=0;i<10000000;i++){ int i=0; if(j>0){ } void rekloop(){ else if(j<0){ if(i<100000){ } i++; else{ rekloop(); } } } } } void main(){ /* rekloop(); Benchmark 15 test 1: } Strängallokering Loopvärde: 10^7 /* */ Benchmark 18 test 1: Linpacktest #include <stdio.h> Loopvärde: 10^7 int i; */ char* j; #include <stdio.h> int i; void main(){ double j; for(i=0;i<10000000;i++){ void main(){ j = (char*)malloc(50); for(i=0;i<10000000;i++){ free(j); j = (3*(i/10)+2*(i/100)); } j = 2/j; } } } /* Benchmark 16 test 1: Typecast Loopvärde: 10^7 */ #include <stdio.h> 5 Bilaga A. Källkod för benchmarkprogram Källkod för Javaprogram /* public static void main (String Benchmark 1 test 1: args[]) { Loopoverhead. Testar hur mycket int i; overhead en tom loop orsakar. for(i=0;i<10000000;i++){ Loopvärde: 10^5 } */ } import java.lang.System; } class ben1t1 { /* public static void main (String Benchmark 1 test 4: args[]) { Loopoverhead. Testar hur mycket int i; overhead en tom loop orsakar. for(i=0;i<100000;i++){ Loopvärde: 10^8 } */ } import java.lang.System; } class ben1t4 { /* public static void main (String Benchmark 1 test 2: args[]) { Loopoverhead. Testar hur mycket int i; overhead en tom loop orsakar. for(i=0;i<100000000;i++){ Loopvärde: 10^6 } */ } import java.lang.System; } class ben1t2 { /* public static void main (String Benchmark 2 test 1: Lokal args[]) { variabeltilldelning int i; Loopvärde: 10^7 for(i=0;i<1000000;i++){ */ } import java.lang.System; } } class ben2 { public static void loop(){ /* int i; Benchmark 1 test 3: int j; Loopoverhead. Testar hur mycket for(i=0;i<100000000;i++){ overhead en tom loop orsakar. j=i; Loopvärde: 10^7 } */ } import java.lang.System; public static void main (String class ben1t3 { args[]) { 6 Bilaga A. Källkod för benchmarkprogram loop(); */ } import java.lang.System; } class ben5 { /* public static void main (String Benchmark 3 test 1: Global args[]) { variabeltilldelning int i; Loopvärde: 10^7 byte j=0; */ for(i=0;i<100000000;i++){ import java.lang.System; j++; } class ben3 { } public static void main (String } args[]) { int i; /* int j; Benchmark 6 test 1: Uppräkning for(i=0;i<100000000;i++){ av 16-bitarsvariabel j=i; Loopvärde: 10^7 } */ } import java.lang.System; } class ben6 { /* public static void main (String Benchmark 4 test 1: args[]) { Matristilldelning int i; Loopvärde: 10^7 short j=0; */ for(i=0;i<100000000;i++){ import java.lang.System; j++; } class ben4{ } public static void main (String } args[]) { int i; /* int j[]=new int[4]; Benchmark 7 test 1: Uppräkning for(i=0;i<100000000;i++){ av 32-bitarsvariabel j[0]=i; Loopvärde: 10^7 j[1]=i; */ j[2]=i; import java.lang.System; j[3]=i; } class ben7 { } public static void main (String } args[]) { int i; /* int j=0; Benchmark 5 test 1: Uppräkning for(i=0;i<100000000;i++){ av 8-bitarsvariabel j++; Loopvärde: 10^7 } 7 Bilaga A. Källkod för benchmarkprogram } import java.util.Vector; } class ben10 { /* public static void main (String Benchmark 8 test 1: Uppräkning args[]) { av 64-bitarsvariabel int i; Loopvärde: 10^7 Vector j; */ for(i=0;i<100000000;i++){ import java.lang.System; j = new Vector(4); } class ben8 { } public static void main (String } args[]) { int i; /* long j=0; Benchmark 11 test 1: for(i=0;i<100000000;i++){ Funktionsanrop j++; Loopvärde: 10^7 } */ } import java.lang.System; } class ben11 { /* public static void loop(){ Benchmark 9 test 1: Uppräkning } av 64-bitars flyttalsvariabel Loopvärde: 10^7 public static void main (String */ args[]) { import java.lang.System; int i; for(i=0;i<100000000;i++){ class ben9 { loop(); public static void main (String } args[]) { } int i; } double j=0; for(i=0;i<100000000;i++){ /* j++; Benchmark 12 test 1: Matematisk } funktion 1 } Loopvärde: 10^7 } */ import java.lang.System; /* import java.lang.Math; Benchmark 10 test 1: Matrisallokering, inte helt riktig. class ben12 { Allokerar bara upp en vektor med public static void main (String plats för fyra värden. args[]) { Loopvärde: 10^7 int i; */ int j=0; import java.lang.System; for(i=0;i<100000000;i++){ 8 Bilaga A. Källkod för benchmarkprogram j=Math.abs(-30); */ } import java.lang.System; } import java.lang.Math; } class ben13t3 { /* public static void main (String Benchmark 13 test 1: Matematisk args[]) { funktion 2 int i; Loopvärde: 10^7 double j; */ for(i=0;i<100000000;i++){ import java.lang.System; j=Math.cos(45.0); import java.lang.Math; } } class ben13t1 { } public static void main (String args[]) { /* int i; Benchmark 13 test 4: Matematisk double j; funktion 2 for(i=0;i<100000000;i++){ Loopvärde: 10^7 j=Math.cos(15.0); */ } import java.lang.System; } import java.lang.Math; } class ben13t4 { /* public static void main (String Benchmark 13 test 2: Matematisk args[]) { funktion 2 int i; Loopvärde: 10^7 double j; */ for(i=0;i<100000000;i++){ import java.lang.System; j=Math.cos(60.0); import java.lang.Math; } } class ben13t2 { } public static void main (String args[]) { /* int i; Benchmark 14 test 1: double j; Villkorstest for(i=0;i<100000000;i++){ Loopvärde: 10^7 j=Math.cos(30.0); */ } import java.lang.System; } import java.lang.Math; } class ben14 { /* public static void main (String Benchmark 13 test 3: Matematisk args[]) { funktion 2 int i; Loopvärde: 10^7 int j=1; 9 Bilaga A. Källkod för benchmarkprogram for(i=0;i<100000000;i++){ int j; if(j>0){ double k = 1000; } for(i=0;i<100000000;i++){ else if(j<0){ j = (int)k; } } else{ } } } } } /* } Benchmark 17 test 1: Rekursivt anrop /* Loopvärde: 10^7 Benchmark 15 test 1: */ Strängallokering import java.lang.System; Loopvärde: 10^7 import java.lang.Math; */ import java.lang.System; class ben17 { import java.lang.Math; public static int i=0; import java.lang.String; import java.util.Vector; public static void rekloop(){ if(i<100000){ class ben15 { i++; public static void main (String rekloop(); args[]) { } int i; } Vector j; String s = " public static void main (String "; args[]) { for(i=0;i<100000000;i++){ rekloop(); j=new Vector(); } j.addElement(s); } j.removeAllElements(); } /* } Benchmark 18 test 1: Lintest } Loopvärde: 10^7 */ /* import java.lang.System; Benchmark 16 test 1: Typecasting import java.lang.Math; Loopvärde: 10^7 */ class ben18 { import java.lang.System; import java.lang.Math; public static void main (String args[]) { class ben16 { int i; public static void main (String double j=0; args[]) { for(i=0;i<10000000;i++){ int i; j = (3*(i/10)+2*(i/100)); 10 Bilaga A. Källkod för benchmarkprogram j = 2/j; } } } 11 Bilaga A. Källkod för benchmarkprogram Källkod för Delphiprogram // Benchmark 1 test 1: uses sysutils; Loopoverhead. Testar hur mycket overhead en tom loop orsakar. var i:integer; // Loopvärde: 10^5 begin program ben1t1; for i:=1 to 100000000 do begin uses sysutils; end; end. var i:integer; begin // Benchmark 2 test 1: Lokal for i:=1 to 100000 do begin variabeltilldelning end; end. // Loopvärde: 10^7 program ben2; uses sysutils; // Benchmark 1 test 2: Loopoverhead. Testar hur mycket procedure loop; overhead en tom loop orsakar. var i:integer; // Loopvärde: 10^6 var j:integer; program ben1t2; begin uses sysutils; for i:=1 to 10000000 do begin j:=i; var i:integer; end; begin end; for i:=1 to 1000000 do begin end; begin end. loop; end. // Benchmark 1 test 3: Loopoverhead. Testar hur mycket // Benchmark 3 test 1: Global overhead en tom loop orsakar. variabeltilldelning // Loopvärde: 10^7 // Loopvärde: 10^7 program ben1t3; program ben3; uses sysutils; uses sysutils; var i:integer; var i:integer; begin var j:integer; for i:=1 to 10000000 do begin begin end; for i:=1 to 10000000 do begin end. j:=i; end; // Benchmark 1 test 4: end. Loopoverhead. Testar hur mycket overhead en tom loop orsakar. // Benchmark 4 test 1: // Loopvärde: 10^8 Matristilldelning program ben1t4; 12 Bilaga A. Källkod för benchmarkprogram // Loopvärde: 10^7 uses sysutils; program ben4; uses sysutils; var i:integer; j:integer; var i:integer; begin var j: array[1..4] of integer; for i:=1 to 10000000 do begin begin j:=j+1; for i:=1 to 10000000 do begin end; j[1]:=i; end. j[2]:=i; j[3]:=i; // Benchmark 8 test 1: Uppräkning j[4]:=i; av 64-bitsvariabel end; // Loopvärde: 10^7 end. program ben8; uses sysutils; // Benchmark 5 test 1: Uppräkning av 8-bitarsvariabel var i:integer; // Loopvärde: 10^7 j:int64; program ben5; begin uses sysutils; for i:=1 to 10000000 do begin j:=j+1; var i:integer; end; var j:byte; end. begin for i:=1 to 10000000 do begin // Benchmark 9 test 1: Uppräkning j:=j+1; av 64-bits flyttalsvariabel end; // Loopvärde: 10^7 end. program ben9; uses sysutils; // Benchmark 6 test 1: Uppräkning av 16-bitsvariabel var i:integer; // Loopvärde: 10^7 j:double; program ben6; begin uses sysutils; for i:=1 to 10000000 do begin var i:integer; end; j:=j+1; j:smallint; end. begin for i:=1 to 10000000 do begin // Benchmark 10 test 1: j:=j+1; Matrisallokering end; // Loopvärde: 10^7 end. program ben10; uses sysutils; // Benchmark 7 test 1: Uppräkning av 32-bitsvariabel type intarr = array[1..4] of // Loopvärde: 10^7 integer; program ben7; var i:integer; 13 Bilaga A. Källkod för benchmarkprogram j:^intarr; begin begin for i:=1 to 10000000 do begin for i:=1 to 10000000 do begin j:=cos(15.0); new(j); end; dispose(j); end. end; end. // Benchmark 13 test 2: Matematisk funktion 2 // Benchmark 11 test 1: // Loopvärde: 10^7 Funktionsanrop program ben13t2; // Loopvärde: 10^7 uses sysutils; program ben11; uses sysutils; var i:integer; j:double; var i:integer; begin procedure funk(); for i:=1 to 10000000 do begin begin j:=cos(30.0); end; end; end. begin for i:=1 to 10000000 do begin // Benchmark 13 test 3: Matematisk funk; funktion 2 end; // Loopvärde: 10^7 end. program ben13t3; uses sysutils; // Benchmark 12 test 1: Matematisk funktion 1 var i:integer; // Loopvärde: 10^7 j:double; program ben12; begin uses sysutils; for i:=1 to 10000000 do begin var i:integer; end; j:=cos(45.0); j:integer; end. begin for i:=1 to 10000000 do begin // Benchmark 13 test 4: Matematisk j:=abs(-30); funktion 2 end; // Loopvärde: 10^7 end. program ben13t4; uses sysutils; // Benchmark 13 test 1: Matematisk funktion 2 var i:integer; // Loopvärde: 10^7 j:double; program ben13t1; begin uses sysutils; for i:=1 to 10000000 do begin j:=cos(60.0); var i:integer; end; j:double; end. 14 Bilaga A. Källkod för benchmarkprogram program ben16; // Benchmark 14 test 1: uses sysutils; Villkorstest begin // Loopvärde: 10^7 end. program ben14; uses sysutils; // Benchmark 17 test 1: Rekursivt anrop var i:integer; // Loopvärde: 10^7 j:double; program ben16; begin uses sysutils; for i:=1 to 10000000 do begin if(j>0) then begin var i:integer; end procedure rekloop(); else if(j<0) then begin begin end if(i<100000) then begin else begin i:=i+1; end; rekloop(); end; end; end. end; begin // Benchmark 15 test 1: i:=0; Strängallokering rekloop(); // Loopvärde: 10^7 end. program ben15; uses sysutils; // Benchmark 18 test 1: Linjär type strang = string[50]; funktiontest. var i:integer; // Loopvärde: 10^7 j:^strang; program ben18; begin uses sysutils; for i:=1 to 10000000 do begin new(j); var i:integer; dispose(j); j:double; end; begin end. for i:=1 to 10000000 do begin j:=3*(i/10)+2*(i/100); // Benchmark 16 test 1: j:=2/j; Typecasting, funkar inte i Pascal end; // Loopvärde: 10^7 end. 15 Bilaga A. Källkod för benchmarkprogram Källkod för Perlprogram }; # Benchmark 1 test 1: Loopoverhead. Testar hur mycket overhead en tom # Benchmark 4 test 1: loop orsakar Matristilldelning # Loopvärde: 10^5 # Loopvärde: 10^7 for($i=0;$i<100000;$i++){ @j=(); }; for($i=0;$i<10000000;$i++){ @j[0]=$i; # Benchmark 1 test 2: Loopoverhead. @j[1]=$i; Testar hur mycket overhead en tom @j[2]=$i; loop orsakar. @j[3]=$i; # Loopvärde: 10^6 }; for($i=0;$i<1000000;$i++){ }; # Benchmark 5 test 1: Uppräkning av 8-bitarsvariabel. # Benchmark 1 test 3: Loopoverhead. # Loopvärde: 10^7 Testar hur mycket overhead en tom $j=0; loop orsakar. for($i=0;$i<10000000;$i++){ # Loopvärde: 10^7 $j++; for($i=0;$i<10000000;$i++){ }; }; # Benchmark 6 test 1: Uppräkning av # Benchmark 1 test 3: Loopoverhead. 16-bitarsvariabel. Testar hur mycket overhead en tom # Loopvärde: 10^7 loop orsakar. $j=0; # Loopvärde: 10^8 for($i=0;$i<10000000;$i++){ for($i=0;$i<100000000;$i++){ $j++; }; }; # Benchmark 2 test 1: Lokal # Benchmark 7 test 1: Uppräkning av variabeltilldelning 32-bitarsvariabel. # Loopvärde: 10^7 # Loopvärde: 10^7 $j=0; sub loop{ for($i=0;$i<10000000;$i++){ $j=0; $j++; for($i=0;$i<10000000;$i++){ }; $j=$i; }; # Benchmark 8 test 1: Uppräkning av } 64-bitarsvariabel. loop() # Loopvärde: 10^7 $j=0; # Benchmark 3 test 1: Global for($i=0;$i<10000000;$i++){ variabeltilldelning $j++; # Loopvärde: 10^7 }; $j=0; for($i=0;$i<10000000;$i++){ # Benchmark 9 test 1: Uppräkning av $j=$i; 64-bitars flyttalsvariabel. 16 Bilaga A. Källkod för benchmarkprogram # Loopvärde: 10^7 # Benchmark 13 test 3: Matematisk $j=0.0; funktion 2 for($i=0;$i<10000000;$i++){ # Loopvärde: 10^7 $j++; $j=0; }; for($i=0;$i<10000000;$i++){ $j=cos(45.0); # Benchmark 10 test 1: }; Matrisallokering - Går ej implementera. Dynamisk # Benchmark 13 test 4: Matematisk minnesallokering funktion 2 # finns ej i Perl. # Loopvärde: 10^7 # Loopvärde: 10^7 $j=0; for($i=0;$i<10000000;$i++){ # Benchmark 11 test 1: $j=cos(60.0); Funktionsanrop }; # Loopvärde: 10^7 sub funktion{ # Benchmark 14 test 1: Villkorstest } # Loopvärde: 10^7 $j=1; for($i=0;$i<10000000;$i++){ for($i=0;$i<10000000;$i++){ funktion; if($j>0){ }; } elsif($j<0){ # Benchmark 12 test 1: Matematisk } funktion 1 else{ # Loopvärde: 10^7 } $j=0; }; for($i=0;$i<10000000;$i++){ $j=abs(-30); # Benchmark 15 test 1: }; Strängallokering. Funkar inte i Perl p.g.a. av avsaknad av dyn. # Benchmark 13 test 1: Matematisk allokering. funktion 2 # Loopvärde: 10^7 # Loopvärde: 10^7 $j=0; # Benchmark 16 test 1: Typecasting. for($i=0;$i<10000000;$i++){ Perl har inga datatyper. $j=cos(15.0); # Loopvärde: 10^7 }; # Benchmark 13 test 2: Matematisk # Benchmark 17 test 1: Rekursivt funktion 2 anrop. Funkar inte i Perl. # Loopvärde: 10^7 # Loopvärde: 10^7 $j=0; for($i=0;$i<10000000;$i++){ # Benchmark 18 test 1: Lintest $j=cos(30.0); # Loopvärde: 10^7 }; $j=0; 17 Bilaga A. Källkod för benchmarkprogram for($i=1;$i<10000001;$i++){ $j=2/$j; $j=(3*($i/10))+(2*($i/100)); } 18 Bilaga B. Poäng vid betygsbedömningar Bilaga B. Poäng vid betygsbedömningar Benchmarktest GNAT (Ada) Visual C++ MS J++ Borland Delphi Active Perl 1 3 3 4 5 1 2 4 4 3 5 1 3 5 5 3 5 1 4 5 5 3 5 1 5 4 4 2 5 1 6 4 4 3 5 1 7 4 4 3 5 2 8 3 5 3 5 1 9 5 5 3 5 1 11 4 4 3 5 1 12 5 4 3 5 1 13 1 3 4 5 2 14 4 5 2 3 1 18 4 5 5 4 1 1