Создание DLL для использования в LabVIEW

Простейшие вопросы в области инженерной разработки
Аватара пользователя
IvanLis

Activity Professionalism Tutorials Gold Man of the year 2012
Автор
guru
guru
Сообщения: 5602
Зарегистрирован: 02 дек 2009, 17:44
Награды: 7
Версия LabVIEW: 2015, 2016
Откуда: СССР
Благодарил (а): 34 раза
Поблагодарили: 113 раз

Re: Создание DLL для использования в LabVIEW

Сообщение IvanLis »

Над векторными вычислениями я думал уже, но это следующий этап.
Сейчас сделал предварительный расчет SIN и COS, в основном цикле использую уже готовые значения.
Снимок экрана от 2024-07-01 12-07-20.png
Никогда не задумывался, что такие элементарные операции требуют столько времени :crazy:
Аватара пользователя
IvanLis

Activity Professionalism Tutorials Gold Man of the year 2012
Автор
guru
guru
Сообщения: 5602
Зарегистрирован: 02 дек 2009, 17:44
Награды: 7
Версия LabVIEW: 2015, 2016
Откуда: СССР
Благодарил (а): 34 раза
Поблагодарили: 113 раз

Re: Создание DLL для использования в LabVIEW

Сообщение IvanLis »

FMA почему то проигрывает, пробовал компилировать по разному и проверял в разных OS.
Проигрывает всегда, но разница в Linux меньше.
Снимок экрана от 2024-07-01 13-21-07.png

Код: Выделить всё

inline void TestNoFMA(float a[], float b[], float c[], float res[], int Size)
{
   int i;
	
   for(i=0; i < Size; i++) {
      res[i] =  a[i] * b[i] + c[i];
   }
}

inline void TestYesFMA(float a[], float b[], float c[], float res[], int Size)
{
   int i;
	
   for(i=0; i < Size; i++) {
      res[i] =  fmaf(a[i], b[i], c[i]);
   }
}
AndreyDmitriev

Activity Professionalism Tutorials Gold Black
VIP
VIP
Сообщения: 1421
Зарегистрирован: 03 фев 2010, 00:42
Награды: 8
Версия LabVIEW: 6.1 - 2025
Откуда: Германия
Благодарил (а): 1 раз
Поблагодарили: 78 раз
Контактная информация:

Re: Создание DLL для использования в LabVIEW

Сообщение AndreyDmitriev »

IvanLis писал(а): 01 июл 2024, 13:23 FMA почему то проигрывает, пробовал компилировать по разному и проверял в разных OS.
Проигрывает всегда, но разница в Linux меньше.
Снимок экрана от 2024-07-01 13-21-07.png

Код: Выделить всё

inline void TestNoFMA(float a[], float b[], float c[], float res[], int Size)
{
   int i;
	
   for(i=0; i < Size; i++) {
      res[i] =  a[i] * b[i] + c[i];
   }
}

inline void TestYesFMA(float a[], float b[], float c[], float res[], int Size)
{
   int i;
	
   for(i=0; i < Size; i++) {
      res[i] =  fmaf(a[i], b[i], c[i]);
   }
}
Там много факторов, начиная от пропускной способности памяти, в которую обычно и упирается на больших массивах, и заканчивая тем, что не стоит гонять это дело по невыровненным данным.
Если данных немного, чтоб они из кеша брались, то выигрыш в общем есть, особенно если цикл развернуть, хотя б вчетверо:

Код: Выделить всё

SINECOSINE_API int fnNoFMACompute(int n)
{
    for (int i = 0; i < n; i++) dst[i] = a[i] * b[i] + c[i];
    return 0;
}


