Главная Случайная страница


Полезное:

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


Категории:

АрхитектураАстрономияБиологияГеографияГеологияИнформатикаИскусствоИсторияКулинарияКультураМаркетингМатематикаМедицинаМенеджментОхрана трудаПравоПроизводствоПсихологияРелигияСоциологияСпортТехникаФизикаФилософияХимияЭкологияЭкономикаЭлектроника






Масштабируемость Hyper-Threading архитектур





А сейчас проанализируем продемонстрированные особенности масштабируемости Hyper-Threading архитектур -- здесь есть над чем задуматься! Напомню, что с точки зрения маркетинга использование HT процессора как бы удваивает количество доступных CPU, т.е. 1 CPU, HT компьютер должен вести себя как 2 CPU, SMP, а 2 CPU, HT -- как 4 CPU, SMP, а ожидаемая масштабируемость функции start_ders на 2 CPU, SMP и 4 CPU, SMP составляет 0,5 и 0,25 соответственно. Следующая ниже таблица показывает насколько ожидаемая масштабируемость отличается от реальной:

  numThr
             
1 CPU 0.997 0.994 0.997 0.999 1.004 1.004 1.003
1 CPU, HT 2.810 2.802 2.806 2.790 2.826 2.818 2.800
2 CPU, SMP 1.026 1.026 1.026 1.024 1.026 1.050 1.018
2 CPU, HT 2.008 3.376 2.536 2.620 2.596 2.588 2.544

Увы, все очень печально: масштабируемость HT вариантов приблизительно в 2,6-2,8 раз хуже ожидаемой. Данная цифра тем более любопытна, что даже если считать один HT процессор не за два, а один CPU, то полученные значения 1,3-1,4 все равно больше единицы, т.е.

С точки зрения масштабируемости один Hyper-Threading процессор не только хуже двух обычных -- он хуже даже одного!

Любопытно, не так ли?

3.2. example2.exe: работа с файлами

Второй пример более приближен к жизни и предназначен для измерения относительной производительности MT приложения, работающего с файлами. Работа с файлами главным образом "упирается" в производительность файловой подсистемы ОС, так что прикладной код, использующий системные вызовы ввода/вывода, вряд ли покажет существенную разницу производительности...

Или все же покажет?!

Как известно, стандартные потоки ввода/вывода C++ работают существенно медленнее потоков ввода/вывода C. Но, к сожалению, даже C-шные потоки FILE не подходят для MT приложений, т.к. все операции над ними обязаны быть thread-safe по умолчанию, а быстрые варианты функций getc() и putc(), не использующие пресловутые mutex-ы, имеют другие имена: getc_unlocked() и putc_unlocked() соответственно. Тем самым, написание кода, одинаково хорошо работающего как в ST, так и в MT окружении становится невозможным.

Главным образом, для решения этой проблемы и был создан класс file, не использующий блокировок:

example2/main.cpp
#include <memory>#include <vector>#include <stdio.h>#include <time.h>#include <ders/file.hpp>#include <ders/text_buf.hpp>#include <ders/thread.hpp> using namespace std;using namespace ders; const int BUF_SIZE=64*1024; struct MainData { const char* fname; MainData(const char* fn): fname(fn) {}}; struct ThreadData { MainData* md; int n; ThreadData(MainData* md_, int n_): md(md_), n(n_) {}}; void start_std(void* arg){ ThreadData* td=(ThreadData*)arg; auto_ptr<ThreadData> guard(td); mem_pool mp; file err(mp, fd::err); FILE* fin=fopen(td->md->fname, "rb"); if (!fin) { err.write(text_buf(mp)+"Can't open "+td->md->fname+'\n'); return; } sh_text oname=text_buf(mp)+td->md->fname+'.'+td->n; FILE* fout=fopen(oname->c_str(), "wb"); if (!fout) { err.write(text_buf(mp)+"Can't create "+oname+'\n'); fclose(fin); return; } setvbuf(fin, 0, _IOFBF, BUF_SIZE); setvbuf(fout, 0, _IOFBF, BUF_SIZE); for (int ch; (ch=fgetc(fin))!=EOF;) fputc(ch, fout); fclose(fout); fclose(fin);} void start_ders(void* arg){ ThreadData* td=(ThreadData*)arg; auto_ptr<ThreadData> guard(td); mem_pool mp; file err(mp, fd::err); file fin(mp); if (!fin.open(td->md->fname, file::rdo, 0)) { err.write(text_buf(mp)+"Can't open "+td->md->fname+'\n'); return; } sh_text oname=text_buf(mp)+td->md->fname+'.'+td->n; file fout(mp); if (!fout.open(oname, file::wro, file::crt|file::trnc)) { err.write(text_buf(mp)+"Can't create "+oname+'\n'); return; } buf_reader br(mp, fin, BUF_SIZE); buf_writer bw(mp, fout, BUF_SIZE); for (int ch; (ch=br.read())!=-1;) bw.write(ch);} int main(int argc, char** argv){ mem_pool mp; file err(mp, fd::err); file out(mp, fd::out); if (argc!=4) { m1: err.write("main file num_threads std|ders"); return 1; } int numThr=atoi(argv[2]); if (!(numThr>=1 && numThr<=100)) { err.write("num_threads must be in [1, 100]"); return 1; } void (*start)(void*); if (strcmp(argv[3], "std")==0) start=start_std; else if (strcmp(argv[3], "ders")==0) start=start_ders; else goto m1; MainData md(argv[1]); clock_t c1=clock(); vector<sh_thread> vthr; for (int i=0; i<numThr; i++) vthr.push_back(new_thread(mp, start, new ThreadData(&md, i))); for (int i=0; i<numThr; i++) vthr[i]->join(); clock_t c2=clock(); out.write(text_buf(mp)+numThr+'\t'+int(c2-c1)+'\t'+argv[3]+'\n'); return 0;}

