Форум программистов
 

Восстановите пароль или Зарегистрируйтесь на форуме, о проблемах и с заказом рекламы пишите сюда - alarforum@yandex.ru, проверяйте папку спам!

Вернуться   Форум программистов > Низкоуровневое программирование > Assembler - Ассемблер (FASM, MASM, WASM, NASM, GoASM, Gas, RosAsm, HLA) и не рекомендуем TASM
Регистрация

Восстановить пароль
Повторная активизация e-mail

Купить рекламу на форуме - 42 тыс руб за месяц

Ответ
 
Опции темы Поиск в этой теме
Старый 25.09.2012, 19:37   #11
veniside
Старожил
 
Регистрация: 03.01.2011
Сообщений: 2,508
По умолчанию

в винде фасткол описан в мсдн, интересно, как это стандартизировано в линухе и прочих маках.
"Когда приходит положенное время, человек перестаёт играть в пинбол. Только и всего."
veniside вне форума Ответить с цитированием
Старый 29.09.2012, 13:23   #12
f.hump
C/C++, Asm
Участник клуба
 
Аватар для f.hump
 
Регистрация: 02.03.2010
Сообщений: 1,323
По умолчанию

пример 2: AMD cpuid
Так случилось, по непонятным причинам, что то что может Intel cpu всегда, AMD cpu может только иногда. Поэтому, и не только поэтому, бывает полезно знать на что способен cpu. Данный пример только для AMD cpu, даже если этот код сработает на Intel, его резутат не будет иметь смысла, поскольку AMD CPUID и Intel CPUID практически не имеют ничего общего.
Кому интересно может обратиться к первоисточникам, которые можно найти здесь и здесь.
Вложения
Тип файла: txt amd_cpuid.asm.txt (5.4 Кб, 157 просмотров)
Тип файла: txt amd_cpuid.h.txt (14.8 Кб, 159 просмотров)
f.hump вне форума Ответить с цитированием
Старый 04.11.2012, 23:22   #13
8Observer8
Старожил
 
Аватар для 8Observer8
 
Регистрация: 02.01.2011
Сообщений: 3,322
По умолчанию

Вызов функции dll из программы на C.

Описание: при нажатии на кнопку вызывается функция Add, которая находится в dll. Add принимает два числа и возращает их сумму.



Код:
#include <windows.h>
#include <tchar.h>
#include "resource.h"

#define IDD_DIALOG1 100

INT_PTR CALLBACK DialogProc(HWND hwndDlg,
                            UINT uMsg,
                            WPARAM wParam,
                            LPARAM lParam);

LPCTSTR DlgName = MAKEINTRESOURCE(IDD_DIALOG1);

int WINAPI _tWinMain(HINSTANCE hInstance,
                     HINSTANCE hPrevInstance,
                     LPTSTR lpCmdLine,
                     int nCmdShow)
{
    DialogBoxParam(hInstance, DlgName, NULL, DialogProc, (LPARAM) NULL);

    return TRUE;
}

HMODULE hLib;
FARPROC AddDigsAddr;
int (_stdcall *AddDigs)(int, int);

INT_PTR CALLBACK DialogProc(HWND hWnd,
                            UINT uMsg,
                            WPARAM wParam,
                            LPARAM lParam)
{
    int result;

    switch(uMsg) {
    case WM_COMMAND:
        if (LOWORD(wParam) == IDC_BUTTON1) {
            hLib = LoadLibrary(_T("add.dll"));
            if (hLib == NULL) {
                MessageBox(NULL, _T("Cannot load library"), _T("Load Library"), MB_OK);
                break;
            }

            AddDigsAddr = GetProcAddress(hLib, "AddDigs");
            if (AddDigsAddr == NULL) {
                MessageBox(NULL, _T("AddDigs function not found"), _T("Load Library"), MB_OK);
                break;
            }

            AddDigs = (int(_stdcall*)(int, int))AddDigsAddr;

            result = (*AddDigs)(2, 5);

            FreeLibrary(hLib);
        }
        break;
    case WM_CLOSE:
        EndDialog(hWnd, 0);
        break;
    default:
        return FALSE;
    }

    return TRUE;
}
Код:
// Generated by ResEdit 1.5.11
// Copyright (C) 2006-2012
// http://www.resedit.net

