![]() |
| ||
|
|
Новости |
Поиск |
FAQ |
Статьи |
Книги |
Новости VCL
| | Форумы ЧАТ |
Орешник |
|
| |||
| |||
« предыдущая ветвь | форум | следующая ветвь »
Оптимизация кода
Вова © (2013-06-25 20:17:00)
Страницы: 1 2 3 4 5 6 7 8 9 10 11 >
Есть процедура задачей которой является приведения пикселя любого цвета к цвету одного из диапазонов. Но скорость ее работы огорчает меня, можно ли ее как нибудь ускорить?
function almostEqual(a, b: byte): Boolean;
begin
if ABS(a - b) < 10 then
result := true
else
result := false;
end;
Function ConvertColorToRGBColorMap(color: LongInt; ColSel: String = 'None')
: TColorWithName;
var
RGBV: trgbcolor;
begin
RGBV.red := color;
RGBV.green := color shr 8;
RGBV.blue := color shr 16;
with RGBV do
if almostEqual(red, green) and almostEqual(red, blue) and
almostEqual(blue, green) then // тогда это серый или черный или белый
begin
if almostEqual(red, 255) then // белый
begin
result.color := RGB(255, 255, 255);
result.Name := 'White';
end
else if red > 120 then
begin
result.color := RGB(190, 190, 190);
result.Name := 'LightGray';
end
else if (red > 92) then // темно серый
begin
result.color := RGB(100, 100, 100);
result.Name := 'DarkGray';
end
else
begin
result.color := RGB(0, 0, 0);
result.Name := 'Black';
end // черный
end
else
begin
if (red < green) and (red < blue) and almostEqual(green, blue) and
(blue > 130) then
begin
result.color := RGB(0, 0, 255);
result.Name := 'Blue';
end // result:= rgb(0,255,255)//голубой
else if (green < red) and (green < blue) and almostEqual(red, blue) and
(red > 130) then
begin
result.color := RGB(255, 0, 255);
result.Name := 'Pink';
end // розовый
else if (blue < green) and (blue < red) and almostEqual(green, red) and
(green > 130) then
begin
result.color := RGB(255, 255, 0);
result.Name := 'Yellow';
end // желтый
else if (red > green) and (red > blue) and (red > 130) then
begin
result.color := RGB(255, 0, 0);
result.Name := 'Red';
end // красный
else if (blue > green) and (blue > red) and (blue > 130) then
begin
result.color := RGB(0, 0, 255);
result.Name := 'Blue';
end // синий
else if (green > blue) and (green > red) and (green > 130) then
begin
result.color := RGB(0, 255, 0);
result.Name := 'Green';
end // зеленый
else
begin
result.color := RGB(0, 0, 0);
result.Name := 'Black';
end
end;
if not (ColSel = 'None') then
if not (ColSel = Result.Name) then
begin
result.color := RGB(255, 255, 255);
result.Name := 'White'; //Если выборка только по одному цвету, то
//все остальные цвета переводим в белый
end
else
begin
result.color := RGB(0, 0, 0);
result.Name := 'Black'; //Если выборка только по одному цвету, то
//этот цвет переводим в черный
end
end;
ProgRAMmer Dimonych (2013-09-02 21:22:00) [1]> забоялся делать много потоков (таки может и зря?) и только
> несколько процессов туда поместил
Осторожнее с понятиями: таки слово процесс уже зарезервировано для обозначения кой-чего другого, чем кусок кода, и в том значении как раз несколько потоков в процессе, а не наоборот :)
Sha (2013-09-02 09:41:00) [2]1. А что в книжках про это пишут?
2. Новые обсуждения лучше затевать в новых ветках.
3. А про коней в вакууме лучше в разделе "Прочее"
Вова (2013-09-02 01:58:00) [3]кстати хорошо или плохо если создать много потоков? ну там например 100? )) вообщем дело в том, что когда все делается в основном потоке, то делается это последовательно, и нельзя какое то звено тормознуть, т.к. этим ты тормознешь и тех кто идет следом. В то же время когда это делается в потоке, то процедура выполняется и ставится на паузу - т.е. во втором случае во первых никто не ждет пока процесс себя выполнит, т.к. он сам обновляет результат с необходимой переодичностью, во вторых процессор не шарашит как оголделый по 20 раз одно итоже и следовательно меньше нагружается. Я б даже сказал по опыту разница загрузки проца огромна. Хотя с другой стороны цикл основного процесса все равно будет носится и грузить проц. А во вторых в одно время выполняется только одна из веток программы, т.е. часть процедур может минут по 20 не использоваться, а если они в потоке будут крутиться, то постоянно, т.к. нужны они становятся по событию, а не по таймеру. вообщем сколько потоков юзать оптимально? особенно учитывая, что желательный режим работы на одном ядре (виртуальном) процессора?
на данный момент я как то забоялся делать много потоков (таки может и зря?) и только несколько процессов туда поместил, а остальное в основном кручу. Т.е. класс создается, но не запускается и процедуры выполняются в основном потоке его )
Sha (2013-08-25 12:30:00) [4]> Sapersky (25.08.13 03:26) [205]
вот, оказывается, где талант педагога скрывается )
Sapersky (2013-08-25 03:39:00) [5]Строго говоря, таблица в первом и втором вариантах не of Integer, а of TRGB из [160], хотя я думаю и так можно сообразить, но кто ж Вову знает...
Sapersky (2013-08-25 03:26:00) [6]
> Вова (24.08.13 21:18) [203]
Если старшие-младшие биты совсем не понятны, можно переформулировать задачу и без них.
Нужно преобразовать один набор цветов в другой с помощью таблицы. Вариант в лоб - создаём таблицу для всех возможных цветов: ColorMap = array [0..255, 0..255, 0..255] of Integer, прописываем в ней результирующие цвета и выполняем простую как мычание операцию:
Color = ColorMap[Color.r, Color.g, Color.b]
Всё работает, но размер таблицы получается 64 мб, как-то многовато.
Далее, наша операция - огрубление цветов, поэтому нам не очень-то нужны тонкие оттенки. Создадим таблицу не для каждого возможного цвета, а с шагом 16, 256 / 16 = 16 значений на канал.
ColorMap = array [0..15, 0..15, 0..15] of Integer.
Соответственно, чтобы выбрать из такой таблицы, цвета нужно делить на 16:
Color = ColorMap[Color.r div 16 , Color.g div 16, Color.b div 16].
Деление на 16 - это на самом деле и есть "взять старшие биты", точнее, "сдвинуть старшие биты на место младших", фактически компилятор заменяет его на shr 4. Можно заменить самостоятельно, вроде так оптимальнее:
Color = ColorMap[Color.r shr 4, Color.g shr 4, Color.b shr 4].
Если оставить всё именно так - о битах можно не думать. И это неплохой вариант, памяти много не жрёт и по скорости гораздо лучше расчёта на лету. Но если оптимизировать до упора, нужно заменить трёхмерный массив на одномерный:
Color = ColorMap[(Color.r div 16) * 16*16 + (Color.g div 16) * 16 + (Color.b div 16)].
и убрать обращения к отдельным компонентам Color, учитывая, что:
Color.r = Color shr 16 and $FF
Color.g = Color shr 8 and $FF
Color.b = Color and $FF
Подробно мне расписывать не очень хочется, но в общем, в результате всех подстановок и упрощений мы должны получить строчку из [175]. У Sha ещё чуть заковыристее, но принцип тот же.
Вот для этого последнего уровня оптимизации и нужно понимание битовых операций. А так, таблица она и есть таблица, с какими заклинаниями её ни вызывай.
Плохиш (2013-08-25 00:31:00) [7]Зачем оно тебе, это программирование? Тебе всë-равно не интересно.
PS. Вместо обид остановись, успокойся и подумай, что ты хочешь сделать.
Вова (2013-08-24 21:18:00) [8]равно как и в [119]
> Каждую из них усекаем до старших 4х битов:
> i:=i and $F0F0F0;
все ж понятно, с чего это вдруг мы вдруг отсекаем старших 4 бита и получаем осмысленный результат. Для меня это звучит также как если мы у машины снимаем колеса, а она после этого ездит. как почему...яхз...
> Формируем число от 0 до 16*16*16-1 - будущий индекс цвета
> в таблице:
> i:=(i shr 12 and $F) + (i and $F0) + (i shr 12 and $F00);
>
ну тут тоже все понятно ваще...и так далее )
Sha (2013-08-24 21:17:00) [9]значит, нам придется поработать над собой )
Вова (2013-08-24 21:07:00) [10]если цель программы научить кого то чему то, то там в изобилии должны быть комментарии объясняющие процесс. Ибо в 100 раз повторяю я не понимаю что происходит в [81]. Благодаря тем же причинам я не понимаю, что написано в твоем блоге, который я честно пытался читать, потому что там также стены кода в которых можно что либо понять, только если ты итак уже это знал.
имеет смысл учить на языке понятном обучаемому. Так что я выучил то что сумел понять.
Sha (2013-08-24 19:51:00) [11]> ведь никто ж не ценит твоего времени и не показывает как
Пример чтения четырех пикселей за три операции в приведен [81].
Там же показано, как развернуть цикл и сэкономить на проверках.
В [12] и [119] разжевано кеширование результата функции.
По ходу обсуждения немного разобраны битовые операции.
В [193] в 100й раз повторено, что *ВСЯ* твоя задача решается
за один проход без предварительного копирования, переворотов и т.п.
Но это мало помогает, никто ж не ценит твоего времени, и не решает за тебя.
Имеет смысл учить только тех, кто хочет учиться.
Вова (2013-08-24 19:43:00) [12]ощем нет смысла бороться из за одной миллисекунды, т.к. тут еще и загруженность компа влияет и содержимое скриншота... так что это просто погрешность )
Вова (2013-08-24 19:39:00) [13]а нет....это не из за этого...
Вова (2013-08-24 19:36:00) [14]хе хе, попробовал второй массив через указатель, получилось на пол милисекунды медленней.
m^ := (Mass + (i shr 12 and $F0F or i and $F0))^;
Вова (2013-08-24 19:29:00) [15]
Procedure Convert(Start,Stop:pInteger;Width:integer;m:pByte;Mass: TColorMapByte);
var
count,i:Integer;
begin
count := Width;
repeat
repeat
i := Start^;
m^ := Mass[i shr 12 and $F0F or i and $F0];
inc(Start);
inc(m);
dec(count);
until count = 0;
count := Width;
dec(Start,Width*2);
until Start = Stop;
end;
function ConvertBmpToRGBColorMapM(tBmp: Tbitmap; var M: TMass;
Mass: TColorMapByte): Boolean;
var
Start, Stop: pInteger;
begin
//tbmp.SaveToFile('C:\yui.bmp');
Start := tBmp.ScanLine[0];
dec(Start,tBmp.Width);
Stop := tBmp.ScanLine[tBmp.Height - 1];
inc(Stop,tBmp.Width);
Convert(Start,Stop,tBmp.Width,@M.Mass[0],Mass);
Result := true;
end;
я вкурил как указатель на массив передать, 3 миллисекунды, вместо 4.
Sha (2013-08-24 18:49:00) [16]> оке, за счет чего тогда в 2 раз медленней?
за счет чего угодно, если интересно, приведи здесь оба кода, посмотрим
Вова (2013-08-24 17:53:00) [17]1) однобайтный массив еще нужно получить в коде вверху я это и делаю.
2) несомневаюсь, но как перевернуть картинку в 1 проход я не знаю, и я не могу сделать то чего не знаю как сделать )
3) может и бред, но у меня не хватает воображения чтобы потом с отзеркаленым изображением работать (даже если его по вертикали развернуть легко, но еще нужно по x отразить). тем более что поиск должен идти сверху вниз и слева направо, т.е. если не перевернуть его тут, то потом при поиске все равно нужно извращаться. Что у меня и не получилось )
4) оке, за счет чего тогда в 2 раз медленней? без разворота будет 3-3.5 миллисекунды
5) все стоит времени и не всегда результат окупает затраты. Может и научусь, так ведь никто ж не ценит твоего времени и не показывает как. 20 минут гугла ничему мну не научили )
Sha (2013-08-24 17:41:00) [18]> Вова (24.08.13 16:00) [187], [188]
1. правильная работа массивом 1-байтных элементов будет заметно быстрее
2. все можно сделать за один проход
3. перевороты изображения - очевидный бред
4. рассуждения про скорость позиционирования - самоуспокаивающий бред
5. то, что ты не умеешь - не аргумент, учись
Вова (2013-08-24 17:39:00) [19]ощема, вот так вот 4 миллисекунды, вместо 2х когда конвертишь сам битмап. В указателях слаб, поэтому невкурил как вместо m:TArrInt указатель на него передать и указателем по массиву бежать, так что ввел num (
Procedure Convert(Start,Stop:pInteger;Width:integer;m:TArrInt;Mass: TColorMapByte);
var
count,i,num:Integer;
begin
count := Width;
num := 0;
repeat
repeat
i := Start^;
m[num] := Mass[i shr 12 and $F0F or i and $F0];
inc(Start);
inc(num);
dec(count);
until count = 0;
count := Width;
dec(Start,Width*2);
until Start = Stop;
end;
function ConvertBmpToRGBColorMapM(tBmp: Tbitmap; var M: TMass;
Mass: TColorMapByte): Boolean;
var
Start, Stop: pInteger;
begin
//tbmp.SaveToFile('C:\yui.bmp');
Start := tBmp.ScanLine[0];
dec(Start,tBmp.Width);
Stop := tBmp.ScanLine[tBmp.Height - 1];
inc(Stop,tBmp.Width);
Convert(Start,Stop,tBmp.Width,M.Mass,Mass);
Result := true;
end;
Вова (2013-08-24 16:40:00) [20]*т.е. для неперевернутого....ну вообщем для такого как в битмапе ))