PHP与C++性能比较

PHP是速度很快的脚本语言,但是用了框架以后好像感觉挺慢的。于是猜测会不会PHP本身也不是很快。如果不是很快,能否采用PHP调用本地动态链接库的形式来提升速度。 于是有了下面的对比实验。

测试环境

1. 硬件环境如下图所示。

Selection_186

2. 软件环境

系统: Ubuntu 12.10

gcc版本:

Thread model: posix
gcc version 4.7.2 (Ubuntu/Linaro 4.7.2-2ubuntu1)

php版本:

PHP 5.3.22 (cli) (built: Mar 14 2013 20:37:16)
Copyright (c) 1997-2013 The PHP Group
Zend Engine v2.3.0, Copyright (c) 1998-2013 Zend Technologies

php开发环境: LAMP,所有安装包均是通过源码编译安装而成,编译过程中会自动根据本机各项参数进行最优配置。性能比apt-get install命令直接安装好。 关于以源码包方式搭建LAMP请参考文章:http://keping.me/linux-php-dev-by-source-style/

测试方法

由于冒泡排序在时间复杂度上相当稳定——O(n2),在最大程度上减少了数据可能带来的影响,故采取计算冒泡排序的运行时间的方法来进行此次实验。

对比测试分组

分组1: C++直接调用程序内的函数

分组2: C++调用打包好的动态链接库文件(.so文件,该文件也是自己写好并打包)

分组3: PHP直接调用程序内的函数

分组4: PHP调用打包好的动态链接库文件(.so文件,该文件也是自己写好并打包)

测试数据

数据总体规模为5,500,000个0~999的整数。

每一实验组,循环执行次数为30250,000,000,000次。

测试所用数据可以从以下地址下载:

http://keping.me/david-uploads/data/data_cpp.tar.gz

测试数据生成代码如下

[cpp]
#include
#include
#include

using std::cout;
using std::endl;
int main (int argc, char *argv[])
{
int scale;

scale = atoi(argv[1]);      // argv[1] will be 10000, 20000 … 90000, 100000

srand(unsigned(time(0)));   // use the UNIX timestamp as seeds
for(int i = 1; i <= scale; i++)
{
cout << rand() % 1000 << "\t";
if(i % 10 == 0) cout << endl;
}
}
[/cpp]

可以看到是0~999的整数,一共有10种测试数据的规模,分别放在十个不同目录下,如下图所示

Selection_159

目录名称代表数据的规模,如data_90000 表示数据规模为90,000个,同理data_100000表示数据规模为100,000个,依次类推。每一个目录下面包含10组测试数据,以data_100000举例,如下图所示

Selection_161

每个文件则包含100,000个0~999的随机数

Selection_163

数据文件打包下载地址:  地址1(包含数据以及 “分组1” 的测试结果)

以下是对比测试。

首先是 “分组1” 的测试

为了尽量保证算法一致,所以没有采用指针等数据结构,变量的交换也采用最原始的设置中间变量机制,关键代码如下:

[cpp]
void bubble(int *arr, int len)
{
int tmp;

for(int i = 0; i < len – 1; i++)
{
for(int j = i + 1; j < len; j++)
{
if(arr[i] > arr[j])
{
tmp = arr[i];
arr[i] = arr[j];
arr[j] = tmp;
}
}
}
}

[/cpp]

以下是data_100000目录下num_1数据文件的运行结果文件result_1的部分截图, 用时13.74秒

Selection_164

第一行输出记录的是每一秒中所含的时钟数;第二行记录的是排序开始之前程序已经运行的时钟数;第三行记录的是排序结束以后程序运行的时钟数;第四行则可以根据前面的数据得出本次排序运行的时间,以秒记,保留两位小数便于对比。

下表则是data_100000目录下的所有数据文件(num_1 至 num_9)的运行结果

cpp_y

可以看到,对于100,000级别的数据,进行冒泡排序, “分组1” 用了大约13.8秒左右的时间。下面将给出对于不同数据规模(data_10000~data_100000)的平均测试数据