Пример запускает заданное количество потоков, в каждом из которых создается копия указанного в командной строке файла посредством выбранной функции: start_std() или start_ders(). Потоками используется посимвольное копирование через буфер размера BUF_SIZE.


В таблице представлены усредненные результаты запуска (в секундах) на трех разных компиляторах с файлом десятимегабайтного размера:

1 CPU
  numThr
               
  std 2.02 3.98 5.91 7.94 9.85 11.89 14.00 15.81
ders 0.74 2.06 3.64 5.14 6.72 8.81 9.40 10.96
std/ders 2.73 1.93 1.62 1.54 1.47 1.35 1.49 1.44
  std 1.45 2.74 4.29 5.75 7.01 8.53 9.82 11.25
ders 0.80 1.98 3.72 5.40 7.24 8.41 10.41 11.19
std/ders 1.81 1.38 1.15 1.06 0.97 1.01 0.94 1.01
  std 1.03 2.16 3.67 5.10 7.20 8.01 9.55 10.55
ders 0.52 1.93 3.59 5.59 6.92 7.86 9.68 10.88
std/ders 1.98 1.12 1.02 0.91 1.04 1.02 0.99 0.97
2 CPU, SMP
  numThr
               
  std 2.13 4.53 5.35 7.67 8.79 10.46 10.59 11.76
ders 0.06 0.39 0.65 0.99 1.09 1.22 1.77 1.81
std/ders 35.50 11.62 8.23 7.75 8.06 8.57 5.98 6.50
  std 1.97 7.36 11.82 14.39 17.76 20.64 22.44 25.77
ders 0.06 0.38 0.66 0.71 1.11 1.51 1.50 1.93
std/ders 32.83 19.37 17.91 20.27 16.00 13.67 14.96 13.35
  std 1.75 2.35 4.36 4.94 5.39 6.19 6.97 8.14
ders 0.14 0.34 0.96 0.64 1.29 1.47 1.48 2.08
std/ders 12.50 6.91 4.54 7.72 4.18 4.21 4.71 3.91

Как можно видеть,

  1. 1 CPU. Функция start_ders работает быстрее в 1.4-2.7, 0.9-1.8 и 0.9-2.0 раз для каждого из трех компиляторов соответственно. Вероятно, значения меньше 1.0 (т.е. небольшое замедление, а не ускорение работы) являются следствием ошибок измерения, неизменно возникающих при замерах времени работы приложений, интенсивно работающих с файловой подсистемой.
  2. 2 CPU, SMP. В данном случае преимущество start_ders, так скажем, более очевидно: в 6.0-35.5, 13.4-32.9 и 4.2-12.5 раз соответственно!
  3. Во всех случаях наибольший выигрыш производительности получен на единственном рабочем потоке, когда встроенная синхронизация функций getc() и putc() полностью невостребована. А по ходу увеличения количества рабочих потоков выигрыш постепенно уменьшается в силу того, что узким местом масштабируемости является сама файловая подсистема, не поддерживающая эффективного распараллеливания запросов ввода/вывода в силу своего устройства.

Таким образом,


Использование класса ders::file способно ускорить выполнение MT приложений в десятки раз!

Конечно, это не сотни раз предыдущего примера, но, тем не менее, даже подобная "скромная" разница производительности заставляет пристально присмотреться к устройству стандартной библиотеки C++: любопытно, задумывались ли ее проектировщики об эффективности работы в многопоточной среде?

И, в свете всего вышеперечисленного, у нас есть вполне определенный ответ на вопрос "Оправдано ли создание своих собственных классов, предоставляющих альтернативную реализацию давным-давно известных стандартных функций ввода/вывода?": увы, "не мона, а нуна"!







Date: 2015-12-12; view: 353; Нарушение авторских прав



mydocx.ru - 2015-2024 year. (0.011 sec.) Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав - Пожаловаться на публикацию