刚研究了log4r的使用,就想着也不是什么大包,自己也没写r项目的经验,看看源码学习一下吧。然后在appender里看file_appender的时候,看到了一个force函数。 本能的去Rstudio上查了一下文档,因为软件看文档比终端好一点。然后发现这个不简单啊。 幸好之前写过python对这种坑机制有了解,感觉上R与Python很多地方相似。 安装文档说明,R函数的参数在定义的闭包中使用的时候,比如在循环和apply函数的调用,会导致从变量作用域中获取这些参数变量,也就是所谓的惰性求值。虽然我现在看的文档中还没有涉及变量作用域的问题,感觉在R中应该和在Python中是差不多的,循环体是不具有变量作用范围的保证的。而force函数能保证这个参数被执行求值,从而保证闭包里作用域里的变量的值的变化。 ?force文档里有个例子很好的说明了这一点。

Continue

看log4r的名字就感觉跟log4j有渊源啊,其中还要好多个日志包,最后决定log4r和logging里边一个,logging是仿的python日志模块的包,但是我最后没选这个,主要是因为接口设计的不太好,就选择了log4r。 安装包 install.packages("log4r") 使用 library(log4r) logger <- logger("INFO", appenders = file_appender("log/base.log")) info(logger, "info") debug(logger, "debug") error(logger, "error") 使用的时候有几个点需要注意, 我目前安装最新的版本是 0.3.2,但是?log4r的文档版本是0.2,然后我用那个文档里的老接口和github上的新接口混用,导致日志文件没更新成功。后来才意识到文档不对。 日志文件在输出第一条日志的时候才会创建。

Continue

找了点关于python和r语言的对比文章看了看,总结了下边几条。 R 统计模型新 可视化,动态报告稍微领先 统计,数据分析,领域专用 Python 效率领先 语言稳定规范 数据清洗方便 工程开发,领域广 我刚开始学习R语言,还没有太多体会,不过从语言层面,感觉R语言的语法分析系统都不完善,不知道为啥函数参数的缺少只能运行时知道,不知道有没有别的作用。

Continue

第一步添加需要的密钥: sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys E298A3A825C0D65DFD57CBB651716619E084DAB9 第二步,选择安装镜像网站,添加软件源 sudo add-apt-repository "deb https://mirrors.tuna.tsinghua.edu.cn/CRAN/bin/linux/ubuntu $(lsb_release -cs)-cran40/" 第三步,安装R语言包 sudo apt install r-base 第四步,验证: 终端输入R,启动交互模式。 print("hello world;")

Continue

neutron-dhcp-agent服务启动命令 /usr/bin/neutron-dhcp-agent --config-file /etc/neutron/neutron.conf --config-file /etc/neutron/dhcp_agent.ini 调用了neutron_service.Service.create()来创建的server。Service(n_rpc.Service)继承自neutron_lib.rpc,这次得去看看了,感觉应该也没啥看的。 Service(service.Service)继承自oslo_service.service.Service,里边只是个定义类接口,里边还有个线程池self.tg。 Service传入的manager为neutron.agent.dhcp.agent.DhcpAgentWithStateReport, 然后实例化,最后初始化父类。 DhcpAgentWithStateReport继承自DhcpAgent,再继承自manager.Manager, 然后继承自periodic_task.PeriodicTasks,看起来是个定时任务。先不细看了,按初始化流程走。 在DhcpAgent里dhcp_driver_cls存的dhcp_driver的类,文档安装使用的neutron.agent.linux.dhcp.Dnsmasq,这个先记录,后边看。DhcpPluginApi这个是定义了dhcp rpc的client端,从注释看server端在neutron.api.rpc.handlers.dhcp_rpc.DhcpRpcCallback,具体还不知道为啥这么搞,先记录。看接口内容感觉就是网络的几个常规接口。DhcpAgentWithStateReport里的初始化,也是初始化了一个心跳进行状态上报。 launch方法传入初始化完的server, 启动一个进程,restart方法mutate_config_files on SIGHUP,这个先记录。workers为1走的是ServiceLauncher,继承自Launcher,然后初始化Services,这个也有个线程池。最后初始化SignalHandler。.wait()里边看注释是等待重启服务的信号. 看了一下DhcpRpcCallback,这个看不太懂了,其中的plugin的初始化不太清楚。状态不行,不看这个了。 Dnsmasq继承自DhcpLocalProcess, 再继承自DhcpBase,初始化了一个DeviceManager是与之前的rpc调用相关的没细看。 是通过call_driver来实例化的,但是看起来都是用到的时候才初始化的,看初始化方法和调用,基本都是传入network和action然后,初始化完成后,执行action的方法,所以应该是针对不同网络进行实例化并操作。 主要逻辑都是在Dnsmasq里定义的,existing_dhcp_networks看注释是返回dhcp网络,直接从配置文件目录读取,这个也说明创建网络会在这个目录创建配置文件。_build_cmdline_callback看样子是启动dnsmasq的命令生成的地方。spawn_process启动dnsmasq的进程,调用_get_process_manager,这个是在DhcpLocalProcess, 然后还加入监控。启动是先使用external_process.ProcessManager初始化ProcessManager类,在调用enable()通过传入的cmd_callback,也就是_build_cmdline_callback那个生成cmd,启动服务进程. 随便看了一下,流程连不起来了。这里代码太绕了,不知道怎么个调用的。感觉流程最好搭个环境,打打日志比较好。 这个看的有点失败,获得了很少的东西。

