GARTHWILSON wrote:
Unfortunately these benchmarks seem a bit dubious to me. It seems to be using its own framework and emulator, so it is a bit cumbersome to verify. Therefore I just looked at some points that seemed suprising to me.
Many of the tests have been written for or optimized/adapted to certain compilers. Obviously this can make a difference compared to neutral code. For example, the RLE/unzip benchmark for kickc has been adapted, so that the compiler sees the entire input data as a constant C array, whereas the unmodified version just gets an external reference to data from an assembly file.
Also, the declarations do not seem to match and probably random data will be decompressed:
Code:
kickc_unzip.c:
extern const uint8_t* zipped_data;
kickc_unzip_data.c:
const uint8_t zipped_data[] = { ...
Not sure how much of a difference that makes, but it does not raise confidence.
Then I had a look at the RPG example, because the gcc results were so much better. I did not reproduce his tests or the gcc results, but I had a look at the code generated by vbcc:
Code:
$ cat rpg.c
#include <stdint.h>
//
// Your typical RPG foo
//
typedef struct {
uint8_t pv;
uint8_t attack;
} Monster;
static void monster_init(Monster* monster) {
monster->pv = 10;
monster->attack = 2;
}
static void monster_take_hit(Monster* monster, uint8_t attack) {
if (monster->pv > attack) {
monster->pv -= attack;
}else {
monster->pv = 0;
}
}
//
// A hero wielding his weapon
//
typedef struct {
uint8_t attack;
uint8_t durability;
} Weapon;
typedef struct {
uint8_t pv;
uint8_t mana;
Weapon weapon;
} Hero;
static void hero_init(Hero* hero);
static void hero_change_weapon(Hero* hero, Weapon new_weapon);
static void hero_hit_monster(Hero* hero, Monster* monster);
static const Weapon fist = {.attack = 1, .durability = 255};
static const Weapon sword = {.attack = 3, .durability = 10};
static void hero_init(Hero* hero) {
hero->pv = 10;
hero->mana = 10;
hero_change_weapon(hero, fist);
}
static void hero_change_weapon(Hero* hero, Weapon new_weapon) {
hero->weapon = new_weapon;
}
static void hero_hit_monster(Hero* hero, Monster* monster) {
monster_take_hit(monster, hero->weapon.attack);
--hero->weapon.durability;
if (hero->weapon.durability == 0) {
hero_change_weapon(hero, fist);
}
}
//
// Benched routine: initialize things and begin a fight!
//
void benched_routine() {
// Game state:
// hero is in zero page,
// There is 3 monsters at the begining of page 4xx
Hero* hero = (Hero*)0x0080;
Monster* monsters = (Monster*)0x0400;
const uint8_t NB_MONSTERS = 3;
// Initialize gamestate
hero_init(hero);
hero_change_weapon(hero, sword);
for (uint8_t monster_num = 0; monster_num < NB_MONSTERS; ++monster_num) {
monster_init(&monsters[monster_num]);
}
// Fight!
hero_hit_monster(hero, monsters + 1);
}
$ vc +c64 -O3 -S rpg.c
$ cat rpg.asm
;vcprmin=10000
section text
global _benched_routine
_benched_routine:
ldx #10
stx 128
stx 129
lda #1
sta 130
lda #255
sta 131
lda #3
sta 130
stx 131
stx 1024
lda #2
sta 1025
stx 1026
sta 1027
stx 1028
sta 1029
lda 130
sta r0
lda 1026
cmp r0
bcc l47
beq l47
lda 1026
sec
sbc r0
sta 1026
jmp l48
l47:
lda #0
sta 1026
l48:
dec 131
lda 131
bne l49
lda #1
sta 130
lda #255
sta 131
l49:
rts
For the life of me I do not see how gcc could generate code several times faster than that. So I kind of doubt that vbcc was used correctly.
As a problem was mentioned with vbcc and the cc65-optimized game code (which apparently was the reason to call vbcc "buggy" in the summary), I tried to look into that and found this in the code:
Code:
#define SCREEN_ADDR ((unsigned char*)0x300)
and this in the linker file:
Code:
ram: org=0x0300, len=0x0500
Now obviously that is not going to work. So I would take those results with a few pounds of salt.