<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Erlang非业余研究 &#187; pipe</title>
	<atom:link href="http://blog.yufeng.info/archives/tag/pipe/feed" rel="self" type="application/rss+xml" />
	<link>http://blog.yufeng.info</link>
	<description>Erlang系统深度探索和应用</description>
	<lastBuildDate>Tue, 17 Jan 2012 06:05:54 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.3.1</generator>
		<item>
		<title>Linux下pipe使用注意事项</title>
		<link>http://blog.yufeng.info/archives/1485</link>
		<comments>http://blog.yufeng.info/archives/1485#comments</comments>
		<pubDate>Wed, 09 Nov 2011 12:52:43 +0000</pubDate>
		<dc:creator>Yu Feng</dc:creator>
				<category><![CDATA[Erlang探索]]></category>
		<category><![CDATA[网络编程]]></category>
		<category><![CDATA[调优]]></category>
		<category><![CDATA[fcntl]]></category>
		<category><![CDATA[O_NOATIME]]></category>
		<category><![CDATA[pipe]]></category>

		<guid isPermaLink="false">http://blog.yufeng.info/?p=1485</guid>
		<description><![CDATA[原创文章，转载请注明： 转载自Erlang非业余研究 本文链接地址: Linux下pipe使用注意事项 Linux下的pipe使用非常广泛, shell本身就大量用pipe来粘合生产者和消费者的. 我们的服务器程序通常会用pipe来做线程间的ipc通讯. 由于unix下的任何东西都是文件,只要是文件,在读取的时候,,就会设置last access time, 所以pipe也不例外., 但是这个时间对我们没有意义 如果pipe使用的非常频繁的时候会碰到由于设置访问时间导致的性能问题. 这个开销远比pipe读写的本身开销大. 相比文件读写的开销, atime微不足道,但是对pipe来讲就不同了. 这个事情是上次和多隆同学在把玩他的网络框架的时候,无意发现的. 我们来分析下pipe的这部分代码: 我们可以看到在pipe读的时候要设置 file_accessed时间的,接着: 如果文件没设置 O_NOATIME就真正动手设置atime,接着: 我们可以看出上面的流程还是比较复杂的,开销也很大. 我们来演示下: 我们可以看到touch_atime的开销很大,远比pipe的读写大. 这次把这行注释去掉: fcntl(fds[0], F_SETFL, O_NOATIME); 指示pipe在读的时候不更新atime,看下效果: 这下看不到touch_atime了,开销省了,对于高性能服务器是很重要的. 小结: 细节很重要,记得开文件open的时候设置O_NOATIME或者用fcntl搞定它. 祝玩得开心! Post Footer automatically generated by wp-posturl plugin for wordpress.]]></description>
			<content:encoded><![CDATA[<div style="margin-top: 15px; font-style: italic">
<p><strong>原创文章，转载请注明：</strong> 转载自<a href="http://blog.yufeng.info/">Erlang非业余研究</a></p>
<p><strong>本文链接地址:</strong> <a href="http://blog.yufeng.info/archives/1485">Linux下pipe使用注意事项</a></p>
</div>
<p>Linux下的pipe使用非常广泛, shell本身就大量用pipe来粘合生产者和消费者的. 我们的服务器程序通常会用pipe来做线程间的ipc通讯. 由于unix下的任何东西都是文件,只要是文件,在读取的时候,,就会设置last access time, 所以pipe也不例外., 但是这个时间对我们没有意义  如果pipe使用的非常频繁的时候会碰到由于设置访问时间导致的性能问题. 这个开销远比pipe读写的本身开销大. 相比文件读写的开销, atime微不足道,但是对pipe来讲就不同了.<br />
这个事情是上次和多隆同学在把玩他的网络框架的时候,无意发现的.</p>
<p>我们来分析下pipe的这部分代码:<br />
<span id="more-1485"></span></p>
<pre class="brush: cpp; title: ; notranslate">
//pipe.c:L349
static ssize_t
pipe_read(struct kiocb *iocb, const struct iovec *_iov,
               unsigned long nr_segs, loff_t pos)
{
...
   if (ret &gt; 0)
        file_accessed(filp);
    return ret;
}
</pre>
<p>我们可以看到在pipe读的时候要设置 file_accessed时间的,接着:</p>
<pre class="brush: cpp; title: ; notranslate">
//fs.h:L1761
extern void touch_atime(struct vfsmount *mnt, struct dentry *dentry);
static inline void file_accessed(struct file *file)
{
        if (!(file-&gt;f_flags &amp; O_NOATIME))
                touch_atime(file-&gt;f_path.mnt, file-&gt;f_path.dentry);
}
</pre>
<p>如果文件没设置 O_NOATIME就真正动手设置atime,接着:</p>
<pre class="brush: cpp; title: ; notranslate">
//inode.c:L1493
void touch_atime(struct vfsmount *mnt, struct dentry *dentry)
{
        struct inode *inode = dentry-&gt;d_inode;
        struct timespec now;

        if (inode-&gt;i_flags &amp; S_NOATIME)
                return;
        if (IS_NOATIME(inode))
                return;
        if ((inode-&gt;i_sb-&gt;s_flags &amp; MS_NODIRATIME) &amp;&amp; S_ISDIR(inode-&gt;i_mode))
                return;

        if (mnt-&gt;mnt_flags &amp; MNT_NOATIME)
                return;
        if ((mnt-&gt;mnt_flags &amp; MNT_NODIRATIME) &amp;&amp; S_ISDIR(inode-&gt;i_mode))
                return;

        now = current_fs_time(inode-&gt;i_sb);

        if (!relatime_need_update(mnt, inode, now))
                return;

        if (timespec_equal(&amp;inode-&gt;i_atime, &amp;now))
                return;

        if (mnt_want_write(mnt))
                return;

        inode-&gt;i_atime = now;
        mark_inode_dirty_sync(inode);
        mnt_drop_write(mnt);
}
</pre>
<p>我们可以看出上面的流程还是比较复杂的,开销也很大.<br />
我们来演示下:</p>
<pre class="brush: bash; title: ; notranslate">
$ cat &gt; pipe_test.c
#include &lt;stdio.h&gt;
#include &lt;stdlib.h&gt;
#include &lt;unistd.h&gt;
#include &lt;assert.h&gt;
#include &lt;pthread.h&gt;
#include &lt;fcntl.h&gt;
#include &lt;sys/types.h&gt;
#include &lt;sys/stat.h&gt;
#include &lt;linux/unistd.h&gt;

static int fds[2];
static pthread_t rp;

static void *rp_entry(void *arg) {
  char c[1];
  while (1 == read(fds[0], c, 1)) {
    if (*c == 'Q') break;
  }
  fprintf(stderr, &quot;pipe read ok\n&quot;);
  return NULL;
}

int main(int argc, char *argv[]) {
  long i, n;
  int rc;
  if (argc &lt; 2) {
    fprintf(stderr, &quot;usage: pipe_test NNNNNN\n&quot;);
    return -1;
  }
  n = atol(argv[1]);
  pipe(fds);
  //fcntl(fds[0], F_SETFL, O_NOATIME);
  pthread_create(&amp;rp, NULL, rp_entry, NULL);
  fprintf(stderr, &quot;pipe write %ld...&quot;, n);
  for (i = 0; i &lt; n; i++) {
    write(fds[1], &quot;A&quot;, 1);
  }
  write(fds[1], &quot;Q&quot;, 1);
  fprintf(stderr, &quot;ok\n&quot;);
  pthread_join(rp, NULL);
  close(fds[0]);
  close(fds[1]);
  return 0;
}
CTRL+D
$ gcc -D_GNU_SOURCE pipe_test.c -lpthread
$ sudo opcontrol --setup --vmlinux=/usr/lib/debug/lib/modules/2.6.18-164.el5/vmlinux
$ sudo opcontrol --init &amp;&amp; sudo opcontrol --reset &amp;&amp; sudo opcontrol --start
$ ./a.out 10000000
pipe write 10000000...ok
pipe read ok
$ sudo opcontrol --shutdown
$ opreport -l|less
samples  %        app name                 symbol name
378654   92.7742  vmlinux                  .text.acpi_processor_idle
12978     3.1797  vmlinux                  current_fs_time
2530      0.6199  vmlinux                  thread_return
2345      0.5745  vmlinux                  touch_atime
2253      0.5520  vmlinux                  .text.acpi_safe_halt
1597      0.3913  vmlinux                  timespec_trunc
1368      0.3352  vmlinux                  file_update_time
1253      0.3070  vmlinux                  __mark_inode_dirty
901       0.2208  vmlinux                  pipe_writev
768       0.1882  vmlinux                  __mutex_lock_slowpath
763       0.1869  vmlinux                  try_to_wake_up
270       0.0662  vmlinux                  copy_user_generic_unrolled
254       0.0622  vmlinux                  acpi_set_register
254       0.0622  vmlinux                  system_call
233       0.0571  vmlinux                  pipe_readv
188       0.0461  vmlinux                  dnotify_parent
167       0.0409  vmlinux                  mutex_unlock
...
</pre>
<p>我们可以看到touch_atime的开销很大,远比pipe的读写大.<br />
这次把这行注释去掉: fcntl(fds[0], F_SETFL, O_NOATIME); 指示pipe在读的时候不更新atime,看下效果:</p>
<pre class="brush: bash; title: ; notranslate">
$ opreport -l|less
samples  %        app name                 symbol name
599018   95.2466  vmlinux                  .text.acpi_processor_idle
4140      0.6583  vmlinux                  .text.acpi_safe_halt
3281      0.5217  vmlinux                  thread_return
2812      0.4471  vmlinux                  current_fs_time
2615      0.4158  vmlinux                  file_update_time
1790      0.2846  vmlinux                  __mutex_lock_slowpath
1657      0.2635  vmlinux                  timespec_trunc
1341      0.2132  vmlinux                  try_to_wake_up
1281      0.2037  vmlinux                  mutex_unlock
1080      0.1717  vmlinux                  mutex_lock
1001      0.1592  vmlinux                  pipe_readv
925       0.1471  vmlinux                  pipe_writev
</pre>
<p>这下看不到touch_atime了,开销省了,对于高性能服务器是很重要的.<br />
小结: 细节很重要,记得开文件open的时候设置O_NOATIME或者用fcntl搞定它.<br />
祝玩得开心!</p>
<div style="margin-top: 0; margin-bottom: 15px; color: #888888; font-size: 80%; font-style: italic">
<p>Post Footer automatically generated by <a href="http://easwy.com/blog/wordpress/wp-posturl/" style="color: #8888FF; text-decoration: underline;">wp-posturl plugin</a> for wordpress.</p>
</div>
]]></content:encoded>
			<wfw:commentRss>http://blog.yufeng.info/archives/1485/feed</wfw:commentRss>
		<slash:comments>10</slash:comments>
		</item>
		<item>
		<title>高強度的port(Pipe)的性能測試</title>
		<link>http://blog.yufeng.info/archives/28</link>
		<comments>http://blog.yufeng.info/archives/28#comments</comments>
		<pubDate>Sun, 13 Sep 2009 19:05:58 +0000</pubDate>
		<dc:creator>Yu Feng</dc:creator>
				<category><![CDATA[Erlang探索]]></category>
		<category><![CDATA[pipe]]></category>
		<category><![CDATA[port]]></category>
		<category><![CDATA[ring]]></category>
		<category><![CDATA[spawn]]></category>

		<guid isPermaLink="false">http://blog.yufeng.info/?p=28</guid>
		<description><![CDATA[原创文章，转载请注明： 转载自Erlang非业余研究 本文链接地址: 高強度的port(Pipe)的性能測試 在我的項目里面, 很多運算logic是由外部的程序來計算的 那么消息先透過pipe發到外部程序,外部程序讀到消息, 處理消息, 寫消息, erlang程序讀到消息, 這條鏈路很長,而且涉及到pipe讀寫,上下文切換,這個開銷是很大的.但是具體是多少呢? 我設計了個這樣的ring. 每個ring有N個環組成, 每個環開個port. 當ring收到個數字的時候 如果數字不為0, 那么把這個數字發到外部成程序,這個外部程序echo回來數字,收到echo回來的消息后,把數字減1,繼續傳遞.當數字減少到0的時候 銷毀整個ring. /* 注意這個數字非常重要 它影響了Erlang程序3個地方 1. epoll的句柄集大小 2. MAX_PORT 以及port的表格大小 3. open_port的時候 子進程關閉的文件句柄大小*/ 參數的意義: N K Par N：ring有幾個環 每個環開一個port K：每個環傳遞多少消息 Par: 多少ring一起跑 總的消息數是 K * Par. 我們可以看到 每秒可以處理大概 3.4W個消息 我有2個核心. 也就是說每個消息的開銷大概是 30us. 每個port的創建時間不算多, 1ms一個. 注意上面的csw 達到6W每秒. 我們運行了80個echo程序(/bin/cat) 讀者有興趣的話可以用systemtap 詳細了解 [...]]]></description>
			<content:encoded><![CDATA[<div style="margin-top: 15px; font-style: italic">
<p><strong>原创文章，转载请注明：</strong> 转载自<a href="http://blog.yufeng.info/">Erlang非业余研究</a></p>
<p><strong>本文链接地址:</strong> <a href="http://blog.yufeng.info/archives/28">高強度的port(Pipe)的性能測試</a></p>
</div>
<p>在我的項目里面, 很多運算logic是由外部的程序來計算的 那么消息先透過pipe發到外部程序,外部程序讀到消息, 處理消息, 寫消息, erlang程序讀到消息, 這條鏈路很長,而且涉及到pipe讀寫,上下文切換,這個開銷是很大的.但是具體是多少呢? </p>
<p>我設計了個這樣的ring. 每個ring有N個環組成, 每個環開個port. 當ring收到個數字的時候 如果數字不為0, 那么把這個數字發到外部成程序,這個外部程序echo回來數字,收到echo回來的消息后,把數字減1,繼續傳遞.當數字減少到0的時候 銷毀整個ring.<br />
/* 注意這個數字非常重要 它影響了Erlang程序3個地方 1. epoll的句柄集大小 2. MAX_PORT 以及port的表格大小 3. open_port的時候 子進程關閉的文件句柄大小*/ </p>
<pre class="brush: bash; title: ; notranslate">
root@nd-desktop:~/test#ulimit -n 1024
root@nd-desktop:~/test# cat pipe_ring.erl
</pre>
<pre class="brush: erlang; title: ; notranslate">
-module(pipe_ring). 

-export([start/1]).
-export([make_relay/1, run/3]). 

make_relay(Next)-&gt;
    Port = open_port({spawn, &quot;/bin/cat&quot;}, [in, out, {line, 128}]),
    relay_loop(Next, Port). 

relay_loop(Next, Port) -&gt;
    receive
        {Port, {data, {eol, Line}}} -&gt;
            Next ! (list_to_integer(Line) - 1),
            relay_loop(Next, Port);
        K when is_integer(K) andalso K &gt; 0 -&gt;
            port_command(Port, integer_to_list(K) ++ &quot;\n&quot;),
            relay_loop(Next, Port);
        K when is_integer(K) andalso K =:=0 -&gt;
            port_close(Port),
            Next ! K
end. 

build_ring(K, Current, N, F) when N &gt; 1 -&gt;
    build_ring(K, spawn(?MODULE, make_relay, [Current]), N - 1, F); 

build_ring(_, Current, _, F) -&gt;
    F(),
    make_relay(Current). 

run(N, K, Par) -&gt;
    Parent = self(),
    Cs = [spawn(fun ()-&gt; Parent!run1(N, K, P) end) || P&lt;-lists:seq(1, Par)],
    [receive _-&gt; ok end || _&lt;-Cs]. 

run1(N, K, P)-&gt;
    T1 = now(),
    build_ring(K, self(), N, fun ()-&gt; io:format(&quot;(ring~w setup time: ~ws)~n&quot;, [P, timer:now_diff(now(), T1) /1000]), self() ! K end). 

start(Args) -&gt;
    Args1 = [N, K, Par] = [list_to_integer(atom_to_list(X)) || X&lt;-Args],
    {Time, _} = timer:tc(?MODULE, run, Args1),
    io:format(&quot;(total run (N:~w K:~w Par:~w) ~wms ~w/s)~n&quot;, [N, K, Par, round(Time/1000), round(K*Par*1000000/Time)]),
    halt(0).
</pre>
<pre class="brush: bash; title: ; notranslate">
root@nd-desktop:~/test# erl +Bd -noshell +K true -smp disable -s pipe_ring start 10 100000 8
(ring1 setup time: 0.021s)
(ring2 setup time: 0.02s)
(ring3 setup time: 0.019s)
(ring4 setup time: 0.03s)
(ring5 setup time: 0.018s)
(ring6 setup time: 0.031s)
(ring7 setup time: 0.027s)
(ring8 setup time: 0.039s)
(total run (N:10 K:100000 Par:8) 23158ms 34546/s)
</pre>
<p>參數的意義:<br />
N K Par<br />
N：ring有幾個環 每個環開一個port<br />
K：每個環傳遞多少消息<br />
Par: 多少ring一起跑 </p>
<p>總的消息數是 K * Par. </p>
<p>我們可以看到 每秒可以處理大概 3.4W個消息 我有2個核心. 也就是說每個消息的開銷大概是 30us. 每個port的創建時間不算多, 1ms一個. </p>
<pre class="brush: bash; title: ; notranslate">
root@nd-desktop:~/test# dstat
----total-cpu-usage---- -dsk/total- -net/total- ---paging-- ---system--
usr sys idl wai hiq siq| read  writ| recv  send|  in   out | int   csw
33  18  50   0   0   1|   0     0 | 438B 2172B|   0     0 |5329    33k
42  11  48   0   0   0|   0     0 | 212B  404B|   0     0 |5729    58k
41  11  49   0   0   0|   0     0 | 244B 1822B|   0     0 |5540    59k
40  11  49   0   0   0|   0     0 | 304B  404B|   0     0 |4970    60k
</pre>
<p>注意上面的csw 達到6W每秒. </p>
<pre class="brush: bash; title: ; notranslate">
root@nd-desktop:~/test# pstree
├─sshd─┬─sshd─┬─bash───pstree
     │      │      └─bash───man───pager
     │      ├─sshd───bash─┬─beam─┬─80*[cat]
     │      │             │      └─{beam}
     │      │             └─emacs
     │      ├─sshd───bash───emacs
     │      └─sshd───bash───nmon
</pre>
<p>我們運行了80個echo程序(/bin/cat) </p>
<p>讀者有興趣的話可以用systemtap 詳細了解 pipe的讀寫花費,以及context_switch情況, 具體腳本可以向我索要. </p>
<pre class="brush: bash; title: ; notranslate">
root@nd-desktop:~# cat /proc/cpuinfo
processor       : 1
vendor_id       : GenuineIntel
cpu family      : 6
model           : 23
model name      : Pentium(R) Dual-Core  CPU      E5200  @ 2.50GHz
stepping        : 6
cpu MHz         : 1200.000
cache size      : 2048 KB
physical id     : 0
siblings        : 2
core id         : 1
cpu cores       : 2
apicid          : 1
initial apicid  : 1
fdiv_bug        : no
hlt_bug         : no
f00f_bug        : no
coma_bug        : no
fpu             : yes
fpu_exception   : yes
cpuid level     : 10
wp              : yes
flags           : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm pbe nx lm constant_tsc arch_perfmon pebs bts pni dtes64 monitor ds_cpl em
bogomips        : 4987.44
clflush size    : 64
power management:
</pre>
<p>結論是: 用port的這種架構的開銷是可以接受的. </p>
<div style="margin-top: 0; margin-bottom: 15px; color: #888888; font-size: 80%; font-style: italic">
<p>Post Footer automatically generated by <a href="http://easwy.com/blog/wordpress/wp-posturl/" style="color: #8888FF; text-decoration: underline;">wp-posturl plugin</a> for wordpress.</p>
</div>
]]></content:encoded>
			<wfw:commentRss>http://blog.yufeng.info/archives/28/feed</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
	</channel>
</rss>