Continue

供应商网络依赖的几个服务: neutron-server.service neutron-linuxbridge-agent.service neutron-dhcp-agent.service neutron-metadata-agent.service 自服务网络除了上边的几个服务外还依赖 neutron-l3-agent.service。 从这里也能看出,自服务网络支持三层模拟,这也是主要区别。自服务可以实现的功能就比较多,比如vxlan,FWaaS,LBaaS等。 先看看neutron-metadata-agent服务,这个服务主要功能是实例启动的时候为cloud-init获取metadata的时候转发用的。因为实例所在网络跟nova-api网络肯定不通的,所以需要一个中间转发,起这个作用的就是neutron-metadata-agent服务。看网上从实例到neutron-metadata-agent服务,中间还经过了haproxy,haproxy是l3-agent或者dhcp-agent启动的,这个也许也是分不同网络类型正好不同(但是我看官方安装文档,两种网络方式配置都是使用dhcp来启动的metadata服务, 默认是通过l3-agent启动)。然后由haproxy转发给neutron-metadata-aget,然后neutron-metadata-agent转发给nova-api-metadata服务。 服务启动命令为/usr/bin/neutron-metadata-agent --config-file /etc/neutron/neutron.conf --config-file /etc/neutron/metadata_agent.ini 很多小模块过掉了,必须要看的时候再梳理吧。 主要是启动一个UnixDomainMetadataProxy。然后调用了agent_utils.UnixDomainWSGIServer启动,在文件neutron/agent/linux/utils.py 里,这个文件定义了好多不同类型的agnet。不得不说Openstack模块封装的太好了,接口封装的好,看起来就基本靠猜就可以了。看代码是启动了一个wsgi服务,handler为MetadataProxyHandler。然后调用_init_state_reporting,这个我看可以设置心跳,主要上报了监听的地址端口之类的信息,感觉也就监控或者排查问题有用。有个agent_rpc/context,也不知道往哪里上报的,先记住不看了。 MetadataProxyHandler里也有一个rpc(MetadataPluginAPI)/context,感觉这里不知道不行,靠猜也许行。 精简了就两行代码 instance_id, tenant_id = self._get_instance_and_tenant_id(req) return self._proxy_request(instance_id, tenant_id, req) 第一行从header里获取请求的信息,得到实例id,我看还做了rpc调用,具体获取啥先不看了。 第二行是转发请求,把实例id和租户id,通过header传递,用requests模块发送请求给nova。返回内容无误后,返回给wsgi,请求就结束了。 好多细节没看,不过不影响流程,整个流程就是一个转发,涉及配置和验证,还有rpc没看。整体没啥复杂流程,就先这样了。

Continue

linux内核使用许多likely和unlikely宏,这两个宏的内容为: #define likely(x) (__builtin_expect(!!(x), 1)) #define unlikely(x) (__builtin_expect(!!(x), 0)) 是用来告诉编译器,当前判断条件是否常用或者不常用。编译器根据提示,生成的二进制的代码流程会有相应改变,以达到让cpu尽可能的顺序执行的目的。gcc官方文档里说,使用-fprofile-arcs来进行实际的性能测试,说程序员对自己的程序的预测一般都是错误的。 然后我搜到是用gcov去做,使用了一下确实很直观。编译的时候,参数加上“-fprofile-arcs -ftest-coverage”。然后运行会生成 .gcda .gcno文件。用gcov source.c会生成相应代码的.gcov文件。vim编辑这些文件,就能看到源码形式的,每一行都执行了多少次。 这里说个疑问,前边说预测不准确,我当时看到那里还说真的是不准确。我之前就感觉明明会大概率走这个分支,然后加上提示后,速度却变慢了。但是当我用gcov去做完统计之后,发现确实很大很大概率走的那个分支,但是不知道为啥会变慢。我O2开反汇编发现内容是一样的,也可能是我测试的时候没开O2也许有不同。

Continue

想到了三种方式,但是结果跟自己预想的不太一样,具体也没有细想。 第一种方式就是char*类型的strcmp比较,这种比较容易想到。但是效率中等。 第二种方式是使用类型__uint128_t,这个是gcc给出的类型,应该可移植性不太高,这种方式速度最快,char*用的时间是差不多1.5倍。 第三种方式是使用sse,并行试试,_mm_xor_si128和_mm_test_all_zeros进行比较,但是速度是最慢的,可能涉及类型的转换,看指令也非常的多。我还以为会是最快的。 除了第三种慢的意外,我第一种还对比了第一个char就不相等的情况,这种特殊情况我以为第一种会快,没想到还是慢,跟之前速度差不多。 我特意看了一下第二种方式的实现方式:通过两个xor,分别对比8字节,然后通过or操作把两个对比结果合并,然后通过test指令判断合并的结果是否为0,test指令会修改ZF标志位,最后通过jne判断ZF标志位进行跳转。

