Тест эффективности кода SDCC 3.5/HiTech 3.09/HiTech 7.50 (demo)
|
DI |
DI/Gl.vars |
Коэфф. |
| Ассемблер (эталон) |
323 |
323 |
1,00 |
| HiTech C v.7.50 (MS-DOS) |
432 |
1814 |
0,74 |
| Z88DK (Win32) |
512 |
- |
0,631 |
| SDCC v.3.51 (Win32) |
528 |
765 |
0,612 |
| IAR (Win32) |
803 |
858 |
0,4022 |
| HiTech C v.3.09 (cp/m, z80) |
979 |
856 |
0,3320/0,3773 |
| Aztec C (cp/m, i8080) |
1738 |
- |
0,186 |
| HiSoft-C v.1.35 (CP/M, Z80) |
|
3660 |
0,0000/0,0882 |
| MI-C v3.18I (CP/M, Z80) |
4160 |
3760 |
0,0776/0,0859 |
| WarpC 14.01 (TR-DOS, Z80) |
4910 |
4640 |
0,0660/0,0696 |
| MeSCC 1.08 (CP/M, i8080) |
8370 |
4850 |
0,0386/0,0667 |
Поскольку меня торкнуло написать печаталку на Сях, и она, таки, заработала, решил устроить небольшое соревнование между компиляторами Си, позволяющими компилировать код для ZX-Spectrum.
Сам тест состоял в вызове 2560 (для очень медленных компиляторов - 256) раз процедуры печати текста. Текст был выбран случайно и оказался длиной 333 символа.
Время запуска и окончания цикла фиксировалось по встроенным CMOS-часам. В таблице результаты времени отработки цикла в секундах.
Изначально время замерялось при включенных прерываниях, что не совсем правильно. - В последующим данные для всех компиляторов будут дополнены.
Кроме того интересно было оценить, насколько на эффективность влияет "локальность" переменных.
Для процедуры на ассемблере ничего не дало - это естественно. Там можно было вынести в глобальные только переменную цикла. В Си-версии процедуры - их шесть. На примере Aztec C, разница заметно видна.
За эталон взята самописная процеура печати 64 сим./стр. на ассемблере. Шрифт используется "экономный" в левых тетрадах байта латинские символы, в правой - кириллица. Итого 1кб.
До кучи, сравнил эффективность кода Хайтек 3.09 до и после оптимизации (прерывания выключены): 483/472 секунд.
Вот сишный исходник:
Код:
#include "minifont.c"
#define LAT 0
#define RUS 1
char text[16];
void i2s(char*, int num);
unsigned int iBCD2int(unsigned int num);
unsigned int rd_cmos_minsec(void);
unsigned char atX,atY;
const unsigned char* ScreenTable[24] = {
0x4000,0x4020,0x4040,0x4060,0x4080,0x40A0,0x40C0,0x40e0,
0x4800,0x4820,0x4840,0x4860,0x4880,0x48A0,0x48C0,0x48e0,
0x5000,0x5020,0x5040,0x5060,0x5080,0x50A0,0x50C0,0x50e0
};
unsigned int iBCD2int(unsigned int num) __naked
{ num;
__asm
pop af
pop de
push de
push af
ld hl,#0
ld a,d
rrca
rrca
rrca
rrca
and a,#0x0f
ld l,a
call 2$ ; x10
ld a,d
and a,#0x0f
ld b,h
ld c,a
add hl,bc
call 2$ ; x10
add hl,hl
ld c,l
ld b,h
add hl,hl
add hl,bc ; x6
ld a,e
rrca
rrca
rrca
rrca
and a,#0x0f
add a,a
ld c,a
add a,a
add a,a
add a,c
ld c,a ; x10
ld a,e
and a,#0x0f
add a,c
ld c,a
ld b,#0
add hl,bc
ret
2$: add hl,hl
ld c,l
ld b,h
add hl,hl
add hl,hl
add hl,bc
ret
__endasm;
}
unsigned int rd_cmos_minsec(void) __naked
{
__asm
ld bc,#0xeff7
ld a,#0x80
out (c),a
ld b,#0xdf
xor a
out (c),a
ld b,#0xbf
in l,(c)
ld b,#0xdf
ld a,#2
out (c),a
ld b,#0xbf
in h,(c)
ld b,#0xef
xor a
out (c),a
ret
__endasm;
}
void i2s(char* d, int num) __naked
{ d, num;
__asm
pop af
pop bc
ex (sp),hl
push bc
push af
xor a,a
ld de,#10000
call 2$
xor a,a
ld de,#1000
call 2$
xor a,a
ld de,#100
call 2$
xor a,a
ld de,#10
call 2$
ld a,#0x30
add a,l
ld (bc),a
inc bc
xor a,a
ld (bc),a
ret
2$: or a,a
sbc hl,de
jp c,1$
inc a
jp 2$
1$: add hl,de
; or a,a
; ret z
add a,#0x30
ld (bc),a
inc bc
ret
__endasm;
}
void put__ch(char sym)
{
unsigned char* dest;
unsigned char* src;
unsigned char lang,q,i;
unsigned int index;
dest = (unsigned char*)(ScreenTable[atY]+atX/2);
lang = LAT;
if(sym&0x80) lang = RUS;
sym = sym&0x7f;
index = sym*8;
src = (unsigned char*)(minifont + index);
if(lang==0) {
if(atX&0x01) {
for(i=0;i<8;i++)
{
q = (*dest)&0xf0;
*dest = q | ((*src++)/16&0x0f);
dest+=256;
}
}
else {
for(i=0;i<8;i++)
{
q = (*dest)&0x0f;
*dest = q | ((*src++)&0xf0);
dest+=256;
}
}
}
else {
if(atX&0x01) {
for(i=0;i<8;i++)
{
q = (*dest)&0xf0;
*dest = q | ((*src++)&0x0f);
dest+=256;
}
}
else {
for(i=0;i<8;i++)
{
q = (*dest)&0x0f;
*dest = q | ((*src++)*16)&0xf0;
dest+=256;
}
}
}
}
void print(char* str)
{
while(*str) {
put__ch(*str++);
atX++;
if(atX>63) {atX=0; atY++;};
}
}
void main(void)
{
unsigned int begin, end;
unsigned int i = 2560;
begin = rd_cmos_minsec();
while(i) {
atX=atY=0;
print("Привет, В форуме проскальзовали идеи о том, что дескать неплохо было бы с реального спектрума выходить в какой-никакой интернет. И даже якобы за рубежом изобрели какую-то хреновину, которая является чем-то типа модема для спектрума и вставляется в стандартный системный разъем фирменного спека. Только вот что было дальше я не понял.");
--i;
};
end = rd_cmos_minsec();
begin = iBCD2int(begin);
end = iBCD2int(end);
end = end - begin;
i2s(text,end);
atX =0;
atY +=2;
print("Печать вышерасположенного текста 2560 раз заняла ");
print(text);
print(" секунд");
}
Кстати, сишный исходник, наверняка, можно оптимизировать. Если знатоки, решат его поправить, - добавлю в таблицу соответствующие данные. Это тоже интересно, сколько может дать оптимизация на высоком уровне. Пока в глаза бросается, что можно было обойтись без вычисления промежуточной переменной index и return(dest).
HiSoft-C Compiler v.1.35, CP/M 80
Добавил в табличку сведения по HiSoft-C.
Вообще, я хотел проверить версию для TR-DOS, однако не вышло - компилятор почему-то ругается на нехватку памяти. Поэтому, чтобы получить представление о возможностях спектрумовского компилятора пришлось использовать его cp/m-ного собрата. Правда, на спектруме используется более старая версия: 1.1.
Для версии с глобальными переменными коэффициент относительно аналогичной программы на ассемблере составил 0,0882.
256 вызовов отработали за 366 секунд.
Этот компилятор генерит сразу машинный код, минуя стадию ассемблера, и не даёт возможности компиляции под произвольный адрес - только с 0x100.
Но, поскольку каких-либо функции, завязанных на CP/M я не использовал, то решил задачу в лоб: полученный com-файл CP/M запустил на Пентеве с адреса 0x100. И всё получилось.
HiSoft-C заточен именно на Z-80, обращается к локальным переменным через IX. Однако качество кода настолько низкое, что по эффективности, фактически, ничем не отличается от компиляторов для I8080.