分散関数呼び出し
Posted feedbacks - Flatten
Nested Hidden同一マシンのCoreDuo1.5GHz で1万回11秒
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | // サーバ
using System;
using System.Web.Services;
[WebService(Namespace = "http://tempuri.org/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
public class Service : System.Web.Services.WebService
{
[WebMethod]
public string Calc(int n, int r) {
int p = n * (100 - r) / 100;
return string.Format("販売価格 {0}円 (定価{1}円から{2}%引き)",
p.ToString("C").Substring(1), n.ToString("C").Substring(1), r);
}
}
// クライアント
using System;
using System.Diagnostics;
using HowWrite.localhost;
class Program
{
static void Main()
{
Service srv = new Service();
Stopwatch st = new Stopwatch();
st.Start();
string s = "";
for (int i = 0; i < 10000; ++i) s = srv.Calc(2000, 20);
Console.WriteLine(s);
st.Stop();
Console.WriteLine(st.ElapsedMilliseconds / 1000);
}
}
|
まずは一番乗り狙いで、最もナイーブな実装をば。 エラー処理や効率は考えていません。 eval-remotelyはリモートホストでインタプリタを起動して評価式を送り込みます。 実行例: gosh> (ref (run-sample "scherzo" 1) 1) "販売価格 1,600円 (定価2,000円から20%引き)" ベンチマーク (CPU Pen4 2GHz, メモリ2GB, ループバック使用、Gauche 0.8.11_pre1, ミドルウェア無し) gosh> (time (run-sample "scherzo" 10000) (values)) ;(time (run-sample "scherzo" 10000) (values)) ; real 5.247 ; user 0.460 ; sys 0.130
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | ;; -*- coding: utf-8 -*-
(use gauche.process)
(use srfi-1)
(define (eval-remotely host exprs)
(let1 p (run-process '(gosh -E "'read-eval-print-loop #f #f #f values'")
:host host :input :pipe :output :pipe)
(unwind-protect
(map-in-order (lambda (expr)
(write expr (process-input p))
(newline (process-input p))
(read (process-output p)))
exprs)
(begin (close-output-port (process-input p))
(process-wait p)))))
;; 実行サンプル
(define (run-sample host repeat)
(eval-remotely host
`((define (pricestring 価格 割引率)
(format "販売価格 ~:d円 (定価~:d円から~d%引き)"
(round->exact (* 価格 (/. (- 100 割引率) 100)))
価格 割引率))
,@(make-list repeat '(pricestring 2000 20)))
))
|
とりあえずPython使ってCGIサーバを立てて見ました。
http://blueeye.bne.jp:7777/cgi-bin/discount.py?2000&33
↑のアドレスにアクセスすると、次のように帰ってきます。
> 販売価格 1340円(定価2000円から33%引き)
以下の記事を参考にしてHTTPサーバを走らせて、その上で下にあるコードを走らせています。
http://coreblog.org/ats/python-de-cgi
今日一日くらい、鯖を立てておきますので遊びたい方はご自由に。
スレッドプールもないようなHTTP鯖なので、それなりに遅いと思いますが、速度測定はそのうちやります。
(ネットゲームのサーバが動いているので、正確な速度測定は難しいと思います)
http://blueeye.bne.jp:7777/cgi-bin/discount.py?2000&33
↑のアドレスにアクセスすると、次のように帰ってきます。
> 販売価格 1340円(定価2000円から33%引き)
以下の記事を参考にしてHTTPサーバを走らせて、その上で下にあるコードを走らせています。
http://coreblog.org/ats/python-de-cgi
今日一日くらい、鯖を立てておきますので遊びたい方はご自由に。
スレッドプールもないようなHTTP鯖なので、それなりに遅いと思いますが、速度測定はそのうちやります。
(ネットゲームのサーバが動いているので、正確な速度測定は難しいと思います)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | # -*- coding: utf-8 -*-
#-------------------------以下サーバー側コード
import sys
if(len(sys.argv) == 2):
(price,discount) = sys.argv[1].split("&")
price = int(price)
discount = int(discount)
print "Content-Type: text/plain"
print ""
print "販売価格 %d円(定価%d円から%d%%引き)" % (int(price * (100.0-discount)/100.0 ), price, discount)
else:
print "Content-Type: text/plain"
print ""
print "引数エラー"
# -*- coding: utf-8 -*-
#------------------------以下呼び出し側コード
import urllib
def pricestring(price, discount):
return urllib.urlopen("http://blueeye.bne.jp:7777/cgi-bin/discount.py?%d&%d" % (price, discount)).read()
print unicode(pricestring(2000,20), "utf8")
|
ああ、一番争いに敗れた…
ちょっと追加説明です。
リモートサーバに必要なのは、sshの公開鍵認証でログインできることと、Gaucheがインストールされていることのみです。特別にサーバ側で別コードを走らせる必要はありません。
インターネット越しにベンチマークを取ってみました。ハワイからロサンゼルスにあるサーバを叩くと、1000回呼び出しで82秒でした。
ちょっと追加説明です。
リモートサーバに必要なのは、sshの公開鍵認証でログインできることと、Gaucheがインストールされていることのみです。特別にサーバ側で別コードを走らせる必要はありません。
インターネット越しにベンチマークを取ってみました。ハワイからロサンゼルスにあるサーバを叩くと、1000回呼び出しで82秒でした。
何なんだこの遅さは。 直列で100回実験したら18秒強。 10個のスレッドで、並列に10回ずつ取りに行ったら6.5秒。 多分コンソール周りのPrintに時間食っているのかなぁ。 コンソールを表示しなければ、早くなるのか? まぁ、裏でネットゲームの鯖が動いているところで、実験ってのが無茶な話ですが。 鯖のPCのスペックはこんな感じ。 Pen4 2.8GHz HT RAM 512MB OS XP so-net ADSL12M線 ネットゲームのサーバプログラムがCPUパワーの80%を常時消費 測定したクライアントPC sinetに接続されている某大学のPC Pen4 3.2GHz HT RAM 2GB OS XP
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | # -*- coding: utf-8 -*-
import urllib
import time
import threading
def pricestring(price, discount):
return urllib.urlopen("http://blueeye.bne.jp:7777/cgi-bin/discount.py?%d&%d" % (price, discount)).read()
t1 = time.time()
for x in xrange(100):
print unicode(pricestring(x * 1000, 20),"utf8")
t2 = time.time()
class TEST_THREAD(threading.Thread):
def __init__(self, price, discount):
threading.Thread.__init__(self)
self.price = price
self.discount = discount
def run(self):
for x in xrange(10):
print unicode(pricestring(self.price, self.discount + x),"utf8")
threadList = []
for x in xrange(10):
threadList.append(TEST_THREAD(x * 1000 , 20))
threadList[-1].start()
for t in threadList:
t.join()
t3 = time.time()
print "series time",t2 - t1
print "pararells time",t3 - t2
|
pythonの標準ライブラリに含まれているXML-RPCを 使ってみました。 とはいっても、参考にしたIBMのサイトに乗っているサンプル そのもののような気がしないでもない。 実は数値のコンマ区切りの方が大変でした。 測定環境 同一マシン上で実行 Athlon XP 1700+ 512MB python 2.5 実行時間 画面出力を行う 64秒 画面出力を行わない 112秒
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 | #
# server.py
#
# -*- coding: utf-8 -*-
import SimpleXMLRPCServer
class PliceString:
def plicestring(self, p, d):
def f(i):
s = str(i)
n = len(s)
return ','.join([s[i<0 and -n or i:i+3] for i in range(n-(n+2)/3*3, n, 3)])
return '販売価格 %s円 (定価%s円から%d%%引き)' % (f(int(p*(1-d*0.01))), f(p), d)
ps = PliceString()
sv = SimpleXMLRPCServer.SimpleXMLRPCServer(('localhost', 8888))
sv.register_instance(ps)
sv.serve_forever()
#
# client.py
#
# -*- coding: utf-8 -*-
import xmlrpclib
import time
sv = xmlrpclib.ServerProxy('http://localhost:8888/')
t0 = time.time()
for i in range(10000):
sv.plicestring(2000, 20)
# print sv.plicestring(2000, 20)
print time.time() - t0
|
Rubyの分散処理の定番dRuby! DRB_URIを書き換えることで別サーバでも可能。 まず ruby 45.rb server でサーバを立ち上げる。そのあと ruby 45.rb でクライアントの実行。 ・呼び出し10000回にかかった時間 7.9秒 ・サーバとクライアントのCPU・メモリ Pentium4 2.66GHz / 1GB ・同一サーバ内での実行か別サーバでの実行か 同一サーバ ・言語のバージョン Ruby 1.8.5
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | require 'drb'
DRB_URI = "druby://localhost:2323"
if ARGV.first == 'server'
obj = Object.new
def obj.pricestring(price, cut_rate)
comma = lambda{|n| n.to_s.reverse.scan(/\d{1,3}/).join(",").reverse}
"販売価格 %s円 (定価%s円から%d%%引き)" % [comma[price*(100-cut_rate)/100], comma[price], cut_rate]
end
DRb.start_service DRB_URI, obj
DRb.thread.join
else # client
require 'benchmark'
DRb.start_service
obj = DRbObject.new(nil, DRB_URI)
puts obj.pricestring(2000,20)
puts Benchmark.measure { 10000.times{ obj.pricestring(2000,20) } }.real
end
|
.NET Remoting版 CoreDuo 1.5GHz の同一マシンで1万回2.7秒
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 | // サーバ用
using System;
public class Server : MarshalByRefObject
{
public string Calc(int n, int r)
{
int p = n * (100 - r) / 100;
return string.Format("販売価格 {0}円 (定価{1}円から{2}%引き)",
p.ToString("C").Substring(1), n.ToString("C").Substring(1), r);
}
}
class Program
{
static void Main()
{
System.Runtime.Remoting.Channels.ChannelServices.RegisterChannel(
new System.Runtime.Remoting.Channels.Tcp.TcpChannel(50000), false);
System.Runtime.Remoting.RemotingConfiguration
.RegisterWellKnownServiceType(typeof(Server), "HowWrite",
System.Runtime.Remoting.WellKnownObjectMode.Singleton);
System.Console.ReadKey();
}
}
// クライアント用
using System;
using System.Diagnostics;
class Program
{
static void Main()
{
Server svr = (Server)Activator.GetObject(typeof(Server),
"tcp://localhost:50000/HowWrite");
Stopwatch st = new Stopwatch();
st.Start();
string s = "";
for (int i = 0; i < 10000; ++i) s = svr.Calc(2000, 20);
Console.WriteLine(s);
st.Stop();
Console.WriteLine(st.ElapsedMilliseconds / 1000.0);
}
}
|
うへぇー rum-process で host を指定できたんだぁ すげぇーっ
RMIの実装です(Java 5ではrmic不要になっているのですね)。 一万回呼び出しにかかった時間:4782 ms ・サーバとクライアントのCPU・メモリ:PowerPC G4 1.67 GHz・メモリ 1GB ・同一サーバ内での実行か別サーバでの実行か:同一サーバ内 ・言語のバージョン:Java HotSpot(TM) Client VM build 1.5.0_07-87 ・ミドルウェアを使用している場合,その名前とバージョン:使用していない
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 | 共通のインタフェース(Price.java)
import java.rmi.Remote;
import java.rmi.RemoteException;
public interface Price extends Remote {
String priceString(int price, int discunt) throws RemoteException;
}
サーバ側実装(Server.java)
import java.rmi.registry.*;
import java.rmi.server.*;
public class Server implements Price {
public String priceString(int price, int discount) {
return String.format("販売価格 %,d円(定価 %,d円から %d%%引き)",
(int)(double)price * (100 - discount) / 100, price, discount);
}
public static void main(String[] args) throws Exception {
Server obj = new Server();
Price stub = (Price) UnicastRemoteObject.exportObject(obj, 0);
Registry reg = LocateRegistry.getRegistry();
reg.bind("Price", stub);
}
}
クライアント側実装(Client.java)
import java.rmi.registry.*;
public class Client {
public static void main(String[] args) throws Exception {
String host = (args.length < 1)? null : args[0];
Registry reg = LocateRegistry.getRegistry(host);
Price stub = (Price) reg.lookup("Price");
long now = System.currentTimeMillis();
for (int i = 0; i < 10000; i++) {
stub.priceString(1000, 20);
}
System.out.printf("%d ms%n", System.currentTimeMillis() - now);
}
}
|
Rinda で実装してみました。 まず、ruby ts.rb でタプルスペースを起動します。タプルスペースは黒板みたいなものです。 次に ruby server.rb でサーバを起動。 最後に ruby client.rb でクライアントを起動します。 server.rb と client.rb の起動順は逆でもokです。 server.rb を複数個起動することもできます。もちろん複数プロセスで処理しますし、途中から起動してもokです。 # でも全然スケールしないですけど。 ・10000回の実行結果 ・サーバ1プロセス: 1分 (CPU 使用率 60%ぐらい) ・サーバ2プロセス: 1分 (同上) 全然スケールしない。。。 ・Intel Core Duo 1.66GHz、メモリ1G ・同一サーバ ・ruby 1.8.6 (2007-03-13 patchlevel 0) [i386-mswin32]
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 | --[ts.rb]----------------------------------------
require 'rinda/tuplespace'
$ts = Rinda::TupleSpace.new
DRb.start_service('druby://:1234', $ts)
puts DRb.uri
DRb.thread.join
-- [server.rb] --------------------------------------
require 'rinda/tuplespace'
class Integer
def commaize
to_s.gsub(/(\d)(?=(?:\d\d\d)+(?!\d))/, '\1,')
end
end
def pricestring(price, discount)
"販売価格 %s円 (定価%s円から%d%%引き)" % [(price*(100-discount)/100).commaize, price.commaize, discount]
end
DRb.start_service
$ts = DRbObject.new_with_uri('druby://localhost:1234')
loop {
req = $ts.take([:pricestring, nil, nil])
price, discount = req[1..2]
$ts.write([:pricestring_result, price, discount, pricestring(price, discount)])
print "."
}
-- [client.rb] ---------------------------------------
require 'rinda/tuplespace'
require "benchmark"
DRb.start_service
$ts = DRbObject.new_with_uri('druby://localhost:1234')
puts Benchmark.measure {
10000.times {
$ts.write([:pricestring, 2000, 20])
r = $ts.take([:pricestring_result, 2000, 20, nil])
print "."
}
}
|
ScalaでActorを使って。
そもそも非同期に使うのがメインなものなので、同期でやるなんて初めてでした。!?とreplyを使うと同期でメッセージを送受信できます。
が・・・直列で使うと遅いですね。
- CPU:Athlon 3000+
- RAM: 1G
- 同一ホスト
で21218 msでした。直列に呼び出すなら素直にRMI使ったほうがいいですね。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 | import scala.actors._
import scala.actors.Actor._
import scala.actors.remote._
import scala.actors.remote.RemoteActor._
import java.io.ByteArrayOutputStream
val PORT = 9010
if(args.length>0 && args(0) == "server") {
//server side
println("server:start")
actor {
alive(PORT)
register('pricestring, self)
loop {
react{
case (price:int, p:int) =>
val s = new ByteArrayOutputStream
Console.withOut[unit](s){
printf("\u8ca9\u58f2\u4fa1\u683c {0}\u5186\uff08\u5b9a\u4fa1{1}\u5186\u304b\u3089{2}%\u5f15\u304d\uff09",
(price*(1-p.toFloat/100)).ceil, price, p)
}
reply(s.toString)
}
}
}
}else {
//client side
val clientActor = actor {
val c = select(Node("127.0.0.1", PORT), 'pricestring)
loop {
react {
case r@(i:int,j:int) => reply((c!?r))
}
}
}
import scala.testing.Benchmark
val test = new Benchmark {
def run:unit = {
var i = -1;while({i= i+1; i<10000}) {
clientActor !? ((2000, 20))
}
return ()
}
}
println(test.runBenchmark(1)(0)+" ms")
System.exit(0)
}
|
普通にErlangのプロセス間通信を使って実装してみました. 夏休み中なのでとりあえず自宅マシンで測定. ・Intel(R) Xeon(R) CPU 5130 @ 2.00GHz ・同一サーバ内 ・Erlang 5.5.4 で 1.04秒くらいでした. Erlangのプロセス間通信は関数呼び出しの形になっていないので, 呼び出し側が引数をメッセージで送り, それを受け取ったサーバが呼び出し側に戻値をメッセージで 送り返す形で実装しています. サーバ側 > erl -noshell -name server@127.0.0.1 -s rcall_s start クライアント側 > time erl -noshell -name client@127.0.0.1 -s rcall_c main -s init stop remote pid <4053.36.0> ret = bench_ok 1.044240 sec. real 0m2.352s user 0m0.908s sys 0m0.156s
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 | ==== rcall_s.erl
-module(rcall_s).
-export([start/0, init/0]).
start() ->
proc_lib:start(?MODULE, init, []),
ok.
init() ->
global:register_name(calc, self()),
proc_lib:init_ack(ok),
loop().
loop() ->
receive
{From, Price, Discount} ->
From ! calc(Price, Discount)
end,
loop().
calc(Price, Discount) ->
lists:flatten(io_lib:format("販売価格 ~s円(定価~s円から~w%引き)",
[comma(Price * (100-Discount) div 100), comma(Price), Discount])).
comma([N1, N2, N3, N4 | RestN], Acc) ->
comma([N4 | RestN], [",", N3, N2, N1 | Acc]);
comma(RestN, Acc) ->
lists:flatten([lists:reverse(RestN), Acc]).
comma(N) when is_integer(N) ->
comma(lists:reverse(integer_to_list(N)), []).
==== rcall_c.erl
-module(rcall_c).
-export([main/0, bench/2]).
main() ->
init(),
Pid = global:whereis_name(calc),
io:format("remote pid ~w~n", [Pid]),
{Time, Value} = timer:tc(?MODULE, bench, [Pid, 10000]),
io:format("ret = ~w~n~f sec.~n", [Value, Time/1000000]).
init() ->
net_adm:ping('server@127.0.0.1'),
global:sync().
% すぐにglobalnameが反映されないので,
% pingしたあとsyncしてから利用する
bench(_, 0) ->
bench_ok;
bench(Pid, N) ->
% 送信元のPIDを知らせないと送り返せない
Pid ! {self(), 2000, 20},
receive
_RetValue ->
ok
end,
bench(Pid, N-1).
|
C言語 + libxmlrpc(サーバーは apache 経由のCGI)で実装しました。 サーバーサイドのスペック * Celeron 1.3GHz * メモリ1G * xmlrpc-c-1.11.00 11.285 秒でした 最初はシリアルに問い合わせてみたのですが目も当てられないぐらい遅かったので非同期dで 100発 × 100回 というようにパラレルに問い合わせるようにしました。 あとは apache のチューニングでもう少し早くなりそうです。(prefork だったので)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 | /* サーバー側 CGIコード */
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<xmlrpc.h>
#include<xmlrpc_cgi.h>
static void int2cstr(int num, char *str)
{
int i, j;
char buf[16];
size_t len;
snprintf(buf, sizeof(buf), "%d", num);
len = strlen(buf);
for(i=j=0; i<len; i++){
str[j++] = buf[i];
if(i != len - 1 && (len - i - 1) % 3 == 0) str[j++] = ',';
}
str[j] = '\0';
}
static xmlrpc_value *pricestring(xmlrpc_env *env, xmlrpc_value *param_array,
void *user_data)
{
xmlrpc_int32 price, discount;
int value;
char str[256], value_str[16], price_str[16];
xmlrpc_parse_value(env, param_array, "(ii)", &price, &discount);
if(env->fault_occurred) return NULL;
value = price - (price * discount / 100);
int2cstr(price, price_str);
int2cstr(value, value_str);
snprintf(str, sizeof(str),
"販売価格 %s円 (定価%s円から%d%引き)",
value_str, price_str, discount);
return xmlrpc_build_value(env, "s", str);
}
int main (int argc, char **argv)
{
xmlrpc_cgi_init(XMLRPC_CGI_NO_FLAGS);
xmlrpc_cgi_add_method_w_doc("pricestring", &pricestring, NULL,
"s:ii", "Add two integers.");
xmlrpc_cgi_process_call();
xmlrpc_cgi_cleanup();
return EXIT_SUCCESS;
}
/* クライアント側コード */
#include <stdio.h>
#include <stdlib.h>
#include <xmlrpc.h>
#include <xmlrpc_client.h>
#define XMLRPC_URL "http://192.168.0.13/pricestring.cgi"
static void print_state_name_callback (char *server_url,
char *method_name,
xmlrpc_value *param_array,
void *user_data,
xmlrpc_env *env,
xmlrpc_value *result)
{
char *str;
if(env->fault_occurred) return;
xmlrpc_parse_value(env, result, "s", &str);
if(env->fault_occurred) return;
printf("%s\n", str);
}
int main (int argc, char **argv)
{
int i, j;
xmlrpc_client_init(XMLRPC_CLIENT_NO_FLAGS, NULL, NULL);
for(i = 0; i < 100; i++){
for(j = 0; j < 100; j++){
xmlrpc_client_call_asynch(XMLRPC_URL, "pricestring",
print_state_name_callback, NULL,
"(ii)",
(xmlrpc_int32)2000, (xmlrpc_int32)20);
}
xmlrpc_client_event_loop_finish_asynch();
}
xmlrpc_client_cleanup();
return 0;
}
|
Gaucheのコードを見て思いついた。 サーバー側の ghci を ssh で起こす。 自宅のクライアントプログラムからインターネットにあるサーバーへアクセスして pricestring を 10000回実行、結果の文字列10000行をプログラムで受けとる。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 | -- サーバー側 pricestring.hs
import qualified Data.UTF8 as U
import Data.List
pricestring :: Int -> Int -> String
pricestring x y = "販売価格 "++h++" 円 (定価 "++t++" 円から "++r++" %引き)"
where h = comint (x*(100-y) `div` 100)
t = comint x
r = comint y
comint :: Int -> String
comint = reverse . concat . intersperse "," . slice 3 . reverse . show
slice :: Int -> [a] -> [[a]]
slice n = unfoldr phi
where phi [] = Nothing
phi xs = Just $ splitAt n xs
-- クライアント側
module Main (main) where
import Data.List
import Data.Char
import qualified Data.UTF8 as U
import System.IO
import System.Process
remote = "foo.example.org"
main :: IO ()
main = do { (i,o,e,p) <- runInteractiveCommand "ssh "++host++" ghci -v0 pricestring.hs"
; cs <- hGetContents o
; es <- hGetContents e
; hPutStrLn i "U.putStr $ unlines $ map (uncurry pricestring) $ replicate 10000 (2000,20)"
; hFlush i
; hPutStrLn i ":q"
; hFlush i
; putStr cs
; putStr es
}
loop0 h = do { l <- getLine
; if "> " `isSuffixOf` l then return ()
else loop0 h
}
--
{-
*Main> :main
販売価格 1,600 円 (定価 2,000 円から 20 %引き)
販売価格 1,600 円 (定価 2,000 円から 20 %引き)
販売価格 1,600 円 (定価 2,000 円から 20 %引き)
...
... 結果が10000行
...
(1.04 secs, 183701028 bytes)
-}
|
あれ?、コードのインデントが変になっちゃった。
よくよく考えると、このコードだと 以下のような処理がシーケンシャルに 行われるのでスケールしなくて当然ですね。 1. client が [:pricestring, ...] を write 2. client が [:pricestring_result] を take 3. server が [:pricestring, ...] をtake して処理 4. server が [:pricestring_result, ] を write 5. client の [:pricestring_result] の take が帰ってくる 6. 1 にもどる で、処理を変えて client の [:pricestring] を write する処理と [:pricestring_result] を take する処理を 別プロセスにしてみたけど、大して変わらなかった。。。
別CPUのマシンで試してみました。 一万回呼び出しにかかった時間:1606 ms ・サーバとクライアントのCPU・メモリ: Intel Pentium M processor 1.60GHz・メモリ 2GB ・同一サーバ内での実行か別サーバでの実行か:同一サーバ内 ・言語のバージョン:Java HotSpot(TM) Client VM build 1.6.0_02-b06 ・ミドルウェアを使用している場合,その名前とバージョン:使用していない
直しておきました。 原因は部分的にタブ文字が混ざっていたことでした。 どう書くorgではタブ文字をスペース4個で表示しているので、 8スペースの環境でタブとスペースの混在したコードを作ると タブの所だけへこむことになります。
測定結果と環境を書きわすれていました. 測定 % time runhaskell rpc.hs ...10000行の出力... runhaskell rpc.hs 0.93s user 0.47s system 12% cpu 10.790 total 通信 下り 8Mbps (best effort) の回線でインターネット接続 サーバー CPU : AMD Sempron 2600+ 1.61 GHz メモリ : 500MB 言語 : ghc-6.6 クライアント CPU : Intel Pentium M 2.13 GHz メモリ : 2GB 言語 : ghc-6.6.1 ミドルウェア ssh
Shiro さんの実装に対抗して速度重視のものを書いてみました。ソケットによって、二つの引数を渡しているだけですが、 RPC ということにしておきます。実験的なコードなので、安全性は考えていません。ソケットを閉じてさえいません。
実行速度はやはり速く、 Celeron 2GHz のマシン(同一)で、 /dev/null に出力したもので、大体3秒程度でした。実行結果は以下のような感じ。
theo-desktop% cd workspace/doukaku
theo-desktop% ./rpc-server.scm&
[1] 22494
theo-desktop% ./rpc-client.scm
# 一万行の出力を省略
;(time (dotimes (i *call-times*) (format out "~d ~d\n" (u16vector-ref nv ...
; real 29.516
; user 1.170
; sys 1.100
theo-desktop%
[1] + done ./rpc-server.scm
theo-desktop% ./rpc-server.scm&
[1] 22499
theo-desktop% ./rpc-client.scm>/dev/null
;(time (dotimes (i *call-times*) (format out "~d ~d\n" (u16vector-ref nv ...
; real 3.434
; user 0.960
; sys 0.840
theo-desktop%
[1] + done ./rpc-server.scm
theo-desktop%
*サーバとクライアントのCPU・メモリ
メモリ: 749.1MB
プロセッサ: Intel Celeron CPU 2.00 GHz
*同一サーバ内での実行か別サーバでの実行か
同一。
*言語のバージョン
Gauche scheme interpreter, version 0.8.10 [utf-8,pthreads]
*ミドルウェアを使用している場合,その名前とバージョン
特にないと思われる。
このプログラムを書くにあたって Gauche (at Lingr) において、びさん、 rui さん、 masa_edw さん、 leque さんらに相談にのっていただきました。どうもありがとう!
実行速度はやはり速く、 Celeron 2GHz のマシン(同一)で、 /dev/null に出力したもので、大体3秒程度でした。実行結果は以下のような感じ。
theo-desktop% cd workspace/doukaku
theo-desktop% ./rpc-server.scm&
[1] 22494
theo-desktop% ./rpc-client.scm
# 一万行の出力を省略
;(time (dotimes (i *call-times*) (format out "~d ~d\n" (u16vector-ref nv ...
; real 29.516
; user 1.170
; sys 1.100
theo-desktop%
[1] + done ./rpc-server.scm
theo-desktop% ./rpc-server.scm&
[1] 22499
theo-desktop% ./rpc-client.scm>/dev/null
;(time (dotimes (i *call-times*) (format out "~d ~d\n" (u16vector-ref nv ...
; real 3.434
; user 0.960
; sys 0.840
theo-desktop%
[1] + done ./rpc-server.scm
theo-desktop%
*サーバとクライアントのCPU・メモリ
メモリ: 749.1MB
プロセッサ: Intel Celeron CPU 2.00 GHz
*同一サーバ内での実行か別サーバでの実行か
同一。
*言語のバージョン
Gauche scheme interpreter, version 0.8.10 [utf-8,pthreads]
*ミドルウェアを使用している場合,その名前とバージョン
特にないと思われる。
このプログラムを書くにあたって Gauche (at Lingr) において、びさん、 rui さん、 masa_edw さん、 leque さんらに相談にのっていただきました。どうもありがとう!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 | ;; クライアント
#!/usr/bin/env gosh
(use gauche.net)
(use srfi-27)
(use gauche.uvector)
(define *call-times* 10000)
(define (random-integers size maxnum)
(let1 v (make-u16vector size)
(dotimes (i size)
(u16vector-set! v i (random-integer maxnum)))
v))
(define (main args)
(let ((nvec (random-integers *call-times* (expt 2 16)))
(mvec (random-integers *call-times* 100)))
(call-with-client-socket
(make-client-socket 'inet "localhost" 3000)
(lambda (in out)
(time
(dotimes (i *call-times*)
(format out
"~d ~d\n"
(u16vector-ref nvec i) (u16vector-ref mvec i))
(flush out)
(print (read-line in))
))))))
;; サーバー
(let* ((server (make-server-socket 'inet 3000 :reuse-addr? #t))
(sock (socket-accept server))
(in (socket-input-port sock :buffering :modest))
(out (socket-output-port sock)))
(with-ports
in out #f
(lambda ()
(do ((n (read) (read))
(m (read) (read)))
((or (eof-object? n) (eof-object? m)))
(format out "販売価格 ~,,',,4:d (定価~,,',,4:d円から~d%引き)"
(ceiling (*. n (/. (-. 100 m) 100)))
n m)
(newline)
(flush)))))
|





沢渡 みかげ
#3401()
Rating0/0=0.00
[ reply ]