関数呼び出しのコスト

ちょっと気になったので計測

//functions.h

#pragma once

void f(__int64& i);

struct normal_function
{
	void f(__int64& i);
};

struct virtual_function_base
{
	virtual void f(__int64& i)=0;
};

struct virtual_function
	:public virtual_function_base
{
	virtual void f(__int64& i);
};
// functions.cpp
#include "functions.h"

void f(__int64& i)
{
	++i;
}

void normal_function::f(__int64& i)
{
	++i;
}

void virtual_function::f(__int64& i)
{
	++i;
}
//main.cpp
#include <cstdio>
#include <conio.h>

#include <tchar.h>
#include <windows.h>
#include <mmsystem.h>
#pragma comment(lib,"winmm.lib")

#include <boost/function.hpp>
#include <boost/bind.hpp>

#include "functions.h"

void time_check()
{
	unsigned long start_time=timeGetTime();
	__int64 target=0;
	for(__int64 i=0;i<100000000;++i)
		f(target);
	std::printf("time=%d target=%d\n\n",timeGetTime()-start_time,target);
}

template <typename T>
void time_check_member(T& t)
{
	unsigned long start_time=timeGetTime();
	__int64 target=0;
	for(__int64 i=0;i<100000000;++i)
		t.f(target);
	std::printf("time=%d target=%d\n\n",timeGetTime()-start_time,target);
}

template <typename T>
void time_check_member_pointer(T* t)
{
	unsigned long start_time=timeGetTime();
	__int64 target=0;
	for(__int64 i=0;i<100000000;++i)
		t->f(target);
	std::printf("time=%d target=%d\n\n",timeGetTime()-start_time,target);
}

template <typename T>
void time_check_functor(const T& t)
{
	unsigned long start_time=timeGetTime();
	__int64 target=0;
	for(__int64 i=0;i<100000000;++i)
		t(target);
	std::printf("time=%d target=%d\n\n",timeGetTime()-start_time,target);
}

int _tmain(int argc, _TCHAR* argv[])
{
	timeBeginPeriod(1);
	Sleep(3000);

	printf("global function\n");
	time_check();

	normal_function normaltype;
	printf("member function\n");
	time_check_member(normaltype);
	
	virtual_function virtualtype;
	printf("virtual member function\n");
	time_check_member(virtualtype);

	printf("virtual_base member function\n");
	time_check_member_pointer((virtual_function_base*)&virtualtype);

	printf("global member functor\n");
	boost::function<void (__int64&)> functor_global=boost::bind(&f,_1);
	time_check_functor(functor_global);

	printf("member functor\n");
	boost::function<void (__int64&)> functor_normal=boost::bind(&normal_function::f
		,normaltype,_1);
	time_check_functor(functor_normal);

	printf("virtual member functor\n");
	boost::function<void (__int64&)> functor_virtual=boost::bind(&virtual_function::f
		,virtualtype,_1);
	time_check_functor(functor_normal);

	printf("virtual_base member functor\n");
	boost::function<void (__int64&)> functor_virtual_base=boost::bind(&virtual_function_base::f
		,(virtual_function_base*)&virtualtype,_1);
	time_check_functor(functor_virtual_base);

	timeEndPeriod(1);

	_getch();
	return 0;
}
global function
time=215 target=100000000

member function
time=216 target=100000000

virtual member function
time=475 target=100000000

virtual_base member function
time=479 target=100000000

global member functor
time=562 target=100000000

member functor
time=647 target=100000000

virtual member functor
time=648 target=100000000

virtual_base member functor
time=689 target=100000000

WindowsXP SP3
QuadCore Q8200
DDR3 4GB

これを気にするほどクリティカルな処理を書いたことはないでござる

IOCPクラス設計の草案

pipeline_element自体が出力先のpipeline_elementのコンテナになってるんだから
pipeline自体がいらないんじゃないの。コンポジションっぽく


あー。pipelineなんてはじめからなかった。


継承でやるかテンプレートでやるか悩むけど・・うーん
コンポジションをテンプレートでやるとETになるんじゃないかな。これは・・


たとえばファイルから取得したバッファをソケットとDBに書き込みたいなっと

pipeline_element<FILE,pipeline_element<BUFFER,pipeline_element<SOCKET>,pipeline_element<DB> > > line;

うはwwだめだこいつ。はやくなんとかしないと

そう考えると継承したほうがいいよなぁ