SINECOSINE_API int fnFMACompute(int n)
{
    // Perform FMA using AVX2
    for (int i = 0; i < n; i += 16) {
        __m256d va = _mm256_load_pd(&a[i]);
        __m256d vb = _mm256_load_pd(&b[i]);
        __m256d vc = _mm256_load_pd(&c[i]);
        __m256d va1 = _mm256_load_pd(&a[i + 4]);
        __m256d vb1 = _mm256_load_pd(&b[i + 4]);
        __m256d vc1 = _mm256_load_pd(&c[i + 4]);
        __m256d va2 = _mm256_load_pd(&a[i + 8]);
        __m256d vb2 = _mm256_load_pd(&b[i + 8]);
        __m256d vc2 = _mm256_load_pd(&c[i + 8]);
        __m256d va3 = _mm256_load_pd(&a[i + 12]);
        __m256d vb3 = _mm256_load_pd(&b[i + 12]);
        __m256d vc3 = _mm256_load_pd(&c[i + 12]);

        // Perform FMA: res[i] = a[i] * b[i] + c[i]
        __m256d vres = _mm256_fmadd_pd(va, vb, vc);
        __m256d vres1 = _mm256_fmadd_pd(va1, vb1, vc1);
        __m256d vres2 = _mm256_fmadd_pd(va2, vb2, vc2);
        __m256d vres3 = _mm256_fmadd_pd(va3, vb3, vc3);

        _mm256_store_pd(&dst[i], vres);
        _mm256_store_pd(&dst[i + 4], vres1);
        _mm256_store_pd(&dst[i + 8], vres2);
        _mm256_store_pd(&dst[i + 12], vres3);
    }
    return 0;
}
Хороший компилятор это и сам сделает.

На 100000 элементов у меня разница в полтора раза в пользу FMA.

У вас скорее всего пенальти при загрузке по невыровненным данным, либо что-то не так с fmaf(). Я б уровнем ниже спустился с явной загрузкой в регистры и вызовом _mm256_fmadd_pd(), либо глянул ассемблерный листинг, во что оно там компиляется.
Мож с опциями компиляции что-то не то. ИИ мне пишет что надо как-то так компилировать: gcc -mavx2 -mfma -O3 -o fma_avx2 fma_avx2.c
Аватара пользователя
IvanLis

Activity Professionalism Tutorials Gold Man of the year 2012
Автор
guru
guru
Сообщения: 5602
Зарегистрирован: 02 дек 2009, 17:44
Награды: 7
Версия LabVIEW: 2015, 2016
Откуда: СССР
Благодарил (а): 34 раза
Поблагодарили: 113 раз

Re: Создание DLL для использования в LabVIEW

Сообщение IvanLis »

AndreyDmitriev писал(а): 01 июл 2024, 15:26 Мож с опциями компиляции что-то не то. ИИ мне пишет что надо как-то так компилировать: gcc -mavx2 -mfma -O3 -o fma_avx2 fma_avx2.c
Всем спасибо за участие :thank: !

Собираю DLL я через объектный файл командой:
i686-w64-mingw32-gcc -c -mavx2 -mfma -msse -O3 -fPIC AFAR.c -o AFARx32.o (для Win32)
а потом преобразую в динамическую библиотеку:
i686-w64-mingw32-gcc AFARx32.o -shared -o AFARx32.dll

Я думаю пока можно остановиться.
Не получается у меня такого выигрыша, как у Вас, но тенденция положительная, буду работать.

Сравнение Win7-32bit (VirtualBox, но вся виртуализация и доступ к функциям процессора включены)
Снимок экрана от 2024-07-01 17-27-13.png
Вычисление ДН
Снимок экрана от 2024-07-01 17-13-47.png
Ubuntu 22.04lts-64bit
Снимок экрана от 2024-07-01 17-41-40.png
Вычисление ДН
Снимок экрана от 2024-07-01 17-49-19.png
Возможно на живой OS Windows результаты будут иные, но сейчас не на чем протестировать.
Заморачиваться с векторными вычислениями пока не вижу смысла, выигрыш совсем маленький, да и нет там особо где развернуться....
AndreyDmitriev

Activity Professionalism Tutorials Gold Black
VIP
VIP
Сообщения: 1421
Зарегистрирован: 03 фев 2010, 00:42
Награды: 8
Версия LabVIEW: 6.1 - 2025
Откуда: Германия
Благодарил (а): 1 раз
Поблагодарили: 78 раз
Контактная информация:

Re: Создание DLL для использования в LabVIEW

