请教算法和php扩展的相关疑问 本帖最后由 ShadowSniper 于 2012-11-27 23:30:22 编辑 解决方案 » 免费领取超大流量手机卡,每月29元包185G流量+100分钟通话, 中国电信官方发货 问题二不知是不是我想错了我开始想的是词表所使用的内存常驻在php-fpm的进程里,也就是说php-fpm进程一启动,就一次性把词表从我的redis或mysql中拉过来。然后脚本调用扩展时就不用每次都去拉了。或者是否能实现每10分钟去拉一次词表但不知php-fpm是否管这事。不知扩展所使用的内存是否也是每次脚本调用时才去申请。也不能常驻再php-fpm的进程里。如果是这样的话,就不如直接连redis,每次都去拉词表了。 我猜是一个php-fpm进程一次? 这样的小规模应用,直接用php代码就可实现。给一个php代码的测试数据字典中共有 52938 个词(现代汉语常用词表en.txt)待匹配文件长度 19415 字节(话说长江解说词(删去html成分))匹配到词 2300 个耗时 146.212 毫秒其中字典加载耗时 146.172 毫秒折算速度 15,730 词/秒算法采用 单数组trie虽然不算很快,但已经是可以接受的了当然写成扩展更好,速度应该更高既然是扩展当然就是动态链接库了,加载了就驻留内存。所以你的第二个问题不是问题像你这样的应用的开发过程大致是这样的:1、以c/c++完成全部功能模块,配上io就是独立软件2、书写php扩展函数去调用需要暴露的接口 <xmp><?php/*高效的关键字查找类字典中共有 52938 个词待匹配文件长度 19415 字节匹配到词 2300 个耗时 146,212 微秒其中字典加载耗时 146,172 微秒折算速度 15,730 词/秒<br />时间: 146,172 微秒<br />内存: 24669072<br /><br />时间: 146,212 微秒<br />内存: 24669120<br />2300*/check_speed(1);/*$fn = 'D:/我的文档/现代汉语常用词表en.txt';$ar1 = file($fn);for($i=0; $i<count($ar1); $i++) { $s = strtok($ar1[$i], "\t"); if(strlen($s) > 2) $ar[] = $s;}*/$p = new keywords($article);//$p->dict = (array_keys($g_keywords));//$p->dict = $ar;$p->doc = '请仔细看一下中国的地图,';$p->doc = file_get_contents('D:/我的文档/新建 文本文档 (2).txt');check_speed();$s = $p;check_speed();$p->parse();check_speed();echo count(explode('<b>', $s));echo '</xmp>';echo $s;class keywords { protected $doc; protected $dict = array(); protected $cache = 'keywordsdict.txt'; function __construct( $doc='' ) { if( file_exists($this->cache) ) $this->dict = unserialize( file_get_contents($this->cache) ); if( ! $this->dict ) $this->dict = array(); $this->doc = $doc; } function __tostring() { $p =& $this->dict; $i = 0; $t = $r = ''; $st = array(); $len = strlen($this->doc); while($i <= $len) { $ch = $this->doc{$i}; if(empty($t)) { $p =& $this->dict; $st = array(); } if( isset($p[$ch]) ) { if($p['val']) $st = array($t, $i - 1); $p =& $p[$ch]; $t .= $ch; }else { if( isset($p['val']) ) { $r .= " <b>$t</b> "; $t = ''; $i--; }elseif($st) { list($t, $i) = $st; $r .= " <b><font color=red>$t</font></b> "; $t = ''; }elseif($t) { $r .= $t{0}; $i -= strlen($t); $t = ''; }else $r .= $ch; } $i++; } return $r; } function parse() { $r = ''; $len = strlen($this->doc); $i = 0; while($i < $len) { if( $t = $this->find($this->dict, $i) ) { $r .= '<b>' . substr($this->doc, $i, $t-$i).'</b>'; $i = $t; } else { $r .= $this->doc{$i++}; } } return $r; } function find(&$p, $i) { $t = 0; $n = ord($ch = $this->doc{$i}); if( isset($p[$n]) ) $t = $this->find($p[$n], $i+1); if($t) return $t; if( isset($p['val']) ) return $i; return $t; } function __set($na, $val) { if($na == 'doc') $this->doc = $val; if($na == 'cache' && file_exists($val) ) { $this->cache = $val; $this->dict = unserialize( file_get_contents($this->cache) ); } if($na != 'dict') return; foreach($val as $k) { if( strlen($k) <= 3 ) continue; $p =& $this->dict; for($i=0; $i<strlen($k); $i++) { $ch = $k{$i}; if(! $p[$ch]) $p[$ch] = array(); $p =& $p[$ch]; } $p['val'] = 1; } file_put_contents($this->cache, serialize($this->dict) ); }} 我觉得应该不是php-fpm的主进程启动时去申请内存,而其他进程都是他生成出来的子进程。子进程是可以访问父进程资源的。所以不必要重新申请内存。它们会访问一块共享内存区域。 其实不算是小规模应用,这个功能需要放到生产环境给用户使用。每天pv量大概几百万。每一个请求就需要调用一次此功能,所以我对效率要求还是比较高的。今天上午用php扩展和php原生代码做了个整数自增100万次的效率测试,发现php扩展中执行的效率比php原生高两个量级。所以我觉得还是非常有必要使用扩展来实现。我看您使用的是前缀树的方式来存储的词表。这样肯定比我想的那样逐行扫描词表的方式效率要高的多。多谢指教。如果这样的话,我可以尝试把词表存储在c的一个struct中。来模拟前缀树的结构并且让其常驻内存。 忘加标点符号了,貌似造成了歧义。php-fpm也是由一个master进程启动的,master进程在启动时,会去加载扩展模块,申请内存之类的,然后它会根据php-fpm.conf的设置,启动一定数量的slave进程。而slave进程是可以访问父进程资源的。他们有权限访问同一块公共内存区域。所以对于某些资源,它没必要去重新向系统申请。当然这只是我的猜想,具体如何还得是得看php的源码。 以下代码是调用24小时内的数据,请问怎么改成调用一个月或一年的数据 php调用python问题 PHP如何删除表里的全部数据?(急) 一个页面中加入多个file_exists()会不会降低网站速度? 如何写超链接的正则表达式 急,50分求解读取数据库的问题 使用Mysql 如何做分页? 我累了!! 大家幫我看看!!這 session_start();給我出錯了,真煩!! 我想下载一个php-4.0.1-win32.zip的按装文件在那里有 php作图问题,请帮忙 求助:用readfile不能显示,用include却可以 如何判断字符串中是否含有数组的某个元素?
给一个php代码的测试数据
字典中共有 52938 个词(现代汉语常用词表en.txt)
待匹配文件长度 19415 字节(话说长江解说词(删去html成分))
匹配到词 2300 个
耗时 146.212 毫秒
其中字典加载耗时 146.172 毫秒
折算速度 15,730 词/秒
算法采用 单数组trie虽然不算很快,但已经是可以接受的了当然写成扩展更好,速度应该更高
既然是扩展当然就是动态链接库了,加载了就驻留内存。所以你的第二个问题不是问题
像你这样的应用的开发过程大致是这样的:
1、以c/c++完成全部功能模块,配上io就是独立软件
2、书写php扩展函数去调用需要暴露的接口
<?php
/*
高效的关键字查找类字典中共有 52938 个词
待匹配文件长度 19415 字节
匹配到词 2300 个
耗时 146,212 微秒
其中字典加载耗时 146,172 微秒折算速度 15,730 词/秒<br />
时间: 146,172 微秒<br />
内存: 24669072<br />
<br />
时间: 146,212 微秒<br />
内存: 24669120<br />
2300
*/
check_speed(1);
/*
$fn = 'D:/我的文档/现代汉语常用词表en.txt';
$ar1 = file($fn);
for($i=0; $i<count($ar1); $i++) {
$s = strtok($ar1[$i], "\t");
if(strlen($s) > 2) $ar[] = $s;
}
*/$p = new keywords($article);
//$p->dict = (array_keys($g_keywords));
//$p->dict = $ar;
$p->doc = '请仔细看一下中国的地图,';
$p->doc = file_get_contents('D:/我的文档/新建 文本文档 (2).txt');
check_speed();
$s = $p;
check_speed();
$p->parse();
check_speed();echo count(explode('<b>', $s));
echo '</xmp>';
echo $s;class keywords {
protected $doc;
protected $dict = array();
protected $cache = 'keywordsdict.txt';
function __construct( $doc='' ) {
if( file_exists($this->cache) )
$this->dict = unserialize( file_get_contents($this->cache) );
if( ! $this->dict ) $this->dict = array();
$this->doc = $doc;
}
function __tostring() {
$p =& $this->dict;
$i = 0;
$t = $r = '';
$st = array();
$len = strlen($this->doc);
while($i <= $len) {
$ch = $this->doc{$i};
if(empty($t)) {
$p =& $this->dict;
$st = array();
}
if( isset($p[$ch]) ) {
if($p['val']) $st = array($t, $i - 1);
$p =& $p[$ch];
$t .= $ch;
}else {
if( isset($p['val']) ) {
$r .= " <b>$t</b> ";
$t = '';
$i--;
}elseif($st) {
list($t, $i) = $st;
$r .= " <b><font color=red>$t</font></b> ";
$t = '';
}elseif($t) {
$r .= $t{0};
$i -= strlen($t);
$t = '';
}else
$r .= $ch;
}
$i++;
}
return $r;
}
function parse() {
$r = '';
$len = strlen($this->doc);
$i = 0;
while($i < $len) {
if( $t = $this->find($this->dict, $i) ) {
$r .= '<b>' . substr($this->doc, $i, $t-$i).'</b>';
$i = $t;
} else {
$r .= $this->doc{$i++};
}
}
return $r;
}
function find(&$p, $i) {
$t = 0;
$n = ord($ch = $this->doc{$i});
if( isset($p[$n]) ) $t = $this->find($p[$n], $i+1);
if($t) return $t;
if( isset($p['val']) ) return $i;
return $t;
} function __set($na, $val) {
if($na == 'doc') $this->doc = $val;
if($na == 'cache' && file_exists($val) ) {
$this->cache = $val;
$this->dict = unserialize( file_get_contents($this->cache) );
}
if($na != 'dict') return;
foreach($val as $k) {
if( strlen($k) <= 3 ) continue;
$p =& $this->dict;
for($i=0; $i<strlen($k); $i++) {
$ch = $k{$i};
if(! $p[$ch]) $p[$ch] = array();
$p =& $p[$ch];
}
$p['val'] = 1;
}
file_put_contents($this->cache, serialize($this->dict) );
}
}
我觉得应该不是php-fpm的主进程启动时去申请内存,而其他进程都是他生成出来的子进程。子进程是可以访问父进程资源的。所以不必要重新申请内存。它们会访问一块共享内存区域。
其实不算是小规模应用,这个功能需要放到生产环境给用户使用。每天pv量大概几百万。每一个请求就需要调用一次此功能,所以我对效率要求还是比较高的。今天上午用php扩展和php原生代码做了个整数自增100万次的效率测试,发现php扩展中执行的效率比php原生高两个量级。所以我觉得还是非常有必要使用扩展来实现。我看您使用的是前缀树的方式来存储的词表。这样肯定比我想的那样逐行扫描词表的方式效率要高的多。多谢指教。
如果这样的话,我可以尝试把词表存储在c的一个struct中。来模拟前缀树的结构并且让其常驻内存。
忘加标点符号了,貌似造成了歧义。php-fpm也是由一个master进程启动的,master进程在启动时,会去加载扩展模块,申请内存之类的,然后它会根据php-fpm.conf的设置,启动一定数量的slave进程。而slave进程是可以访问父进程资源的。他们有权限访问同一块公共内存区域。所以对于某些资源,它没必要去重新向系统申请。当然这只是我的猜想,具体如何还得是得看php的源码。