2015ǯ02��18��

Go�ǥ������������˵���Ĥ��������ɤ����ˡ

¤Ï¤Æ¤Ê¥Ö¥Ã¥¯¥Þ¡¼¥¯¤ËÅÐÏ¿

Go��Python�Τ褦��LL����٤�ȼ¹�®�٤�®���ΤǤ�����GC������®���櫓�ǤϤʤ��Τǡ�����Ū��GC���ѥե����ޥ󥹤�Ϳ����ƶ����礭���ʤ�ޤ���

�ޤ���Java ����٤�ȡ�������֥������ȤʤɤΤ�������ˤ˥ҡ��ץ�������������Ԥ���GC����߻��֤�Ĺ���ʤ꤬���Ǥ����������ǥҡ��ץ��������������򤱤��ץ�����ߥ󥰤����䤹������Ǥ⤢��ޤ���

MySQL �ɥ饤�ФΤ褦����쥤�䡼�Υ饤�֥������硢���ץꥱ�������¦����ǽ�׷�򾡼�˷��뤳�Ȥ��Ǥ��ʤ��Τǡ�����Ū���ϰϤǥ������������򸺤餹���Ϥ򤹤�٤��Ǥ���

�Ȥ������Ȥǡ�����ε��� �ǾҲ𤷤��ץ졼���ۥ���ִ����������ˤ����äƷи��������������������˵���Ȥä��ץ�����ߥ󥰤ˤĤ��ơ����塼�˥󥰤�����䥳���ɾ�Υƥ��˥å���Ҳ𤷤����Ȼפ��ޤ���

1. �ޤ���������ư����Τ���

go-sql-driver/mysql �Υ��ƥʤ˼���������뤫�ɤ���ʬ����ʤ���ǽ���ä��Τǡ��ǽ�Ϥʤ�٤���ñ�˼������ơ����ε�ǽ������फ�ɤ�����API���߷פϤ���Ǥ������Ȥ����ä���Ϥ�ޤ���

2. �٥���ޡ����ץ��������

���塼�˥󥰤�Ϥ�����ˡ����̳�ǧ�ѤΥ٥���ޡ����ץ�������񤭤ޤ��� �ƥ��Ȥ�Ʊ���� _test.go �Ȥ���̾���ǽ����ե�����ˡ� BenchmarkXxxx(b *testing.B) �Ȥ����ؿ���������ޤ���������� b.ReportAllocs() ��Ƥ�Ǥ����ȥ��������Ȳ�����¬���뤳�Ȥ��Ǥ��ޤ���

������������٥���ޡ����ץ������ϼ��Τ褦�ʤ�ΤǤ��� database/sql ���桼�������Ϥ����ͤ����������Ƥ����Τǡ��ɥ饤��¦���Ϥ�����ͤ� int64, float64, bool, time.Time, []byte, string ��6�Ĥη������ˤʤ�ޤ���������6�Ĥ������ͤ�1�Ĥ����Ѱդ��ơ��ץ졼���ۥ���ִ���¹Ԥ��Ƥ��ޤ���

func BenchmarkInterpolation(b *testing.B) {
	mc := &mysqlConn{
		cfg: &config{
			interpolateParams: true,
			loc:               time.UTC,
		},
		maxPacketAllowed: maxPacketSize,
		maxWriteSize:     maxPacketSize - 1,
	}

	args := []driver.Value{
		int64(42424242),
		float64(math.Pi),
		false,
		time.Unix(1423411542, 807015000),
		[]byte("bytes containing special chars ' \" \a \x00"),
		"string containing special chars ' \" \a \x00",
	}
	q := "SELECT ?, ?, ?, ?, ?, ?"

	b.ReportAllocs()
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		_, err := mc.interpolateParams(q, args)
		if err != nil {
			b.Fatal(err)
		}
	}
}

�¹Ԥ���ˤϼ��Τ褦�ʥ��ޥ�ɤ�¹Ԥ��ޤ���

$ go test -bench=BenchmarkInterpolation
PASS
BenchmarkInterpolation	  300000	      3887 ns/op	    1144 B/op	      15 allocs/op
ok  	github.com/go-sql-driver/mysql	2.386s

3. ������������󤬵����äƤ������Ĵ�٤�

GODEBUG=allocfreetrace=1 �Ȥ����Ķ��ѿ������ꤹ��ȡ����������Ȥ������ä����Υ����å��ȥ졼���򸫤뤳�Ȥ��Ǥ��ޤ��� (�ɥ������)