Сообщение AndreyDmitriev »

IvanLis писал(а): 01 июл 2024, 17:57
AndreyDmitriev писал(а): 01 июл 2024, 15:26 Мож с опциями компиляции что-то не то. ИИ мне пишет что надо как-то так компилировать: gcc -mavx2 -mfma -O3 -o fma_avx2 fma_avx2.c
Всем спасибо за участие :thank: !

Возможно на живой OS Windows результаты будут иные, но сейчас не на чем протестировать.
Заморачиваться с векторными вычислениями пока не вижу смысла, выигрыш совсем маленький, да и нет там особо где развернуться....
Я в этом году довольно много поигрался с AVX2/AVX512 и в общем да, выигрыш там не всегда большой. Идея в общем не лишена смысла, попробовать можно всегда, но особенно на AVX512 частота проца начинает дропаться из-за перегрева, потому что для вычислений по 512 битам ему банально надо переключать одномоментно слишком много транзисторов. Ну и память не успевает подгонять данные, выигрыш заметен только на многопроцессорных тачках с двенадцатиканальной памятью.
Аватара пользователя
IvanLis

Activity Professionalism Tutorials Gold Man of the year 2012
Автор
guru
guru
Сообщения: 5602
Зарегистрирован: 02 дек 2009, 17:44
Награды: 7
Версия LabVIEW: 2015, 2016
Откуда: СССР
Благодарил (а): 34 раза
Поблагодарили: 113 раз

Re: Создание DLL для использования в LabVIEW

Сообщение IvanLis »

AndreyDmitriev писал(а): 02 июл 2024, 07:48 Я в этом году довольно много поигрался с AVX2/AVX512 и в общем да, выигрыш там не всегда большой. Идея в общем не лишена смысла, попробовать можно всегда, но особенно на AVX512 частота проца начинает дропаться из-за перегрева, потому что для вычислений по 512 битам ему банально надо переключать одномоментно слишком много транзисторов. Ну и память не успевает подгонять данные, выигрыш заметен только на многопроцессорных тачках с двенадцатиканальной памятью.
Тут в принципе прослеживается аналогия с CUDA.
Иногда, там где ждешь прогресса, его совсем не получаешь :crazy:

Одно дело, когда загрузил в память данные и GPU считает, как например при обучении ИНС.
А другое, когда приходится постоянно работать с памятью, накладные расходы "съедают" скорость вычислений.
ujin1
developer
developer
Сообщения: 256
Зарегистрирован: 06 ноя 2020, 15:37
Версия LabVIEW: 19
Благодарил (а): 19 раз
Поблагодарили: 40 раз
Контактная информация:

Re: Создание DLL для использования в LabVIEW

Сообщение ujin1 »

IvanLis писал(а): 01 июл 2024, 00:05 Ну что я могу сказать, при вызове элементарной функции выигрыша нет.
Видимо на вызов ее из DLL тратиться много времени, что не компенсируется скоростью исполнения.
Если посмотреть ассемблерный листинг dll, то в ней достаточно много дополнительного кода. Наверное важного.
Если скомпилировать dll из ассемблера то лишнего кода нет вообще.
Например 2001 году я программировал микроконтроллеры и пробовал собирать dll ассемблером

Код dll

Код: Выделить всё

;DLLrus.asm
;DLL для WIN32 - перекодировщик из koi8r в cp1251
;
	.486
	.model flat

;Функции определяемые в этой DLL
ifdef _MASM_
	public _koi2win_asm@0	;koi2win_asm - перекодирует символ в AL
	public _koi2win@4		;CHAR WINAPI koi2win(CHAR symbol)
	public _koi2wins_asm@0	;koi2wins_asm - перекодирует строку в [EAX]
	public _koi2wins@4	;VOID WINAPI koi2wins(CHAR * string)
else
	public koi2win_asm	;То же для TASM
	public koi2win
	public koi2wins_asm
	public koi2wins
endif
	.const
