なんかねー、CPU 数×20のスレッド作って「シングル スレッドの52倍だ!」とか喜んでる人がいたので、「ホントかよー」と、やってみた。つかねー、その人後で「ネタです」とか言ってるけど、おかしいと思ったらできるところまで追求する。それが職人ってもんじゃないですかねー。似非職人はヤーネー。
んで、検証するために、スレッド数と、繰り返し回数を指定して変えられるようにした。そんな変更、簡単なはずだよね。でもしない。なぜだろー?
でもね、かくいう私は、明確にスレッド作ってどうこうするプログラムって、1個しか覚えがないんだ-。知ってる人は知っている、J-Phone の、スカイ メール。他社にメール送れるやつね。インターネットを経由する今では当たり前にできることだけど、1997年頃は、メールを送る前に音声ガイドを流していたので、できなかったのね。で、その、音声ガイドを、どこのメーカーの、どのガイダンスか判定しよう、というアイディアだったわけ。ガイド判定を東芝の研究所が担当した。私が担当したのは、電話を受ける装置(そう、メールなんだけど、電話をかけていた)から録音した音声データをもらい、音声判定用のライブラリへ渡し、その結果をもらい、電話を受ける装置へ結果を知らせる、ってところ。想定される最大負荷は、正月の「あけおめメール」。1分間に6千だか6百だかの処理をこなさなきゃならなかった。「6」という数字は覚えているんだけど、桁を忘れてる。まぁ、あれだ。当時は fork して別プロセスを起こすのを普通に使ってたんだけど、最大プロセス数を超えた。fork じゃ捌けないってことで、スレッドになった。それが最初で、記憶にある限り、最後のスレッド プログラミング。ま、そんなのが、VC++ で作るんだ。変なところは勘弁してね。
// FizzBuzz.cpp : コンソール アプリケーション用のエントリ ポイントの定義
//
#include "stdafx.h"
#include <stdlib.h>
#include <malloc.h>
#include <memory.h>
#include <stdio.h>
#include <iostream>
#include <time.h>
#include <sys/types.h>
#include <sys/timeb.h>
#include <windows.h>
char** result;
typedef struct _threadInfo {
int start;
int term;
HANDLE threadHandle;
} ThreadInfo;
DWORD WINAPI FizzBuzz(LPVOID arg);
int main(int argc, char* argv[])
{
int threadNumber = 1;
int maxNumber = 2000000;
if (argc > 1) {
threadNumber = atoi(argv[1]);
if (argc > 2) {
maxNumber = atoi(argv[2]);
}
}
// 各スレッドの負荷を計算する。
int load = maxNumber / (maxNumber % threadNumber == 0 ? threadNumber : (threadNumber + 1));
ThreadInfo *threadHandle;
threadHandle = (ThreadInfo*) malloc(sizeof(ThreadInfo) * threadNumber);
result = (char**)malloc(sizeof(char*) * maxNumber);
// 結果を、数字で埋める。
for (int i = 0; i < maxNumber; ++i) {
result[i] = (char*) malloc(sizeof(char) * 10);
memset(result[i], 0, sizeof(char) * 10);
sprintf(result[i], "%d", i + 1);
}
// ストップウォッチ開始する。
struct _timeb openning;
_ftime(&openning);
// スレッドで計算させる。
for (int c = 0; c < threadNumber; ++c) {
threadHandle[c].start = c * load;
threadHandle[c].term = threadHandle[c].start + load;
threadHandle[c].term = (threadHandle[c].term > maxNumber ? maxNumber : threadHandle[c].term);
threadHandle[c].threadHandle = CreateThread(NULL, 0, FizzBuzz, (LPVOID) &threadHandle[c], 0, NULL);
}
// スレッドの終了を待つ。
for (int t = 0; t < threadNumber; ++t) {
DWORD ex;
do {
Sleep(0);
GetExitCodeThread(threadHandle[t].threadHandle, &ex);
if (ex != STILL_ACTIVE) {
CloseHandle(threadHandle[t].threadHandle);
}
} while (ex == STILL_ACTIVE);
}
// ストップウォッチ停止する。
struct _timeb closing;
_ftime(&closing);
// 後片付け。
for (int j = 0; j < maxNumber; ++j) {
if (j < 100) {
std::cout << result[j] << std::endl;
}
free(result[j]);
}
free(result);
free(threadHandle);
// 結果表示。
unsigned long d;
d = (closing.time * 1000 + closing.millitm) - (openning.time * 1000 + openning.millitm);
std::cout << d << "msec" << std::endl;
return 0;
}
DWORD WINAPI FizzBuzz(LPVOID arg) {
ThreadInfo *pair = (ThreadInfo*)arg;
for (int s = pair->start + 1; s <= pair->term; ++s) {
if (s % 15 == 0) {
strcpy(result[s-1], "FizzBuzz");
} else if (s % 5 == 0) {
strcpy(result[s-1], "Fizz");
} else if (s % 3 == 0) {
strcpy(result[s-1], "Buzz");
}
}
ExitThread(0);
return 0;
}
スレッドの終了を、もうちょっとスマートに待てないのかなぁ?まぁ、動いてるからいいや。
で、結果。
スレッド数1
シングル コア:おおよそ120msec(110msec~140msec)
デュアル コア:おおよそ120msec(110msec~140msec)
スレッド数2
シングル コア:おおよそ120msec(110msec~140msec)
デュアル コア:おおよそ60msec(53msec~120msec)
スレッド数3
デュアル コア:おおよそ57msec(53msec~60msec)
スレッド数4
デュアル コア:おおよそ60msec(57msec~80msec)
後は、スレッドを増やしても、効果はあまりない。スレッドの生成や切り替えの時間の方が、分割することによって短縮される時間をオーバーするんだろう。
まぁ、結構簡単に調査できるね。何で確認しないんだろう?
たとえば、問題に「8の倍数の時には Bang と表示する」なんて追加したら、どうする?
投稿日時 : 2009年10月21日 21:02