一次无聊的性能测试
这个周末,对C、Go、java、erlang做了一次无聊的性能测试。一开始只是在想了解用Go和C实现一个简单的内存数据库性能会差多少,当然不可能完整实现一个内存数据库再测试,于是我模拟了一个最简单的实验:一个内存数据库,库里面有一个表,表的每条记录固定是8个int类型的字段,不考虑动态性,不考虑查询,只做单纯的插入操作,看看执行1w次插入要多少时间。
用Go和C分别实现了代码以后,我又想到跟erlang的ets插入速度对比如何呢?用Java实现性能又如何呢?下面是测试用的代码和实验结果。
C的实现(用--std=c99参数编译):
#include <stdio.h> #include <stdlib.h> #include <time.h> typedef struct s_mdb_table mdb_table; typedef struct s_mdb_row mdb_row; struct s_mdb_table { char* name; mdb_row* head; }; struct s_mdb_row { mdb_row* prev; mdb_row* next; int field_1; int field_2; int field_3; int field_4; int field_5; int field_6; int field_7; int field_8; }; mdb_table* mdb_new(char* name) { mdb_table* table = (mdb_table*)malloc(sizeof(mdb_table)); table->name = name; table->head = NULL; return table; } void mdb_insert(mdb_table* table, mdb_row* row) { row->next = table->head; if (table->head != NULL) { table->head->prev = row; } table->head = row; } int main (int argc, char** argv) { mdb_table* table = mdb_new("test_table"); clock_t t1 = clock(); for (int i = 0; i < 10000; i ++) { mdb_row* row = (mdb_row*)malloc(sizeof(mdb_row)); mdb_insert(table, row); } clock_t t2 = clock(); printf("%fsn", (double)(t2 - t1) / CLOCKS_PER_SEC); return 0; }
Go的实现:
package main import ( "fmt" "time" ) type mdb_table struct { name string head *mdb_row } type mdb_row struct { prev *mdb_row next *mdb_row field_1 int field_2 int field_3 int field_4 int field_5 int field_6 int field_7 int field_8 int } func (table *mdb_table) insert(row *mdb_row) { row.next = table.head if table.head != nil { table.head.prev = row } table.head = row } func main() { table := new(mdb_table) table.name = "test_table"; table.head = nil; t1 := time.Nanoseconds() for i := 0; i < 10000; i ++ { row := new(mdb_row) table.insert(row) } t2 := time.Nanoseconds() fmt.Printf("%fsn", float32(t2 - t1) / 1000000000) }
Java的实现:
public class memdb { public static void main (String[] args) { DbTable table = new DbTable("test_table"); long t1 = System.nanoTime(); for (int i = 0; i < 10000; i ++) { DbRow row = new DbRow(); table.Insert(row); } long t2 = System.nanoTime(); System.out.printf("%fsn", (float)(t2 - t1) / 1000000000); } } class DbTable { public DbTable (String name) { this.name = name; this.head = null; } public void Insert (DbRow row) { row.SetNext(this.head); if (this.head != null) this.head.SetPrev(row); this.head = row; } private String name; private DbRow head; } class DbRow { public DbRow () { } public void SetPrev(DbRow prev) { this.prev = prev; } public void SetNext(DbRow next) { this.next = next; } private DbRow prev; private DbRow next; public int field_1; public int field_2; public int field_3; public int field_4; public int field_5; public int field_6; public int field_7; public int field_8; } class DbData { public int field_1; public int field_2; public int field_3; public int field_4; public int field_5; public int field_6; public int field_7; public int field_8; }
erlang的实现:
-module(memdb). -export([run/0, test/0]). run() -> {Time, _} = timer:tc(?MODULE, test, []), io:format("~ps~n", [Time / 1000]). test() -> Table = ets:new(test_table), insert(Table, 0). insert(Table, 10000) -> ok; insert(Table, N) -> ets:insert(Table, {N, 1, 2, 3, 4, 5, 6, 7}), insert(Table, N + 1).
测试脚本:
#!/bin/sh echo "C: $(./a.out)" echo "GO: $(./6.out)" echo "Java: $(java memdb)" echo "Erlang: $(erl -noshell -s memdb run -s init stop)"
在我的电脑上的一次典型测试结果:
C: 0.000669s GO: 0.003167s Java: 0.003626s Erlang: 0.024s
C毫无悬念的第一,Go和Java的性能相当,Go稍胜一筹,这个测试里面,其实比的是内存申请的速度,如果把内存申请的操作提前,后面1万次循环只对数据赋值,那根本没什么消耗,并且erlang用的是完整的内存数据库实现参与测试,里面包含了hash算法等耗性能的东西,这样测其实很不公平,所以我说这是一个很无聊的性能测试,呵呵。