cpp_average

其中X轴方向上表示数据规模为 10K(即使10,000),20K,,,100K的测试数据。
其中Y轴方向上表示测试的平均时间,对每一数据规模均有10个测试数据,此处为平均值。
从图中可以看出,结果基本符合 y=x的函数曲线,冒泡排序还是相当稳定的。。。

分组2的测试 

目录结构如下,其中libbubble.so文件即为冒泡排序打包成动态链接库。将会在main函数中调用。分组2与分组1的主要区别也在此,分组1中是在main里调用本地的冒泡排序函数,而分组2则是通过调用.so文件中的冒泡排序函数。

Selection_180

运行的测试数据来源与“测试分组1”完全一致,测试结果储存在相应目录下,如下图所示。

Selection_179

将测试结果的数据处理以后,得到下图。

cpp_so_average

速度竟然比C++调用自己内部的函数更快,估计是打包成动态链接库的时候,编译器已经做了优化。

分组3的测试

接下来是PHP执行冒泡排序的测试。

目录结构如下图所示,将调用PHP写的冒泡排序函数进行数据测试。 关

Selection_181

关键代码如下

[php]
for($i = 0; $i < $len – 1; $i++)
{
for($j = $i + 1; $j < $len; $j++)
{
if($arr[$i] > $arr[$j])
{
$tmp = $arr[$i];
$arr[$i] = $arr[$j];
$arr[$j] = $tmp;
}
}
}
[/php]

可以看到,为了保证算法上的一致性,代码结构与实验分组一是一样的。

由于实在是比较慢,所以写了一个shell脚本来执行,shell脚本如下图所示(未完整截图)

Selection_182

将分组3的测试结果的数据处理以后,得到下图。

php_average

可以看到在相同数据规模下,分组3的运行时间要远远大于分组1以及分组2,并且随着数据规模的上升,总体呈现出上升趋势。

分组4的测试

分组4与分组3的区别在于:分组4则是调用以C++编写的动态链接库中的冒泡排序算法,并将该动态链接库以扩展的形势添加到了PHP系统中;分组3则是直接调用PHP写的冒泡排序算法。

将分组4的测试结果的数据处理以后,得到下图。

php_so_average

可以看到虽然还是PHP进行冒泡排序算法,但是效率得到了极大的提高。

最后是对比图

首先看一下数据表

QQ图片20130620232930

其中10000,20000, …, 100000代表数据规模,表中数据为执行的秒数。可以看到分组3,也就是PHP分组,大约是其他分组的100倍至170倍时间。以至于如果不采取log函数,将完全看不到其他三个分组的图。下图是对上表每个数据取以10为底的对数以后,得到的数值描绘的图。

QQ图片20130620230529

大致结论,PHP执行速度很慢,如果实在要采取PHP的方式,请采用将C/C++编写的动态链接库,经过Zend API的转化添加成PHP扩展,PHP再调用该扩展的形势,性能如分组4所示,是非常快的。

参考文献

[1]       Al-Qahtani, S. S., Arif, R., Guzman, L. F., Pietrzynski, P., & Tevoedjre, A. (2010). Comparing selected criteria of programming languages java, PHP, C++, perl, haskell, AspectJ, ruby, COBOL, bash scripts and scheme revision 1.0.Cornell University.

[2]       Sterling Hughes. Extending PHP [J]. Web Techniques, 2001, 6(1), 56 – 60.

[3]       PHP, http://www.php.net/manual/en/internals2.structure.php

[4]       Wikipedia, PHP, https://en.wikipedia.org/wiki/PHP

Linux 常用命令

老是忘记一些命令。于是便记了下来:)

SSH常用命令

1. 不带端口号的连接:
$ ssh -l username ip
$ ssh -l ubuntu0 192.168.123.100

2. 带端口号的链接:
$ ssh -l username ip -p port
$ ssh -l root 224.217.33.111 -p 8888

SCP常用命令