#include <windows.h>
#include <commctrl.h>
#include <richedit.h>
#include "resource.h"




//
// Dialog resources
//
LANGUAGE LANG_NEUTRAL, SUBLANG_NEUTRAL
IDD_DIALOG1 DIALOG 0, 0, 186, 95
STYLE DS_3DLOOK | DS_CENTER | DS_MODALFRAME | DS_SHELLFONT | WS_CAPTION | WS_VISIBLE | WS_POPUP | WS_SYSMENU
CAPTION "Using DLL"
FONT 8, "Ms Shell Dlg"
{
    PUSHBUTTON      "Call function", IDC_BUTTON1, 63, 41, 60, 14
}
add.dll
Код:
.386
option casemap:none

include c:\masm32\include\masm32rt.inc 

.code
LibMain proc instance:dword,reason:dword,unused:dword 
    mov     eax,1
    ret
LibMain     endp

AddDigs proc val1:DWORD, val2:DWORD
    mov eax, val1
    add eax, val2
    ret
AddDigs endp
End LibMain
add.def
Цитата:
LIBRARY add
EXPORTS AddDigs
amake.bat
Цитата:
ml /c /coff "%1.asm"
link /SUBSYSTEM:CONSOLE "%1.obj"
amakedll.bat
Цитата:
link /SUBSYSTEM:CONSOLE /DLL /DEF:"%1.def" "%1.obj"

Последний раз редактировалось 8Observer8; 04.11.2012 в 23:25.
8Observer8 вне форума Ответить с цитированием
Старый 04.11.2012, 23:32   #14
8Observer8
Старожил
 
Аватар для 8Observer8
 
Регистрация: 02.01.2011
Сообщений: 3,322
По умолчанию

Открываем и закрываем CD-ROM

Код:
.386
.model flat, stdcall
option casemap:none

include c:\masm32\include\windows.inc
include c:\masm32\include\user32.inc
include c:\masm32\include\kernel32.inc
include c:\masm32\include\winmm.inc

includelib c:\masm32\lib\user32.lib
includelib c:\masm32\lib\kernel32.lib
includelib c:\masm32\lib\WinMM.lib

mciSendCommand proto :DWORD,:DWORD,:DWORD,:DWORD

.data?
dID MCIDEVICEID ?

.data
sz_open_param db "CDAudio", 0
OpenParm MCI_OPEN_PARMS <>
SetParm MCI_SET_PARMS <>

.code
start:

    mov OpenParm.lpstrDeviceType, offset sz_open_param
    invoke mciSendCommand, 0, MCI_OPEN, MCI_OPEN_TYPE, addr OpenParm
    mov eax, OpenParm.wDeviceID
    mov dID, eax
    invoke mciSendCommand, dID, MCI_SET, MCI_SET_DOOR_OPEN, addr SetParm
    invoke mciSendCommand, dID, MCI_SET, MCI_SET_DOOR_CLOSED, addr SetParm
    invoke mciSendCommand, dID, MCI_CLOSE, MCI_NOTIFY, addr SetParm

    invoke ExitProcess, 0

end start
8Observer8 вне форума Ответить с цитированием
Старый 17.11.2012, 12:25   #15
f.hump
C/C++, Asm
Участник клуба
 
Аватар для f.hump
 
Регистрация: 02.03.2010
Сообщений: 1,323
По умолчанию

Про повышение производительности кода.

Ходят слухи буд-то код написанный на ассемблере по определению будет быстрее чем код полученый от компилятора языка более высокого уровня. Возможно это было правдой в прошлом веке, сегодня это не так, далеко не так. Для того чтобы сделать на пол-такта быстрее чем хороший компилятор прихотся потрахаться, недетски. Единственный надежный способ сделать быстрее это кодить под конкретную архитектуру (legacy x86, Pentium, Core, Sandy Bridge), зная сколько и чего (ресурсов) доступно. Если нет уверенности на какой архитектуре будет использоваться код, то писание на ассемблере не увиличивает ничего кроме геммороя.

Если все-таки сильно хочется сделать побыстрее, то как ни странно самый простой и наиболее эффективный способ повышения производительности кода называется раскрытие циклов (loop unrolling).
Это значит, что если например есть цикл с фиксированным числом повторений
Код:
MOV rcx, 1000
align 16
megaloop:

