LINUX.ORG.RU

Чем заменить медленный PHP

 , , ,


0

3

Есть скрипт, очень простой. Входные данные - два словаря (просто два файла, где по слову на каждой строке). Несколько текстов - это просто строка.

Нужно регулярным выражением найти в тексте все слова из словаря и вернуть в виде массива все, что удалось найти.

Проблема - сделал словарь размером 400Мб и тут даже текст в одно слово - это очень и очень долго.

На чем переписать под Debian? Думал про bash, но вспомнил про python - и понял, что не знаю что выбрать. Учитывая bash я не знаю, а python немного знаю, а нужно максимум скорости.... но получу ли я ее на bash? Есть мечты отделать парой строк через exec...



Последнее исправление: postscreen (всего исправлений: 1)

Ответ на: комментарий от anonymous

Ещё, подумав: можно ещё круче - словарь переделать на дерево, в котором поиск любого слова будет выполнятся очень быстро. На фиг эти слова по отдельности хранить вообще...

anonymous
()
sed 's/.*/\L&/' input.txt | grep -o "[а-я]\+" | sort -u | awk '{if (!system("look -b " $1 " dict.txt>/dev/null")) print}'

sed переводит текст из input.txt в нижний регистр.

grep выделяет слова.

sort удаляет дубликаты слов.

awk фильтрует слова по словарю dict.txt.

look с ключом -b за счет применения бинарного поиска ищет в сортированном файле быстрее, чем grep. При этом look не загружает файл в память целиком.

gorky ★★
()

В топике, за редким исключением, сплошные идиоты. Как впрочем и ТС.

anonymous
()

написать расширение

anonymous
()
Ответ на: комментарий от devl547

Зато сишный код, который будет вызываться башем НАМНОГО быстрее)

А что мешает вызвать его из PHP? :)

KRoN73 ★★★★★
()
Ответ на: комментарий от postscreen

Если отсортирован, то бинарный поиск в помощь. Алгоритмическая сложность по скорости - O(log N), по памяти O(N).

LongLiveUbuntu ★★★★★
()
Ответ на: комментарий от KRoN73

Как вариант)
Мне привычнее шеллскрипт через cgi дергать.

devl547 ★★★★★
()

язык менять не нужно, как и вычислять хэш, как и переходить в БД

Почему бы благородному Дону на разбить свой могучий словарь в 400 Мб на набор словарей по первым двум буквам слов. Например аа.txt, вн.txt, пр.txt etc.

Если в алфавите 32 карты, то это в худшем случае 1024 словарика (впрочем, наверняка меньше, если исключить существование слов на нечинающихся ыы, жц и ьъ).

А далее хоть даже имеющуюся программу использовать.

sshestov
()

Напиши на Rust.

Разбей файл словаря на N файлов и ищи в N потоков.

В зависимости от того, какой регексп, можно сделать что-то типа индекса.

theNamelessOne ★★★★★
()

PicoLisp - на бутстрапе жрет всего 3мб, умеет сам в веб-сервер, есть встроенная БД, для которой 400Мб не предел и не объем.

Писать на нем легко и просто, по скорости примерно как руби.

nihirash ★★★
()
Ответ на: комментарий от theNamelessOne

Кстати, да - есть вменяемые описания подходов как прикрутить rust к PHP. Причем они появились сразу, как ржавчина стала набирать популярность.

Вообще - грамотно построенный алгоритм позволит обходить данные практически мгновенно. Вариантов много.

anonymous
()
Ответ на: комментарий от KRoN73

Как я стар, блин.

3 мб для сервера приложений с базой данных?

Вроде номр

nihirash ★★★
()
Ответ на: комментарий от anonymous

я подозреваю, что ТС просто хотел разжечь языкосрач. почти получилось, интеллектуальная элита ЛОРа в своем стиле

anonymous
()

Пусть будет однострочник, может чуть допилить..