1. 从远端拷贝单个文件到本地,带端口号(-P 为大写):
$ scp -P port root@ip:path_remote path_local
$ scp -P 8888 root@224.217.33.111:/home/aku/www/weekphp/sql.txt /home/david/

2. 从远端拷贝整个文件目录到本地,带端口号(-P 为大写):
$ scp -r -p port username@ip:path_remote path_local
$ scp -r -P 8888 root@224.217.33.111:/home/aku/www/weekphp/ /tmp/

3. 从本地拷贝单个文件到远端,不带端口号:
$ scp path_local username@ip:path_remote
$ scp index.html ubuntu0@192.168.123.100:/tmp/

查找文件内容

1. 在某一目录下查找是否有包含指定内容的文件
$ grep STRING PATH -r
$ grep “function render(” icampus/ -r

2. 使用find在某一目录下查找某一文件
$ find <指定目录> <指定条件> <指定动作>
$ find / -name “mysql.h” -ls
搜索根目录中,所有文件名以mysql.h开头的文件,并显示它们的详细信息。

C++调用自己的.so

由于一些原因,需要在C++中动态加载自己写的动态链接库(.so)文件。网络上的资源挺多,我也看了不少,参考最多的是下面这三篇

1. dlopen加载c++ 函数及类: http://blog.csdn.net/lwj1396/article/details/5204484

2. 上一篇的英文版本: http://www.isotton.com/devel/docs/C++-dlopen-mini-HOWTO/C++-dlopen-mini-HOWTO.html#theproblem

3. 动态调用动态库方法 .so: http://blog.csdn.net/lbmygf/article/details/7401862

再说一说自己的心得吧。

首先介绍一下动态库和静态库之间的区别

静态库是指编译连接时,把库文件的代码全部加入到可执行文件中,所以生成的文件较大,但运行时,就不再需要库文件了。即,程序与静态库编译链接后,即使删除静态库文件,程序也可正常执行。

动态库正好相反,在编译链接时,没有把库文件的代码加入到可执行文件中,所以生成的文件较小,但运行时,仍需要加载库文件。即,程序只在执行启动时才加载动态库,如果删除动态库文件,程序将会因为无法读取动态库而产生异常。

那么如何调用动态库?如何在C语言下,其实是很简单的(调用dlopen、dlsym和dlclose就够了),但对C++来说,情况稍微复杂。动态加载一个C++库的困 难一部分是因为C++的name mangling

然后从介绍Name Mangling开始

在每个C++程序(或库、目标文件)中,所有非静态(non-static)函数在二进制文件中都是以“符号(symbol)”形式出现的。这些符号都是唯一的字符串,从而把各个函数在程序、库、目标文件中区分开来。
在C中,符号名正是函数名:strcpy函数的符号名就是“strcpy”,等等。这可能是因为两个非静态函数的名字一定各不相同的缘故。
而C++允许重载(不同的函数有相同的名字但不同的参数),并且有很多C所没有的特性──比如类、成员函数、异常说明──几乎不可能直接用函数名作符 号名。为了解决这个问题,C++采用了所谓的name mangling。它把函数名和一些信息(如参数数量和大小)杂糅在一起,改造成奇形怪状,只有编译器才懂的符号名。例如,被mangle后的foo可能 看起来像foo@4%6^,或者,符号名里头甚至不包括“foo”。
其中一个问题是,C++标准(目前是[ISO14882])并没有定义名字必须如何被mangle,所以每个编译器都按自己的方式来进行name mangling。有些编译器甚至在不同版本间更换mangling算法(尤其是g++ 2.x和3.x)。即使您搞清楚了您的编译器到底怎么进行mangling的,从而可以用dlsym调用函数了,但可能仅仅限于您手头的这个编译器而已, 而无法在下一版编译器下工作。

解决方案 extern “C”

C++有个特定的关键字用来声明采用C binding的函数:extern “C” 。 用 extern “C”声明的函数将使用函数名作符号名,就像C函数一样。因此,只有非成员函数才能被声明为extern “C”,并且不能被重载。尽管限制多多,extern “C”函数还是非常有用,因为它们可以象C函数一样被dlopen动态加载。冠以extern “C”限定符后,并不意味着函数中无法使用C++代码了,相反,它仍然是一个完全的C++函数,可以使用任何C++特性和各种类型的参数。

示例程序1. 加载简单函数

目录结构
Selection_141
示例程序1在test1目录下,这个例子也主要是参考第一篇博客写的。有一些修改。

main.cpp的代码如下

[cpp]
//———-
//main.cpp:
//———-
#include
#include

int main() {
using std::cout;
using std::cerr;

cout << "C++ dlopen demo\n\n";

// open the library
cout << "Opening hello.so…\n";
void* handle = dlopen("libhello.so", RTLD_LAZY);

if (!handle) {
cerr << "Cannot open library: " << dlerror() << ‘\n’;
return 1;
}

// load the symbol
cout << "Loading symbol hello…\n";
typedef void (*hello_t)();

// reset errors
dlerror();
hello_t hello = (hello_t) dlsym(handle, "hello");
const char *dlsym_error = dlerror();
if (dlsym_error) {
cerr << "Cannot load symbol ‘hello’: " << dlsym_error <<
‘\n’;
dlclose(handle);
return 1;
}

// use it to do the calculation
cout << "Calling hello…\n";
hello();

// load the symbol
cout << "Loading symbol add…\n";
typedef int (*add_t)(int, int);

// reset errors
dlerror();
add_t add = (add_t) dlsym(handle, "add");
if (dlsym_error) {
cerr << "Cannot load symbol ‘add’: " << dlsym_error <<
‘\n’;
dlclose(handle);
return 1;
}

// use it to do the calculation
cout << "Calling the add()…\n";
cout << add(98, 99) << " is the result\n";

// close the library
cout << "Closing library…\n";
dlclose(handle);
}

[/cpp]

然后是hello.cpp的代码,一会儿将会把这个源文件编译打包成.so文件。

[cpp]
//———-
// hello.cpp:
//———-
#include

extern "C" void hello() {
std::cout << "hello" << ‘\n’;
}

extern "C" int add(int a, int b){
return a + b;
}
[/cpp]

介绍一下上面用到的接口函数

1)       dlopen