; {some very important action: rcx}

SUB rcx, 1
JNZ megaloop
будет медленне, чем
Код:
{some very important action: 1000}
{some very important action: 999}
{some very important action: 998}
...
{some very important action: 2}
{some very important action: 1}
Понятно, что объем кода при этом увеличивается даже очень, зато быстрее.
Еще периодически слышу, что LOOP работает быстрее чем SUB rcx & JMP. Так случилось, что ни с чем младше Core работать не приходилось, и по имеющемуся опыту LOOP в среднем в два раза медленнее чем SUB rcx & JMP. Плюс у LOOP _цель есть ограничение в 128 байт на максимальное расстояние до _цели.
Есть еще общее правило оптимизации производительности, которое гласит, что все цели переходов (jump targets) полезно выравнивать по 16 байтной границе. Сильного эффекта от последнего наблюдать не приходилось, но и хуже от него не становится, так что можно использовать.

Последний раз редактировалось f.hump; 18.11.2012 в 01:31.
f.hump вне форума Ответить с цитированием
Старый 17.11.2012, 15:02   #16
f.hump
C/C++, Asm
Участник клуба
 
Аватар для f.hump
 
Регистрация: 02.03.2010
Сообщений: 1,323
По умолчанию

Продолжая тему повышения производительности, напомню очевидные вещи.

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

Кеширует процессор кеш-линиями шириной 32 или 64 байта (зависит от архитектуры). И тут полезно, чтобы размер данных/параметров был кратен ширине кеш-линии , либо целое число параметров заполняло линию целиком (не было разрывов кеш-линии). Наиболее известный пример такой оптимизации это фактический отказ от 10-байтного формата данных с плавающей точкой, который поддерживается х87 FPU. Если четыре 8-ми байтных с плавающей точкой покрывают 32-х байтную линию без остатка, для доступа к четвертому 10-ти байтному с плавающей точкой потребуется заполнение второй линии и дополнительное выравнивание, что фактически убивает производительность.

Последний раз редактировалось f.hump; 18.11.2012 в 01:30.
f.hump вне форума Ответить с цитированием
Старый 18.11.2012, 01:23   #17
f.hump
C/C++, Asm
Участник клуба
 
Аватар для f.hump
 
Регистрация: 02.03.2010
Сообщений: 1,323
По умолчанию

С появлением потоковых расширений (streaming extensions) на x86, появилсь возможность поднять производительность кода, который можно распараллелить. Должен отметтить, что коду, в котором нечего распараллеливать, наличие потоковых расшерений абсолютно параллельно. По имеющемуся опыту вычислительные задачи действительно имеют хороший потенциал для распаллеливания и серезно выигрывают при использовании потоковых расширений. Тут, я думаю, место для простого примера. Однажды столнулся с конвертацией массива целых из big-endian в little-endian и обратно. С одной стороны это можно сделать так:
Код:
__declspec(align(16)) unsigned int codes[4];

for (int i(0);i<4;i++) {
    codes[i] = (codes[i]>>24) | (codes[i]<<24) | ((codes[i]<<8) & 0x00ff0000) | ((codes[i]>>8) & 0x00ff00);

}
или так

Код:
LEA rcx, [codes]

MOV eax, [rcx]
BSWAP eax
MOV [rcx], eax

MOV eax, [rcx+4]
BSWAP eax
MOV [rcx+4], eax

MOV eax, [rcx+8]
BSWAP eax
MOV [rcx+8], eax

MOV eax, [rcx+12]
BSWAP eax
MOV [rcx+12], eax
еще вот так

Код:
.CONST
ALIGN 16
swap_shuf		DWORD	00010203h, 04050607h, 08090A0Bh, 0C0D0E0Fh
______

LEA rcx, [codes]

MOVDQA xmm0, [rcx]
PSHUFB xmm0, XMMWORD PTR [swap_shuf]
MOVDQA [rcx], xmm0
Случай 3 оказался в 1.5 раза быстрее случая 2 в пересчете на 1 елемент массива. Я рыдал от переполнявшего меня счастья.
f.hump вне форума Ответить с цитированием
Старый 25.11.2012, 23:21   #18
f.hump
C/C++, Asm
Участник клуба
 
