Консультация № 173385
18.10.2009, 13:16
25.00 руб.
0 4 1
Здравствуйте дорогие эксперты, помогите пожалуйста в решении этой лабораторной работы!
Вот её описание:

Цель: изучить расширение системы команд MMX процессоров Intel.
Задачи: Составить програмы для выполнения матричных(векторных) вычислений с использованием и без использования инструкций MMX и сравнить время их выполнения.
Используемое ПО: MS Visual C++ 6.0

Задание: Нахождение суммы квадратов всех элементов матрицы



Приложение:
Порядок выполнения работы на примере:

1) рассмотреть пример программы вычисления скалярного произведения (см листинг) с использованием MMX.
2) прогнать программу несколько раз (5-10) и занести результаты прогона (времена выполнения модулей и коэффициент ускорения в таблицу) Определить средние показатели в серии опытов. Проанализировать среднее значение ускорения и сравнить с теоретическими. Проанализировать причины расхождения с теоретическими оценками.

Табл:
№п/п Время вычисления с ММХ, мс Время вычисл без ММХ, мс Коэф ускорения, К ОТклонение от среднего, dK


Листинг программы - примера на языке C (MS Visual C++ 6.0)

#include "stdio.h"
#include "windows.h"
int main(int argc, char* argv[])
{
short a_vect[16], b_vect[16];
short cnt = 16;
int res = 0, res1 = 0;
int Time1, Time2, Delay1;
double Speedup;
for (int i=0; i<16; i++)
{
a_vect[i] = 15;
b_vect[i] = 7;
}
//printf("\n Скалярное произведение с ММХ и без...\n\n");
printf("\nScalar product with or without MMX...\n\n");
printf("\nResult = %d\n", res);
printf("Result1 = %d\n", res1);

// Земетить время...
Time1 = GetTickCount();

// Многократный прогон кода с MMX ...
for (i=0; i<10000000; i++)
{
cnt = 16;
__asm
{
push eax
push ebx
push ecx
push esi
xor esi, esi
pxor MM7, MM7
loop1:
movq MM0, a_vect[esi]
movq MM1, b_vect[esi]
pmaddwd MM0, MM1
paddd MM7, MM0
add esi, 8
sub cnt, 4
jnz loop1
movq MM0, MM7
psrlq MM7, 32
paddd MM7, MM0
movd res, MM7
emms
pop esi
pop ecx
pop ebx
pop eax
}
}
Time2 = GetTickCount();
//printf("\n С использованием ММХ ... \n");
printf("\n Using MMX ...\n");
printf("\nResult = %d", res);
Delay1 = Time2 - Time1;
printf("\nTime elapsed = %d ms\n", Delay1);
// Заметить время ...

Time1 = GetTickCount();
// Многократный прогон кода без MMX ...
for (i=0; i<10000000; i++)
{
cnt = 16;
__asm
{
push eax
push ebx
push ecx
push esi
xor esi, esi
xor ecx, ecx
loop2:
mov ax, a_vect[esi] // Чтение из памяти
mov bx, b_vect[esi]
mov ax, 15 // Непосредственная адресация

mov bx, 7
imul ax, bx
add cx, ax
add esi, 2
sub cnt, 1
jnz loop2
mov res1, ecx
pop esi
pop ecx
pop ebx
pop eax
}
}
Time2 = GetTickCount();
//printf("\n Без использования ММХ ...\n");
printf("\n Without MMX ...\n");
printf("\nResult1 = %d", res1);
printf("\nTime elapsed = %d ms\n", Time2 - Time1);
Speedup = float((Time2 - Time1))/float(Delay1);
printf("\nMMX SpeedUp = %10.4f times\n\n", Speedup);
return 0;
}


Обсуждение

давно
Посетитель
7438
7205
18.10.2009, 23:40
общий
это ответ
Здравствуйте, Dimarik59.
Программа в приложении.
Рассматриваем матрицу 4х4, как вектор размерности 16
В результате получаем ту же задачу, что и приведена, только с той разницей, что умножаем сам на себя.
Кроме того, сделал так, чтобы был вывод русскими буковками.
Убрал вывод начального значения res=res1=0
Если что не понятно, спрашивайте в мини-форуме

Приложение:
#include <iostream> //для применения потокового ввода/вывода (cout, wcout)
#include <locale> //для русских сообщений
#include <windows.h> //для GetTickCount

using namespace std; //для применения потокового ввода/вывода (cout, wcout)