k2w_tbl	db	16 dup(0)	;
		db	16 dup(0)	;
		db	00h, 00h, 00h, 38h, 00h, 00h, 00h, 00h
		db	00h, 00h, 00h, 00h, 00h, 00h, 00h, 00h
		db	00h, 00h, 00h, 28h, 00h, 00h, 00h, 00h
		db	00h, 00h, 00h, 00h, 00h, 00h, 00h, 00h
		db	7Eh, 60h, 61h, 76h, 64h, 65h, 74h, 63h
		db	75h, 68h, 69h, 6Ah, 6Bh, 6Ch, 6Dh, 6Eh
		db	6Fh, 7Fh, 70h, 71h, 72h, 73h, 66h, 62h
		db	7Ch, 7Bh, 67h, 78h, 7Dh, 79h, 77h, 7Ah
		db	5Eh, 40h, 41h, 56h, 44h, 45h, 54h, 43h
		db	55h, 48h, 49h, 4Ah, 4Bh, 4Ch, 4Dh, 4Eh
		db	4Fh, 5Fh, 50h, 51h, 52h, 53h, 46h, 42h
		db	5Ch, 5Bh, 47h, 58h, 5Dh, 59h, 57h, 5Ah

	.code
_start@12:
		mov	al,1	;надо вернуть ненулевое число
		ret	12

;Процедура BYTE WINAPI koi2win(BYTE symbol)
;Точка входа для вызова из С
ifdef _MASM_
	_koi2win@4	proc
else
	koi2win	proc
endif
		pop 	ecx	; обратный адрес в ECX
		pop 	eax	; параметр в ECX, теперь стек очищен от параметров
		push	ecx	; обратный адрес надо вернуть в стек для RET

ifdef _MASM_
	_koi2win@4	endp
else
	koi2win	endp
endif

;Процедура koi2win_asm
;Точка входа для ассемблерных программ
;ввод AL - код символа в koi
;вывод AL - код того же символа в WIN
ifdef _MASM_
	_koi2win_asm@0	proc
else
	koi2win_asm		proc
endif

		test	al,80h
		jz	dont_decode
		push	ebx
		mov	ebx, offset k2w_tbl
		sub	al,80h
		xlat
		add	al,80h
		pop	ebx
dont_decode:
		ret

ifdef _MASM_
	_koi2win_asm@0	endp
else
	koi2win_asm	endp
endif

;Процедура VOID WINAPI koi2wins(BYTE * koistring)
;Точка входа для вызова из С
ifdef _MASM_
	_koi2wins@4	proc
else
	koi2wins	proc
endif
		pop 	ecx	; обратный адрес в ECX
		pop 	eax	; параметр в ECX, теперь стек очищен от параметров
		push	ecx	; обратный адрес надо вернуть в стек для RET

ifdef _MASM_
	_koi2wins@4	endp
else
	koi2wins	endp
endif

;Процедура koi2wins_asm
;Точка входа для ассемблерных программ
;ввод EAX - адрес строки
;вывод EAX - адрес той же перекодированной строки
ifdef _MASM_
	_koi2wins_asm@0	proc
else
	koi2wins_asm	proc
endif

		push	esi
		push	edi
		push	ebx
		mov	esi,eax
		mov	edi,eax
		mov	ebx,offset k2w_tbl
decode_string:
		lodsb
		test	al,80h
		jz	dont_decode2
		sub	al,80h
		xlat
		add	al,80h
dont_decode2:
		stosb
		test	al,al
		jnz	decode_string
		pop	ebx
		pop	edi
		pop	esi
		ret

ifdef _MASM_
	_koi2wins_asm@4	endp
else
	koi2wins_asm	endp
endif

end	_start@12
код exe вызова этой dll

Код: Выделить всё

include def32.inc
include user32.inc
include kernel32.inc


	.486
	.model flat
	.const
title_string1 	db	"koi2win demo: string in KOI8",0
title_string2 	db	"koi2win demo: string in cp1251",0
koi_win_dll		db "DLLrus.dll",0 
akoi2wins		db 'koi2wins',0   
	.data
koi_string	db	0F3h,0D4h,0D2h,0CFh,0CBh,0C1h,20h,0CEh,0C1h
		db	20h,0EBh,0EFh,0E9h,2Dh,28h,0