Аватар для f.hump
 
Регистрация: 02.03.2010
Сообщений: 1,323
По умолчанию

В общем и в целом потоковые расширения (SSE, AVX) очень и очень полезная вещь: 16 регистров (xmm) шириной 16 байт (SSE), или 16 регистров (ymm) шириной 32 байта (AVX) + целочиcленная арифметика/арифметика с плавающей точкой/перестановки на этих регистрах. (про MMX я не пишу, потому что это прошлый век).

При использовании потоковых расширений выравнивание памяти - необходимость. Все операции с операндом указывающим на память (за исключением MOVxxx) требуют, чтобы память, на которую указывает операнд была выравнена по 16-ти байтной границе, иначе general protection exception.

Должен признать, что когда речь идет о целочисленной арифметике, старая добрая legacy x86 очень даже ничего, в том смысле, что если сделать полный unroll код оказывается всего в 1.5-2 раза медленне кода с использованием потоковых расширений (хотя может это я где-то слажал).
Правильное использование потоковых расширений на флоатах теоретически ускоряет выполнение задачи в 4 раза. Простой пример по этой теме линейный метод наименьших квадратов.

С одной стороны его можно записать так:
Код:
float LinearLeastSquares2D_CPP(float * output, unsigned int datacount, float * yv, float * xv);
extern float LinearLeastSquares2D_SSE(float * output, unsigned int datacount, float * yv, float * xv);



float LinearLeastSquares2D_CPP(float * output, unsigned int datacount, float * yv, float * xv) {
	float r2(0.0f), x2(0.0f), y2(0.0f), xa(0.0f), ya(0.0f), xy(0.0f);

	for (unsigned int i(0);i<datacount;i++) {
		xa += xv[i]; ya += yv[i];
		xy += xv[i]*yv[i];

		x2 += xv[i]*xv[i];
		y2 += yv[i]*yv[i];
	}

	x2 = x2*datacount - xa*xa;
	y2 = y2*datacount - ya*ya;

	xy = xy*datacount - xa*ya;

	if (x2) {
		output[0] = xy/x2;
		output[1] = (ya - output[0]*xa)/datacount;

		r2 = xy*xy/x2/y2;
	}


	return r2;

}

Последний раз редактировалось f.hump; 25.11.2012 в 23:27.
f.hump вне форума Ответить с цитированием
Старый 25.11.2012, 23:24   #19
f.hump
C/C++, Asm
Участник клуба
 
Аватар для f.hump
 
Регистрация: 02.03.2010
Сообщений: 1,323
По умолчанию

с другой стороны так:

Код:
.CODE

?LinearLeastSquares2D_SSE@@YAMPEAMI00@Z	PROC
	SUB rsp, 16

	MOV [rsp], edx
	MOV [rsp+4], edx

	ADD edx, 3
	SHR edx, 2

	XORPS xmm3, xmm3 ; y
	XORPS xmm4, xmm4 ; x
	XORPS xmm5, xmm5 ; xy
	XORPS xmm6, xmm6 ; x2
	XORPS xmm7, xmm7 ; y2

align 16
	calcloop:

		MOVAPS xmm0, [r8]
		MOVAPS xmm1, [r9]

		ADDPS xmm3, xmm0
		ADDPS xmm4, xmm1

		MOVAPS xmm2, xmm0

		MULPS xmm0, xmm1
		MULPS xmm1, xmm1

		ADDPS xmm5, xmm0
		ADDPS xmm6, xmm1

		MULPS xmm2, xmm2
		ADDPS xmm7, xmm2

		ADD r8, 16
		ADD r9, 16

		SUB edx, 1
	JNZ calcloop

	CVTPI2PS xmm0, QWORD PTR [rsp]	

	HADDPS xmm3, xmm3
	HADDPS xmm4, xmm4
	HADDPS xmm5, xmm5
	HADDPS xmm6, xmm6
	HADDPS xmm7, xmm7
		
	MOVLHPS xmm0, xmm0 ; N

	HADDPS xmm3, xmm4 ; y, x
	HADDPS xmm5, xmm5 ; xy
		
	MOVAPS xmm4, xmm3
	SHUFPS xmm4, xmm4, 33h ; x, y, x, y

	HADDPS xmm7, xmm6 ; y2, x2
	
	MULPS xmm4, xmm3 ; x.y, y.y, x.x, x.y

	MOVSS xmm7, xmm5 ; xy, y2, x2, x2

	MULPS xmm7, xmm0
	SUBPS xmm7, xmm4

	SHUFPS xmm7, xmm7, 60h ; Sxy, Sxy, Sxx, Syy

	MOVHLPS xmm6, xmm7
	MOVLHPS xmm7, xmm7
	
	UNPCKLPS xmm6, xmm7

	MOVHLPS xmm4, xmm3

	DIVPS xmm7, xmm6 ; b, b, c, c
	
	MULSS xmm4, xmm7 ; xb
	SUBSS xmm3, xmm4 ; y - bx
	DIVSS xmm3, xmm0 ; a

	MOVHLPS xmm0, xmm7
	MULSS xmm0, xmm7 ; r2

	MOVSS DWORD PTR [rcx], xmm7
	MOVSS DWORD PTR [rcx+4], xmm3


	ADD rsp, 16
	RET
