0%

优化程序性能

来源于CSAPP第五章

消除不必要的内存引用

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
//可以设计不同的数据类型来测量程序性能
typedef long data_t;
typedef struct
{
long len;
data_t *data;
} vec_rec, *vec_ptr;

//设计对数据执行不同的运算
#define IDENT 0 //求和,如果是求乘积设为1
#define OP + //如果求乘积设为*

void combin3(vec_ptr v, data_t* dest)
{
long i;
long length = vec_length(v); //减少循环中的函数调用
data_t* data = get_vec_start(v); //减少过程调用的优化

*dest = IDENT;

for(int i = 0; i < length; ++i)
{
*dest = *dest OP data[i];
}
}

上述代码的汇编码如下:

1
2
3
4
5
6
7
8
# vmovsd代表从指定的位置读数据
.L17: # loop:
vmovsd (%rbx), %xmm0 # Read product from dest 指针dest的地址从%xmm0读出来,存在寄存器%rbx中
vmulsd (%rdx), %xmm0 # Multiply product by data[i] 第i个元素的指针保存在%rdx中
vmovsd %xmm0, (%rbx) # Store product at dest 将rbx中的数据读出,存入%xmm0中
addq $8, %rdx # Increment data + i
cmpq %rax, %rdx # Compare to data+length data+length存放在%rax中
jne .L17 # If !=, goto loop

进入循环后,从内存中读数据,和data[i]乘,再写入到内存中。这种操作比较浪费,因为循环下次读的数据是上次写入内存的数据。

可以用一个临时变量来存储结果,等循环结束后再写入内存中。这样每次循环可以减少一次写的过程。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
void combin4(vec_ptr v, data_t* dest)
{
long i;
long length = vec_length(v);
data_t* data = get_vec_start(v);
data_t acc = IDENT; //temp variable

for(int i = 0; i < length; ++i)
{
acc = acc OP data[i];
}

*data = acc;
}

值得注意的是,从代码层面看指令是一条一条执行的,但是实际处理器中是同时对多条指令求值,这种现象称为指令级并行。所以这两种操作限制了程序的瓶颈:第一个是指令必须按照严格顺序执行,那就会遇到延迟界限;第二个是吞吐量界限(取决于处理器原始计算能力)。

看了一篇博客,个人感觉很有帮助,目前转载过来留存,正在征询作者意见,如果不同意就删掉啦。

原文链接:https://blog.csdn.net/kenanxiuji/article/details/48547285

最近,我一直忙于追踪分析,咱公司高并发代理服务器内存一直占用过高的问题。该问题表现如下,使用python脚本压测,服务器使用的物理内存一直飙升很快上G、虚拟内存更是高达数10G,没有下降的趋势。当压测程序停止运行后,整个服务占用的物理内存以及虚拟内存并没有下降的趋势。

首先简诉咱服务器运行的平台是centos-6.3(linux-2.6.32),该服务器是多进程的,基于libevent网络通信框架,以bufferevent为核心,并使用google::protobuf通信协议进行数据传输。

根据top -p /pmap -dx 等命令,检测出咱服务器进程内存占用很高,我揣测产生了内存泄露。内存泄露有两种,第一种是进程一直持有不在需要或者使用的内存,这不是狭义的内存泄露,因为没有野指针的产生,可以归为广义的内存泄露,这是危害最大的,也是很难捕捉的内存泄露,使用valgrind等工具是没有办法检测出来的;第二种是进程通过malloc、new分配了堆内存,却由于各种原因,未能释放,这是狭义的内存泄露,可以通过valgrind等内存泄露工具检测到的。

Read more »

在makefile中用了-lmysqlclient,但是会报错/usr/bin/ld: cannot find -lmysqlclient

先在/user/lib中查,发现没有,用find / -name mysqlclient发现也没有。

于是借助搜索引擎去查,结果国内论坛怎么也没找到解决办法,不得已去看官方C API,发现让重新下载解压,遂放弃。

随后又在stack overflow搜索了一下,果不其然,成功解决。附上解决方案:https://stackoverflow.com/questions/15958612/libmysqlclient-a-is-nowhere-to-be-found

缓存读写一致性问题

在知乎看(搜索)到这么一个问题:如何用redis/memcache做Mysql缓存层?刚好最近学的项目只是用到了mysql,这里学习整理一下缓存的知识。PS:强烈建议学习陈皓大神的文章以及阅读英文原文,中文博客知识需要进行筛选。

知乎问题https://www.zhihu.com/question/27738066

陈皓(左耳朵耗子)的文章https://coolshell.cn/articles/17416.html

首先要讨论的是,缓存删除还是更新的问题。什么情况下选择删除,什么情况下选择更新

  • 缓存存储的value是序列化的对象

    序列化和反序列化需要消耗cpu的资源,删除缓存成本更低,等出现下次数据汇源时再更新。

  • 高并发的场景

    两个并发操作,一个查询缓存,一个更新操作。先更新(选择删除缓存),再查询,此时无法命中缓存,会先把老的数据从数据库读出放入缓存中,然后更新数据库。此时缓存中是脏的数据,并且一直这样脏下去。所以,这种场景应该选择缓存更新,而不是删除Cache后再从数据库更新。