�����ǡ��٥���ޡ����ץ������Υ롼�פ���Ȥ�����¹Ԥ��뾮���ʥץ��������Ѱդ������ΤǤ����������Ѱդ��Ƥ���ΤϾ�ʸ������Ϥޤ�̾�������������ʤΤǡ� main �ѥå���������ľ�ܸƤӽФ����ȤϤǤ��ޤ��� mysql �ѥå������˥��ߥåȤ˴ޤ�ʤ��������ե�������ɲä��Ƥ�������ƤӽФ����ɤ��ΤǤ���������ϥ٥���ޡ����ץ�������ľ�����Ѥ��뤳�Ȥˤ��ޤ�����

GODEBUG �����ꤷ���ޤ� go test ��¹Ԥ���ȥΥ�����¿������Τǡ���� go test -c ��Ȥä� mysql.test �Ȥ����¹ԥե�������ꡢ�����¹Ԥ��ޤ������κݡ����ץ����� go test ���Ϥ����ץ����� test. �Ȥ��� prefix ��Ĥ��뤳�Ȥˤʤ�ޤ���

$ go test -c
$ GODEBUG=allocfreetrace=1 ./mysql.test -test.run=none -test.bench=BenchmarkInter -test.benchtime=10ms 2>trace.log
PASS
BenchmarkInterpolation      5000          4095 ns/op        1144 B/op         15 allocs/op

������Ǥϡ� -test.run �ˤɤΥƥ��Ȥˤ�ޥå����ʤ�Ŭ����̾�� none ����ꤷ�ƥ٥���ޡ����ʳ��Υƥ��Ȥ��¹Ԥ���뤳�Ȥ��ɤ��� -test.bench ����Ū�ʳ��Υ٥���ޡ����ؿ��μ¹Ԥ��޻ߤ��� -test.benchtime=10ms �ǥǥե����1�äΥ٥���ޡ������֤�û�����Ƥ��ޤ���ɸ�२�顼���Ϥ˥��������ȤΥ����å��ȥ졼�������Ϥ���Ƥ���Τǡ������ trace.log �˽��Ϥ��Ƥ��ޤ���

��Ū�δؿ��Ǥ��� interpolateParams ��õ��������������������ޤ�������ȼ��Τ褦�ʥ����å��ȥ졼���������ޤ���

tracealloc(0xc2080100e0, 0x70, string)
goroutine 5 [running]:
runtime.mallocgc(0x70, 0x22e4e0, 0x0, 0x2)
	/usr/local/Cellar/go/1.4.1/libexec/src/runtime/malloc.go:327 +0x32a fp=0xc20802ea60 sp=0xc20802e9b0
runtime.newarray(0x22e4e0, 0x7, 0x15c5e)
	/usr/local/Cellar/go/1.4.1/libexec/src/runtime/malloc.go:365 +0xc1 fp=0xc20802ea98 sp=0xc20802ea60
runtime.makeslice(0x2229c0, 0x7, 0x7, 0x0, 0x0, 0x0)
	/usr/local/Cellar/go/1.4.1/libexec/src/runtime/slice.go:32 +0x15c fp=0xc20802eae0 sp=0xc20802ea98
strings.genSplit(0x30c190, 0x17, 0x2e1f10, 0x1, 0x0, 0x7, 0x0, 0x0, 0x0)
	/usr/local/Cellar/go/1.4.1/libexec/src/strings/strings.go:287 +0x14d fp=0xc20802eb60 sp=0xc20802eae0
strings.Split(0x30c190, 0x17, 0x2e1f10, 0x1, 0x0, 0x0, 0x0)
	/usr/local/Cellar/go/1.4.1/libexec/src/strings/strings.go:325 +0x76 fp=0xc20802ebb0 sp=0xc20802eb60
github.com/go-sql-driver/mysql.(*mysqlConn).interpolateParams(0xc20802eed0, 0x30c190, 0x17, 0xc20802ee70, 0x6, 0x6, 0x0, 0x0, 0x0, 0x0)
	/Users/inada-n/go1.4/src/github.com/go-sql-driver/mysql/connection.go:180 +0x86 fp=0xc20802ed38 sp=0xc20802ebb0
github.com/go-sql-driver/mysql.BenchmarkInterpolation(0xc20806a400)
	/Users/inada-n/go1.4/src/github.com/go-sql-driver/mysql/benchmark_test.go:240 +0x437 fp=0xc20802ef58 sp=0xc20802ed38
