Pawn. Быстрая замена strtok

Всем, кто использует strtok посвящается. Решил сравнить скорость работы strtok и strcharsplit.

Функции

stock strtok(const string[], &index, seperator=' ')
{
	new length = strlen(string);
	while ((index < length) && (string[index] <= seperator))
	{
		index++;
	}
 
	new offset = index;
	new result[20];
	while ((index < length) && (string[index] > seperator) && ((index - offset) < (sizeof(result) - 1)))
	{
		result[index - offset] = string[index];
		index++;
	}
	result[index - offset] = EOS;
	return result;
}

stock strcharsplit(const string[], &index, seperator=' ')
{
	new result[20], i = 0;
	if (index != 0 && string[index] != '\0') index++;
	while (string[index] && string[index] != seperator && string[index] != '\r' && string[index] != '\n')
	{
		result[i++] = string[index++];
	}
	return result;
}

Скрипт

new string[128] = "Pe4eneg Rulit Vsegda Oga";
new str[64];
new idx = 0;
// ----------
new tick = GetTickCount();
for (new i=0;i<50000;i++)
{
	str = strtok(string,idx,' ');
	str = strtok(string,idx,' ');
	str = strtok(string,idx,' ');
	str = strtok(string,idx,' ');
	idx = 0;
}
printf("strtok: %d",GetTickCount() - tick);
// ----------
idx = 0;
tick = GetTickCount();
for (new i=0;i<50000;i++)
{
	str = strcharsplit(string,idx,' ');
	str = strcharsplit(string,idx,' ');
	str = strcharsplit(string,idx,' ');
	str = strcharsplit(string,idx,' ');
	idx = 0;
}
printf("strcharsplit: %d",GetTickCount() - tick);

Результаты

strtok: 1449
strcharsplit: 1159
 
strtok: 1497
strcharsplit: 1147
 
strtok: 1376
strcharsplit: 1233

Вывод

Функция strcharsplit быстрее strtok примерно на 20%.

Обновлено
Была найдена(тут) более быстрая функция парсинга строки. К сожалению, та функция имеет недостатки: не очищается dest, не работает со спец. символами \n, \r и т.д., нельзя изменить разделитель(потому что используется switch/case).
В итоге я решил написать нечто среднее между скоростью функции u_strtok и правильностью работы функции strtok. Функция strsplit быстрее strcharsplit, но медленнее, чем u_strtok. Зато, эта функция работает идентично strtok.