comm -1 -2 < `cat slovar.txt | sort | uniq` <`cat text.txt | sed 's/[ ,.;:-_\t]/\n/' | sort | uniq`
самому интересно что будет с 400м словарем.

chenger ★★
()
Последнее исправление: chenger (всего исправлений: 3)

Перл уже советовали? :)

hobbit ★★★★★
()
Ответ на: комментарий от anonymous

Вот моё решение - создать самодельный индекс и искать по нему. Поиск будет выполнятся очень быстро. Демонстрация в этом скрипте (думаю, разберёшься, что там к чему):

<?php

define('DICTIONARY_FILE', 'words.txt');
define('INDEX_FILE', 'words-index.bin');
define('PACK_AS', 'L');
define('PACK_SIZE', 4);

//Initial file sort: cat w.txt | sort -f -u  > words.txt

//To create new index use this:
//createIndex();

//To search for word with index, use this:
//echo "exist: " . doesWordExist('may') . "\n";

function createIndex()
{
    $dictHandle = fopen(DICTIONARY_FILE, 'r');
    $indexHandle = fopen(INDEX_FILE, 'wb');

    $currIndex = 0;
    do {
        $newPos = pack(PACK_AS, $currIndex);
        fwrite($indexHandle, $newPos);
        $newString = fgets($dictHandle);
        $currIndex += strlen($newString);
    } while(!feof($dictHandle));

    fclose($indexHandle);
    fclose($dictHandle);
}

function doesWordExist($wordRaw)
{
    $word = mb_strtolower($wordRaw);
    $exists = false;
    $wordsCount = filesize(INDEX_FILE) / PACK_SIZE;

    $dictHandle = fopen(DICTIONARY_FILE, 'r');
    $indexHandle = fopen(INDEX_FILE, 'rb');

    $minIndex = 0;
    $maxIndex = $wordsCount - 1;

    $minWord = getWord($minIndex, $indexHandle, $dictHandle);
    $maxWord = getWord($maxIndex, $indexHandle, $dictHandle);

    if (in_array($word, [$minWord, $maxWord])) {
        $exists = true;
    }
    while (!$exists && ($maxIndex - $minIndex > 1)) {
        $avgIndex = ceil(($maxIndex + $minIndex) / 2);
        $avgWord = getWord($avgIndex, $indexHandle, $dictHandle);
        $diff = strcmp($avgWord, $word);
        if (!$diff) {
            $exists = true;
        } elseif ($diff > 0) {
            $maxIndex = $avgIndex;
        } else {
            $minIndex = $avgIndex;
        }
        echo "$minIndex $maxIndex $exists $avgWord/$word\n";
    }

    fclose($indexHandle);
    fclose($dictHandle);

    return $exists;
}

function getWord($index, $indexHandle, $dictHandle)
{
    fseek($indexHandle, $index * PACK_SIZE);
    $wordPosPacked = fread($indexHandle, PACK_SIZE);
    $wordPos = unpack(PACK_AS . 'int', $wordPosPacked)['int'];
    fseek($dictHandle, $wordPos);
    $word = mb_strtolower(trim(fgets($dictHandle)));
    return $word;
}
anonymous
()

PHP это энтерпрайз. А энтерпройз по определению не можит быть быстрым. Вот так вот

anonymous
()
Ответ на: комментарий от anonymous

Профильните, ужаснётесь. Тут реально «медленный PHP», а точнее, in_array не по делу.

$words = array_combine($words, $words) - разовая операция, дающая на выходе хеш-уник. Далее заменить in_array($str, $words) на шустрый хеш-поиск isset($words[$str]).

<?php
$text = "Мама мыла раму";
$words = array_map('mb_strtolower', preg_split("~\W+~u", $text));
$words = array_combine($words, $words);

$found = [];
$fh = fopen('dictionary.txt', 'r');
while (($word = fgets($fh)) !== false) {
    $word = trim($word);
    if (isset($words[$str])) $found[$str] = $str;
}
fclose($fh);

print_r($found);
AlexAT
()
Ответ на: комментарий от AlexAT