函数原型:void *dlopen(const char *libname,int flag);

功能描述:dlopen必须在dlerror,dlsym和dlclose之前调用,表示要将库装载到内存,准备使用。如果要装载的库依赖于其它库,必须首先装载依赖库。如果dlopen操作失败,返回NULL值;如果库已经被装载过,则dlopen会返回同样的句柄。

参数中的libname一般是库的全路径,这样dlopen会直接装载该文件;如果只是指定了库名称,在dlopen会按照下面的机制去搜寻:

a.根据环境变量LD_LIBRARY_PATH查找

b.根据/etc/ld.so.cache查找

c.查找依次在/lib和/usr/lib目录查找。

flag参数表示处理未定义函数的方式,可以使用RTLD_LAZY或RTLD_NOW。RTLD_LAZY表示暂时不去处理未定义函数,先把库装载到内存,等用到没定义的函数再说;RTLD_NOW表示马上检查是否存在未定义的函数,若存在,则dlopen以失败告终。

2)       dlerror

函数原型:char *dlerror(void);

功能描述:dlerror可以获得最近一次dlopen,dlsym或dlclose操作的错误信息,返回NULL表示无错误。dlerror在返回错误信息的同时,也会清除错误信息。

3)       dlsym

函数原型:void *dlsym(void *handle,const char *symbol);

功能描述:在dlopen之后,库被装载到内存。dlsym可以获得指定函数(symbol)在内存中的位置(指针)。如果找不到指定函数,则dlsym会返回NULL值。但判断函数是否存在最好的方法是使用dlerror函数,

4)       dlclose

函数原型:int dlclose(void *);