stock strsplit(dest[], const string[], &index, seperator=' ')
{
    if (index != 0 && string[index] != '\0') {
        index++;
    }
    new i = 0;
    for (;;) {
        if (i == 0) {
            for (;;) {
                if (string[index] == seperator) {
                    index++;
                } else {
                    switch (string[index])
                    {
                        case '\0', '\a', '\b', '\f', '\n', '\r', '\t', '\v': index++;
                        default: break;
                    }
                }
            }
        }
        if (string[index] == seperator) {
            break;
        }
        switch (string[index])
        {
            case '\0', '\a', '\b', '\f', '\n', '\r', '\t', '\v': break;
            default: dest[i++] = string[index++];
        }
    }
    dest[i] = '\0';
}
  • Borog25

    Используя предложенную функцию strcharsplit заместо старой strtok, при отсутствии параметра в строку все равно запишется значение. Таким образом стандартная проверка if(!strlen(tmp)) по системе команд стандартного ГФ будет работать некорректно.

    • ZiGGi

      Приведите пример команды, которая будет работать не так, как с strtok.

  • Borog25

    Решил проблему, добавив в код проверку.
    Не уверен в ее корректности, но работает.
    Правда еще один минус кода в том, что он рассчитывает только на один пробел между параметрами, т.о. команда «/command test» не сработает так, как должна

    P.S. Прошу прощения, если оффтоп. Вдруг кому пригодится

    strcharsplit(const string[], &index, seperator=' ')
    {
    	new result[20], i = 0;
    	if(index != 0) index++;
    	if(index+strlen(string[index]) == strlen(string[0]))
    	{
    		while (string[index] && string[index] != seperator)
    		{
    			result[i++] = string[index++];
    		}
    	}
    	return result;
    }
    • ZiGGi

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

  • Borog25

    strtok: 412
    strcharsplit: 321
    strcharsplit (edited): 366

    Правда с таким раскладом половина выигрыша теряется, а это не тру

  • ZiGGi

    Спасибо, заметил проблему с возвращением числа index. Дело в том, что при работе с обычными строками, без символа-разделителя, к index прибавлялась лишняя единица.
    Проблема решается заменой проверки:

    if(index != 0) index++;

    на

    if(index != 0 && string[index] != '\0') index++;

    Статья обновлена.

  • Borog25

    Действительно работает. Спасибо)
    А почему именно зиг.. это «»?

    • ZiGGi

      Ник был придуман где-то в 11-12 лет, происхождение не помню уже. Ник абсолютно нейтрален ко всему и к всяким зигам не имеет отношения:). Вообще мне не нравится, когда говорят про зигу:-/

      • asdd

        Мне кажется, что он имеет ввиду «»

        • asdd

          \ 0

  • Borog25

    Чорт. Вопрос не в этом заключался. Потерялись символы куда-то и суть вместе с ними. :D
    Я хотел спросить, почему ты добавил проверку string[index] != » (слэш и нолик)
    Почему слэш и нолик?

    • ZiGGi

      Используй HTML тег <pre lang="cpp">код</pre>

      string[index] != '\0'

      В данной строчке мы проверяем является ли символ под номером index, в строке string, пустым(нуль-символом).
      Можно-было записать так:

      string[index] != 0

      Но я думаю, что правильнее в кавычках:)

      • Borog25

        Понял, спасибо) Отличный блог, так держать ;)

  • \o

    • ZiGGi

      Что вы этим хотели сказать?

      • OKStyle

        Что он дибил. Смотрю вот, ты взял strtok с указанием разделителя. А если более старый, где нельзя выбирать разделитель, так как он уже вписан в код, будт ли разница в скорости? А во второй функции попробуй заменить на EOS. Хочу результаты видеть…

        • ZiGGi

          Результаты не изменились. Различие на 200-300 тиков, редко 100.
          По моему, EOS менее нагляден, чем ‘\0’.

          • OKStyle

            Различие в какую сторону? С выбором сепаратора медленнее?

          • ZiGGi

            Очень сложно заметить различия, но я думаю, что наличие сепаратора не имеет значения.

  • fabervox

    Нашел ещё один вариант, u_str, получается ещё быстрее: http://forum.gs-games.ru/topic/4544/
    Но может есть скрытые недостатки, как считаете?

    • Она не совсем правильно работает, не так, как оригинальный strtok. Плюс она не возвращает строку, будет сложнее адаптировать. Ну и самый главный минус — нельзя использовать в качестве разделителя что-то другое, кроме пробела(в case должны быть только константы).

      Немного результатов тестов:
      Input: 'test1 test2 test3 test4'

      u_strtok:
      tmp: 'test1', idx: 5
      tmp: 'test2', idx: 11
      tmp: 'test3', idx: 17
      tmp: 'test4', idx: 23
      tmp: 'test4', idx: 24

      strcharsplit:
      tmp: 'test1', idx: 5
      tmp: 'test2', idx: 11
      tmp: 'test3', idx: 17
      tmp: 'test4', idx: 23
      tmp: '', idx: 23

      strtok:
      tmp: 'test1', idx: 5
      tmp: 'test2', idx: 11
      tmp: 'test3', idx: 17
      tmp: 'test4', idx: 23
      tmp: '', idx: 23

      Input: 'test1'

      u_strtok:
      tmp: 'test1', idx: 5
      tmp: 'test1', idx: 6
      tmp: 'test1', idx: 7
      tmp: 'test1', idx: 8
      tmp: 'test1', idx: 9

      strcharsplit:
      tmp: 'test1', idx: 5
      tmp: '', idx: 5
      tmp: '', idx: 5
      tmp: '', idx: 5
      tmp: '', idx: 5

      strtok:
      tmp: 'test1', idx: 5
      tmp: '', idx: 5
      tmp: '', idx: 5
      tmp: '', idx: 5
      tmp: '', idx: 5

      Их можно исправить, но немного скорости теряется:

      stock u_strtok(dest[], const st[], &index)
      {
      	if (index && st[index] != EOS) index++;
      	for (new i;;)
      	{
      		dest[i] = EOS;
      		switch (st[index])
      		{
      			case EOS, ' ': break;
      			default: dest[i++] = st[index++];
      		}
      	}
      }
  • Vagrant

    Моя замена strtok и strcharsplit подобным функциям:

    stock strvag(const string[], &amp;index, seperator=' ')
    {
    	new res[20],i,k = string[index];
    	for(;k != seperator;index++,i++,k = string[index])
    	switch(k)
    	{
    		case '','\n','\r': break;
    		default: res[i] = string[index];
    	}
    	i = 0;
    	index++;
    	return res;
    }

    Результаты:

    strtok: 445
    strcharsplit: 430
    strvag: 237

    strtok: 451
    strcharsplit: 428
    strvag: 238

    strtok: 450
    strcharsplit: 427
    strvag: 236

    Ну а u_strtok замена такая:

    stock strvagns(res[], const string[], &amp;index)
    {
    	new i,k = string[index];
    	for(;k != ' ' &amp;&amp; k != '';k = string[++index],i++) res[i] = string[index];
    	res[i] = '';
    	i = 0;
    	index++;
    }

    Не на много быстрее но быстрее :)
    Результаты:

    u_strtok: 274
    strvagns: 235

    u_strtok: 275
    strvagns: 238

    u_strtok: 277
    strvagns: 242

  • Pingback: Pawn. SA-MP. Взаимодействие с игроком | ziggi - blog()

Перейти к верхней панели