быстрое заполнение strcpy для одного слова

0

Я пытаюсь написать очень дешевый фрагмент кода C++, чтобы выполнить следующую операцию в короткой нулевой завершаемой строке.

Вход представляет собой строку типа "ABC". Он имеет нулевой конец и имеет максимальную длину 4 (или 5 с нулевым терминатором).

Выход идет на char[4] который не является нулевым, и должен иметь пробел справа. Поэтому в этом случае это будут {'A','B','C',' '}

Это нормально предположить, что входная строка правильно завершена нулем, поэтому нет необходимости читать второе слово ввода, чтобы убедиться. 4 байта - это самое длинное, что может быть.

Таким образом, код вокруг него выглядит так:

char* input = "AB";
char output[4];
// code snippet goes here
// afterward output will be populated with {'A','B',' ',' '}

Как дешево это можно сделать? Если это имеет значение: я работаю с:

Linux 2.6.32-358.11.1.el6.x86_64 #1 SMP x86_64 x86_64 x86_64 GNU/Linux

Наконец, вход выравнивается по слову.

  • 2
    Имейте в виду, что быстрый и дешевый не то же самое, что хакерство, даже если для получения оптимальной скорости может потребоваться взлом. Не забудьте сравнить различные решения, иногда удивительно, что могут сделать компиляторы и современные процессоры;)
  • 0
    Большое спасибо, Антуан. Я собираюсь сделать кучу сравнительного анализа этих. Я принял ваш ответ, потому что это был тот, кого я искал. Я попробовал несколько более очевидных решений с помощью циклов, но решил, что зайду в stackoverflow, чтобы посмотреть, сможет ли кто-нибудь создать решение без ответвлений, как вы. Это может быть не тот, который я использую.
Теги:
strcpy
memcpy
word

6 ответов

3
Лучший ответ

Как насчет чего-то вроде этого:

typedef unsigned int word;
int spacePad(word input) {
    static const word spaces = 0x20202020;

    word mask =
       !input ?                0 :
       !(input & 0x00ffffff) ? 0xff:
       !(input & 0x0000ffff) ? 0xffff :
       !(input & 0x0000ff)   ? 0xffffff :
                               0xffffffff;
    // or without branches
    word branchless_mask =
       1u << (8 * (
         bool(input & 0xff000000) +
         bool(input & 0x00ff0000) +
         bool(input & 0x0000ff00) +
         bool(input & 0x000000ff)
       ));

    return (spaces & mask) | (input & ~mask);
}

И если я не spacePad(0xaabb0000), spacePad(0xaabb0000) будет 0xaabb2020.

Вместо вычислений и масок вы можете использовать встроенные функции SSE, которые, вероятно, будут быстрее, так как вы получите маску в нескольких инструкциях, а затем замаскированное перемещение сделает все остальное, но компилятор, вероятно, переместит ваши переменные arround из SSE к стандартным регистрам, которые могут привести к снижению веса. Все зависит от того, сколько данных вам нужно обрабатывать, как оно упаковано в память и т.д.

Если вход в char* а не int, обычно необходим дополнительный код, так как кастинг может считываться в нераспределенную память. Но так как вы упоминаете, что все строки выравниваются по словам, достаточно сделать актерский процесс, даже если есть несколько нераспределенных байтов, они находятся на том же слове, что и по меньшей мере один выделенный байт. Поскольку вы только читаете, нет риска повреждения памяти и всех архитектур, о которых я знаю, защита аппаратной памяти имеет зернистость, большую, чем слово. Например, на x86 страница памяти часто выравнивается по 4k.

Теперь, когда все хорошо и взломано, но: прежде чем выбрать решение, сравните его, что единственный способ узнать, что лучше для вас (за исключением, конечно, теплого нечеткого чувства написания кода, подобного этому ^^)

  • 0
    Благодарю. Я думаю, что это как можно ближе. Я надеялся на что-то совершенно безответственное, но это может быть невозможно. Очень признателен. Я собираюсь немного подождать, прежде чем принять, если кто-то придумает что-то без веток.
  • 0
    Вы уверены, что последний байт (после '\0' ) будет 0?
Показать ещё 8 комментариев
1

Если скорость ваша проблема - используйте грубую силу.

Это не имеет доступа input за ее пределами, и не разрушает его.

 const char* input = TBD();
 char output[4] = {' '};
 if (input[0]) {
   output[0] = input[0];
   if (input[1]) {
     output[1] = input[1];
     if (input[2]) {
       output[2] = input[2];
       if (input[3]) {
         output[3] = input[3];
       }
     }
   }
 }
1
char* input = "AB";
char output[4];

input += (output[0] = *input ? *input : ' ') != ' ';
input += (output[1] = *input ? *input : ' ') != ' ';
input += (output[2] = *input ? *input : ' ') != ' ';
output[3] = *input ? *input : ' ';

Обратите внимание, что это уничтожает исходный указатель input, поэтому сделайте копию этого, если вам нужно его сохранить.

1

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

char buffer[4];

const char * input = "AB";
const char * in = input;
char * out = buffer;
char * end = buffer + sizeof buffer;

while (out < end)
{
    *out = *in != 0 ? *in++ : ' ';
    out++;
}
0

Если ваш ввод имеет нулевое завершение, достаточно простого strcpy. Memcpy работает быстрее, но скопирует любой мусор, найденный после нулевого символа.

  • 0
    Я, конечно, мог бы использовать strcpy или memcpy, но должна быть возможность сделать это быстрее, потому что они будут смотреть на следующее слово в случае ввода длины 4. Кроме того, вывод должен быть дополнен пробелом, что может повлечь дополнительные расходы. strncpy с максимальной длиной 4 избегает смотреть на следующее слово, но все еще не заботится о пробелах.
  • 0
    Нет, strcpy не будет заполнять буфер, как он просит.
0

Вы ищете memcpy:

char* input = "AB\0\0";
char output[4];
memcpy(output, input, 4);

Если ваш вход является переменным, сначала вам нужно рассчитать размер:

char* input = "AB";
std::size_t len = strlen(input);
char output[4] = {' ', ' ', ' ', ' '};
memcpy(output, input, std::min(4, len));
  • 0
    Благодарю. Я знаком с memcpy, но я пытаюсь создать что-то быстрее, чем это, потому что а) strlen может излишне смотреть на другое слово ввода и б) это не пробел в выводе.
  • 0
    Единственный способ сделать это быстрее - это узнать длину заранее или написать собственную версию strlen которая принимает максимальную длину (есть нестандартное расширение strnlen которое делает именно это).
Показать ещё 1 комментарий

Ещё вопросы

Сообщество Overcoder
Наверх
Меню