testing.(*B).runN(0xc20806a400, 0x1)
	/usr/local/Cellar/go/1.4.1/libexec/src/testing/benchmark.go:124 +0x95 fp=0xc20802ef68 sp=0xc20802ef58
testing.(*B).launch(0xc20806a400)
	/usr/local/Cellar/go/1.4.1/libexec/src/testing/benchmark.go:199 +0x78 fp=0xc20802efd8 sp=0xc20802ef68
runtime.goexit()
	/usr/local/Cellar/go/1.4.1/libexec/src/runtime/asm_amd64.s:2232 +0x1 fp=0xc20802efe0 sp=0xc20802efd8
created by testing.(*B).run
	/usr/local/Cellar/go/1.4.1/libexec/src/testing/benchmark.go:179 +0x3e

...(³��)

���Υ����å��ȥ졼���� interpolateParams() ���� strings.Split() ��ƤӽФ��Ƥ�����ʬ�ǥ��������Ȥ�ȯ�����Ƥ��뤳�Ȥ�ʬ����ޤ��� �����������ɤΥե�����̾�ȹԿ���񤫤�Ƥ���Τǡ���ñ�˳�����ʬ������Ǥ��ޤ���

func (mc *mysqlConn) interpolateParams(query string, args []driver.Value) (string, error) {
	chunks := strings.Split(query, "?")
	if len(chunks) != len(args)+1 {
		return "", driver.ErrSkip
	}

;�̤Ǥ����������å��ȥ졼���Τ褦�ʡ֤�������αѿ����פ򸫤����ˡ�ȿ��Ū��̵�뤷�ƥ��顼��å����������򥳥ԥڤ��Ƽ��䤹��ͤ�褯�������ޤ����Τ����ɤ�Τ˵��Ϥ����뤫�⤷��ޤ��󤬡������å��ȥ졼���Ϥ��θ���䥢�ץꥱ�������򿼤��Τ���פʼ꤬����ʤΤǡ��ǥХå�������塼�˥󥰤���Ȥ��ʤɤϤ��ä���ȸ��������ޤ��礦��

4. ���塼�˥�

����ǽ�������ü�Ǥ������Ȥ�1��1�IJ������ʤ���Ǹ��̤��ǧ���Ƥ��������Ǥ����Ȥꤢ����������֤Υ٥���ޡ�����̤�Ĥ��Ƥ����ޤ��礦��

$ go test -bench=BenchmarkInterpolation | tee old.txt
PASS
BenchmarkInterpolation	  500000	      3942 ns/op	    1144 B/op	      15 allocs/op
ok  	github.com/go-sql-driver/mysql	3.219s

4.1. ʸ����Ϣ�뤫�� []byte �ؤ� append ��

�ǽ�μ����ϡ�������ʸ����� ? ��ʬ�䤷�ơ� parts []string ��ʬ�䤵�줿�ƥ�����ʸ����ȡ��ѥ�᡼����ʸ���󲽤�����Τ��ߤ˳�Ǽ�� ("SELECT SLEEP(0)", 42 �ʤ� []string{"SELECT SLEEP(", "42", ")"})���Ǹ�� strings.Join(parts, "") �Ƿ�礹��Ȥ�����LL�ǽ񤯤褦�ʥ����ɤǤ�����

Go ��ʸ����� immutable �ʤΤǡ�������ʸ����򤿤�����Ĥ��뤳�������Ǥϥ��������Ȥ�������Τ��򤱤��ޤ��󡣤����ǡ��ǽ�� []byte �ΥХåե�����ݤ��Ƥ������ɵ����Ƥ����褦�ˤ��ޤ�����

�Хåե������̤���˥�����ʸ����ȳƥѥ�᡼�����鳵�����Ƴ��ݤ��뤳�Ȥǡ��ꥢ�������������򤱤ޤ���������¿��� strconv.AppendInt() �Ȥ����� []byte ���ɵ����Ĥ�ʸ���󲽤���ؿ���ɸ��饤�֥����Ѱդ���Ƥ���ΤǤ����Ȥ��ޤ�������Ǽ��Τ褦�ʥ����ɤˤʤ�ޤ�����

	buf := make([]byte, 0, estimated)
	argPos := 0

	// Go 1.5 will optimize range([]byte(string)) to skip allocation.
	for _, c := range []byte(query) {
		if c != '?' {
			buf = append(buf, c)
			continue
		}

		arg := args[argPos]
		argPos++

		if arg == nil {
			buf = append(buf, []byte("NULL")...)
			continue
		}

		switch v := arg.(type) {
		case int64:
			buf = strconv.AppendInt(buf, v, 10)

4.2. benchcmp

����ǰ�ö���̤��ǧ���ޤ������κݡ� benchcmp �Ȥ����ġ����Ȥä���Ӥ�������򥳥ߥåȥ����˴ޤ��� Go �Υ��ߥå��ߤ����ǥ�����Ǥ���

$ go get -u golang.org/x/tools/cmd/benchcmp
$ go test -bench=BenchmarkInterpolation > new.txt
$ benchcmp old.txt new.txt
benchmark                  old ns/op     new ns/op     delta
BenchmarkInterpolation     3942          2573          -34.73%

benchmark                  old allocs     new allocs     delta
BenchmarkInterpolation     15             6              -60.00%

benchmark                  old bytes     new bytes     delta
BenchmarkInterpolation     1144          560           -51.05%

���������Ȳ���� 15 ���� 6 �˸��äƤ���Τ�ʬ����ޤ����¹�®�٤�������������˺︺�Ǥ��Ƥ��ޤ��͡�

4.3. Time.Format() ���򤱤�

int64 �� float64 �� strconv.AppendInt() �� strconv.AppendFloat() �Ǥ��줾�� []byte ���ɵ����뤳�Ȥ�����ޤ���������ǰ�ʤ��� time.Time �ˤϤ��Τ褦�ʥ᥽�åɤ�����ޤ��� �����ǡ��٥å��٥��ʥ����ɤ�ʸ����ɽ������ޤ���

before:

		case time.Time:
			if v.IsZero() {
				buf = append(buf, []byte("'0000-00-00'")...)
			} else {
				fmt := "'2006-01-02 15:04:05.999999'"
				if v.Nanosecond() == 0 {
					fmt = "'2006-01-02 15:04:05'"
				}
				s := v.In(mc.cfg.loc).Format(fmt)
				buf = append(buf, []byte(s)...)
			}

after:

		case time.Time:
			if v.IsZero() {
				buf = append(buf, "'0000-00-00'"...)
			} else {
				v := v.In(mc.cfg.loc)
				v = v.Add(time.Nanosecond * 500) // To round under microsecond
				year := v.Year()
				year100 := year / 100
				year1 := year % 100
				month := v.Month()
				day := v.Day()
				hour := v.Hour()
				minute := v.Minute()
				second := v.Second()
				micro := v.Nanosecond() / 1000

				buf = append(buf, []byte{
					'\'',
					digits10[year100], digits01[year100],
					digits10[year1], digits01[year1],
					'-',
					digits10[month], digits01[month],
					'-',
					digits10[day], digits01[day],
					' ',
					digits10[hour], digits01[hour],
					':',
					digits10[minute], digits01[minute],
					':',
					digits10[second], digits01[second],
				}...)

				if micro != 0 {
					micro10000 := micro / 10000
					micro100 := micro / 100 % 100
					micro1 := micro % 100
					buf = append(buf, []byte{
						'.',
						digits10[micro10000], digits01[micro10000],
						digits10[micro100], digits01[micro100],
						digits10[micro1], digits01[micro1],
					}...)
				}
				buf = append(buf, '\'')
			}

after �Ϻǽ��ǤΥ����ɤǡ����塼�˥󥰰ʳ��˥ޥ���������ʬ��0�ʤ��ά����ʤɤβ��������äƤ��ޤ��� digits10, digits01 �Ͻ����򸺤餹����� 0~99 ��10�ΰ̤�1�ΰ̤�ʸ�����¤٤���ΤǤ�������Ϻ����Ѱդ�����ΤǤϤʤ��ƴ��� mysql �ɥ饤�Ф�¾�βս�����Ѥ���Ƥ�����Ŭ����Ŭ�Ѥ��ޤ�����

����ǥ��������Ȥ�2�ĸ��餻�ޤ�����

    benchmark                  old allocs     new allocs     delta
    BenchmarkInterpolation     6              4              -33.33%

4.4. ʸ������Ф��� range ��Ȥ�ʤ�

Go �ǥ롼�פ�Ȥ��Ȥ����ɤ� range ��ʸ��Ȥ��ΤǤ����� string ���� s ���Ф��� for i, c := range s { �Ȥ���ȡ��Х���ñ�̤ǤϤʤ� unicode �� code point ñ�̤ǥ롼�פ��Ƥ��ޤ��ޤ���

for i, c := range([]byte(s)) { �Τ褦�� []byte �˥��㥹�Ȥ��ƥ롼�פ��Ƥ����ΤǤ����������� string ���� []byte �ؤ��Ѵ��ǥ��������Ȥȥ��ԡ����¹Ԥ���Ƥ��ޤ��ޤ��� (Go 1.5 �ǤϺ�Ŭ������ޤ�)

�����ǡ������ C �������� for ʸ�˽�ľ���ޤ���

@@ -210,8 +210,8 @@ func (mc *mysqlConn) interpolateParams(query string, args []driver.Value) (strin
        buf := make([]byte, 0, estimated)
        argPos := 0

-       // Go 1.5 will optimize range([]byte(string)) to skip allocation.
-       for _, c := range []byte(query) {
+       for i := 0; i < len(query); i++ {
+               c := query[i]
                if c != '?' {

����Ǥ⤦1�ĥ��������Ȥ򸺤餹���Ȥ��Ǥ��ޤ�����

    benchmark                  old allocs     new allocs     delta
    BenchmarkInterpolation     4              3              -25.00%

4.5. []byte �� string ��ľ�� append ����

Go �� append �ˤϡ� []T ���Υ��饤�� ts �� T �������� t ��1���ɲä��� ts = append(ts, t) �ȡ��̤� []T ���Υ��饤�� ts2 ���礹�� ts = append(ts, ts2...) ��2�ĤλȤ���������ޤ���

������ []byte �� string ���ɵ�������ˤĤ��Ƥϡ� string �� []byte ���Ǥ��뤫�Τ褦��ľ�� append �����Ѥ��뤳�Ȥ��Ǥ��ޤ��� buff = append(buff, s...)

���������Ȥ��򤱤뤿��� 1 �Х��Ȥ��� append ���Ƥ�����ʬ����ɤ����ꡢʸ�����ƥ��򥭥㥹�Ȥ��ʤ��� append ���Ƥ�����ʬ(��Ŭ�������;�פʥ��������Ȥȥ��ԡ���ȯ�����ʤ�)��ʷ�ˤ�������Ǥ��ޤ�����

@@ -210,17 +210,19 @@ func (mc *mysqlConn) interpolateParams(query string, args []driver.Value) (strin
        argPos := 0

        for i := 0; i < len(query); i++ {
-               c := query[i]
-               if c != '?' {
-                       buf = append(buf, c)
-                       continue
+               q := strings.IndexByte(query[i:], '?')
+               if q == -1 {
+                       buf = append(buf, query[i:]...)
+                       break
                }
+               buf = append(buf, query[i:i+q]...)
+               i += q

                arg := args[argPos]
                argPos++

                if arg == nil {
-                       buf = append(buf, []byte("NULL")...)
+                       buf = append(buf, "NULL"...)
                        continue
                }

����Ϥ���ǥ��������Ȥ��򤱤�����ʬ��̵���ä��Ǥ����� []byte �� append ���Ƥ��������ɤ�񤯤Ȥ��˳Ф��Ƥ������� Tips �Ǥ�����

4.6. []bytes �Ѥδؿ��� string �Ѥδؿ����̤��Ѱդ���

[]byte ���ΰ����� string ���ΰ�����ξ��Ʊ���褦�˥������ȡ����������פ��ޤ��� ����������ǰ�ʤ��� Go �� []byte �� string �δ֤��Ѵ��ǥ��������ȡ����ԡ���ȯ�����Ƥ��ޤ��ޤ���

��Ŭ���ǽ�����Τ� buf = append(buf, []byte("NULL")...) �Τ褦�ʥ���ץ�ʥ����������ǡ� string ������˼��ؿ��� []byte �� string(bs) �ǥ��㥹�Ȥ����Ϥ����䤽�εդΥ������Ǥϸ��ߤ� Go �ǤϺ�Ŭ������ޤ���

�����ǡ�����Ʊ�����������׽�����Ԥ��ؿ��� []byte ������˼��С������� string ������˼��С�������2���Ѱդ��ޤ��� ����ƥ롼����� switch �����Ǥⶦ�̲��Ǥ��ʤ����Ȼפ��ޤ�����������饤��Ÿ����Ԥ��������ͤȤʤ��礭����Ķ���Ƥ��ޤ��褦����ǽ������Ƥ��ޤä��Τǡ��ؤɥ��ԥڤ�2�Ĥδؿ����Ѱդ��뤳�Ȥˤʤ�ޤ�����

����ϼ¹Ԥ���٤����¤ä��ΤǤ�����������ʸ�������٤ơ��ѥ�᡼�����Ϥ����ʸ���󡿥Х������ BLOB �� TEXT �����礭�ʥǡ����Ǥ����ǽ��������Τǡ�®�١����������ȶ����Ŷ����ʤ����Ȥˤ��ޤ�����

���ԥڥ����ɤϤ��������ƥʥ����˰��ƶ���ڤܤ��Τǡ�����ϺǸ�μ��ʤǤ�������� Go �ΥС������Dz��餫�β�����ʤ��Ѱդ���뤳�Ȥ���Ԥ��ޤ���

����ǡ����������Ȳ����2�ĤˤޤǸ���ޤ�����

    benchmark                  old ns/op     new ns/op     delta
    BenchmarkInterpolation     2463          2118          -14.01%

    benchmark                  old allocs     new allocs     delta
    BenchmarkInterpolation     3              2              -33.33%

    benchmark                  old bytes     new bytes     delta
    BenchmarkInterpolation     496           448           -9.68%

4.7. �������Хåե��κ�����

�Ǹ�˻Ĥä�2�ĤΥ��������Ȥϡ��ǽ�˥Хåե����Ѱդ�����ʬ�ȡ��Ǹ�� MySQL �˥��ޥ�ɤ��ꤲ��ؿ����Ϥ�ʸ������뤿��˥Хåե��� string �˥��㥹�Ȥ���2�ս�Ǥ���

MySQL �˥��ޥ�ɤ���������Ȥ������Ѥ��Ƥ��������ѤΥХåե��ˡ��ѥ��åȥإå�����ʬ�������ľ�ܥ�������������뤳�ȤǺǸ��2�ĤΥ��������Ȥ�ä����Ȥ��Ǥ��ޤ���

����������¸�Υ��ޥ�ɤ�������ʬ���߷פ�����Ƥ��ޤ��Τȡ��ƥ��Ȥ����ˤ����ʤ�Τǡ�ľ�ܥѥ��åȤ��ä�����Τϼ��Ť��Ƥ����ơ������ѤΥХåե���interpolate�˻Ȥ������ˤ��ޤ�������������ѥ��åȤϥ������4�Х��ȤΥإå����դ�����ΤʤΤǡ�������Ū�ˤ�����Ѥ���ΤˤԤä���Ǥ���

    benchmark                  old ns/op     new ns/op     delta
    BenchmarkInterpolation     1900          1403          -26.16%

    benchmark                  old allocs     new allocs     delta
    BenchmarkInterpolation     2              1              -50.00%

    benchmark                  old bytes     new bytes     delta
    BenchmarkInterpolation     448           160           -64.29%

�ޤȤ�

���������Ȳս�����ꤹ����ˡ�䡢���ʸ�����������塼�˥󥰤���ݤ˳Ф��Ƥ������������ǥ��󥰾�� Tips ��Ҳ𤷤ޤ�����


@methane

songofacandy at 16:07��Comments(0)��TrackBack(0)��golang 

�ȥ�å��Хå�URL

���ε����˥����Ȥ���

̾��:
URL:
  ����òµ­²ï¿½: ɾ��: ��    ��
 
 
 
Blog�⸡��
�ǿ�����
Archives
���Υ֥����ˤĤ���
DSAS�Ȥϡ�KLab �����ۤ����Ѥ��Ƥ��륳��ƥ�ĥ����ӥ��Ѥ�Linux�١����Υ���ե�Ǥ�������5����Υǡ������󥿤ˤƹ��ۤ������Ѥ��Ƥ��ޤ������桹��DSAS����Ȥ��䤹�����������ˡ������Ƥ����ϤDZ��ѤǤ��뤳�Ȥ��ܻؤ��ơ��������ɤ˶Ф���Ǥ��ޤ���
���Υ֥����Ǥϡ������ DSAS �ǻȤäƤ��뵻�ѤξҲ�䡢�¸����Ƥߤ���̤���𡢥ȥ�֥�˴������ޤ줿���ηи��̤ʤɡ���������������������򿥤�ޤ��ƾҲ𤷤Ƥ��������Ȼפ��ޤ���
�ǿ�������
<%==comments[n].author%>
<% } %>