Continue

最近看c底层相关的指令,操作,编码,多线程,磁盘文件读写相关的看的比较多。不经理永远不知道这里边的东西有多少,还有很多不易理解的。由于时间仓猝,本来想好好整理一下,作为一个总结,但还是决定只是记一个笔记,而且内容来自搜索引擎,有一部分不是看的官方文档解释等,可能不正确,而且没有写全。以后无聊的时候想起来再搞吧(感觉用不到以后不会再搞了) 先说为什么写这个文章,我碰到一个问题。多线程读写文件,然后单线程读取,线上后读取结果不一致。之前有过分步的测试,是没有问题的。 然后我排查原因,先从读入手,发现数据有问题,然后转到看写。写先通过单独打印几条日志,和对写入的数据错误判断,在某个函数。这时候,我打开编译的debug模式,就是能打印更详细的日志,然后发现问题没了,数据正常了。然后我就玩起了编译参数。发现打印日志就没事,不打印就出问题了,然后我就想到是编译器给我优化过头了,那时候还是开始02的优化。因为那个函数不涉及多线程的操作。然后我去掉 -DNDEBUG参数,然后用assert()判断出是哪一行出了问题。然后实验了几个方法,发现都是可以的,于是网上搜索资料,大体比较了一下这集中方式,选了一种。 1,把变量声明称volatile,主要功能是,编译器不会优化掉这个变量,然后变量的值不会存到寄存器,保证从内存中读取。看我的程序具体也看不出来,除非反汇编代码看看。也懒得看了。 2,__sync_synchronize (...),This builtin issues a full memory barrier. 内存栅栏,这个是个硬件栅栏,效率相比下边不太高。 Memory Barrier (Memory Fence)分为两种,一种软件的,只对编译器起作用,一种是硬件的,看文章说是总线信号控制的,这个计算机组成结构没学好,也懒得细看。 下面几种跟linux内核中的相对应,没看源码,不知道对不对 #define mb() __asm__ __volatile__("mfence":::"memory")这个跟__sync_synchronize一个作用,是硬件的栅栏。 #define rmb() __asm__ __volatile__("lfence":::"memory")不允许将barrier之前的内存读取指令移到barrier之后 (release barrier) #define wmb() __asm__ __volatile__("sfence":::"memory")不允许将barrier之后的内存读取指令移到barrier之前(acquire barrier) #define barrier() __asm__ __volatile__("":::"memory") barrier()是软栅栏, rmb wmb好像分单处理器和多处理器不一样,单处理器为软栅栏。 期间写的程序也有好多cas操作,atomic原子操作,编译器主要用的gcc的,然后直接用的gcc的,官方地址: https://gcc.gnu.org/onlinedocs/gcc/_005f_005fatomic-Builtins.html

Continue

perf工具应该都听说过,我也试了一把,感觉很好很强大。 先使用sudo perf stat ./a.out命令,查看一下性能统计信息。结果展示为: Performance counter stats for './a.out': 2,740.62 msec task-clock # 3.903 CPUs utilized 39 context-switches # 0.014 K/sec 2 cpu-migrations # 0.001 K/sec 128,974 page-faults # 0.047 M/sec 10,394,863,898 cycles # 3.793 GHz 14,055,693,753 instructions # 1.35 insn per cycle 2,865,438,627 branches # 1045.545 M/sec 17,411,072 branch-misses # 0.61% of all branches 0.702237190 seconds time elapsed 2.533414000 seconds user 0.208445000 seconds sys 因为程序基本跑内存的,这里cpu使用比较高,就是开始的时候读了文件。四个线程高达3.9的使用率 context-switches 是进程上下文切换,这个不知道为啥,可能我系统跑的东西太多了。 CPU-migrations 这个是cpu迁移次数,没办法避免吧,程序跑的多,或者需要cpu绑定。 page-faults 这个感觉有点高,不知道为啥,我这还没涉及文件读写相关,已经这么高了。 branch-misses 这个感觉也比较高,看看使用分支预测改善一下,能不能好。这个需要对比一下。 获取程序的cpu运行时间统计,sudo perf record -e cpu-clock ./a.out,命令会生成perf.data文件,使用perf report查看报告 下边还有个直接展示的高级货,叫做火焰图。用perl写的,感觉好强大,以后可以稍微研究一下怎么生成的。 需要先clone下代码来:git clone --depth=1 https://github.com/brendangregg/FlameGraph.git 上一步收集信息的时候必须加-g参数,用来收集堆栈信息,不然后边生成图像的时候会报错。 sudo perf record -g ./a.out 数据解析 sudo perf script -i perf.data &> perf.unfold 数据折叠 ./stackcollapse-perf.pl perf.unfold &> perf.folded 图像生成 ./flamegraph.pl perf.folded > perf.svg 生成的图片用chrome查看是有效果的,我用ubuntu自带的图片查看软件,发现不能交互。 我发现对于递归程序,展示的不太友好,虽然能从调用栈中获取到递归深度,但是调用的函数差不多都放到递归最里边了,可能是取样的问题嘛,,

Continue