Добро пожаловать на Pro Pawn - Портал о PAWN-скриптинге.
Страница 1 из 2 1 2 ПоследняяПоследняя
Показано с 1 по 10 из 17
  1. #1
    Аватар для VVWVV
    ?

    Статус
    Оффлайн
    Регистрация
    09.07.2015
    Сообщений
    731
    Репутация:
    353 ±

    memset - самая быстрая функция

    Описание:
    Заполняет все ячейки массива переданным значением.

    Параметры:
    array[] - массив/строковая переменная, в которую будет записан результат
    val - заполняемое значение

    Возвращаемое значение:
    Всегда возвращает нуль.

    Плюсы реализации:
    • Быстрое заполнение. В 4-5 раз быстрее варианта от Slice.

    Минусы реализации:
    • Несовместимость с JIT-плагином.

    Код:
     Старая версия (с зависимостью)
    PHP код:
    stock memset(array[], valsize sizeof array) {
        
    #pragma unused array, val

        
    static
            
    amx_header[AMX_HDR],
            
    bool:is_amx_header_initialized false;


        if (
    == _:is_amx_header_initialized) {
            
    is_amx_header_initialized true;
            
    GetAmxHeader(amx_header);
        }

        new
            
    base,
            
    ctx[AsmContext],
            
    cod = -(amx_header[AMX_HDR_DAT] - amx_header[AMX_HDR_COD]);

        
    #emit CONST.pri     memset
        #emit LOAD.S.alt    cod
        #emit ADD
        #emit STOR.S.pri    base

        
    AsmInitPtr(ctxbase200);

        @
    emit PROC
        
    @emit LOAD.S.alt    12
        
    @emit LOAD.S.pri    16
        
    @emit FILL          (size << 2)
        @
    emit RETN

        
    #emit LCTRL         5
        #emit SCTRL         4
        #emit CONST.pri     memset
        #emit ADD.C         4
        #emit SCTRL         6

        
    return 0;

     Новая версия
    PHP код:
    stock memset(array[], valsize sizeof array)
    {
        
    #pragma unused array, val
        
    static
            
    fill_inst_offset;
        if (
    fill_inst_offset == 0) {
            
    #emit lctrl 6
            #emit move.alt                  // 4
            #emit lctrl 0                   // 8
            #emit add                       // 4
            #emit move.alt                  // 4
            #emit lctrl 1                   // 8
            #emit sub.alt                   // 4
            #emit add.c 92                  // 8
            #emit stor.pri fill_inst_offset // 8
        
    } {}                                // 
        #emit load.s.pri size               // 8
        #emit shl.c.pri 2                   // 8
        #emit sref.pri fill_inst_offset     // 8
        #emit load.s.alt 12                 // 8
        #emit load.s.pri 16                 // 8
        #emit fill 1                        // 4
        #emit zero.pri
        #emit retn



    Тесты:
    Количество итераций: 1 000 000
    1) Размер массива: 100
    memset(VVWVV without AMX_asm): 423ms
    memset(Владокс): 729ms (с фиксом аргументов, иначе она работала бы неправильно)
    memset(Slice): 2270ms
    2) Размер массива: 10 000
    memset(VVWVV without AMX_asm): 7920ms
    memset(Владокс): 8336ms (с фиксом аргументов, иначе она работала бы неправильно)
    memset(Slice): 12001ms

    Пример использования:
    PHP код:
    main()
    {
        new array[
    1000] = {1000,1000+1,...};

        print(!
    "Before:");
        
    printf("0:   %d", array[0]);
        
    printf("499: %d", array[499]);
        
    printf("999: %d", array[999]);

        
    memset(array,6);

        print(!
    "After:");
        
    printf("0:   %d", array[0]);
        
    printf("499: %d", array[499]);
        
    printf("999: %d", array[999]);

    См. также:

    Автор: VVWVV
    Копирование данной статьи на других ресурсах без разрешения автора запрещено.
    Последний раз редактировалось VVWVV; 10.02.2017 в 02:45.

  2. Пользователь сказал cпасибо:
    Osetin (08.02.2017)
  3. #2
    Аватар для Daniel_Cortez
    "Это не хак, это фича"

    Статус
    Оффлайн
    Регистрация
    06.04.2013
    Адрес
    Novokuznetsk, Russia
    Сообщений
    2,192
    Репутация:
    2589 ±
    Готов поспорить, есть способ и получше: использовать всего одну инструкцию fill, но перед её выполнением перезаписывать аргумент: количество ячеек для заполнения указанным значением. По сути это ни что иное, как модификация кода. Можно даже попробовать обойтись безо всяких amx_assembly.
    Индивидуально в ЛС по скриптингу не помогаю. Задавайте все свои вопросы здесь (click).

  4. #3
    Аватар для VVWVV
    ?

    Статус
    Оффлайн
    Регистрация
    09.07.2015
    Сообщений
    731
    Репутация:
    353 ±
    Цитата Сообщение от Daniel_Cortez Посмотреть сообщение
    Готов поспорить, есть способ и получше: использовать всего одну инструкцию fill, но перед её выполнением перезаписывать аргумент: количество ячеек для заполнения указанным значением. По сути это ни что иное, как модификация кода. Можно даже попробовать обойтись безо всяких amx_assembly.
    Т.е. ты предлагаешь такой вариант?
    PHP код:
    stock memset(array[], valsize sizeof array)
    {
        
    size <<= 2;
        
    #emit LOAD.S.alt 12
        #emit LOAD.S.pri 16
        #emit FILL size


  5. #4
    Аватар для Daniel_Cortez
    "Это не хак, это фича"

    Статус
    Оффлайн
    Регистрация
    06.04.2013
    Адрес
    Novokuznetsk, Russia
    Сообщений
    2,192
    Репутация:
    2589 ±
    Цитата Сообщение от VVWVV Посмотреть сообщение
    Т.е. ты предлагаешь такой вариант?
    PHP код:
    stock memset(array[], valsize sizeof array)
    {
        
    size <<= 2;
        
    #emit LOAD.S.alt 12
        #emit LOAD.S.pri 16
        #emit FILL size

    Не совсем. Аргумент инструкции fill представляет собой целочисленную константу, т.е. перед выполнением fill нужно перезаписывать тот аргумент в секции кода.
    PHP код:
    memset(array[], valuesize sizeof(array))
    {
        goto 
    rewrite_fill_arg;
    do_fill:
        
    #emit load.s.alt array
        #emit load.s.pri value
        #emit fill 1
        #emit zero.pri
        #emit retn
    rewrite_fill_arg:
        static 
    fill_arg_offset 0;
        if (
    fill_arg_offset != 0)
        { 
    // Горячий путь: смещение аргумента fill уже вычислено
            #emit load.alt fill_arg_offset
            #emit load.s.pri value
            #emit stor.i
            
    goto do_fill;
        }
        
    // (Здесь вычисление адреса аргумента)
        #emit load.alt fill_arg_offset
        #emit load.s.pri value
        #emit stor.i
        
    goto do_fill;

    Индивидуально в ЛС по скриптингу не помогаю. Задавайте все свои вопросы здесь (click).

  6. #5
    Аватар для VVWVV
    ?

    Статус
    Оффлайн
    Регистрация
    09.07.2015
    Сообщений
    731
    Репутация:
    353 ±
    Цитата Сообщение от Daniel_Cortez Посмотреть сообщение
    Не совсем. Аргумент инструкции fill представляет собой целочисленную константу, т.е. перед выполнением fill нужно перезаписывать тот аргумент в секции кода.
    PHP код:
    memset(array[], valuesize sizeof(array))
    {
        goto 
    rewrite_fill_arg;
    do_fill:
        
    #emit load.s.alt array
        #emit load.s.pri value
        #emit fill 1
        #emit zero.pri
        #emit retn
    rewrite_fill_arg:
        static 
    fill_arg_offset 0;
        if (
    fill_arg_offset != 0)
        { 
    // Горячий путь: смещение аргумента fill уже вычислено
            #emit load.alt fill_arg_offset
            #emit load.s.pri value
            #emit stor.i
            
    goto do_fill;
        }
        
    // (Здесь вычисление адреса аргумента)
        #emit load.alt fill_arg_offset
        #emit load.s.pri value
        #emit stor.i
        
    goto do_fill;

    Всё же придётся использовать библиотеку, ведь необходимо вычислить адрес аргумента. Конечно, мы можем вычислить это сами, но это будет нерационально.

  7. #6
    Аватар для Daniel_Cortez
    "Это не хак, это фича"

    Статус
    Оффлайн
    Регистрация
    06.04.2013
    Адрес
    Novokuznetsk, Russia
    Сообщений
    2,192
    Репутация:
    2589 ±
    Вполне рационально, если знать набор инструкций AMX.

    <адрес аргумента fill> = <физ. адрес блока памяти с кодом> - <физ. адрес блока памяти с данными> + <адрес функции memset> + <смещение аргумента fill от начала функции>
    Из этих составляющих смещение аргумента найти очень легко - это 32 бита: 4 на опкод proc, 8 - на jump, ещё по 8 на load.s.pri/alt и оставшиеся 4 сам опкод fill, без аргумента.
    По поводу физических адресов секций кода и данных уже не помню, но они как-то находились в YSI.
    Индивидуально в ЛС по скриптингу не помогаю. Задавайте все свои вопросы здесь (click).

  8. #7
    Аватар для VVWVV
    ?

    Статус
    Оффлайн
    Регистрация
    09.07.2015
    Сообщений
    731
    Репутация:
    353 ±
    Цитата Сообщение от Daniel_Cortez Посмотреть сообщение
    Вполне рационально, если знать набор инструкций AMX.

    <адрес аргумента fill> = <физ. адрес блока памяти с кодом> - <физ. адрес блока памяти с данными> + <адрес функции memset> + <смещение аргумента fill от начала функции>
    Из этих составляющих смещение аргумента найти очень легко - это 32 бита: 4 на опкод proc, 8 - на jump, ещё по 8 на load.s.pri/alt и оставшиеся 4 сам опкод fill, без аргумента.
    По поводу физических адресов секций кода и данных уже не помню, но они как-то находились в YSI.
    Хорошо. Ты меня убедил.. Но всё же мне это не очень нравится.

    Пожалуй, добавлю твой вариант. В скором времени добавлю тесты.

    UPD: Как оказалось, такой алгоритм уже существует в открытом доступе. Его подготовил так называемый BJIADOKC. Если говорить о скорости, то его алгоритм выигрывает.
      Открыть/закрыть

    PHP код:
    stock memset3(array[], valuesize sizeof(array))
    {
        if(!
    size)
        {
            print(
    "memset: Не указан размер массива");

            
    /*
            //размер массива
            #emit ADDR.pri array
            #emit ADD.c 4
            #emit PUSH.pri
            #emit SHL.C.pri 2

            //вызов заного
            #emit LREF.S.alt value
            #emit LCTRL 1
            #emit POP.alt
            #emit JZER
            #emit CALL.pri
            */

            
    return 0;
        }

        new 
    phys_addr;

        
    #emit LOAD.S.pri array
        #emit STOR.S.pri phys_addr

        /*return */
    memfill_byaddr(phys_addrvaluesize);
    }

    stock memfill_byaddr(addresscellsizevalue
    {
        new 
    idx;
        
        
    #emit LCTRL       6 //FIXME: #emit SCTRL 3
        #emit MOVE.alt
        #emit LCTRL       0 //FIXME: #emit LCTRL 2

        /*
        #emit LBL.S ADDR
        #emit PUSH.S
        idx += heapspace() - ADDR:0x97A1B35C;
        */

        #emit ADD
        #emit MOVE.alt

        //#emit POP.pri

        #emit LCTRL       1
        #emit SUB.alt
        #emit ADD.C       92

        //#emit LOADB.I

        #emit STOR.S.pri  idx
        #emit LOAD.S.pri  cellsize
        #emit SHL.C.pri   2
        #emit SREF.S.pri  idx
        
        #emit LOAD.S.alt  address
        #emit LOAD.S.pri  value
        #emit FILL        0



    UPD(2): Проверяя данный алгоритм, выяснил, что его алгоритм не полностью заполняет массив, а только частично.
    UPD(3): Алгоритм всё же работает, но, как выяснилось, он немного медленнее алгоритма ниже. Проблема была в том, что автор функции неправильно указал параметры memfill_byaddr при вызове.

    Сделал так, как ты и предлагал:
    PHP код:
    memset(array[], valuesize sizeof(array))
    {
        static
            
    fill_inst_offset;
        if (
    fill_inst_offset == 0) {
            
    #emit lctrl 6
            #emit move.alt                  // 4
            #emit lctrl 0                   // 8
            #emit add                       // 4
            #emit move.alt                  // 4
            #emit lctrl 1                   // 8
            #emit sub.alt                   // 4
            #emit add.c 92                  // 8
            #emit stor.pri fill_inst_offset // 8
        
    } {}                                // 
        #emit load.s.pri size               // 8
        #emit shl.c.pri 2                   // 8
        #emit sref.pri fill_inst_offset     // 8
        #emit load.s.alt 12                 // 8
        #emit load.s.pri 16                 // 8
        #emit fill 1                        // 4
        #emit zero.pri
        #emit retn

    Последний раз редактировалось VVWVV; 09.02.2017 в 12:18.

  9. #8
    Аватар для Daniel_Cortez
    "Это не хак, это фича"

    Статус
    Оффлайн
    Регистрация
    06.04.2013
    Адрес
    Novokuznetsk, Russia
    Сообщений
    2,192
    Репутация:
    2589 ±
    Цитата Сообщение от VVWVV Посмотреть сообщение
    Сделал так, как ты и предлагал:
    PHP код:
    memset(array[], valuesize sizeof(array))
    {
        static
            
    fill_inst_offset;
        if (
    fill_inst_offset == 0) {
            
    #emit lctrl 6
            #emit move.alt                  // 4
            #emit lctrl 0                   // 8
            #emit add                       // 4
            #emit move.alt                  // 4
            #emit lctrl 1                   // 8
            #emit sub.alt                   // 4
            #emit add.c 92                  // 8
            #emit stor.pri fill_inst_offset // 8
        
    } {}                                // 
        #emit load.s.pri size               // 8
        #emit shl.c.pri 2                   // 8
        #emit sref.pri fill_inst_offset     // 8
        #emit load.s.alt 12                 // 8
        #emit load.s.pri 16                 // 8
        #emit fill 1                        // 4
        #emit zero.pri
        #emit retn

    Что на счёт скорости работы этой функции, в частности, в сравнении с вариантом в 1-м посте?
    И да, ты проверял, как эта функция компилируется при разных значениях флагов -d и -O? Например, при -d(1/2/3) компилятор дополнительно ставит инструкции break для отладчика (которого в SA-MP, конечно же, нет), а при -O1 оптимизирует генерируемый байткод, что может повлиять на используемую комбинацию опкодов в выражении if - если эта комбинация будет занимать меньше или больше места, то смещение инструкции fill тоже станет другим.
    Индивидуально в ЛС по скриптингу не помогаю. Задавайте все свои вопросы здесь (click).

  10. #9
    Аватар для VVWVV
    ?

    Статус
    Оффлайн
    Регистрация
    09.07.2015
    Сообщений
    731
    Репутация:
    353 ±
    Цитата Сообщение от Daniel_Cortez Посмотреть сообщение
    Что на счёт скорости работы этой функции, в частности, в сравнении с вариантом в 1-м посте?
    И да, ты проверял, как эта функция компилируется при разных значениях флагов -d и -O? Например, при -d(1/2/3) компилятор дополнительно ставит инструкции break для отладчика (которого в SA-MP, конечно же, нет), а при -O1 оптимизирует генерируемый байткод, что может повлиять на используемую комбинацию опкодов в выражении if - если эта комбинация будет занимать меньше или больше места, то смещение инструкции fill тоже станет другим.
    Скорость работы в разы быстрее, чем в первом посте.

    Это было очевидно. Но повлиять это вряд ли сможет, ведь отсчёт идёт от инструкции lctrl 6. И при включении/выключении флагов меняется только то, что выше этой инструкции.

    -O0 -d3
    Код:
    	proc	; memset
    	; line e9
    	break	; 118
    	;$lcl size 14
    	;$lcl value 10
    	;$lcl array c
    	; line ea
    	break	; 11c
    	; line ec
    	break	; 120
    	load.pri cc
    	zero.alt
    	eq
    	jzer 18
    	;$exp
    	lctrl 6
    	move.alt      ; Начало 
    	lctrl 0
    	add 
    	move.alt 
    	lctrl 1
    	sub.alt 
    	add.c 5c
    	stor.pri cc
    l.18		; 174
    	load.s.pri 14
    	shl.c.pri 2
    	sref.pri cc
    	load.s.alt c
    	load.s.pri 10
    	fill 0
    	zero.pri 
    	retn 
    	zero.pri
    	retn
    -O1 -d0
    Код:
    	proc	; memset
    	; line e9
    	;$lcl size 14
    	;$lcl value 10
    	;$lcl array c
    	; line ea
    	break	; e8
    	; line ec
    	break	; ec
    	load.pri cc
    	jnz 18
    	;$exp
    	lctrl 6       
    	move.alt ; Начало
    	lctrl 0
    	add 
    	move.alt 
    	lctrl 1
    	sub.alt 
    	add.c 5c
    	stor.pri cc
    l.18		; 138
    	load.s.pri 14
    	shl.c.pri 2
    	sref.pri cc
    	load.s.alt c
    	load.s.pri 10
    	fill 0
    	zero.pri 
    	retn 
    	zero.pri
    	retn
    Тесты:
    Количество итераций: 1 000 000

    Без JIT:
    1) Размер массива: 100
    memset(VVWVV without AMX_asm): 423ms
    memset(Владокс): 729ms (с фиксом аргументов, иначе она работала бы неправильно)
    memset(Slice): 2270ms
    2) Размер массива: 10 000
    memset(VVWVV without AMX_asm): 7920ms
    memset(Владокс): 8336ms (с фиксом аргументов, иначе она работала бы неправильно)
    memset(Slice): 12001ms

    C JIT:
    1) Размер массива: 100
    memset(VVWVV without AMX_asm): 17ms
    memset(Владокс): 30ms (с фиксом аргументов, иначе она работала бы неправильно)
    memset(Slice): 117ms
    2) Размер массива: 10 000
    memset(VVWVV without AMX_asm): 17ms
    memset(Владокс): 31ms (с фиксом аргументов, иначе она работала бы неправильно)
    memset(Slice): 2130ms

  11. #10
    Аватар для vovandolg
    Пользователь

    Статус
    Оффлайн
    Регистрация
    17.11.2015
    Адрес
    Stavropol
    Сообщений
    1,369
    Репутация:
    113 ±
    зачем компилировать с JIT если JIT плагин не поддерживает корректно emit конструкции
    [Anticheat]___Invisible Fly Hack
    [Anticheat]____Weapon/Ammo Hack
    [Function]______ResetPlayerWeaponSlot
    [Function]_______FIX_SetPlayerAmmo
    [ServerMod]______TDM | Zombie Apokalypse

 

 
Страница 1 из 2 1 2 ПоследняяПоследняя

Информация о теме

Пользователи, просматривающие эту тему

Эту тему просматривают: 1 (пользователей: 0 , гостей: 1)

Ваши права

  • Вы не можете создавать новые темы
  • Вы не можете отвечать в темах
  • Вы не можете прикреплять вложения
  • Вы не можете редактировать свои сообщения
  •