Read more »

unordered_map定义(C++11)

参考博客

1
2
3
4
5
6
7
template<
class Key,
class T,
class Hash = std::hash<Key>,
class KeyEqual = std::equal_to<Key>,
class Allocator = std::allocator< std::pair<const Key, T> >
> class unordered_map;

Key代表键值(key),T是根据哈希函数得到的值(value),Hash是哈希函数的函数对象,KeyEqual是等比函数的函数对象,通过"=="来判断两个key是否相等。
想实现自定义的键类型,必须实现hash函数和等比函数。

Read more »

malloc和alloc

在网上找到的转载的英文资料,附上自己的一些理解点击连接

Both the malloc() and the calloc() functions are used to allocate dynamic memory. Each operates slightly different from the other.

malloc和calloc函数都用来分配动态内存,他们之间的差别很小

malloc() takes a size and returns a pointer to a chunk of memory at least that big:

void *malloc( size_t size );

malloc返回至少有申请内存大小的一块内存(可能更大)

calloc() takes a number of elements, and the size of each, and returns a pointer to a chunk of memoryat least big enough to hold them all:

void *calloc( size_t numElements, size_t sizeOfElement );

calloc返回至少有多块内存大小之和的内存(可能更大)

There are one major difference and one minor difference between the two functions. The major difference is that malloc() does not initialize the allocated memory. The first time malloc() gives you a particular chunk of memory, the memory might be full of zeros. If memory has been allocated, freed, and reallocated, it probably has whatever junk was left in it. That means, unfortunately, that a program might run in simple cases (when memory is never reallocated) but break when used harder (and when memory is reused).

他们之间最主要的差别是,malloc不初始化分配的内存,可能内存中充满的是零。如果内存已经被分配、释放或重新分配过,它可能存放着未知的内容。这意味着当这块内存从申请,修改,再到被重新分配,包含未知的内容会导致程序崩溃

calloc() fills the allocated memory with all zero bits. That means that anything there you are going to use as a char or an int of any length, signed or unsigned, is guaranteed to be zero. Anything you are going to use as a pointer is set to all zero bits. That is usually a null pointer, but it is not guaranteed.Anything you are going to use as a float or double is set to all zero bits; that is a floating-point zero on some types of machines, but not on all.

calloc()用0 bits填充分配的内存,所以不需要初始化。通常这是一个空指针,但并不保证一定是。使用float或double类型被置为zero bits,在某些机器上是浮点零,但不保证一定是。

The minor difference between the two is that calloc() returns an array of objects; malloc() returns one object. Some people use calloc() to make clear that they want an array.

一些细小差别是calloc()返回一个包含对象的数组,而malloc返回一个对象。

Buffer的功能需求

设计buffer可以从易用性和性能两方面考虑,muduo的buffer更偏向于易用性。

  • 对外表现是一块连续的内存(char* p, int len)
  • size()可以自动增长,不是固定大小的数组
  • 内部以std::vector<int>来保存数据
  • buffer更像一个queue,从尾部写入数据,从头部读取数据
  • input buffer:连接从socket中读取数据,写入input buffer;客户代码从中读取数据
  • output buffer:客户代码把数据写入output buffer,连接从output buffer中读数据并写入socket

muduo/base基础库

在学习muduo如何实现Buffer之前,先阅读Buffer.h头文件中所需要的依赖。

types.h

基本类型的声明,如muduo::string
具体有:

1
2
3
4
5
6
7
8
9
10
11
12
inline void memZero(void* p, size_t n);

//作为static_cast<>或const_cast<>的安全版本
template<typename To, typename From>
inline To implicit_cast(From const &f)

//当upcast使用implicit_cast<>,downcast时static_cast<>不再安全,这里使用
//dynamic_cast<>来检查downcast是否合法
//不支持RTTI,如
//if(dynamic_cast<subclass>(foo)) HandleSubclassObj(foo);
template<typename To, typename From>
inline To down_cast(From* f)
Read more »

约瑟夫环来源于犹太历史学家Josephus所讲述的一个故事。罗马人占领某地后,39个犹太人和Josephus以及他的朋友躲到了一个洞中。
他们决定围成一个圈,逐个自杀也不要被罗马人抓住。自杀规则是从第一个人开始,数到3的人就自杀,然后由下一个人重新报数。
Josephus和他的朋友不想自杀,他们自己选择了两个位置,活到了最后。

将这个问题抽象以后,在LeetCode上有这么一道题圆圈中最后剩下的数字
在我做笔试的经历中,出现了题目的变种,变化之处在于删除的数字间隔是变化的,随着删除个数而增长。

Read more »

单例模式

单例模式的精髓是一个类只产生一个实例对象,当对象被构造以后,阻止构造、拷贝构造、赋值和析构操作。

单例模式可以考虑以下问题,如线程安全性和如何优化等。本文讨论学习单例模式过程中通过博客和书籍学习到的解决方案。

Read more »