Да, ты прав. Только что сам перепроверил - и действительно, разница огромная. Особенно, когда слов в массиве много.

Это была моя первая идея, так сказать, мысленный эксперимент, который себя не оправдал. Дальнейшая идея, с самодельным индексом, намного лучше. :)

anonymous
()

Нормально опиши задачу. Задачу, а не свои фантазии на тему ее решения.

anonymous
()
Ответ на: комментарий от vtVitus

Вот бы в 30-100 раз :)) На самом деле, я тестирую на строке в 250 символов и жду по 2-3 минуты иногда. А хотелось бы тестировать на длинной в 25.000 символов строке и ждать максимум 10 секунд =(

postscreen
() автор топика
Ответ на: комментарий от anonymous

Задача простая, описана еще в начале.

function persent_word($input_text, $input_slovar){

// Тут проверка насколько текст соответствует словарю // То есть что текст состоит из словарных слов на 99,0546%, например

}

postscreen
() автор топика
Ответ на: комментарий от anonymous

Упаковываешь словарь в prefix tree или dawg, потом просто ищешь. Да даже обычный хешмап подойдет, если памяти не жалко.

anonymous
()
Ответ на: комментарий от anonymous

Это казалось коротким путем, да и ведь они вроде не такие медленные на самом деле... с виду... попробую с примера, но все таки все кажется пока безуспешно и жутко долгим...

postscreen
() автор топика
Ответ на: комментарий от zolden

Да нет, подлинный. Это у вас, вероятно, китайщина типа 100 в 1.

Zmicier ★★★★★
()
Ответ на: комментарий от anonymous

А если не стараться и взять вдруг все и поместить в БД? Mysql на 2-3Млн записей не так много, но мне ее проще оптимизировать, чем файлы... Я бы сказал с файлами, я буду так мучительно изобретать велосипед, что боюсь потрачу уйму времени за зря...

postscreen
() автор топика
Ответ на: комментарий от anonymous

Ну он вроде ищет по текстам... Да и мне кажется, он чуть избыточный в этом смысле, там ведь движок поиска целый.

postscreen
() автор топика
Ответ на: комментарий от postscreen

ДАТЫЖЕТУПОЙСУКАТУПОЙ

блядь, какой же ты тупой нахуй, тебе вторые сутки объясняют что не надо браться за то, на что у тебя мозгов нет, куда ты нахуй лезешь?

anonymous
()

Замените медленный PHP на быстрый.

Exmor_RS ★★★
()
Ответ на: комментарий от postscreen

хотелось бы тестировать на длинной в 25.000 символов строке и ждать максимум 10 секунд =(

Однострочник, который я привел, за 10 секунд обрабатывает файл длиной 70.000 символов, используя словарь объемом 270 мегабайт, с максимальным потреблением оперативной памяти 24 мегабайта.

gorky ★★
()
Ответ на: комментарий от postscreen

Да, разумеется, можно и так. Просто создай индекс по столбику. Я думал, это тебе по каким-то причинам не вариант. :)

anonymous
()
Ответ на: комментарий от postscreen

Ой, не на тот комментарии ответил. Подразумевалось - «А если не стараться и взять вдруг все и поместить в БД?»

anonymous
()
Ответ на: комментарий от anonymous

Только тобой упомянутый MySQL ищет со своими нюансами. Например, для него строки «ąčę», «ACe», «ace» - то же самое. С другой стороны, терять скорость, предоставляемую индексом, не хочется. Запрос должен будет выглядеть как-то так:

SELECT count(*) FROM words WHERE word = "txt" AND BINARY LOWER(word) = BINARY LOWER("txt");
anonymous
()
Ответ на: комментарий от nihirash

каникулы кончились сентябреже

так что такое пиколипс на бутстрапах?

icefreeze
()
Ответ на: комментарий от gorky

Спасибо, я его не заметил сходу)) Все таки можно и в одну строку засунуть же все...

postscreen
() автор топика
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.