dll_handler dd	?

	.code
_start:
	push	MB_OK
	push	offset title_string1
	push	offset koi_string
	push	0
	call	MessageBox

	push	offset koi_win_dll
	call	LoadLibrary

	push	offset akoi2wins
	push	eax
	push	edx
	mov	edx,offset dll_handler
	mov	[edx],eax
	pop	edx
	call	GetProcAddress

	push	edx
	mov	edx,eax
	mov	eax,offset koi_string
	push	eax
	call	edx
	pop	edx

	push	MB_OK
	push	offset title_string2
	push	offset koi_string
	push	0
	call	MessageBox

	push	offset dll_handler
	call	FreeLibrary

	push	0
	call ExitProcess	

end	_start
Все это собрано TASM в 2001 году и до сих пор работает (во вложении).
В связи с этим есть путь выделить ассемблерный код из функций и скомпилировать современными ассемблерами.
Все скажут, что современные компиляторы настолько умные, что никакого выигрыша не получится. По листингу dll скомпилированного из C кода возникают сомнения.
Кроме этого на сегодняшний день для выполнения параллельных вычислений очевидно есть более подходящие инструменты.
Если не получится на LabVIEW есть еще варианты.
PS. Перед применением этого кода можно посмотреть его в IDA на предмет отсутствия лишнего.
Вложения
DLL.zip
(1.91 КБ) 201 скачивание
Изображение
AndreyDmitriev

Activity Professionalism Tutorials Gold Black
VIP
VIP
Сообщения: 1421
Зарегистрирован: 03 фев 2010, 00:42
Награды: 8
Версия LabVIEW: 6.1 - 2025
Откуда: Германия
Благодарил (а): 1 раз
Поблагодарили: 78 раз
Контактная информация:

Re: Создание DLL для использования в LabVIEW

Сообщение AndreyDmitriev »

