いまさらベーシックマスターの開発環境を作ってみる(17) プロファイラを作ってみる

2024/07/14BASICMASTER, 昔のパソコン

私が使っていたBASICMASTER L2II (MB-6881)は、標準で16KBのRAMが内蔵されていた。もちろん、これでは少なすぎるので16KBを追加して32KBで使っていた。今のCPUならL1 cache以下のサイズである。

外部記憶装置もテープしかなかったので、なんでもかんでもメモリに載せて実行する必要があり、ただでさえ狭いメモリ空間が余計に狭く感じられたものだ。

もっとも、これでも初期のマイコン環境に比べれば非常に広大な空間だった。

GAME言語やTL/1が開発されていたH68/TRは標準で1KBしかなく拡張しても4KBだった。メモリは貴重な資源であり、短いプログラムを書くテクニックが必要だった。速度よりも省メモリな時代である。

エミュレータ環境は天国だ

当時を知っている人からすると、現代のエミュレーター環境は非常に快適に感じられる。BASICMASTER Jr.互換環境だとメモリはさらに+12KBが使えて、さらにROMの裏も使える。

アセンブルやコンパイルはホスト側でクロス開発すれば良く、一瞬で作業が終わる。ファイルの読み込みもDrag&Dropで一瞬だ。

そして、当時の省メモリ環境で開発されたプログラムを振り返って見ると、もう少しメモリ食ってもいいから高速にできないものかと思うのである。

「推測するな、計測せよ」

高速化するためには、どこで時間を食っているのかの計測が必要である。昔は計測の仕組みが乏しかったので、マシンサイクルを数えてちまちまと高速にしたものだが、エミュレーター環境ならもっと富豪な計測ができる。

まずは、簡易的なプロファイラを作ってみた。

bm2 emulator用のprofiler

j68はソースコードが公開されていないし、ebmjrはMacOSでコンパイルできるかわからなかったので bm2  を使うことにした。ソースコードは下記にある。

とりあえず、起動時から終了時までのプロファイルを取れるようにしてみた。64KBのアドレス空間全体をカウントできる配列を用意して、命令開始時のPCの位置でカウントアップするだけだ。
64KB*4=256KBも使うのだが、今の環境ならなんでもないサイズだ。


diff -uNr bm2/Makefile bm2p/Makefile
--- bm2/Makefile	2016-11-13 11:39:45
+++ bm2p/Makefile	2024-07-14 13:25:32
@@ -3,7 +3,7 @@
 OBJS = main.o m6800.o m6800asm.o bm2mem.o bm2sub.o sound.o util.o menu.o init.o depend.o sdlxpm.o srecord.o conf.o
 
 # SDL2.0
-CFLAGS = -O3 -finline-limit-20000 -DM68_TRACE -DM68_SUB -Wall $(shell sdl2-config --cflags)
+CFLAGS = -O3 -finline-limit-20000 -DM68_TRACE -DM68_SUB -DM68_PROFILE -Wall $(shell sdl2-config --cflags)
 LDFLAGS = -s $(shell sdl2-config --libs)
 
 # SDL1.2
diff -uNr bm2/bm2config bm2p/bm2config
--- bm2/bm2config	2024-07-14 14:59:00
+++ bm2p/bm2config	2024-07-14 14:58:13
@@ -25,3 +25,6 @@
 
 # キーボードの種類 (jp:日本語, en:英語, de:ドイツ語)
 keyboard	jp
+
+#
+profile		y
diff -uNr bm2/init.c bm2p/init.c
--- bm2/init.c	2016-11-13 11:38:40
+++ bm2p/init.c	2024-07-14 14:54:07
@@ -8,6 +8,8 @@
 #include 
 #include 
 #include 
+#include 
+#include 
 #include "bm2.h"
 
 /* キーボード */