class pipeline_element
{
	pipeline_element(device& dev)
	{
	...
}

file_device f;
buffer_device b;
socket_device s;
db_device d;
pipeline_element line1(f);
pipeline_element line2(b);
pipeline_element line3(s);
pipeline_element line4(d);
line1 >> line2;
line2 >> line3;
line2 >> line4;

これはこれで・・

でも継承のほうが見やすいかなー

テンプレートを使うとコンテナが使えないのが痛いよね

boost::anyはいやですし

もう少し考えて見よっと。

IOCPクラス設計の草案

フィクションなソース
このソースはフィクションであり実在の人物・団体とは一切関係ありません
でもなんかうまくいきそう


foreach(pipeline* line in next_piplelines)ってどこのC++ですかw


なんかI/O完了ポートとまったく関係なさそうに見えるorz

struct pipeline
{
	pipeline_element[] elements;
	void start()
	{
		elements[0].start();
	}
}

struct pipeline_element
{
	device dev;

	void start()
	{
		p=new buffer;
		input(p);
	}
	void end(buffer* p)
	{
		delete p;
	}
	void input(buffer* p)
	{
		dev.start(p);
	}
	void on_complete(buffer* p,int bytes)
	{
		output(p);
	}
	void output(buffer* p)
	{
		if(next_pipleline_count>0)
		{
			buffer buffers[];
			//copy from p
			foreach(pipeline* line in next_piplelines)
				line->input(buffers[i]);
		}
		else
			end(p);
	
		if(dev.iteration)
			start();
	}
}

struct file_reader_device
{
	iteration=true;
	void start(buffer* p)
	{
		ReadEx(p);
	}
}
struct file_writer_device
{
	iteration=false;
	void start(buffer* p)
	{
		WriteEx(p);
	}
}
struct reader_buffer_pool_device
{
	iteration=false;
	binary b;
	void start(buffer* p)
	{
		b << p;
		on_complete(p);
	}
}
struct writer_buffer_pool_device
{
	iteration=false;
	void write(buffer* p)
	{
		on_complete(p);
	}
	void start(buffer* p)
	{
	}
}

ちらうら

I/O Completion Portの設計。
どうしようか悩みすぎ・・あうあう
ちょっと整理する意味でチラシの裏的な何か。
「ここはお前の日記帳じゃないんだ、チラシの裏にでも書いてろ、な?」
ちなみにソースはフィクションなのでコンパイルはできません

こんな風に使いたい。使えたらいいな。

	completion_port port;

	file f1,f2;
	socket s;
	buffer b;//memoryじゃなくbufferなのは、同期処理をするから
	calc c;//I/Oと関係ない計算。この前やったやつ。たとえばDBから取得した生データをファイル形式に・・とか

	piple_line p(port);//完了ポートと関連付け
	p(f1>>b>>c>>(f2,s));//ファイルから読み込んでバッファに入れて加工して別のファイルとソケットに書き込んでね

	line.start_io();//作業開始

終了処理は普通に

	//終わるのを待つ
	line.wait();

これは非同期の意味があるのか・・
せっかくなのでコールバック

	// completion_portのスレッドから呼ばれる
	line.onComplete=complete_handler;

まだこっちのがいいけど、completion_portのWorkerスレッドから呼ばれるのが
コールバック関数の中でもスレッド間の同期処理を考えないといけないのが痛い

// mainのスレッドから呼んでほしい場合は内部でinvokeしてね part3
{
	line.onComplete=delegator(complete_handler,mainthread_callback);
	while(1)
	{
		// でもその場合はプーリングしないとだめ?messageじゃなくてEventとかでね
		line.pooling();
	}
}

これがいいんだけどパフォーマンス的にIOCPの意味があるのか謎な・・

基本的にcompletion_portは完了通知とスレッドを提供してくれるだけで
piple_lineは完了通知を受け取ってoutputからinputに垂れ流すだけ

ここはシンプルでいいのかな。


問題はbuffer。
ファイルから読み込んだデータをバッファにコピーするのでf>>bでいいし
バッファの内容をソケットに出力するのだからb>>sでいいと思う。

この場合同じバッファを使ったほうが効率言いわけだからf>>b>>sとなる
まぁここまではいいんだけど、そもそもバッファはメモリであって
memcpyを非同期で行うわけじゃない。
しかもfやsへの入出力は必ずバッファが必要になる
予め分かってるならf>>sでいいんじゃない?


でもその場合、ファイルから読み込む処理だけの場合はどう書くんだろう
p(f)
だけ?うーん・・・


もしpile_lineの仕事が
piple_lineは完了通知を受け取ってoutputからinputに垂れ流すだけ
ただしpile_lineは最高で1つの出力用バッファ(要するに出口)もしくは入力用バッファ(入り口)のみ受け付ける
その他のbufferはpiple_lineが自動的に管理する
だとすると・・


p(f1>>b>>c>>(f2,s));

p(f1>>c>>(f2,s));
になる


この場合pipe_lineは内部バッファを
f1>>b1>>c>>b2>>(f2,s)
と勝手に作ればおk


ソケットからデータを受信するには
p(s>>b)
と書く。
この場合pipe_lineは内部バッファを作らない。


どっちがいいの・・うーん・・


ちらうらすぎて困る