找了点关于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

Callgrind是valgrind的一个工具,能够分析程序运行效率,帮助找到程序瓶颈。 命令tool知道使用的valgrind的工具, valgrind --tool=callgrind ./a.out 运行完之后会生成一个callgrind.out.PID文件,然后执行下面命令进行分析 callgrind_annotate callgrind.out.PID 这个命令能够展示每个调用函数对应的执行指令的次数,展示已经排序,可以优先优化最顶部的函数。 cachegrind也是valgrind的一个工具,主要分析内存使用情况的,比如cpu cache的使用等。 简单使用命令: valgrind --tool=cachegrind ./a.out ==12810== ==12810== I refs: 13,413,053,205 ==12810== I1 misses: 3,851 ==12810== LLi misses: 3,552 ==12810== I1 miss rate: 0.00% ==12810== LLi miss rate: 0.00% ==12810== ==12810== D refs: 4,991,204,111 (3,140,940,594 rd + 1,850,263,517 wr) ==12810== D1 misses: 49,675,548 ( 38,504,518 rd + 11,171,030 wr) ==12810== LLd misses: 29,710,307 ( 19,488,129 rd + 10,222,178 wr) ==12810== D1 miss rate: 1.0% ( 1.2% + 0.6% ) ==12810== LLd miss rate: 0.6% ( 0.6% + 0.6% ) ==12810== ==12810== LL refs: 49,679,399 ( 38,508,369 rd + 11,171,030 wr) ==12810== LL misses: 29,713,859 ( 19,491,681 rd + 10,222,178 wr) ==12810== LL miss rate: 0.2% ( 0.1% + 0.6% ) 看着好像程序我的程序允许的比预期的缓存命中高很多,看官方文档说的。On a modern machine, an L1 miss will typically cost around 10 cycles, an LL miss can cost as much as 200 cycles, and a mispredicted branch costs in the region of 10 to 30 cycles. Detailed cache and branch profiling can be very useful for understanding how your program interacts with the machine and thus how to make it faster.现代机器,L1缓存丢失通常花费10个cpu周期,LL丢失花费200个周期,分支预测错误花费10-30个周期,所以这部分性能分析很重要啊。 LL指的是最后一级的cpu缓存,许多cpu架构可能有多级缓存,L1和LL具有代表性,所以只分析了这两种。 程序还是会生成一个cachegrind.out.PID文件,同样可以具体分析每个函数的内存使用情况 cg_annotate cachegrind.out.12810 这俩工具在官方手册上,每个一章进行介绍,具体也没研究,先初步了解一下

Continue

Valgrind可以模拟cpu执行你的程序,然后给出内存使用或者程序错误信息。之前只使用过gdb来调试程序逻辑错误,现在准备多看几个,包括性能方面的调试。 安装直接使用的apt源安装的,使用也比较简单。直接valgrind ./a.out允许程序,运行过程中会给出程序建议。 这个程序我有一个一百万长度的uint64的数组,提示了"Invalid write of size 8"的错误,Warning: client switching stacks? SP change: 0x6c55ef0 --> 0x64b4c60 to suppress, use: --max-stackframe=8000144 or greater 我搜了一下发现说是栈空间消耗太大,我改成calloc,两个错误提示都没了。 ==15130== HEAP SUMMARY: ==15130== in use at exit: 457,122,934 bytes in 4,659,095 blocks ==15130== total heap usage: 4,659,118 allocs, 23 frees, 457,166,878 bytes allocated ==15130== ==15130== LEAK SUMMARY: ==15130== definitely lost: 48,850,904 bytes in 177,241 blocks ==15130== indirectly lost: 400,271,992 bytes in 4,481,851 blocks ==15130== possibly lost: 8,000,000 bytes in 1 blocks ==15130== still reachable: 38 bytes in 2 blocks ==15130== suppressed: 0 bytes in 0 blocks ==15130== Rerun with --leak-check=full to see details of leaked memory 最后有内存统计信息,malloc后没有free的内存都会在统计。下面这段解释是我摘自网上的,我感觉不太准确(可能是valgrind检测的就不太准确),但是个参考。 Memcheck将内存泄露分为两种,一种是可能的内存泄露(Possibly lost),另外一种是确定的内存泄露(Definitely lost)。 Possibly lost 是指仍然存在某个指针能够访问某块内存,但该指针指向的已经不是该内存首地址。Definitely lost 是指已经不能够访问这块内存。而Definitely lost又分为两种:直接的(direct)和间接的(indirect)。直接和间接的区别就是,直接是没有任何指针指向该内存,间接是指指向该内存的指针都位于内存泄露处。在上述的例子中,根节点是directly lost,而其他节点是indirectly lost。 possibly lost: 8,000,000 bytes in 1 blocks这个就是我那个一百万的数组malloc后没有free释放的,然后我free后,这行就没了。但我指针没修改,是个多线程的内存申请,但是只检测出一个来。四个线程没一个都malloc了。 Valgrind User Manual写的很详细,好多功能,一时半会也试不完。先体验一把,有需求再说。

Continue