功能描述:将已经装载的库句柄减一,如果句柄减至零,则该库会被卸载。如果存在析构函数,则在dlclose之后,析构函数会被调用。

好了,现在来编译打包,命令如下:

$ g++ -shared -fPIC -o libhello.so hello.cpp
$ g++ main.cpp -ldl

在上面dlopen函数中,看到我们传的第一个参数并没有指定路径,只给出了库的名称。那是因为已经在环境变量LD_LIBRARY_PATH中指定了 ./ 目录,如下图所示。
Selection_142

如果你想放在其他目录,修改该环境变量即可。

运行a.out,输入结果如下图所示

Selection_143

示例程序2. 加载类

几篇文章的意思基本都是这样:

加载类有点困难,因为我们需要类的一个实例,而不仅仅是一个函数指针。我们无法通过new来创建类的实例,因为类不是在可执行文件(这里即指由main.cpp编译成的a.out文件)中定义的,况且(有时候)我们连它的名字都不知道。
解决方案是:利用多态性! 我们在可执行文件中定义一个带虚成员函数的接口基类,而在模块中定义派生实现类。通常来说,接口类是抽象的(如果一个类含有虚函数,那它就是抽象的)。
因为动态加载类往往用于实现插件,这意味着必须提供一个清晰定义的接口──我们将定义一个接口类和派生实现类。
接下来,在模块中,我们会定义两个附加的helper函数,就是众所周知的“类工厂函数(class factory functions)(译者注:或称对象工厂函数)”。其中一个函数创建一个类实例,并返回其指针; 另一个函数则用以销毁该指针。这两个函数都以extern “C”来限定修饰。
为了使用模块中的类,我们用dlsym像示例1中加载hello函数那样加载这两个函数,然后我们就可以随心所欲地创建和销毁实例了。

———————————-polygon.hpp—————————————————————

[cpp]
//———-
//polygon.hpp:
//———-
#ifndef POLYGON_HPP
#define POLYGON_HPP

class polygon {
protected:
double side_length_;

public:
polygon()
: side_length_(0) {}

virtual ~polygon() {}

void set_side_length(double side_length) {
side_length_ = side_length;
}

virtual double area() const = 0;
};

// the types of the class factories
typedef polygon* create_t();
typedef void destroy_t(polygon*);

#endif

[/cpp]

这里把我小小的纠结了一下,函数名后面加 const=0是什么意思呢?两个typedef好像是要定义函数指针,但又不是在定义函数指针。google了一阵以后,大致都了解了。

首先,const 和 =0 没有关系,要分开理解
成员函数后面用 const 修饰,通俗的理解就是在这个函数内不能修改类的成员变量,除非那个成员变量是 mutable 的

=0表示这个成员函数是纯虚函数,也就是它可以没有定义,只有接口,由它的继承类具体定义它的行为,当然,你也可以给它定义缺省的函数体
一个类里如果包含 =0 的纯虚函数,那么这个类就是一个抽象类,它不能具体实例化(不能创建它的对象),而只能由它去派生子类

