Browse Source

reviewed bvar.md and move c++ stuff into bvar_c++.md

gejun 6 years ago
parent
commit
79d67067ed
3 changed files with 314 additions and 468 deletions
  1. 52 186
      docs/cn/bvar.md
  2. 210 96
      docs/cn/bvar_c++.md
  3. 52 186
      docs/en/bvar.md

+ 52 - 186
docs/cn/bvar.md

@@ -1,198 +1,76 @@
-# 什么是bvar?
-
-[bvar](https://github.com/brpc/brpc/blob/master/src/bvar)是多线程环境下的计数器类库,方便记录和查看用户程序中的各类数值,它利用了thread local存储避免了cache bouncing,相比UbMonitor几乎不会给程序增加性能开销,也快于竞争频繁的原子操作。brpc集成了bvar,[/vars](http://brpc.baidu.com:8765/vars)可查看所有曝光的bvar,[/vars/VARNAME](http://brpc.baidu.com:8765/vars/rpc_socket_count)可查阅某个bvar,在rpc中的具体使用方法请查看[这里](vars.md)。brpc大量使用了bvar提供统计数值,当你需要在多线程环境中计数并展现时,应该第一时间想到bvar。但bvar不能代替所有的计数器,它的本质是把写时的竞争转移到了读:读得合并所有写过的线程中的数据,而不可避免地变慢了。当你读写都很频繁并得基于数值做一些逻辑判断时,你不应该用bvar。
+[English version](../en/bvar.md)
 
-# 什么是cache bouncing
+# 什么是bvar?
 
-为了以较低的成本大幅提高性能,现代CPU都有[cache](https://en.wikipedia.org/wiki/CPU_cache)。百度内常见的Intel E5-2620拥有32K的L1 dcache和icache,256K的L2 cache和15M的L3 cache。其中L1和L2cache为每个核独有,L3则所有核共享。为了保证所有的核看到正确的内存数据,一个核在写入自己的L1 cache后,CPU会执行[Cache一致性](https://en.wikipedia.org/wiki/Cache_coherence)算法把对应的[cacheline](https://en.wikipedia.org/wiki/CPU_cache#Cache_entries)(一般是64字节)同步到其他核。这个过程并不很快,是微秒级的,相比之下写入L1 cache只需要若干纳秒。当很多线程在频繁修改某个字段时,这个字段所在的cacheline被不停地同步到不同的核上,就像在核间弹来弹去,这个现象就叫做cache bouncing。由于实现cache一致性往往有硬件锁,cache bouncing是一种隐式的的全局竞争。关于竞争请查阅[atomic instructions](atomic_instructions.md)
+[bvar](https://github.com/brpc/brpc/tree/master/src/bvar/)是多线程环境下的计数器类库,方便记录和查看用户程序中的各类数值,它利用了thread local存储减少了cache bouncing,相比UbMonitor(百度内的老计数器库)几乎不会给程序增加性能开销,也快于竞争频繁的原子操作。brpc集成了bvar,[/vars](http://brpc.baidu.com:8765/vars)可查看所有曝光的bvar,[/vars/VARNAME](http://brpc.baidu.com:8765/vars/rpc_socket_count)可查阅某个bvar,在brpc中的使用方法请查看[vars](vars.md)。brpc大量使用了bvar提供统计数值,当你需要在多线程环境中计数并展现时,应该第一时间想到bvar。但bvar不能代替所有的计数器,它的本质是把写时的竞争转移到了读:读得合并所有写过的线程中的数据,而不可避免地变慢了。当你读写都很频繁或得基于最新值做一些逻辑判断时,你不应该用bvar
 
-cache bouncing使访问频繁修改的变量的开销陡增,甚至还会使访问同一个cacheline中不常修改的变量也变慢,这个现象是[false sharing](https://en.wikipedia.org/wiki/False_sharing)。按cacheline对齐能避免false sharing,但在某些情况下,我们甚至还能避免修改“必须”修改的变量。bvar便是这样一个例子,当很多线程都在累加一个计数器时,我们让每个线程累加私有的变量而不参与全局竞争,在读取时我们累加所有线程的私有变量。虽然读比之前慢多了,但由于这类计数器的读多为低频展现,慢点无所谓。而写就快多了,从微秒到纳秒,几百倍的差距使得用户可以无所顾忌地使用bvar,这便是我们设计bvar的目的。
+为了理解bvar的原理,你得先阅读[Cacheline这节](atomic_instructions.md#cacheline),其中提到的计数器例子便是bvar。当很多线程都在累加一个计数器时,每个线程只累加私有的变量而不参与全局竞争,在读取时累加所有线程的私有变量。虽然读比之前慢多了,但由于这类计数器的读多为低频的记录和展现,慢点无所谓。而写就快多了,极小的开销使得用户可以无顾虑地使用bvar监控系统,这便是我们设计bvar的目的。
 
-下图是bvar和原子变量,静态UbMonitor,动态UbMonitor的性能对比。可以看到bvar的耗时基本和线程数无关,一直保持在极低的水平(~20纳秒)。而动态UbMonitor在24核时每次累加的耗时达7微秒,这意味着使用300次bvar的开销才抵得上使用一次动态UbMonitor变量。
+下图是bvar和原子变量,静态UbMonitor,动态UbMonitor在被多个线程同时使用时的开销。可以看到bvar的耗时基本和线程数无关,一直保持在极低的水平(~20纳秒)。而动态UbMonitor在24核时每次累加的耗时达7微秒,这意味着使用300次bvar的开销才抵得上使用一次动态UbMonitor变量。
 
 ![img](../images/bvar_perf.png)
 
-# 用noah监控bvar
-
-![img](../images/bvar_flow.png)
-
-- bvar 将被监控的项目定期打入文件:monitor/bvar.<app>.data。
-- noah 自动收集文件并生成图像。
-- App只需要注册相应的监控项即可。
-
-每个App必须做到的最低监控要求如下:
-
-- **Error**: 要求系统中每个可能出现的error都有监控 
-- **Latency**: 系统对外的每个rpc请求的latency;  系统依赖的每个后台的每个request的latency;  (注: 相应的max_latency,系统框架会自动生成)
-- **QPS**: 系统对外的每个request的QPS; 系统依赖的每个后台的每个request的QPS.
-
-后面会在完善monitoring框架的过程中要求每个App添加新的必需字段。
-
-# RD要干的事情
-
-## 定义bvar
-
-```c++
-#include <bvar/bvar.h>
- 
-namespace foo {
-namespace bar {
- 
-// bvar::Adder<T>用于累加,下面定义了一个统计read error总数的Adder。
-bvar::Adder<int> g_read_error;
-// 把bvar::Window套在其他bvar上就可以获得时间窗口内的值。
-bvar::Window<bvar::Adder<int> > g_read_error_minute("foo_bar", "read_error", &g_read_error, 60);
-//                                                     ^          ^                         ^
-//                                                    前缀       监控项名称                  60秒,忽略则为10秒
- 
-// bvar::LatencyRecorder是一个复合变量,可以统计:总量、qps、平均延时,延时分位值,最大延时。
-bvar::LatencyRecorder g_write_latency(“foo_bar", "write”);
-//                                      ^          ^
-//                                     前缀       监控项,别加latency!LatencyRecorder包含多个bvar,它们会加上各自的后缀,比如write_qps, write_latency等等。
- 
-// 定义一个统计“已推入task”个数的变量。
-bvar::Adder<int> g_task_pushed("foo_bar", "task_pushed");
-// 把bvar::PerSecond套在其他bvar上可以获得时间窗口内*平均每秒*的值,这里是每秒内推入task的个数。
-bvar::PerSecond<bvar::Adder<int> > g_task_pushed_second("foo_bar", "task_pushed_second", &g_task_pushed);
-//       ^                                                                                             ^
-//    和Window不同,PerSecond会除以时间窗口的大小.                                   时间窗口是最后一个参数,这里没填,就是默认10秒。
- 
-}  // bar
-}  // foo
-```
-
-在应用的地方:
-
-```c++
-// 碰到read error
-foo::bar::g_read_error << 1;
- 
-// write_latency是23ms
-foo::bar::g_write_latency << 23;
- 
-// 推入了1个task
-foo::bar::g_task_pushed << 1;
-```
-
-注意Window<>和PerSecond<>都是衍生变量,会自动更新,你不用给它们推值。
-
->  你当然也可以把bvar作为成员变量或局部变量,请阅读[bvar-c++](bvar_c++.md)。
+# 监控bvar
 
-**确认变量名是全局唯一的!**否则会曝光失败,如果-bvar_abort_on_same_name为true,程序会直接abort。
+下图是监控bvar的示意图:
 
-程序中有来自各种模块不同的bvar,为避免重名,建议如此命名:**模块_类名_指标**
-
-- **模块**一般是程序名,可以加上产品线的缩写,比如inf_ds,ecom_retrbs等等。
-- **类名**一般是类名或函数名,比如storage_manager, file_transfer, rank_stage1等等。
-- **指标**一般是count,qps,latency这类。
-
-一些正确的命名如下:
+![img](../images/bvar_flow.png)
 
-```
-iobuf_block_count : 29                          # 模块=iobuf   类名=block  指标=count
-iobuf_block_memory : 237568                     # 模块=iobuf   类名=block  指标=memory
-process_memory_resident : 34709504              # 模块=process 类名=memory 指标=resident
-process_memory_shared : 6844416                 # 模块=process 类名=memory 指标=shared
-rpc_channel_connection_count : 0                # 模块=rpc     类名=channel_connection  指标=count
-rpc_controller_count : 1                        # 模块=rpc     类名=controller 指标=count
-rpc_socket_count : 6                            # 模块=rpc     类名=socket     指标=count
-```
+其中:
 
-目前bvar会做名字归一化,不管你打入的是foo::BarNum, foo.bar.num, foo bar num , foo-bar-num,最后都是foo_bar_num。
+- APP代表用户服务,使用bvar API定义监控各类指标。
+- bvar定期把被监控的项目打入$PWD/monitor/目录下的文件(用log指代)。此处的log和普通的log的不同点在于是bvar导出是覆盖式的,而不是添加式的。
+- 监控系统(用noah指代)收集导出的文件,汇总至全局并生成曲线。
 
-关于指标:
+建议APP做到如下监控要求:
 
-- 个数以_count为后缀,比如request_count, error_count。
-- 每秒的个数以_second为后缀,比如request_second, process_inblocks_second,已经足够明确,不用写成_count_second或_per_second。
-- 每分钟的个数以_minute为后缀,比如request_minute, process_inblocks_minute
+- **Error**: 系统中可能出现的error个数 
+- **Latency**: 系统对外的每个RPC接口的latency(平均和分位值),系统依赖的每个后台的每个RPC接口的latency
+- **QPS**: 系统对外的每个RPC接口的QPS信息,系统依赖的每个后台的每个RPC接口的QPS信息
 
-如果需要使用定义在另一个文件中的计数器,需要在头文件中声明对应的变量。
+增加C++ bvar的方法请看[快速介绍](bvar_c++.md#quick-introduction). bvar默认统计了进程、系统的一些变量,以process\_, system\_等开头,比如:
 
-```c++
-namespace foo {
-namespace bar {
-// 注意g_read_error_minute和g_task_pushed_per_second都是衍生的bvar,会自动更新,不要声明。
-extern bvar::Adder<int> g_read_error;
-extern bvar::LatencyRecorder g_write_latency;
-extern bvar::Adder<int> g_task_pushed;
-}  // bar
-}  // foo
 ```
-
-**不要跨文件定义全局Window或PerSecond**
-
-不同编译单元中全局变量的初始化顺序是[未定义的](https://isocpp.org/wiki/faq/ctors#static-init-order)。在foo.cpp中定义`Adder<int> foo_count`,在foo_qps.cpp中定义`PerSecond<Adder<int> > foo_qps(&foo_count);`是**错误**的做法。
-
-计时可以使用butil::Timer,接口如下:
-
-```c++
-#include <butil/time.h>
-namespace butil {
-class Timer {
-public:
-    enum TimerType { STARTED };
-     
-    Timer();
- 
-    // butil::Timer tm(butil::Timer::STARTED);  // tm is already started after creation.
-    explicit Timer(TimerType);
- 
-    // Start this timer
-    void start();
-     
-    // Stop this timer
-    void stop();
- 
-    // Get the elapse from start() to stop().
-    int64_t n_elapsed() const;  // in nanoseconds
-    int64_t u_elapsed() const;  // in microseconds
-    int64_t m_elapsed() const;  // in milliseconds
-    int64_t s_elapsed() const;  // in seconds
-};
-}  // namespace butil
+process_context_switches_involuntary_second : 14
+process_context_switches_voluntary_second : 15760
+process_cpu_usage : 0.428
+process_cpu_usage_system : 0.142
+process_cpu_usage_user : 0.286
+process_disk_read_bytes_second : 0
+process_disk_write_bytes_second : 260902
+process_faults_major : 256
+process_faults_minor_second : 14
+process_memory_resident : 392744960
+system_core_count : 12
+system_loadavg_15m : 0.040
+system_loadavg_1m : 0.000
+system_loadavg_5m : 0.020
 ```
 
-## 打开bvar的dump功能
+还有像brpc内部的各类计数器:
 
-bvar可以定期把进程内所有的bvar打印入一个文件中,默认不打开。有几种方法打开这个功能:
-
-- 用[gflags](flags.md)解析输入参数,在程序启动时加入-bvar_dump。gflags的解析方法如下,在main函数处添加如下代码:
-```c++
-  #include <gflags/gflags.h>
-  ...
-  int main(int argc, char* argv[]) {
-      google::ParseCommandLineFlags(&argc, &argv, true/*表示把识别的参数从argc/argv中删除*/);
-      ...
-  }
 ```
-- 不想用gflags解析参数,希望直接在程序中默认打开,在main函数处添加如下代码:
-```c++
-#include <gflags/gflags.h>
-...
-int main(int argc, char* argv[]) {
-    if (google::SetCommandLineOption("bvar_dump", "true").empty()) {
-        LOG(FATAL) << "Fail to enable bvar dump";
-    }
-    ...
-}
+bthread_switch_second : 20422
+bthread_timer_scheduled_second : 4
+bthread_timer_triggered_second : 4
+bthread_timer_usage : 2.64987e-05
+bthread_worker_count : 13
+bthread_worker_usage : 1.33733
+bvar_collector_dump_second : 0
+bvar_collector_dump_thread_usage : 0.000307385
+bvar_collector_grab_second : 0
+bvar_collector_grab_thread_usage : 1.9699e-05
+bvar_collector_pending_samples : 0
+bvar_dump_interval : 10
+bvar_revision : "34975"
+bvar_sampler_collector_usage : 0.00106495
+iobuf_block_count : 89
+iobuf_block_count_hit_tls_threshold : 0
+iobuf_block_memory : 729088
+iobuf_newbigview_second : 10
 ```
 
-bvar的dump功能由如下参数控制,产品线根据自己的需求调节,需要提醒的是noah要求bvar_dump_file的后缀名是.data,请勿改成其他后缀。更具体的功能描述请阅读[Export all variables](bvar_c++.md#export-all-variables)。
-
-| 名称                      | 默认值                     | 作用                                       |
-| ----------------------- | ----------------------- | ---------------------------------------- |
-| bvar_abort_on_same_name | false                   | Abort when names of bvar are same        |
-| bvar_dump               | false                   | Create a background thread dumping all bvar periodically, all bvar_dump_* flags are not effective when this flag is off |
-| bvar_dump_exclude       | ""                      | Dump bvar excluded from these wildcards(separated by comma), empty means no exclusion |
-| bvar_dump_file          | monitor/bvar.<app>.data | Dump bvar into this file                 |
-| bvar_dump_include       | ""                      | Dump bvar matching these wildcards(separated by comma), empty means including all |
-| bvar_dump_interval      | 10                      | Seconds between consecutive dump         |
-| bvar_dump_prefix        | <app>                   | Every dumped name starts with this prefix |
-| bvar_dump_tabs          | 见代码                     | Dump bvar into different tabs according to the filters (seperated by semicolon), format: *(tab_name=wildcards) |
-
-## 编译并重启应用程序
-
-检查monitor/bvar.<app>.data是否存在:
+打开bvar的[dump功能](bvar_c++.md#export-all-variables)以导出所有的bvar到文件,格式就入上文一样,每行是一对"名字:值"。打开dump功能后应检查monitor/下是否有数据,比如:
 
 ```
 $ ls monitor/
@@ -206,20 +84,8 @@ process_time_user : 0.741887
 process_username : "gejun"
 ```
 
-## 打开[noah](http://noah.baidu.com/)
-
-搜索监控节点:
-
-![img](../images/bvar_noah1.png)
-
- 
-
-点击“文件”tab,勾选要查看的统计量,bvar已经统计了进程级的很多参数,大都以process开头。
+监控系统会把定期把单机导出数据汇总到一起,并按需查询。这里以百度内的noah为例,bvar定义的变量会出现在noah的指标项中,用户可勾选并查看历史曲线。
 
 ![img](../images/bvar_noah2.png)
 
- 
-
-查看趋势图:
-
 ![img](../images/bvar_noah3.png)

+ 210 - 96
docs/cn/bvar_c++.md

@@ -1,55 +1,136 @@
-# Introduction
-源文件中`#include <bvar/bvar.h>`
-bvar分为多个具体的类,常用的有:
-- bvar::Adder<T> : 计数器,默认0,varname << N相当于varname += N。
-- bvar::Maxer<T> : 求最大值,默认std::numeric_limits<T>::min(),varname << N相当于varname = max(varname, N)。
-- bvar::Miner<T> : 求最小值,默认std::numeric_limits<T>::max(),varname << N相当于varname = min(varname, N)。
-- bvar::IntRecorder : 求自使用以来的平均值。注意这里的定语不是“一段时间内”。一般要通过Window衍生出时间窗口内的平均值。
-- bvar::Window<VAR> : 获得某个bvar在一段时间内的累加值。Window衍生于已存在的bvar,会自动更新。
-- bvar::PerSecond<VAR> : 或的某个bvar在一段时间内平均每秒的累加值。PerSecond也是会自动更新的衍生变量。
-- bvar::LatencyRecorder : 专用于记录延时和qps的变量。输入延时,平均延时/最大延时/qps/总次数 都有了。
-
-例子:
+# Quick introduction
+
 ```c++
-// 构造时不带名字,则无法被查询到。并不是所有的bvar都会显示在/vars
-bvar::Adder<int> request_count1;
- 
-// 构造时带了名字就可以被查询到,现在查询/vars应该可以看到"request_count2: 0"
-bvar::Adder<int> request_count2("request_count2");
- 
-// 或者可以调用expose,第一个变量也可以被查询了
-request_count1.expose("request_count1");
- 
-// 换一个名字
-request_count1.expose("request_count1_another");
- 
-// 让第一个变量重新不能被查询
-request_count1.hide();
- 
-// 各自加上一些数,两个变量目前的值应当分别是6和-1
-request_count1 << 1 << 2 << 3;
-request_count2 << -1;
-LOG(INFO) << "result1=" << request_count1 << " result2=" << request_count2;
- 
-// 统计上一分钟request_count1的变化量
-bvar::Window<bvar::Adder<int> > request_count1_minute("request_count1_minute", &request_count1, 60);
-// 统计上一分钟request_count1的"qps"
-bvar::PerSecond<bvar::Adder<int> > request_count1_per_second("request_count1_per_second", &request_count1, 60);
-// 让我们每隔一秒钟给request_count1加上1.
-request_count1.reset();  // 清0
-for (int i = 0; i < 120; ++i) {
-    request_count1 << 1;
-    // 依次看到1, 2, 3 ...直到60后保持不变,其中一些数字可能跳过或重复,最差情况下Window有1秒延时。
-    LOG(INFO) << "request_count1_minute=" << request_count1_minute;           
-    // 开始可能由0,之后一直看到1, 1, 1 ...,最差情况下PerSecond也有1秒延时。
-    LOG(INFO) << "request_count1_per_second=" << request_count1_per_second;                             
-    sleep(1);
-}
+#include <bvar/bvar.h>
+
+namespace foo {
+namespace bar {
+
+// bvar::Adder<T>用于累加,下面定义了一个统计read error总数的Adder。
+bvar::Adder<int> g_read_error;
+// 把bvar::Window套在其他bvar上就可以获得时间窗口内的值。
+bvar::Window<bvar::Adder<int> > g_read_error_minute("foo_bar", "read_error", &g_read_error, 60);
+//                                                     ^          ^                         ^
+//                                                    前缀       监控项名称                  60秒,忽略则为10秒
+
+// bvar::LatencyRecorder是一个复合变量,可以统计:总量、qps、平均延时,延时分位值,最大延时。
+bvar::LatencyRecorder g_write_latency(“foo_bar", "write”);
+//                                      ^          ^
+//                                     前缀       监控项,别加latency!LatencyRecorder包含多个bvar,它们会加上各自的后缀,比如write_qps, write_latency等等。
+
+// 定义一个统计“已推入task”个数的变量。
+bvar::Adder<int> g_task_pushed("foo_bar", "task_pushed");
+// 把bvar::PerSecond套在其他bvar上可以获得时间窗口内*平均每秒*的值,这里是每秒内推入task的个数。
+bvar::PerSecond<bvar::Adder<int> > g_task_pushed_second("foo_bar", "task_pushed_second", &g_task_pushed);
+//       ^                                                                                             ^
+//    和Window不同,PerSecond会除以时间窗口的大小.                                   时间窗口是最后一个参数,这里没填,就是默认10秒。
+
+}  // bar
+}  // foo
+```
+
+在应用的地方:
+
+```c++
+// 碰到read error
+foo::bar::g_read_error << 1;
+
+// write_latency是23ms
+foo::bar::g_write_latency << 23;
+
+// 推入了1个task
+foo::bar::g_task_pushed << 1;
 ```
+
+注意Window<>和PerSecond<>都是衍生变量,会自动更新,你不用给它们推值。你当然也可以把bvar作为成员变量或局部变量。
+
+常用的bvar有:
+
+- `bvar::Adder<T>` : 计数器,默认0,varname << N相当于varname += N。
+- `bvar::Maxer<T>` : 求最大值,默认std::numeric_limits<T>::min(),varname << N相当于varname = max(varname, N)。
+- `bvar::Miner<T>` : 求最小值,默认std::numeric_limits<T>::max(),varname << N相当于varname = min(varname, N)。
+- `bvar::IntRecorder` : 求自使用以来的平均值。注意这里的定语不是“一段时间内”。一般要通过Window衍生出时间窗口内的平均值。
+- `bvar::Window<VAR>` : 获得某个bvar在一段时间内的累加值。Window衍生于已存在的bvar,会自动更新。
+- `bvar::PerSecond<VAR>` : 或的某个bvar在一段时间内平均每秒的累加值。PerSecond也是会自动更新的衍生变量。
+- `bvar::LatencyRecorder` : 专用于记录延时和qps的变量。输入延时,平均延时/最大延时/qps/总次数 都有了。
+
+**确认变量名是全局唯一的!**否则会曝光失败,如果-bvar_abort_on_same_name为true,程序会直接abort。
+
+程序中有来自各种模块不同的bvar,为避免重名,建议如此命名:**模块_类名_指标**
+
+- **模块**一般是程序名,可以加上产品线的缩写,比如inf_ds,ecom_retrbs等等。
+- **类名**一般是类名或函数名,比如storage_manager, file_transfer, rank_stage1等等。
+- **指标**一般是count,qps,latency这类。
+
+一些正确的命名如下:
+
+```
+iobuf_block_count : 29                          # 模块=iobuf   类名=block  指标=count
+iobuf_block_memory : 237568                     # 模块=iobuf   类名=block  指标=memory
+process_memory_resident : 34709504              # 模块=process 类名=memory 指标=resident
+process_memory_shared : 6844416                 # 模块=process 类名=memory 指标=shared
+rpc_channel_connection_count : 0                # 模块=rpc     类名=channel_connection  指标=count
+rpc_controller_count : 1                        # 模块=rpc     类名=controller 指标=count
+rpc_socket_count : 6                            # 模块=rpc     类名=socket     指标=count
+```
+
+目前bvar会做名字归一化,不管你打入的是foo::BarNum, foo.bar.num, foo bar num , foo-bar-num,最后都是foo_bar_num。
+
+关于指标:
+
+- 个数以_count为后缀,比如request_count, error_count。
+- 每秒的个数以_second为后缀,比如request_second, process_inblocks_second,已经足够明确,不用写成_count_second或_per_second。
+- 每分钟的个数以_minute为后缀,比如request_minute, process_inblocks_minute
+
+如果需要使用定义在另一个文件中的计数器,需要在头文件中声明对应的变量。
+
+```c++
+namespace foo {
+namespace bar {
+// 注意g_read_error_minute和g_task_pushed_per_second都是衍生的bvar,会自动更新,不要声明。
+extern bvar::Adder<int> g_read_error;
+extern bvar::LatencyRecorder g_write_latency;
+extern bvar::Adder<int> g_task_pushed;
+}  // bar
+}  // foo
+```
+
+**不要跨文件定义全局Window或PerSecond**。不同编译单元中全局变量的初始化顺序是[未定义的](https://isocpp.org/wiki/faq/ctors#static-init-order)。在foo.cpp中定义`Adder<int> foo_count`,在foo_qps.cpp中定义`PerSecond<Adder<int> > foo_qps(&foo_count);`是**错误**的做法。
+
 About thread-safety:
+
 - bvar是线程兼容的。你可以在不同的线程里操作不同的bvar。比如你可以在多个线程中同时expose或hide**不同的**bvar,它们会合理地操作需要共享的全局数据,是安全的。
 - **除了读写接口**,bvar的其他函数都是线程不安全的:比如说你不能在多个线程中同时expose或hide**同一个**bvar,这很可能会导致程序crash。一般来说,读写之外的其他接口也没有必要在多个线程中同时操作。
 
+计时可以使用butil::Timer,接口如下:
+
+```c++
+#include <butil/time.h>
+namespace butil {
+class Timer {
+public:
+    enum TimerType { STARTED };
+
+    Timer();
+
+    // butil::Timer tm(butil::Timer::STARTED);  // tm is already started after creation.
+    explicit Timer(TimerType);
+
+    // Start this timer
+    void start();
+
+    // Stop this timer
+    void stop();
+
+    // Get the elapse from start() to stop().
+    int64_t n_elapsed() const;  // in nanoseconds
+    int64_t u_elapsed() const;  // in microseconds
+    int64_t m_elapsed() const;  // in milliseconds
+    int64_t s_elapsed() const;  // in seconds
+};
+}  // namespace butil
+```
+
 # bvar::Variable
 
 Variable是所有bvar的基类,主要提供全局注册,列举,查询等功能。
@@ -72,18 +153,18 @@ int expose(const butil::StringPiece& prefix, const butil::StringPiece& name);
 下面是一些曝光bvar的例子:
 ```c++
 bvar::Adder<int> count1;
- 
+
 count1 << 10 << 20 << 30;   // values add up to 60.
 count1.expose("my_count");  // expose the variable globally
 CHECK_EQ("60", bvar::Variable::describe_exposed("count1"));
 my_count.expose("another_name_for_count1");  // expose the variable with another name
 CHECK_EQ("", bvar::Variable::describe_exposed("count1"));
 CHECK_EQ("60", bvar::Variable::describe_exposed("another_name_for_count1"));
- 
+
 bvar::Adder<int> count2("count2");  // exposed in constructor directly
 CHECK_EQ("0", bvar::Variable::describe_exposed("count2"));  // default value of Adder<int> is 0
- 
-bvar::Status<std::string> status1("count2", "hello");  // the name conflicts. if -bvar_abort_on_same_name is true, 
+
+bvar::Status<std::string> status1("count2", "hello");  // the name conflicts. if -bvar_abort_on_same_name is true,
                                                        // program aborts, otherwise a fatal log is printed.
 ```
 
@@ -108,47 +189,43 @@ int expose_as(const butil::StringPiece& prefix, const butil::StringPiece& name);
 
 # Export all variables
 
-我们提供dump_exposed函数导出进程中的所有已曝光的bvar:
+最常见的导出需求是通过HTTP接口查询和写入本地文件。前者在brpc中通过[/vars](vars.md)服务提供,后者则已实现在bvar中,默认不打开。有几种方法打开这个功能:
+
+- 用[gflags](flags.md)解析输入参数,在程序启动时加入-bvar_dump,或在brpc中也可通过[/flags](flags.md)服务在启动后动态修改。gflags的解析方法如下,在main函数处添加如下代码:
+
 ```c++
-// Implement this class to write variables into different places.
-// If dump() returns false, Variable::dump_exposed() stops and returns -1.
-class Dumper {
-public:
-    virtual bool dump(const std::string& name, const butil::StringPiece& description) = 0;
-};
- 
-// Options for Variable::dump_exposed().
-struct DumpOptions {
-    // Contructed with default options.
-    DumpOptions();
-    // If this is true, string-type values will be quoted.
-    bool quote_string;
-    // The ? in wildcards. Wildcards in URL need to use another character
-    // because ? is reserved.
-    char question_mark;
-    // Separator for white_wildcards and black_wildcards.
-    char wildcard_separator;
-    // Name matched by these wildcards (or exact names) are kept.
-    std::string white_wildcards;
-    // Name matched by these wildcards (or exact names) are skipped.
-    std::string black_wildcards;
-};
- 
-class Variable { 
-    ...
+  #include <gflags/gflags.h>
+  ...
+  int main(int argc, char* argv[]) {
+      google::ParseCommandLineFlags(&argc, &argv, true/*表示把识别的参数从argc/argv中删除*/);
+      ...
+  }
+```
+
+- 不想用gflags解析参数,希望直接在程序中默认打开,在main函数处添加如下代码:
+
+```c++
+#include <gflags/gflags.h>
+...
+int main(int argc, char* argv[]) {
+    if (google::SetCommandLineOption("bvar_dump", "true").empty()) {
+        LOG(FATAL) << "Fail to enable bvar dump";
+    }
     ...
-    // Find all exposed variables matching `white_wildcards' but
-    // `black_wildcards' and send them to `dumper'.
-    // Use default options when `options' is NULL.
-    // Return number of dumped variables, -1 on error.
-    static int dump_exposed(Dumper* dumper, const DumpOptions* options);
-};
+}
 ```
-最常见的导出需求是通过HTTP接口查询和写入本地文件。前者在brpc中通过[/vars](vars.md)服务提供,后者则已实现在bvar中,由用户选择开启。该功能由5个gflags控制,你的程序需要使用[gflags](flags.md)。
 
-![img](../images/bvar_dump_flags.png)
+dump功能由如下gflags控制:
 
-用户可在程序启动前加上对应的gflags,在brpc中也可通过[/flags](flags.md)服务在启动后动态修改某个gflag。
+| 名称                 | 默认值                     | 作用                                       |
+| ------------------ | ----------------------- | ---------------------------------------- |
+| bvar_dump          | false                   | Create a background thread dumping all bvar periodically, all bvar_dump_* flags are not effective when this flag is off |
+| bvar_dump_exclude  | ""                      | Dump bvar excluded from these wildcards(separated by comma), empty means no exclusion |
+| bvar_dump_file     | monitor/bvar.<app>.data | Dump bvar into this file                 |
+| bvar_dump_include  | ""                      | Dump bvar matching these wildcards(separated by comma), empty means including all |
+| bvar_dump_interval | 10                      | Seconds between consecutive dump         |
+| bvar_dump_prefix   | \<app\>                 | Every dumped name starts with this prefix |
+| bvar_dump_tabs     | \<check the code\>      | Dump bvar into different tabs according to the filters (seperated by semicolon), format: *(tab_name=wildcards) |
 
 当bvar_dump_file不为空时,程序会启动一个后台导出线程以bvar_dump_interval指定的间隔更新bvar_dump_file,其中包含了被bvar_dump_include匹配且不被bvar_dump_exclude匹配的所有bvar。
 
@@ -159,7 +236,7 @@ class Variable {
 导出文件为:
 
 ```
-$ cat bvar.echo_server.data                 
+$ cat bvar.echo_server.data
 rpc_server_8002_builtin_service_count : 20
 rpc_server_8002_connection_count : 1
 rpc_server_8002_nshead_service_adaptor : brpc::policy::NovaServiceAdaptor
@@ -183,6 +260,43 @@ LOG(INFO) << "Successfully set bvar_dump_include to *service*";
 请勿直接设置FLAGS_bvar_dump_file / FLAGS_bvar_dump_include / FLAGS_bvar_dump_exclude。
 一方面这些gflag类型都是std::string,直接覆盖是线程不安全的;另一方面不会触发validator(检查正确性的回调),所以也不会启动后台导出线程。
 
+用户也可以使用dump_exposed函数自定义如何导出进程中的所有已曝光的bvar:
+```c++
+// Implement this class to write variables into different places.
+// If dump() returns false, Variable::dump_exposed() stops and returns -1.
+class Dumper {
+public:
+    virtual bool dump(const std::string& name, const butil::StringPiece& description) = 0;
+};
+
+// Options for Variable::dump_exposed().
+struct DumpOptions {
+    // Contructed with default options.
+    DumpOptions();
+    // If this is true, string-type values will be quoted.
+    bool quote_string;
+    // The ? in wildcards. Wildcards in URL need to use another character
+    // because ? is reserved.
+    char question_mark;
+    // Separator for white_wildcards and black_wildcards.
+    char wildcard_separator;
+    // Name matched by these wildcards (or exact names) are kept.
+    std::string white_wildcards;
+    // Name matched by these wildcards (or exact names) are skipped.
+    std::string black_wildcards;
+};
+
+class Variable {
+    ...
+    ...
+    // Find all exposed variables matching `white_wildcards' but
+    // `black_wildcards' and send them to `dumper'.
+    // Use default options when `options' is NULL.
+    // Return number of dumped variables, -1 on error.
+    static int dump_exposed(Dumper* dumper, const DumpOptions* options);
+};
+```
+
 # bvar::Reducer
 
 Reducer用二元运算符把多个值合并为一个值,运算符需满足结合律,交换律,没有副作用。只有满足这三点,我们才能确保合并的结果不受线程私有数据如何分布的影响。像减法就不满足结合律和交换律,它无法作为此处的运算符。
@@ -207,14 +321,14 @@ reducer << e1 << e2 << e3的作用等价于reducer = e1 op e2 op e3。
 bvar::Adder<int> value;
 value<< 1 << 2 << 3 << -4;
 CHECK_EQ(2, value.get_value());
- 
+
 bvar::Adder<double> fp_value;  // 可能有warning
 fp_value << 1.0 << 2.0 << 3.0 << -4.0;
 CHECK_DOUBLE_EQ(2.0, fp_value.get_value());
 ```
 Adder<>可用于非基本类型,对应的类型至少要重载`T operator+(T, T)`。一个已经存在的例子是std::string,下面的代码会把string拼接起来:
 ```c++
-// This is just proof-of-concept, don't use it for production code because it makes a 
+// This is just proof-of-concept, don't use it for production code because it makes a
 // bunch of temporary strings which is not efficient, use std::ostringstream instead.
 bvar::Adder<std::string> concater;
 std::string str1 = "world";
@@ -287,7 +401,7 @@ class Window : public Variable;
 获得之前一段时间内平均每秒的统计值。它和Window基本相同,除了返回值会除以时间窗口之外。
 ```c++
 bvar::Adder<int> sum;
- 
+
 // sum_per_second.get_value()是sum在之前60秒内*平均每秒*的累加值,省略最后一个时间窗口的话默认为bvar_dump_interval。
 bvar::PerSecond<bvar::Adder<int> > sum_per_second(&sum, 60);
 ```
@@ -296,10 +410,10 @@ bvar::PerSecond<bvar::Adder<int> > sum_per_second(&sum, 60);
 上面的代码中没有Maxer,因为一段时间内的最大值除以时间窗口是没有意义的。
 ```c++
 bvar::Maxer<int> max_value;
- 
+
 // 错误!最大值除以时间是没有意义的
 bvar::PerSecond<bvar::Maxer<int> > max_value_per_second_wrong(&max_value);
- 
+
 // 正确,把Window的时间窗口设为1秒才是正确的做法
 bvar::Window<bvar::Maxer<int> > max_value_per_second(&max_value, 1);
 ```
@@ -322,7 +436,7 @@ Window的优点是精确值,适合一些比较小的量,比如“上一分
 //
 //   bvar::Status<int> foo_count2;
 //   foo_count2.set_value(17);
-//  
+//
 //   bvar::Status<int> foo_count3("my_value", 17);
 //
 // Notice that Tp needs to be std::string or acceptable by boost::atomic<Tp>.
@@ -360,7 +474,7 @@ static void get_username(std::ostream& os, void*) {
         os << buf;
     } else {
         os << "unknown";
-    }  
+    }
 }
 PassiveStatus<std::string> g_username("process_username", get_username, NULL);
 ```
@@ -370,12 +484,12 @@ PassiveStatus<std::string> g_username("process_username", get_username, NULL);
 Expose important gflags as bvar so that they're monitored (in noah).
 ```c++
 DEFINE_int32(my_flag_that_matters, 8, "...");
- 
+
 // Expose the gflag as *same-named* bvar so that it's monitored (in noah).
 static bvar::GFlag s_gflag_my_flag_that_matters("my_flag_that_matters");
 //                                                ^
 //                                            the gflag name
- 
+
 // Expose the gflag as a bvar named "foo_bar_my_flag_that_matters".
 static bvar::GFlag s_gflag_my_flag_that_matters_with_prefix("foo_bar", "my_flag_that_matters");
 ```

+ 52 - 186
docs/en/bvar.md

@@ -1,198 +1,76 @@
-# What is bvar?
-
-[bvar](https://github.com/brpc/brpc/tree/master/src/bvar/) is a counting utility designed for multiple threaded applications. It stores data in thread local storage(TLS) to avoid costly cache bouncing caused by concurrent modification. It is much faster than UbMonitor(a legacy counting utility used inside Baidu) and atomic operation in highly contended scenarios. bvar is builtin within brpc, through [/vars](http://brpc.baidu.com:8765/vars) you can access all the exposed bvars inside the server, or a single one specified by [/vars/`VARNAME`](http://brpc.baidu.com:8765/vars/rpc_socket_count). Check out [bvar](https://github.com/brpc/brpc/blob/master/docs/cn/bvar.md) if you'd like add some bvars for you own services. bvar is widely used inside brpc to calculate indicators of internal status. It is **almost free** in most scenarios to collect data. If you are looking for a utility to collect and show internal status of your application, try bvar at the first time. However bvar is designed for general purpose counters, the read process of a single bvar have to combines all the TLS data from the threads that the very bvar has been written, which is very slow compared to the write process and atomic operations.
+[中文版](../cn/bvar.md)
 
-## What is "cache bouncing"
+# What is bvar?
 
-为了以较低的成本大幅提高性能,现代CPU都有[cache](https://en.wikipedia.org/wiki/CPU_cache)。百度内常见的Intel E5-2620拥有32K的L1 dcache和icache,256K的L2 cache和15M的L3 cache。其中L1和L2cache为每个核独有,L3则所有核共享。为了保证所有的核看到正确的内存数据,一个核在写入自己的L1 cache后,CPU会执行[Cache一致性](https://en.wikipedia.org/wiki/Cache_coherence)算法把对应的[cacheline](https://en.wikipedia.org/wiki/CPU_cache#Cache_entries)(一般是64字节)同步到其他核。这个过程并不很快,是微秒级的,相比之下写入L1 cache只需要若干纳秒。当很多线程在频繁修改某个字段时,这个字段所在的cacheline被不停地同步到不同的核上,就像在核间弹来弹去,这个现象就叫做cache bouncing。由于实现cache一致性往往有硬件锁,cache bouncing是一种隐式的的全局竞争。关于竞争请查阅[atomic instructions](atomic_instructions.md)。
+[bvar](https://github.com/brpc/brpc/tree/master/src/bvar/) is a set of counters to record and view miscellaneous statistics conveniently in multi-threaded applications. The implementation reduces cache bouncing by storing data in thread local storage(TLS), being much faster than UbMonitor(a legacy counting library inside Baidu) and even atomic operations in highly contended scenarios. brpc integrates bvar by default, namely all exposed bvars in a server are accessible through [/vars](http://brpc.baidu.com:8765/vars), and a single bvar is addressable by [/vars/VARNAME](http://brpc.baidu.com:8765/vars/rpc_socket_count). Read [vars](vars.md) to know how to query them in brpc servers. brpc extensively use bvar to expose internal status. If you are looking for an utility to collect and display metrics of your application, consider bvar in the first place. bvar definitely can't replace all counters, essentially it moves contentions occurred during write to read: which needs to combine all data written by all threads and becomes much slower than an ordinary read. If read and write on the counter are both frequent or decisions need to be made based on latest values, you should not use bvar.
 
-cache bouncing使访问频繁修改的变量的开销陡增,甚至还会使访问同一个cacheline中不常修改的变量也变慢,这个现象是[false sharing](https://en.wikipedia.org/wiki/False_sharing)。按cacheline对齐能避免false sharing,但在某些情况下,我们甚至还能避免修改“必须”修改的变量。bvar便是这样一个例子,当很多线程都在累加一个计数器时,我们让每个线程累加私有的变量而不参与全局竞争,在读取时我们累加所有线程的私有变量。虽然读比之前慢多了,但由于这类计数器的读多为低频展现,慢点无所谓。而写就快多了,从微秒到纳秒,几百倍的差距使得用户可以无所顾忌地使用bvar,这便是我们设计bvar的目的。
+To understand how bvar works, read [explanations on cacheline](atomic_instructions.md#cacheline) first, in which the mentioned counter example is just bvar. When many threads are modifying a counter, each thread writes into its own area without joining the global contention and all private data are combined at read, which is much slower than an ordinary one, but OK for low-frequency logging or display. The much faster and very-little-overhead write enables users to monitor systems from all aspects without worrying about hurting performance. This is the purpose that we designed bvar.
 
-下图是bvar和原子变量,静态UbMonitor,动态UbMonitor的性能对比。可以看到bvar的耗时基本和线程数无关,一直保持在极低的水平(~20纳秒)。而动态UbMonitor在24核时每次累加的耗时达7微秒,这意味着使用300次bvar的开销才抵得上使用一次动态UbMonitor变量。
+Following graph compares overhead of bvar, atomics, static UbMonitor, dynamic UbMonitor when they're accessed by multiple threads simultaneously. We can see that overhead of bvar is not related to number of threads basically, and being constantly low (~20 nanoseconds). As a contrast, dynamic UbMonitor costs 7 microseconds on each operation when there're 24 threads, which is the overhead of using the bvar for 300 times.
 
 ![img](../images/bvar_perf.png)
 
-# 3.用noah监控bvar
-
-![img](../images/bvar_flow.png)
-
-- bvar 将被监控的项目定期打入文件:monitor/bvar.<app>.data。
-- noah 自动收集文件并生成图像。
-- App只需要注册相应的监控项即可。
-
-每个App必须做到的最低监控要求如下:
-
-- **Error**: 要求系统中每个可能出现的error都有监控 
-- **Latency**: 系统对外的每个rpc请求的latency;  系统依赖的每个后台的每个request的latency;  (注: 相应的max_latency,系统框架会自动生成)
-- **QPS**: 系统对外的每个request的QPS; 系统依赖的每个后台的每个request的QPS.
-
-后面会在完善monitoring框架的过程中要求每个App添加新的必需字段。
-
-# 4.RD要干的事情
-
-## 1.定义bvar
-
-```c++
-#include <bvar/bvar.h>
- 
-namespace foo {
-namespace bar {
- 
-// bvar::Adder<T>用于累加,下面定义了一个统计read error总数的Adder。
-bvar::Adder<int> g_read_error;
-// 把bvar::Window套在其他bvar上就可以获得时间窗口内的值。
-bvar::Window<bvar::Adder<int> > g_read_error_minute("foo_bar", "read_error", &g_read_error, 60);
-//                                                     ^          ^                         ^
-//                                                    前缀       监控项名称                  60秒,忽略则为10秒
- 
-// bvar::LatencyRecorder是一个复合变量,可以统计:总量、qps、平均延时,延时分位值,最大延时。
-bvar::LatencyRecorder g_write_latency(“foo_bar", "write”);
-//                                      ^          ^
-//                                     前缀       监控项,别加latency!LatencyRecorder包含多个bvar,它们会加上各自的后缀,比如write_qps, write_latency等等。
- 
-// 定义一个统计“已推入task”个数的变量。
-bvar::Adder<int> g_task_pushed("foo_bar", "task_pushed");
-// 把bvar::PerSecond套在其他bvar上可以获得时间窗口内*平均每秒*的值,这里是每秒内推入task的个数。
-bvar::PerSecond<bvar::Adder<int> > g_task_pushed_second("foo_bar", "task_pushed_second", &g_task_pushed);
-//       ^                                                                                             ^
-//    和Window不同,PerSecond会除以时间窗口的大小.                                   时间窗口是最后一个参数,这里没填,就是默认10秒。
- 
-}  // bar
-}  // foo
-```
-
-在应用的地方:
-
-```c++
-// 碰到read error
-foo::bar::g_read_error << 1;
- 
-// write_latency是23ms
-foo::bar::g_write_latency << 23;
- 
-// 推入了1个task
-foo::bar::g_task_pushed << 1;
-```
-
-注意Window<>和PerSecond<>都是衍生变量,会自动更新,你不用给它们推值。
-
->  你当然也可以把bvar作为成员变量或局部变量,请阅读[bvar-c++](bvar_c++.md)。
+# Monitor bvar
 
-**确认变量名是全局唯一的!**否则会曝光失败,如果-bvar_abort_on_same_name为true,程序会直接abort。
+Following graph demonstrates how bvars in applications are monitored.
 
-程序中有来自各种模块不同的bvar,为避免重名,建议如此命名:**模块_类名_指标**
-
-- **模块**一般是程序名,可以加上产品线的缩写,比如inf_ds,ecom_retrbs等等。
-- **类名**一般是类名或函数名,比如storage_manager, file_transfer, rank_stage1等等。
-- **指标**一般是count,qps,latency这类。
-
-一些正确的命名如下:
+![img](../images/bvar_flow.png)
 
-```
-iobuf_block_count : 29                          # 模块=iobuf   类名=block  指标=count
-iobuf_block_memory : 237568                     # 模块=iobuf   类名=block  指标=memory
-process_memory_resident : 34709504              # 模块=process 类名=memory 指标=resident
-process_memory_shared : 6844416                 # 模块=process 类名=memory 指标=shared
-rpc_channel_connection_count : 0                # 模块=rpc     类名=channel_connection  指标=count
-rpc_controller_count : 1                        # 模块=rpc     类名=controller 指标=count
-rpc_socket_count : 6                            # 模块=rpc     类名=socket     指标=count
-```
+In which:
 
-目前bvar会做名字归一化,不管你打入的是foo::BarNum, foo.bar.num, foo bar num , foo-bar-num,最后都是foo_bar_num。
+- APP means user's application which uses bvar API to record all sorts of metrics.
+- bvar periodically prints exposed bvars into a file (represented by "log") under directory $PWD/monitor/ . The "log" is different from an ordinary log that it's overwritten by newer content rather than concatenated.
+- The monitoring system collects dumped files (represented by noah), aggregates the data inside and plots curves.
 
-关于指标:
+The APP is recommended to meet following requirements:
 
-- 个数以_count为后缀,比如request_count, error_count。
-- 每秒的个数以_second为后缀,比如request_second, process_inblocks_second,已经足够明确,不用写成_count_second或_per_second。
-- 每分钟的个数以_minute为后缀,比如request_minute, process_inblocks_minute
+- **Error**: Number of every kind of error that may occur. 
+- **Latency**: latencies(average/percentile) of each public RPC interface, latencies of each RPC to back-end servers.
+- **QPS**: QPS of each public RPC interface, QPS of each RPC to back-end servers.
 
-如果需要使用定义在另一个文件中的计数器,需要在头文件中声明对应的变量。
+Read [Quick introduction](bvar_c++.md#quick-introduction) to know how to add bvar in C++.  bvar already provides stats of many process-level and system-level variables by default, which are prefixed with `process_` and `system_`, such as:
 
-```c++
-namespace foo {
-namespace bar {
-// 注意g_read_error_minute和g_task_pushed_per_second都是衍生的bvar,会自动更新,不要声明。
-extern bvar::Adder<int> g_read_error;
-extern bvar::LatencyRecorder g_write_latency;
-extern bvar::Adder<int> g_task_pushed;
-}  // bar
-}  // foo
 ```
-
-**不要跨文件定义全局Window或PerSecond**
-
-不同编译单元中全局变量的初始化顺序是[未定义的](https://isocpp.org/wiki/faq/ctors#static-init-order)。在foo.cpp中定义`Adder<int> foo_count`,在foo_qps.cpp中定义`PerSecond<Adder<int> > foo_qps(&foo_count);`是**错误**的做法。
-
-计时可以使用butil::Timer,接口如下:
-
-```c++
-#include <butil/time.h>
-namespace butil {
-class Timer {
-public:
-    enum TimerType { STARTED };
-     
-    Timer();
- 
-    // butil::Timer tm(butil::Timer::STARTED);  // tm is already started after creation.
-    explicit Timer(TimerType);
- 
-    // Start this timer
-    void start();
-     
-    // Stop this timer
-    void stop();
- 
-    // Get the elapse from start() to stop().
-    int64_t n_elapsed() const;  // in nanoseconds
-    int64_t u_elapsed() const;  // in microseconds
-    int64_t m_elapsed() const;  // in milliseconds
-    int64_t s_elapsed() const;  // in seconds
-};
-}  // namespace butil
+process_context_switches_involuntary_second : 14
+process_context_switches_voluntary_second : 15760
+process_cpu_usage : 0.428
+process_cpu_usage_system : 0.142
+process_cpu_usage_user : 0.286
+process_disk_read_bytes_second : 0
+process_disk_write_bytes_second : 260902
+process_faults_major : 256
+process_faults_minor_second : 14
+process_memory_resident : 392744960
+system_core_count : 12
+system_loadavg_15m : 0.040
+system_loadavg_1m : 0.000
+system_loadavg_5m : 0.020
 ```
 
-## 2.打开bvar的dump功能
+and miscellaneous bvars used by brpc itself:
 
-bvar可以定期把进程内所有的bvar打印入一个文件中,默认不打开。有几种方法打开这个功能:
-
-- 用[gflags](flags.md)解析输入参数,在程序启动时加入-bvar_dump。gflags的解析方法如下,在main函数处添加如下代码:
-```c++
-  #include <gflags/gflags.h>
-  ...
-  int main(int argc, char* argv[]) {
-      google::ParseCommandLineFlags(&argc, &argv, true/*表示把识别的参数从argc/argv中删除*/);
-      ...
-  }
 ```
-- 不想用gflags解析参数,希望直接在程序中默认打开,在main函数处添加如下代码:
-```c++
-#include <gflags/gflags.h>
-...
-int main(int argc, char* argv[]) {
-    if (google::SetCommandLineOption("bvar_dump", "true").empty()) {
-        LOG(FATAL) << "Fail to enable bvar dump";
-    }
-    ...
-}
+bthread_switch_second : 20422
+bthread_timer_scheduled_second : 4
+bthread_timer_triggered_second : 4
+bthread_timer_usage : 2.64987e-05
+bthread_worker_count : 13
+bthread_worker_usage : 1.33733
+bvar_collector_dump_second : 0
+bvar_collector_dump_thread_usage : 0.000307385
+bvar_collector_grab_second : 0
+bvar_collector_grab_thread_usage : 1.9699e-05
+bvar_collector_pending_samples : 0
+bvar_dump_interval : 10
+bvar_revision : "34975"
+bvar_sampler_collector_usage : 0.00106495
+iobuf_block_count : 89
+iobuf_block_count_hit_tls_threshold : 0
+iobuf_block_memory : 729088
+iobuf_newbigview_second : 10
 ```
 
-bvar的dump功能由如下参数控制,产品线根据自己的需求调节,需要提醒的是noah要求bvar_dump_file的后缀名是.data,请勿改成其他后缀。更具体的功能描述请阅读[Export all variables](bvar_c++.md#export-all-variables)。
-
-| 名称                      | 默认值                     | 作用                                       |
-| ----------------------- | ----------------------- | ---------------------------------------- |
-| bvar_abort_on_same_name | false                   | Abort when names of bvar are same        |
-| bvar_dump               | false                   | Create a background thread dumping all bvar periodically, all bvar_dump_* flags are not effective when this flag is off |
-| bvar_dump_exclude       | ""                      | Dump bvar excluded from these wildcards(separated by comma), empty means no exclusion |
-| bvar_dump_file          | monitor/bvar.<app>.data | Dump bvar into this file                 |
-| bvar_dump_include       | ""                      | Dump bvar matching these wildcards(separated by comma), empty means including all |
-| bvar_dump_interval      | 10                      | Seconds between consecutive dump         |
-| bvar_dump_prefix        | <app>                   | Every dumped name starts with this prefix |
-| bvar_dump_tabs          | 见代码                     | Dump bvar into different tabs according to the filters (seperated by semicolon), format: *(tab_name=wildcards) |
-
-## 3.编译并重启应用程序
-
-检查monitor/bvar.<app>.data是否存在:
+Turn on [dump feature](bvar_c++.md#export-all-variables) of bvar to export all exposed bvars to files, which are formatted just like above texts: each line is a pair of "name" and "value". Check if there're data under $PWD/monitor/ after enabling dump, for example:
 
 ```
 $ ls monitor/
@@ -206,20 +84,8 @@ process_time_user : 0.741887
 process_username : "gejun"
 ```
 
-## 4.打开[noah](http://noah.baidu.com/)
-
-搜索监控节点:
-
-![img](../images/bvar_noah1.png)
-
- 
-
-点击“文件”tab,勾选要查看的统计量,bvar已经统计了进程级的很多参数,大都以process开头。
+The monitoring system should combine data on every single machine periodically and provide on-demand queries. Take the "noah" system inside Baidu as an example, variables defined by bvar appear as metrics in noah, which can be checked by users to view historical curves.
 
 ![img](../images/bvar_noah2.png)
 
- 
-
-查看趋势图:
-
 ![img](../images/bvar_noah3.png)