?LinearLeastSquares2D_SSE@@YAMPEAMI00@Z	ENDP

END
По имеющимся данным, случай два серьезно отрывается от случая 1, показывая цифру близкую к теоретическому пределу.

Последний раз редактировалось f.hump; 25.11.2012 в 23:36.
f.hump вне форума Ответить с цитированием
Старый 27.11.2012, 22:53   #20
f.hump
C/C++, Asm
Участник клуба
 
Аватар для f.hump
 
Регистрация: 02.03.2010
Сообщений: 1,323
По умолчанию

Еще пару слов про потоковые расширения. Не смотря на то что SSE/AVX иструкции стоят в одной очереди с x86 иструкциями, выполняются они на независимых исполнительных блоках. Это значит, что код для x86 может выполнятся одновременно с логически слабо зависимым/независимым кодом для SSE/AVX, тем самым повышая производительность. Конечно, процессор сканирует очедь с целью обнаружения таких ситуаций, учитывая что глубина сканирования конечна, имеет смысл упростить задачу процессора чередуя код для x86 и SSE/AVX.

Код:
		NOT eax									
		MOV r13d, r10d
		MOV r10d, eax
																		
		MOV edi, eax
		ROR edi, 6

	PADDD xmm2, [r11+64]
	MOVDQA [r11+64], xmm2

		ROR eax, 25									
		XOR eax, edi
																	

		ROR edi, 5
		XOR eax, edi ; S1
	MOVDQA xmm2, xmm3
				
		ADD eax, ebx ; S1 + Ch
		MOV edi, edx
		MOV ebx, edx
																
		ADD eax, [r11+64] ; T1
		ROR edi, 13

	PSRLDQ xmm1, 4
	PSLLDQ xmm3, 12
Еще нужно помнить, что для достижения максимальной производительности стоит избегать чередования целочисленной арифметики и арифметики с плавающей точкой SSE/AVX, любой преход от целочисленной к плавающей точке и наоборот требует сброса состояния исполнительного блока, что очень и очень медленно. Еще одна медленная операция это MOVD xmm0, eax / MOVD eax, xmm0 с одной стороны приятно переместить чего-нибудь с одного регистра на другой, с другой чтороны эти регистры принадлежат разным исполнительным блокам, и для выполнения необходима синхронизация этих блоков, а любая операция требующая синхронизации это безбожно медленная операция.

Последний раз редактировалось f.hump; 27.11.2012 в 23:03.
f.hump вне форума Ответить с цитированием
Ответ


Купить рекламу на форуме - 42 тыс руб за месяц

Опции темы Поиск в этой теме
Поиск в этой теме:

Расширенный поиск


Похожие темы
Тема Автор Раздел Ответов Последнее сообщение
Составить на языке ассемблера IBM PC подпрорамму вычисления Airat1790 Помощь студентам 0 18.04.2012 13:39
Нужны шаблоны(примеры программ) по Паскалю Сержuk Помощь студентам 1 10.03.2011 14:48
На языке ассемблера IBM PC создать подпрограммы: Gertryda Assembler - Ассемблер (FASM, MASM, WASM, NASM, GoASM, Gas, RosAsm, HLA) и не рекомендуем TASM 1 09.01.2011 23:13