Pull to refresh
33
0
Кирилл Плешивцев @drBasic

User

Send message
Мне был нужен ubuntu x64. И видеокарты все-таки разные. И да, два монитора не запускалось на драйвере из репозитория тоже.
Что бы не говорили гуру линукса, но в неопытных руках он действительно ломается очень легко. Одно неловкое движение — и вот ты видишь консоль и абсолютно непонятно как это все чинить обратно.
Этот самый юзер-френдли убунту я переустанавливал раз 5, пока сносно не заработала система и не разобрался в премудростях установки дров.
Казалось бы что такого — 2 видеокарты Nvidia(680 и 560), 2 монитора. Но установка драйверов тот еще квест. Для установки драйвера надо остановить сервис lightdm. Все бы хорошо, но консоль включается в несовместимом с мониторе разрешении и ничего не видно. И начинается 2хчасовой поиск по форумам как же изменить режим консоли именно текстовый. При этом вспоминаешь как 5 минут назад ты установил новую версию драйверов в винде буквально в несколько кликов но вариантов нет, нужно сделать и ты продолжаешь колоться этим чертовым кактусом.
Наконец все настроил, что-то работает, что то нет (не хватило терпения), ты ничего не трогаешь, жизнь прекрасна! Потом проходит месяц и ты наконец соглашаешься на предложение установить все сто тысяч обновлений, после которых итог предсказуем, видео драйвера перестают нормально работать.
Переустановка снова квест, вспомни как же ты заставил монитор показывать консоль а не мешанину линий.
И да! Второй монитор я заставить работать так и не смог! Может недостаточно долго шерстил по форумам, но после пол дня экспериментов я решил что черт с ним, буду ютиться на одном.

Автор молодец, действительно уловил суть сложностей, с которыми сталкивается новичек.
А можно по-подробнее про сломавшуюся сортировку?
Loki::SmallObjAllocator не подходит идеологически — это аллокатор для классов типа std::list, std::map.
boost::object_pool делает в точности что нужно, но есть ньюансы… Он не многопоточен!

void func()
{
  boost::object_pool<X> p;
  for (int i = 0; i < 10000; ++i)
  {
    X * const t = p.malloc();
    ... // Do something with t; don't take the time to free() it.
  }
} // on function exit, p is destroyed, and all destructors for the X objects are called.
В библиотеке VTK есть объемный рендеринг через CPU и GPU, нужно лишь подготовить данные, что достаточно просто. Смотрел в этом направлении?
В критическую секцию конечно заходят по одному. Имел ввиду другое — первый захвативший секцию будет не обязательно поток который первый наткнулся на завершение данных в блоке.
Цикл while(true) {… } прогонятеся вхолостую в x64 примерно 6000 раз без yield и 4500 с yield.
В х86 примерно 2500-3000.
В статье упустил момент, что curAtomicIndex_ объявлен как std::atomic<uint64_t>.
Его инкримент будет атомарен, за этим следит компилятор. Для х64 транслируется в
lock xadd QWORD PTR [rcx+32], r8. 

Для х86 код сложнее, но сводится к атомарному cmpxchg8b
$again$158:

; 2424 : 	again:
; 2425 : 		mov ecx, edx;

	mov	ecx, edx

; 2426 : 		mov ebx, eax;

	mov	ebx, eax

; 2427 : 		add ebx, dword ptr _Value;

	add	ebx, DWORD PTR $T7[ebp]

; 2428 : 		adc ecx, dword ptr _Value[4];

	adc	ecx, DWORD PTR $T7[ebp+4]

; 2429 : 		lock cmpxchg8b [esi];

	lock	 cmpxchg8b QWORD PTR [esi]

; 2430 : 		jnz again;

	jne	SHORT $again$158

Память выделяется большими блоками, по этому ситуация с выделением памяти происходит сравнительно редко и замедление не так заметно. В тесте я выделяю память блоками кажется по 65 тысяч объектов.
index = curAtomicIndex_++;

volatile в данном случае для index здесь не нужен, index локальная переменная и не может быть изменнена внешним кодом.
Для х64 curAtomicIndex_++ транслируется в InterlockedExchangeAdd64, который ассемблируется в команду
lock xadd QWORD PTR [rcx+32], r8. 
Так что атомарность есть.
Для х86 атомарность реализуется сложнее, но тем не меннее
index = curAtomicIndex_++;
в любом случае работает атомарно.
mmap не позволяет зарезевировать в памяти регион например на 4 гигабайта, а потом по частям отображать его на физическую память. Поправьте меня, если я не прав.
Во-первых, при ожидании имеет смысл не yield сразу вызывать, а раз от 5..30 цикл с проверкой прокрутить, так как современных процессорах велика вероятность, что другой поток исполняется на отдельном ядре. А Yield очень тяжёлая и длительная операция. Процентов 15% можно прироста скорости получить.

Делал и вообще без yield и с ним. Разницы в скоросте не заметил. Решил оставить вариант с yield, так как с ним ведем себя дружественнее по отношению к другим потокам, которые тоже хотят работать. Циклов ожидания в обоих случаях получалось в районе нескольких тысяч, с yield немного меньше.
Про переполнение счётчика тоже не понял. Если после инкремента выясняется, что блок закончился, то нужно выделять новый внутри классической критической секции (с повторной проверкой внутри критической секции). А если есть вероятность, что переполнится 32-бит счётчик (что невероятно, так как нужно иметь порядка 2^32 потоков), то нужно ещё раз проверять счётчик на переполнение ДО инкремента.

Критических секций не хотелось по причинам:
1 хотелось обойтись без критических секций :-)
2 войти в критическую секцию первым мог как поток, первым наткнувшийся на нехватку места, так и остальные. Пришлось бы усложнять логику, на решение кто же выделяет следующий блок, хотя по чесному конечно ничего сложного в этом нет
3 переполнение может произойти потому что потоки не останавливаются, а непрерывано запрашивают через атомарную переменную новый индекс, и как только он станет валидным с радостью выделяют элемент
Сначала было так:
auto index = curAtomicIndex_++;
uint32_t blocksCount = index >> 32;
uint32_t lastIndexInBlock = index & 0xffffffff;

Потом я начал искать способы оптимизации и вариант с union дал небольшой прирост. А вообще да, union — хак.
Для этого требуется чтобы все объекты находились в ожном непрерывном блоке памяти.
Сколько объектов будет получено не известно на начало работы, может тысяча, а может и сто миллионов. Можно было конечно делать VirtualAlloc с флагом MEM_RESERVE для самого большого возможного количества объектов, а потом получать физическую память по необходимости, но проект кросс-платформенный и не хотелось завязываться на архитектуру.
Индексы в последствии использовались для сериализации, по этому их монотонность была очень важна. Для первоначального варианта, где объекты выделялись через new, потом приходилось делать проход по все объектам и назначать индексы.
Мне требовалось чтобы объекты выделялись последовательно, без пропусков, каждому присваивался индекс, и этот индекс можно было использовать для быстрого обращения к самому объекту.
Поделитесь инвайтом для seleznev.vad@gmail.com, пожалуйста!

Information

Rating
Does not participate
Location
Новосибирск, Новосибирская обл., Россия
Date of birth
Registered
Activity