ujin1 писал(а): 02 июл 2024, 12:32
IvanLis писал(а): 01 июл 2024, 00:05 Ну что я могу сказать, при вызове элементарной функции выигрыша нет.
Видимо на вызов ее из DLL тратиться много времени, что не компенсируется скоростью исполнения.
В связи с этим есть путь выделить ассемблерный код из функций и скомпилировать современными ассемблерами.
Все скажут, что современные компиляторы настолько умные, что никакого выигрыша не получится. По листингу dll скомпилированного из C кода возникают сомнения.
Кроме этого на сегодняшний день для выполнения параллельных вычислений очевидно есть более подходящие инструменты.
Путь есть всегда. На самом деле современные компиляторы действительно длстаточно умные. Я ассемблером воспользовался лишь тогда, когда компилятор не смог самом понять, что данные выровнены и упорно пихал мне функции для чтения записи в невыровненную память.
По существу - всегда имеет смысл глянуть в листинг, или просто деассемблировать. Я раньше IDA пользовался, но вот Ghidra ещё неплоха.
Из ассемблеров открыл для себя EuroAssembler (https://euroassembler.eu/download/), до этого использовал MASM. Горячо рекомендую, прост как пять копеек, не требует инсталляции, без зависимостей, написан на самом себе, есть макросы, поддерживает AVX512.
Минимальный код, складывающий два числа
32-bit, оба соглашения

Код: Выделить всё

AsmDLL32 PROGRAM FORMAT=DLL, MODEL=FLAT

EXPORT fnAsmCdecl
fnAsmCdecl PROC
	mov     eax, [esp+4]
	add     eax, [esp+8]
	retn
ENDP fnAsmCdecl

EXPORT fnAsmStdCall
fnAsmStdCall PROC
	mov     eax, [esp+4]
	add     eax, [esp+8]
	retn 8
ENDP fnAsmStdCall

ENDPROGRAM AsmDLL32
64 бит

Код: Выделить всё

EUROASM CPU=X64
AsmDLL64 PROGRAM FORMAT=DLL, MODEL=FLAT, WIDTH=64

EXPORT fnAsm64
fnAsm64 PROC
	mov     rax, rcx
	add     rax, rdx
	ret
ENDP fnAsm64

ENDPROGRAM AsmDLL64
Но при программировании на интрисиках в общем-то всё переводится "один в один", это фактически всё равно что на асме программить, только на си.
Опять же лет двадцать назад можно было поднять производительность просто переставляя команды для лучшей конвейеризации, либо делать предвыборку в память (префетчинг). Однако современные процессоры настолько усложнились, что всё это в общем эффекта уже не даёт.
А так вот здесь - https://www.agner.org/optimize/ - просто библия низкуровневой оптимизации.
Аватара пользователя
IvanLis

Activity Professionalism Tutorials Gold Man of the year 2012
Автор
guru
guru
Сообщения: 5602
Зарегистрирован: 02 дек 2009, 17:44
Награды: 7
Версия LabVIEW: 2015, 2016
Откуда: СССР
Благодарил (а): 34 раза
Поблагодарили: 113 раз

Re: Создание DLL для использования в LabVIEW

Сообщение IvanLis »

Прогнал несколько вариантов диаграммы направленности.
Но дома на стоковом вентиляторе не особо, что бы не гудел постоянно, приходится между итерациями делать паузу на охлаждение :cantbe: , что значительно замедляет процесс.
Сейчас заказал "башню", с ней должно быть получше.
Вот первая проба: https://youtu.be/S00UUs5nmIo

Но ввиду изложенных обстоятельств, до "тонкой" настройки дело не дошло :crazy: .

Не буду тут спамить видео, если кому интересен процесс, то можно посмотреть в отдельном плейлисте.
На последних видео, вычисление делаю с шагом 3deg, что значительно ускоряет процесс, но влияет на конечный результат :D .

Сейчас работа осуществляется только с амплитудой и фазой тока возбуждения элементарного излучателя, который представляет собой изотропную антенну с идеальной (сферической) ДН, а координаты элементов фиксированы в узлах равномерной решетки.
Дальнейшая работа будет связана с управлением топологией антенной решетки с использованием излучателей различного типа.

В принципе эволюционные алгоритмы достаточно интересное направление, а при правильном использовании, могут дать неплохие результаты, которые аналитически получить невозможно.
Но как и любое направление ИИ для достижения приемлемого результата требует колоссальных ресурсов.
Одним из последних примеров нейроэволюционного обучения был Snake AI.

Всем спасибо за помощь и сочувствие :dance:
ujin1
developer
developer
Сообщения: 256
Зарегистрирован: 06 ноя 2020, 15:37
Версия LabVIEW: 19
Благодарил (а): 19 раз
Поблагодарили: 40 раз
Контактная информация:

Re: Создание DLL для использования в LabVIEW

Сообщение ujin1 »

ujin1 писал(а): 27 июн 2024, 23:19 вызов dll.png
Сейчас почему то сломана, но разбираться некогда.
Разобрался почему сломана. Нужно было починить использование Linx toolkit.
В Linx toolkit есть библиотека liblinxdevice.dll. На целевом устройстве liblinxdevice.so
Если помещаем программу в проекте на устройство Raspberry, то vi с этой библиотекой открывается.
Если помещаем программу в проекте на устройство Мой компьютер, то vi с этой библиотекой сломано.
Я сделал проект заглушку на c++. То есть убрал весь код из функций, оставил только Return 0 где не void. Где void весь код из функции убрал.
Под windows перекомпилировал liblinxdevice.dll старым компилятором тоже сломано. Хотя библиотека компилируется.
Современным компилятором (скачал свежую версию msys/mingw) все нормально.
vi на устройстве Мой компьютер перестали быть сломанными.
Далее перекомпилировал старый проект для обработки аналоговых входов. Тоже работает. Проверил запуском с одним параметром
вызов dll2.png
Итог. Под WIN10 64 разряда библиотеки скомпилированные старыми компиляторами могут не работать.
Что не так не разбирался.
Изображение
Ответить
  • Похожие темы
    Ответы
    Просмотры
    Последнее сообщение

Вернуться в «Для чайников»