(updated)MongoDB:PHP中存储和调用server side 自定义函数
在MongoDB 从1.1.x版本开始可以将server side code存储,这样可以一次性导入或者存储函数定义后,
就可以在$where等中使用这些函数.
在PHP driver中如何存储和定义这些js 函数? 目前似乎没有直接的简单方法. 如果调用MongoDb::execute是不行的.
我的解决方法使用曲线救国,通过将代码save到system.js进行存储,通过execute js closure来调用.,
UPDATED:
使用MongoCode的scope方式来简介传递命名参数. 实现命名参数传递, 更加简洁.
例子如下:
public function store_server_function($fun_name,$fun_body) {
$code = sprintf(’
var _fun = %s;
db.system.js.save({_id:”%s”, value: _fun });
‘,$fun_body,$fun_name);
self::$_db->execute($code);
}
public function call_function($function,array $args = array()) {
$closure = “function(){ return $function.apply
(this,arguments); }”;
// echo $closure,”\n”;
$result = self::$_db->execute($closure,$args);
// var_dump($result);
return $result['retval'];
}
public function call_function($fun_name, array $named_args = array()) {
$response = $this->db->execute(new MongoCode(”$fun_name()”,$named_args));
return isset($response['retval'])?$response['retval']:$response;
}
使用例子:
$base->store_server_function(’x',’function(i){ return i+5; }’);
$i = 10;
$ok = $base->call_function(’x',array($i));
ok = $base->call_function(’x',array(’i'=>5));
is($ok,$i+5,’store_server_function’);
这里,store_server_function用处不大,因为可以直接写成js然后用mongo导入.
但是call_function还是很有用的. 通过调用这些函数,可以简化很多工作.
比如一个简单的例子是sequence(js):
function(seq) {
db.sequence.update({name:seq},{$inc:{val:1}},true);
var row = db.sequence.findOne({name:seq});
return row.val;
};
//php
public function next_seq_id($seq_name) {
return $this->call_function(’next_seq_id’,array(’seq’=>$seq_name));
}
MongoDB的Perl driver的中文乱码问题
Perl下面向mongodb插入中文字符串会出现乱码.
根据MongoDB的文档, MongoDB支持UTF-8的编码. 但在Perl中,
如果直接使用utf8的字符串,也会出现问题.
测试代码:
my $mongo_dbh = $mongo_connection->get_database( $mongo_db );
my $t = $mongo_dbh->get_collection(’test’);
my $word = ‘测试’;
$t->insert({ title => $word });
my $row = $t->find_one();
say “title:”,$row->{title};
$t->remove();
输出结果是乱码. 在mongo shell和PHP中得到的也是乱码.
我初步判断是perl driver没有能够识别utf8编码而是强制encode成utf8编码后存储.
修改如下:
my $mongo_dbh = $mongo_connection->get_database( $mongo_db );
my $t = $mongo_dbh->get_collection(’test’);
my $word = ‘测试’;
$t->insert({ title => decode_utf8($word) });
my $row = $t->find_one();
say “title:”,$row->{title};
$t->remove();
输出正常. 判断正确, 问题解决. 希望Kristina能够修改就无须多此一举(当然,如果是非utf8编码还是需要转换的),
也许并不是bug而是个feature?
UPDATE: Kristina的回复很迅速, 一觉醒来, master里已经加入判断是否为utf8的代码. CPAN .27(下周2发布)以上不会存在这个问题.
但是, 其他格式的编码仍然需要转换为utf8编码,因为BSON只支持UTF8编码.
Patch for build gmagick on mac osx 10.6(snow leopard)
在mac osx 10.6.2(snow leopard)编译gmagick失败. 错误如下:
ld: duplicate symbol _php_gmagick_sc_entry in .libs/gmagick_methods.o and .libs/gmagick_helpers.o
collect2: ld returned 1 exit status
make: *** [gmagick.la] Error 1
感觉很奇怪,因为在centos上没问题. 检查了下gmagick_methods.c和gmagic_helpers.c 也没有重复定义啊.
$ nm .libs/gmagick_methods.o |grep _php_gmagick_sc_entry
000000000000f570 S _php_gmagick_sc_entry
$ nm .libs/gmagick_helpers.o |grep _php_gmagick_sc_entry
00000000000034b8 S _php_gmagick_sc_entry
Oh, ld没错, 的确是重复定义了,由于类型是S,那么还是php_gmagick_sc_entry的声明有问题.
再仔细查看了一下,果然. 由于php_gmagick_sc_entry是在php_gmagick.h中声明,而在gmagick_methods.c
和gmagic_helpers.c中都include了这个文件. 由于没有显示声明为exten导致了问题. 重新加入exten修饰符,
ok.
===========Patch=============
— php_gmagick.h 1970-01-01 17:13:08.000000000 +0800
+++ php_gmagick.h.ns 2009-12-03 01:17:52.000000000 +0800
@@ -18,7 +18,7 @@
*/
#ifndef HAVE_PHP_GMAGICK_H
-# define HAVE_PHP_GMAGICK_H
+#define HAVE_PHP_GMAGICK_H
/* Define Extension Properties */
#define PHP_GMAGICK_EXTNAME “gmagick”
@@ -107,12 +107,12 @@
#endif
/* Class entries */
-zend_class_entry *php_gmagick_sc_entry;
-zend_class_entry *php_gmagickdraw_sc_entry;
-zend_class_entry *php_gmagickpixel_sc_entry;
-zend_class_entry *php_gmagick_exception_class_entry;
-zend_class_entry *php_gmagickdraw_exception_class_entry;
-zend_class_entry *php_gmagickpixel_exception_class_entry;
+extern zend_class_entry *php_gmagick_sc_entry;
+extern zend_class_entry *php_gmagickdraw_sc_entry;
+extern zend_class_entry *php_gmagickpixel_sc_entry;
+extern zend_class_entry *php_gmagick_exception_class_entry;
+extern zend_class_entry *php_gmagickdraw_exception_class_entry;
+extern zend_class_entry *php_gmagickpixel_exception_class_entry;
/* Forward declarations */
PHP_METHOD(gmagick, __construct);
— gmagick.c 1970-01-01 17:13:08.000000000 +0800
+++ gmagick.c.ns 2009-12-03 01:44:20.000000000 +0800
@@ -27,6 +27,13 @@
static zend_object_handlers gmagickdraw_object_handlers;
static zend_object_handlers gmagickpixel_object_handlers;
+zend_class_entry *php_gmagick_sc_entry;
+zend_class_entry *php_gmagickdraw_sc_entry;
+zend_class_entry *php_gmagickpixel_sc_entry;
+zend_class_entry *php_gmagick_exception_class_entry;
+zend_class_entry *php_gmagickdraw_exception_class_entry;
+zend_class_entry *php_gmagickpixel_exception_class_entry;
+
/* {{{ static void php_gmagick_object_free_storage(void *object TSRMLS_DC)
*/
static void php_gmagick_object_free_storage(void *object TSRMLS_DC)
===========END PATCH============
UPDATE: 作者Vito回信很迅速啊, 他只使用Linux,patch已经被采纳了.
解决GraphicsMagick 和 ImageMagick冲突(PHP imagick and gmagick extension)
发现PHP imagick or magickwand无法正确加载. 经过测试发现是由于和gmagick冲突. 解决, 在编译GraphicsMagick时候加入:
–enable-symbol-prefix
重新编译后正常.
Php-fpm 0.6+PHP 5.2.11+nginx 0.8.28 设置PATH_INFO
从5.2.6升级到5.2.11后PATH_INFO突然无法使用了(no input file). 不知道是谁的问题(新的php-fpm导致). 解决:
必须使用NGINX的fastcgi_split_path_info.
location ~ .*\.php(.*)$ {
fastcgi_split_path_info ^(.+\.php)(.*)$;
include fastcgi_params;
fastcgi_param PATH_INFO $fastcgi_path_info;
}
之前,以下配置是能工作的(设置php.ini=>cgi.fix_pathinfo =1):
fastcgi_param PATH_INFO $fastcgi_script_name;
升级真麻烦, 真不如回到5.2.6吧.
GraphicsMagick OpenMP 性能比较(icc+iomp vs gcc+gomp)
GraphicsMagick(GM)是ImageMagick(IM)的可替代的图片处理
方案,但是, GraphicsMagick比ImageMagick具有性能高,稳定的优点. 而且, IM能实现的,GM都可以做到.
IM的最大问题就是代码变动太大,不够稳定. GM相对而言要稳定对了, 此外体积也没有GM那么臃肿.
Flickr 从2004年后就放弃了ImageMagick而使用GraphicsMagick, 可谓GM最佳的成功案例.
GraphicsMagick性能提升的一个亮点就是支持OpenMP, 通过OpenMP的优化,性能提升数倍以上.
虽然IM也能够支持OpenMP,但即便如此, 也比GM要慢很多.
ImageMagick也无法能够使用Icc进行支持OpenMP的编译, 而GraphicMP则可以.
为了了解OpenMP对性能有何影响,以及,icc 和 gcc相比,有多大的差异, 我做了以下简单的测试:
1. 测试环境
* CentOS 5.4
* GCC v4.1.2-46.el5_4.1
* PowerEdge R710(Intel(R) Xeon(TM) CPU 3.00GHz *2)
2. 编译脚本
build_icc() {
OPENMP=’-openmp’
CC=’icc’ \
CXX=’icpc’ \
LD=’xild’ \
CFLAGS=”-std=gnu99 $OPENMP -O3 -ip -restrict -xSSE3 -axSSE3,SSSE3,SSE4.1,SSE4.2″ \
CXXFLAGS=” $OPENMP -O3 -ip -restrict -xSSE3 -axSSE3,SSSE3,SSE4.1,SSE4.2″ \
CPPFLAGS=’-I/opt/local/include’ \
LDFLAGS=’ -L/opt/local/lib -L/usr/lib64 ‘ \
LIBS=’-liomp5 -ltcmalloc_minimal ‘ \
./configure –prefix=/opt/GraphicsMagick \
–disable-static \
–enable-openmp \
–enable-shared
}
build_gcc() {
OPENMP=’-fopenmp’
CFLAGS=”$OPENMP -O3 -msse3 -mssse3″ \
CXXFLAGS=”$OPENMP -O3 -msse3 -mssse3″ \
CPPFLAGS=’-I/opt/local/include’ \
LDFLAGS=’ -L/opt/local/lib -L/usr/lib64 ‘ \
./configure –prefix=/opt/GraphicsMagick \
–disable-static \
–enable-openmp \
–enable-shared
}
make distclean
#build_icc
#build_gcc
build_gcc使用gcc编译,使用的GNU的openmp库libgomp,
build_icc则使用icc, link icc的高效openmp库iomp5.
3. 测试脚本
# cat bench.sh
for threads in 1 2 3 4
do
env OMP_NUM_THREADS=$threads /opt/GraphicsMagick/bin/gm benchmark -duration 10 convert \
-size 2048×1080 pattern:granite -operator all Noise-Gaussian 30% null:
done
在这个脚本中,通过设置OMP_NUM_THREADS环境变量,分别使用1-4个线程( R710共有8 core,但我只测试使用4个)
4. 测试结果
——————gcc(gomp+O3)————–
Results: 5 iter 11.05s user 11.07s total 0.452 iter/s (0.452 iter/s cpu)
Results: 10 iter 22.14s user 11.07s total 0.903 iter/s (0.452 iter/s cpu)
Results: 12 iter 31.26s user 10.42s total 1.152 iter/s (0.384 iter/s cpu)
Results: 16 iter 41.50s user 10.38s total 1.541 iter/s (0.386 iter/s cpu)
——————icc(iomp5+O3)————–
Results: 16 iter 10.39s user 10.39s total 1.540 iter/s (1.540 iter/s cpu)
Results: 27 iter 20.53s user 10.35s total 2.609 iter/s (1.315 iter/s cpu)
Results: 40 iter 30.37s user 10.23s total 3.910 iter/s (1.317 iter/s cpu)
Results: 60 iter 40.41s user 10.12s total 5.929 iter/s (1.485 iter/s cpu)
以上结果中, iter/s代表每个cpu时间能够执行的循环的次数, 数值越高,性能越大.
从结果看,虽然上述数值还有一些随机性, 根据当前负载会有一些波动,但是,OpenMP的效果很明显, 启用4个线程,执行次数是单CPU的3倍以上.而ICC的运行效果也是GCC的3倍以上!