然后是两个typedef,这里其实是typedef了两个函数类型,在后面
create_t* create_triangle = (create_t*) dlsym(triangle, “create”);
的时候是加了指针符号的。
对比一下示例1中的typedef,示例1中是直接定义的函数指针,所以在dlsym这里就不用再添加指针符号。
[cpp]
typedef int (*add_t)(int, int);
add_t add = (add_t) dlsym(handle, "add";
[/cpp]

———————————————-main.cpp———————————————-

[cpp]
//———-
//main.cpp:
//———-
#include "polygon.hpp"
#include
#include

int main() {
using std::cout;
using std::cerr;

// load the triangle library
void* triangle = dlopen("triangle.so", RTLD_LAZY);
if (!triangle) {
cerr << "Cannot load library: " << dlerror() << ‘\n’;
return 1;
}

// reset errors
dlerror();

// load the symbols
create_t* create_triangle = (create_t*) dlsym(triangle, "create");
const char* dlsym_error = dlerror();
if (dlsym_error) {
cerr << "Cannot load symbol create: " << dlsym_error << ‘\n’;
return 1;
}

destroy_t* destroy_triangle = (destroy_t*) dlsym(triangle, "destroy");
dlsym_error = dlerror();
if (dlsym_error) {
cerr << "Cannot load symbol destroy: " << dlsym_error << ‘\n’;
return 1;
}

// create an instance of the class
polygon* poly = create_triangle();

// use the class
poly->set_side_length(10);
cout << "The area is: " << poly->area() << ‘\n’;

// destroy the class
destroy_triangle(poly);

// unload the triangle library
dlclose(triangle);
}
[/cpp]

———————————————-triangle.cpp———————————————-

[cpp]
//———-
//triangle.cpp:
//———-
#include "polygon.hpp"
#include

class triangle : public polygon {
public:
virtual double area() const {
return side_length_ * side_length_ * sqrt(3) / 2;
}
};

// the class factories
extern "C" polygon* create() {
return new triangle;
}

extern "C" void destroy(polygon* p) {
delete p;
}

[/cpp]

go on..

好了,现在来编译打包,命令如下:

$ g++ -shared -fPIC -o triangle.so triangle.cpp
$ g++ main.cpp -ldl
$ ./a.out

结果如下图所示

Selection_144

调用类成功。恩,大致就是这些啦。

Animal Farm

未命名

🙂

一个猪变人的童话故事有木有——《Animal Farm》

这本书是大学姐陪我去买的哟!!!很有纪念意义有木有!!!其他已知出场人物动物都没有这个待遇哟有木有!!!(*^__^*) 嘻嘻……

================================背景割====================================

看完整本书,依然是角色的名字都记不全,甚至连Napoleon这个大大大大大反派头头的名字都是在有道词典里搜了一下“拿破仑”才拼全,实在有点不好意思。

Napoleon这头顶着男主角光环的猪,这头腹黑猪,这头盗取了革命果实的猪,这头越来越残暴的猪,这头应该已经被无数世人咒骂过的猪,早已死在了一九不知多少年(好的,如果一头猪能活过55岁,那它也可能死于二零多少年。不要纠结这里了好吗,强迫症么有)。

被盗取了革命果实,其他动物确是值得同情的。好不容易推翻了农场主的统治,好不容易击退了敌人的反扑,好不容易盼来了平等、自由,即使是在最后的最后的前一刻,在那划不清人猪界限的前一刻,动物们依然是乐观的,依然认为:至少,我们现在是在为自己劳动,不被人类所奴役,我们是自由的,这就够了。殊不知,理想已经离他们很远很远,只是逐渐麻木而不觉罢了。

逐渐麻木——我想,这或许是Napoleon采取的连它自己都不知道的一个策略。这么对比并不那么恰当,就像你觉得你大学室友4年都没变,但一个4年不见他的人觉得几乎换了一个人一样。慢慢的,总是不易察觉的。

在欲望本生会无限膨胀这个属性的作用下,Napoleon慢慢的一点一点的往集权的方向走去。动物们慢慢适应,慢得觉得生活好像就应该是这样的,慢得即使有一些觉得不对的地方也只会是短暂的不安而已。最终它终于达到了权利的巅峰,当然也伴随着残暴的统治。最初的乌托邦,最终的不知道什么汤(加了两勺铊盐的?)。

不了解那个年代的历史,也就无清楚作者在映射什么了。不过能看到猪变人这个神奇的事情也挺高兴呢。

晚安,

2013-4-28

02:21:27

记—浮躁

记—浮躁

by 囡囡

fz

开始,支持一下北大荒文艺2B青年—猴子,如果你今天不是喊我写,我也不想装文艺。
结束,几年没写东西了,心乱了,写出来的也乱。

其实 ,我狠不想用电脑写东西。
记2008,那时候我们随波逐流,看到猴子买了一个小黑,自己也想要一个,总是觉得自己用它的时候有种小小的骄傲。
在人前人后说,它有好好好好,直到现在我也不否认它的好。
但是当时只因为我们都是T系,4个小黑。
随着时间过来,我们更多的是选择自己适合的东西,而不是像当年一样,选择一样不适合自己的,其实它对于当时的我来说,更好的用处没真正的发挥出来。
也许,在我们盲目的,不停的在追寻我们所需要的东西的时候,我们没有停下我们寻找的脚步,我听听我们自己的心声,也许,最适合的一直停留在我们身边,只是眼睛看花了这个世界而已。
我们不停的东张西望,根本就不知道自己需要的是什么,觉得这样的是好的,那样的也是好的,但是就是没想过,这些东西不是自己的,而是别人的,我们看的仅仅是别人外表显示出来给我们看的表面现象而已,从而我们忘了去讨论本质的。于是,我们就说,原来我需要的就是这种,因为我需要在别人面前炫耀这些属于我的东西,来证明我是不错的,因为我拥有这些。但是却忘了,当我们拥有的越多的时候,心是越累的。

心乱了,写上面一段话的时候,完全听歌都听不进去了。曾经,我们只是单纯的听歌,它所表达给我们的意思。听它单纯的旋律,因为那些旋律正好扣住了当时听歌的心情。

猴子喊我支持他的校内,keping,空间.。请问,现在的你们有多少的交流工具?有多少的地址是一直留到现在也在用的?
就连现在的电话,某些人也是1年多2年换一个。QQ空间永远都对人设有权限。无意之间点开某一人的消息,才发现。你不在他的好友范围之内,你没有访问的权利,原来以前的电话已经换人了。原来有这么多人,我们都一直没有了联系,联系时候才知道“噢,那就这样嘛,我还有事情”。这就是几年之前你为之自豪,现在漠视的友情。
因为你现在不能给予我更多的好处,你留给我的就是那段青春,我需要更多的朋友来让我不停的往前走,从而,我遗忘了过去的我们。走走停停,越走越空虚,越走越孤单。不停的留宿,不停的想找人代替,却找不到那曾经的心有灵犀。

今年过年之前,买了4本书,包括一本《百年孤独》,好久没翻开这类型的书了。我以为我还可以像以前一样感兴趣的全部看完,心完全净下来,去感受里面的故事,心乱了。

如果,还可以半夜不睡觉聊聊单纯的心事,还可以坐在江边的寺庙里吹着江风喝茶,或者在书店里看一天的书,在巴国城的坝坝上骑自行车,看着那些跳坝坝舞的阿姨,曾经开玩笑的说,如果当我们老了,还能这样在这里以这样的心情散步就是最大的幸福。

我们总是不停的追求着我们想要过的生活,却忘了停下来。
At:
2013.04.26
1:19

认识一群技术男真好,嘎嘎

IT技术男——居家好帮手,省钱好伙伴!吼吼吼!

本文里面顶着男猪脚光环的就是楼下这个胖子啦,艺名:鲁胖子

large_lSxn_52d60001971e125f

某天,楼主发现相机没有无线遥控器,太不方便了撒,买又太贵老。遂找鲁胖子商量,答曰小菜,于是搞之。

不知道他去哪里找了两个小灯泡(还真不清楚这玩意儿叫啥)?

IMG_0381

加上一根在他们公司垃圾桶里翻出来的报废塑料软管

IMG_0382

还有一个不知道哪个设备又被他拆了以后,撤下来的零件

IMG_0383

估计左焊焊,右焊焊,yeah~~音频信号转红外信号的遥控器出来了。

IMG_0384

硬件就这么愉快的搞定了,插在iphone上就能用。软件也早就去app store下好了

IMG_0385

好了,剩下就是测试无线遥控效果了,好使。

IMG_36962

就这么欢乐地省下了买无线遥控器的钱,以后出门遥控器能忘,手机还能忘嘛。。。除非又被摸走了,我勒个去。

哦对,我猜,他用申通快递邮给我的费用,比原料贵谄笑