int main(int argc, char* argv[])
{
short a_array[4][4]; //матрица 4х4
short cnt = 16; //число элементов
int res = 0, res1 = 0; //результаты расчетов
int Time1, Time2, Delay1;//для хранения времени
double Speedup; //для хранения эффективности (вещественное число)

locale::global(locale("Russian_Russia.866")); //чтобы писалось по-русски

for (int i=0; i<4; i++) //проинициализируем матрицу
for (int j=0; j<4; j++)
a_array[i][j] = i*4+j;

wcout<<endl<<L" Скалярное произведение с ММХ и без..."<<endl<<endl;

// Заметить время...
Time1 = GetTickCount();

// Многократный прогон кода с MMX ...
for (i=0; i<10000000; i++)
{
cnt = 16; //число элементов
__asm
{
push eax //сохраним используемые регистры
push ecx
push esi
xor esi, esi //индекс в массиве
pxor MM7, MM7 //обнулим MM7 - сумма квадратов
loop1:
movq MM0, a_array[esi] //загрузим 4 элемента массива в MM0
movq MM1, MM0 //скопируем в MM1
pmaddwd MM0, MM1 //умножим попарно 4 элемента, результат в MM0,
// плюс сложим 1 с 2, 3 с 4
paddd MM7, MM0 //накапливаем суммы в MM7
add esi, 8 //на следующие 4 слова
sub cnt, 4 //по 4 слова за раз
jnz loop1 //циклим
//теперь надо сложить два 32-битных числа
movq MM0, MM7 //копируем в MM0
psrlq MM7, 32 //сдвигаем MM7 на 32 разряда вправо
paddd MM7, MM0 //складываем
movd res, MM7 //сохраняем
emms //сброс MMX
pop esi //восстановим регистры
pop ecx
pop eax
}
}
Time2 = GetTickCount(); //Еще раз замерим время

wcout<<endl<<L" С использованием ММХ ... "<<endl; //выведем результат
cout<<endl<<"Result = "<<res;

Delay1 = Time2 - Time1; //выведем разность времени
wcout<<endl<<L"Прошедшее время = "<<Delay1<<L" ms"<<endl;

// Заметить время ...

Time1 = GetTickCount();

// Многократный прогон кода без MMX ...
for (i=0; i<10000000; i++)
{
cnt = 16; //число элементов
__asm
{
push eax
push ecx
push esi
xor esi, esi //индекс
xor ecx, ecx //сумма
loop2:
movsx eax, a_array[esi] //Чтение из памяти (расширяем слово на двойное слово)
imul eax, eax //квадрат
add cx, ax //накапливаем (значащая часть только в ax)
add esi, 2 //сдвигаем указатель
sub cnt, 1 //счетчик
jnz loop2 //на повтор
mov res1, ecx //результат
pop esi
pop ecx
pop eax
}
}
Time2 = GetTickCount();
wcout<<endl<<L" Без использования ММХ ... "<<endl;
wcout<<endl<<L"Result1 = "<<res1<<endl<<L"Прошедшее время = "<<Time2-Time1<<L" ms"<<endl;

//расчитаем еффективность, как отношение времен
Speedup = float(Time2 - Time1)/float(Delay1);
wcout<<endl<<L"Эффективность MMX = "<<Speedup<<L" раз"<<endl<<endl;
return 0;
}
Об авторе:
"Если вы заметили, что вы на стороне большинства, —
это верный признак того, что пора меняться." Марк Твен
Неизвестный
19.10.2009, 12:22
общий
Лысков Игорь Витальевич спасибо! не могли бы вы написать очень подробные комментарии к каждой строчке? это очень нужно...
Неизвестный
19.10.2009, 15:42
общий
IMHO, GetTickCount() для измерения продолжительности выполнения использовать в Windows некорректно. Лучше использовать

Код:
BOOL GetThreadTimes(
HANDLE hThread, // handle to thread
LPFILETIME lpCreationTime, // thread creation time
LPFILETIME lpExitTime, // thread exit time
LPFILETIME lpKernelTime, // thread kernel-mode time
LPFILETIME lpUserTime // thread user-mode time
);


или (в данном случае применимо)

Код:
BOOL GetProcessTimes(
HANDLE hProcess, // handle to process
LPFILETIME lpCreationTime, // process creation time
LPFILETIME lpExitTime, // process exit time
LPFILETIME lpKernelTime, // process kernel-mode time
LPFILETIME lpUserTime // process user-mode time
);

давно
Посетитель
7438
7205
19.10.2009, 17:24
общий
amnick:
Я полностью с Вами согласен...
Но, в данной работе вполне годится и GetTickCount()
Тем более стоит задача
Определить средние показатели в серии опытов. Проанализировать среднее значение ускорения и сравнить с теоретическими.
К тому же, именно так и сделано в предлагаемом примере из задания. Значит, так и надо.
Об авторе:
"Если вы заметили, что вы на стороне большинства, —
это верный признак того, что пора меняться." Марк Твен
Форма ответа