本帖最后由 ShadowSniper 于 2012-11-27 23:30:22 编辑

解决方案 »

  1.   

    问题二不知是不是我想错了我开始想的是词表所使用的内存常驻在php-fpm的进程里,也就是说php-fpm进程一启动,就一次性把词表从我的redis或mysql中拉过来。然后脚本调用扩展时就不用每次都去拉了。或者是否能实现每10分钟去拉一次词表但不知php-fpm是否管这事。不知扩展所使用的内存是否也是每次脚本调用时才去申请。也不能常驻再php-fpm的进程里。如果是这样的话,就不如直接连redis,每次都去拉词表了。
      

  2.   

    我猜是一个php-fpm进程一次? 
      

  3.   

    这样的小规模应用,直接用php代码就可实现。
    给一个php代码的测试数据
    字典中共有 52938 个词(现代汉语常用词表en.txt)
    待匹配文件长度 19415 字节(话说长江解说词(删去html成分))
    匹配到词 2300 个
    耗时 146.212 毫秒
    其中字典加载耗时 146.172 毫秒
    折算速度 15,730 词/秒
    算法采用 单数组trie虽然不算很快,但已经是可以接受的了当然写成扩展更好,速度应该更高
    既然是扩展当然就是动态链接库了,加载了就驻留内存。所以你的第二个问题不是问题
    像你这样的应用的开发过程大致是这样的:
    1、以c/c++完成全部功能模块,配上io就是独立软件
    2、书写php扩展函数去调用需要暴露的接口
      

  4.   

    <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) );
      }
    }
      

  5.   


    我觉得应该不是php-fpm的主进程启动时去申请内存,而其他进程都是他生成出来的子进程。子进程是可以访问父进程资源的。所以不必要重新申请内存。它们会访问一块共享内存区域。
      

  6.   


    其实不算是小规模应用,这个功能需要放到生产环境给用户使用。每天pv量大概几百万。每一个请求就需要调用一次此功能,所以我对效率要求还是比较高的。今天上午用php扩展和php原生代码做了个整数自增100万次的效率测试,发现php扩展中执行的效率比php原生高两个量级。所以我觉得还是非常有必要使用扩展来实现。我看您使用的是前缀树的方式来存储的词表。这样肯定比我想的那样逐行扫描词表的方式效率要高的多。多谢指教。
    如果这样的话,我可以尝试把词表存储在c的一个struct中。来模拟前缀树的结构并且让其常驻内存。
      

  7.   


    忘加标点符号了,貌似造成了歧义。php-fpm也是由一个master进程启动的,master进程在启动时,会去加载扩展模块,申请内存之类的,然后它会根据php-fpm.conf的设置,启动一定数量的slave进程。而slave进程是可以访问父进程资源的。他们有权限访问同一块公共内存区域。所以对于某些资源,它没必要去重新向系统申请。当然这只是我的猜想,具体如何还得是得看php的源码。