@@ -752,6 +754,43 @@
 };
 
 /*
+    profiler
+*/
+void m68profile(const struct M68stat *m68)
+{
+	static	uint32_t	*map;
+	static	int	fd;
+	uint32_t		buf=0;
+
+    if(map==NULL){
+		fprintf(stderr,"m68profile open\n");
+        fd = open("bm2profile.bin",O_RDWR|O_CREAT,0644);
+        if(fd<0){
+            perror("open bm2profile.bin");
+            exit(-1);
+        }
+        size_t len= 65536*sizeof(uint32_t);
+        lseek(fd,0,SEEK_SET);
+		for(int i=0; i<65536; i++){
+			write(fd,(char *)&buf,sizeof(buf));
+		}
+        lseek(fd,0,SEEK_SET);
+		fprintf(stderr,"m68profile mmap\n");
+        map = (uint32_t *)mmap(NULL,len,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0);
+        if(map==MAP_FAILED){
+            perror("mmap failed");
+            exit(-1);
+        }
+		fprintf(stderr,"m68profile map=%lx\n",map);
+		fprintf(stderr,"m68profile init\n");
+        for(int i=0; i<65536; i++){
+            map[i]=0;
+        }
+		fprintf(stderr,"m68profile init end\n");
+    }
+    map[m68->pc]++;
+}
+/*
 	ログを出力する
 */
 void m68log(const struct M68stat *m68)
@@ -997,6 +1036,7 @@
 
 	/* トレースモードか? */
 	bm2->cpu.trace = getOptTable(conf, "debug", tableYesNo, FALSE);
+	bm2->cpu.profile = getOptTable(conf, "profile", tableYesNo, FALSE);
 
 	/* 画面の拡大率を設定する */
 	bm2->zoom = getOptInt(conf, "zoom", 2);
diff -uNr bm2/m6800.c bm2p/m6800.c
--- bm2/m6800.c	2016-11-13 01:14:04
+++ bm2p/m6800.c	2024-07-14 13:25:15
@@ -841,6 +841,7 @@
 	m68.total_states = 0;
 	m68.emulate_subroutine = 0;
 	m68.trace = 0;
+	m68.profile = 0;
 	m68.tag = tag;
 	return m68;
 }
@@ -997,6 +998,10 @@
 		_length = length[_op];
 		_states = states[_op];
 
+#if defined(M68_PROFILE)
+		if(m68->profile)
+			m68profile(m68);
+#endif
 #if defined(M68_TRACE)
 		if(m68->trace)
 			m68log(m68);
diff -uNr bm2/m6800.h bm2p/m6800.h
--- bm2/m6800.h	2014-03-19 23:56:06
+++ bm2p/m6800.h	2024-07-14 13:31:16
@@ -31,6 +31,7 @@
 	int total_states;	/* 累積ステート数 */
 	int emulate_subroutine;	/* サブルーチンをエミュレートするか? */
 	int trace;		/* トレースモードか? */
+	int profile;	/* profile mode? */
 	void *tag;		/* その他の情報 */
 };
 
@@ -52,6 +53,9 @@
 
 #if defined(M68_SUB)
 int m68subroutine(struct M68stat *, uint16);
+#endif
+#if defined(M68_PROFILE)
+void m68profile(const struct M68stat *);
 #endif
 #if defined(M68_TRACE)
 void m68log(const struct M68stat *);
diff -uNr bm2/menu.c bm2p/menu.c
--- bm2/menu.c	2014-03-23 07:47:11
+++ bm2p/menu.c	2024-07-14 13:23:10
@@ -8,6 +8,7 @@
 #include 
 #include 
 #include 
+#include 
 #include "bm2.h"
 
 #if defined(WIN32) || defined(_WIN32) || defined(__WIN32)
diff -uNr bm2/sound.c bm2p/sound.c
--- bm2/sound.c	2015-07-05 15:07:46
+++ bm2p/sound.c	2024-07-14 13:22:58
@@ -6,6 +6,7 @@
 #include 
 #include 
 #include 
+#include 
 #include 
 #include 
 #include "bm2.h"

測定結果はbm2profile.binというファイルにベタヅメで入るので、簡単なプログラムで読み出して可視化できる。


$ cat prof2txt.c 
#include	
#include	

extern	int
main()
{
	uint32_t	addr=0;
	uint32_t	count=0;

	while(read(0,&count,sizeof(count))==sizeof(count)){
		if(count){
			printf("%04x %d\n",addr,count);
		}
		addr++;
	}
}

$ ./prof2txt <bm2profile.bin | sort -k2 -nr | grep -v ^f | head -10
2503 993941
24fb 982682
24f9 982682
24f7 982682
24fe 977237
24fd 977237
2502 884174
2500 867470
2381 386028
2380 386028

続く