LINUX.ORG.RU

Программирование и вычисления на GPU (аппаратное ускорение вычислений)

 , , ,


1

2

Приветствую ЛОР, доброго времени суток гики! Заранее извиняюсь, если не в тот раздел пишу, но тема такая, что затрагивает, наверное, не один раздел, а два-три.

Сижу я значит под Centos 7. Знаю язык программирования PHP. Есть видеокарта. Готов выучить любой язык программирования, лишь бы осуществить требуемую задачу и как можно скорее. Английский язык не знаю, поэтому на иноязычных сайтах не лазил, а на русскоязычных нужной инфы не нашёл.

А теперь к делу:

Каждый день, как только запускаю компьютер, открываю терминал и запускаю пхп-скрипт, которые осуществляет перебор, и ищет определённый результат перебора. Но это очень долго. Годами так буду перебирать. Я хочу поступить умно, и задействовать в этом свою видеокарту, то есть сделать аппаратное ускорение вычислений (перебора) за счёт множества ядер (ведь в GPU много ядер) на видеокарте. И тут возникает проблема - я в душе не знаю как это сделать! Ни как задействовать, ни какие языки поддерживают это. Только вот кажется мне, что придётся мне немного сишку подучить, чтоб реализовать это. Но выучить другой язык - не беда. Это легко для меня. А вот реализовать ускорение как - я не знаю. Кто с этим сталкивался? Может мельком где-то слышал, а может занимался? Подскажите, будьте добры. Или ткните носом в доки и маны, даже если те на английском (если что попрошу помощи у знающих английский, да и сам на интуитивном уровне зачастую понимаю).

Буду премного благодарен за любую помощь!

С уважением, Юрий.

P.S.: мне не обязательно использовать PHP! Мне подойдёт любой язык программирования, только подскажите какой лучше взять для аппаратного ускорения! Подозреваю, что им будет C.


Английский язык не знаю

И какого лысого ты тогда в программирование полез? Иди в евросеть, мобилками торговать. Еще, говорят, дворников не хватает.

которые осуществляет перебор,

Перебор чего? Это io-bound или cpu-bound? Расходящихся ветвлений много? Ответь на эти вопросы, и поймешь, годится тебе GPGPU, или нет.

anonymous ()
Ответ на: комментарий от Norgat

В наличии у меня может быть любая видеокарта, поэтому и не указал конкретно технологию. Хотя, думается, что CUDA по мощности лучше, нежели OpenCL. Именно в данный момент есть nVidia 520GT. Но для поставленной задачи слабовата будет, скорее всего.

Готов выслушать совет, какую технологию лучше (может я не прав, что CUDA), так как у меня друг держит компьютерный магазин, то и видеокарту я могу достать любую (в разумную цену, конечно).

intro ()
Ответ на: комментарий от anonymous

И какого лысого ты тогда в программирование полез?

Думаю это некорректный ответ. Это, конечно, ЛОр, но всё же не вам решать лезть мне в программирование или нет. Английский я только учу, из-за того что нужен в программировании. А полез я в программирование, так как это чуть ли не единственно, что я умею.

Перебор чего?

К примеру:

<?
set_time_limit(0);
$hash = 'хэш';
$salt = 'соль';
$arr = array('','a','b');
$i = array(
	0 => 0,
	1 => 0,
);
$result = 'empty';
echo $hash."\r\n\r\n";
while($i[0] != 63 && $result == 'empty')
{
	$str = $arr[$i[0]].$arr[$i[1]];
	$str_hash = crypt($str, $salt);
	echo $str." (".$str_hash.")\r\n";
	if($str_hash == $hash)
		$result = $str;
	$d++;
	$i[9]++;
	...условия номера значений...
}

intro ()
Ответ на: комментарий от intro

Ты сначала определись, годится ли твоя задача для SPMD. Может, тебе нужен как минимум Xeon Phi на самом деле.

anonymous ()
Ответ на: комментарий от intro

так как это чуть ли не единственно, что я умею.

Ты не можешь уметь в программирование, не умея в английский. Каким идиотом надо быть, чтобы учиться программировать, не выучив сначала английский?!?

К примеру:

Не знаю, что в похапе делает crypt, но подозреваю, что ветвится он только так. А значит, ничего хорошего от gpgpu тебе не светит. Все, что вокруг crypt, еще более-менее ничего, сойдет для gpgpu, но узкое место у тебя именно crypt.

anonymous ()
Ответ на: комментарий от anonymous

ЗЫ: а вот на FPGA такое, кстати, элементарно распараллелить.

anonymous ()
Ответ на: комментарий от anonymous

Ты не можешь уметь в программирование, не умея в английский. Каким идиотом надо быть, чтобы учиться программировать, не выучив сначала английский?!?

Это самое полезное, что вы можете сказать? Критиковать человека? Спасибо, таких у меня достаточно.

Не знаю, что в похапе делает crypt, но подозреваю, что ветвится он только так

crypt() в пхп шифрует строку. Насчёт ветвится он только так слегка не понял.

intro ()

gpu computing
php
OpenCL
cuda

Давно так не смеялся. Забудь быдлокодерство на PHP, оно тебе тут никак не поможет. Читай K&R, потом учи английский, потом смотри в сторону OpenCL и CUDA. Русскоязычных доков по этим технологиям нету, и это хорошо.

EXL ★★★★★ ()
Ответ на: комментарий от anonymous

ЗЫ: а вот на FPGA такое, кстати, элементарно распараллелить.

А ссыло для недалёкого не найдётся? А то я ничего не понял..(((

intro ()

Для всех пояснение: я писал - мне не обязательно использовать PHP! Мне подойдёт любой язык программирования, только подскажите какой лучше взять для аппаратного ускорения! Подозреваю, что им будет C.

intro ()
Ответ на: комментарий от intro

Это что, брутфорс каких-то хешей паролей?

Напиши конкретно что тебе нужно, подскажут точнее

anonymous ()
Ответ на: комментарий от EXL

Я понимаю, что троллей на ЛОРе много, но я не думал, что они не умеют читать:

Готов выучить любой язык программирования, лишь бы осуществить требуемую задачу и как можно скорее
И тут возникает проблема - я в душе не знаю как это сделать! Ни как задействовать, ни какие языки поддерживают это. Только вот кажется мне, что придётся мне немного сишку подучить, чтоб реализовать это. Но выучить другой язык - не беда. Это легко для меня

intro ()
Ответ на: комментарий от anonymous

На данном этапе да, брутфорс хэшей. Но в будущем мне всё равно понадобится ускорения для других целей. Но чтоб конкретизировать, то возьмём за пример этот брутфорс. Как реализовать его?

intro ()
Ответ на: комментарий от intro

intro  линуха

марш обратно на винду. В чём конкретно задача? Может стоит посмотреть на всякие oclhashcat (тем более, что оно умеет в cuda, а в наличии я так понимаю видеокарта nvidia)? John The Ripper в конце концов? В баш-скриптинг умеет любой.

wakuwaku ★★★★ ()
Последнее исправление: wakuwaku (всего исправлений: 2)
Ответ на: комментарий от intro

Но выучить другой язык - не беда. Это легко для меня

Конечно! Ведь ты осилил целый PHP! Теперь тебе изучение любого языка покажется простым и лёгким! С PHP можно целые горы свернуть!

EXL ★★★★★ ()
Ответ на: комментарий от wakuwaku

марш обратно на винду.

С винды ушёл уже очень давно, и возвращаться не планирую. Разочаровала она меня, как минимум. Могу поставить на второй комп, только если реально понадобится запустить что-то, чего на линуксе нет.

В чём конкретно задача?

Чтоб конкретизировать, выбрал брутфорс паролей. Имею хэш, сгенерированный функцией crypt(), и соль, с помощью которого хэшировался. Брутфорс делается обычным перебором символов от одного символа до 10-ти. Все буквы алфавита и цифры.

в наличии я так понимаю видеокарта nvidia

На данный момент имеется только nVidia 520GT, но в любой момент могу достать что-нибудь мощнее, но за разумные деньги. nVidia QUADRO мне по-тестить не дадут. :)

intro ()
Ответ на: комментарий от intro

этот брутфорс. Как реализовать его?

Набирай в гугле opencl des, opencl md5, sse md5 и смотри как их реализовывают. Или читай как cделано в John the Ripper

anonymous ()
Ответ на: комментарий от intro

Как реализовать его?

Вот так:

#include <stdio.h>
#include <cuda.h>
#include "main.h"
#define UINT4 uint
extern __shared__ unsigned int words[];	// shared memory where hash will be stored
__constant__ unsigned int target_hash[4];	// constant has we will be searching for
__device__ unsigned int *format_shared_memory(unsigned int thread_id, unsigned int *memory) {
unsigned int *shared_memory;
unsigned int *global_memory;
int x;
// we need to get a pointer to our shared memory portion
shared_memory = &words[threadIdx.x * 16];
global_memory = &memory[thread_id * 16];
for(x=0; x < 16; x++) {
shared_memory[x] = global_memory[x];
}
return shared_memory;
}
/* F, G and H are basic MD5 functions: selection, majority, parity */
#define F(x, y, z) (((x) & (y)) | ((~x) & (z)))
#define G(x, y, z) (((x) & (z)) | ((y) & (~z)))
#define H(x, y, z) ((x) ^ (y) ^ (z))
#define I(x, y, z) ((y) ^ ((x) | (~z)))
/* ROTATE_LEFT rotates x left n bits */
#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32-(n))))
/* FF, GG, HH, and II transformations for rounds 1, 2, 3, and 4 */
/* Rotation is separate from addition to prevent recomputation */
#define FF(a, b, c, d, x, s, ac) \
{(a) += F ((b), (c), (d)) + (x) + (UINT4)(ac); \
(a) = ROTATE_LEFT ((a), (s)); \
(a) += (b); \
}
#define GG(a, b, c, d, x, s, ac) \
{(a) += G ((b), (c), (d)) + (x) + (UINT4)(ac); \
(a) = ROTATE_LEFT ((a), (s)); \
(a) += (b); \
}
#define HH(a, b, c, d, x, s, ac) \
{(a) += H ((b), (c), (d)) + (x) + (UINT4)(ac); \
(a) = ROTATE_LEFT ((a), (s)); \
(a) += (b); \
}
#define II(a, b, c, d, x, s, ac) \
{(a) += I ((b), (c), (d)) + (x) + (UINT4)(ac); \
(a) = ROTATE_LEFT ((a), (s)); \
(a) += (b); \
}
__device__ void md5(uint *in, uint *hash) {
uint a, b, c, d;
const uint a0 = 0x67452301;
const uint b0 = 0xEFCDAB89;
const uint c0 = 0x98BADCFE;
const uint d0 = 0x10325476;
a = a0;
b = b0;
c = c0;
d = d0;
/* Round 1 */
#define S11 7
#define S12 12
#define S13 17
#define S14 22
FF ( a, b, c, d, in[ 0], S11, 3614090360); /* 1 */
FF ( d, a, b, c, in[ 1], S12, 3905402710); /* 2 */
FF ( c, d, a, b, in[ 2], S13, 606105819); /* 3 */
FF ( b, c, d, a, in[ 3], S14, 3250441966); /* 4 */
FF ( a, b, c, d, in[ 4], S11, 4118548399); /* 5 */
FF ( d, a, b, c, in[ 5], S12, 1200080426); /* 6 */
FF ( c, d, a, b, in[ 6], S13, 2821735955); /* 7 */
FF ( b, c, d, a, in[ 7], S14, 4249261313); /* 8 */
FF ( a, b, c, d, in[ 8], S11, 1770035416); /* 9 */
FF ( d, a, b, c, in[ 9], S12, 2336552879); /* 10 */
FF ( c, d, a, b, in[10], S13, 4294925233); /* 11 */
FF ( b, c, d, a, in[11], S14, 2304563134); /* 12 */
FF ( a, b, c, d, in[12], S11, 1804603682); /* 13 */
FF ( d, a, b, c, in[13], S12, 4254626195); /* 14 */
FF ( c, d, a, b, in[14], S13, 2792965006); /* 15 */
FF ( b, c, d, a, in[15], S14, 1236535329); /* 16 */
/* Round 2 */
#define S21 5
#define S22 9
#define S23 14
#define S24 20
GG ( a, b, c, d, in[ 1], S21, 4129170786); /* 17 */
GG ( d, a, b, c, in[ 6], S22, 3225465664); /* 18 */
GG ( c, d, a, b, in[11], S23, 643717713); /* 19 */
GG ( b, c, d, a, in[ 0], S24, 3921069994); /* 20 */
GG ( a, b, c, d, in[ 5], S21, 3593408605); /* 21 */
GG ( d, a, b, c, in[10], S22, 38016083); /* 22 */
GG ( c, d, a, b, in[15], S23, 3634488961); /* 23 */
GG ( b, c, d, a, in[ 4], S24, 3889429448); /* 24 */
GG ( a, b, c, d, in[ 9], S21, 568446438); /* 25 */
GG ( d, a, b, c, in[14], S22, 3275163606); /* 26 */
GG ( c, d, a, b, in[ 3], S23, 4107603335); /* 27 */
GG ( b, c, d, a, in[ 8], S24, 1163531501); /* 28 */
GG ( a, b, c, d, in[13], S21, 2850285829); /* 29 */
GG ( d, a, b, c, in[ 2], S22, 4243563512); /* 30 */
GG ( c, d, a, b, in[ 7], S23, 1735328473); /* 31 */
GG ( b, c, d, a, in[12], S24, 2368359562); /* 32 */
/* Round 3 */
#define S31 4
#define S32 11
#define S33 16
#define S34 23
HH ( a, b, c, d, in[ 5], S31, 4294588738); /* 33 */
HH ( d, a, b, c, in[ 8], S32, 2272392833); /* 34 */
HH ( c, d, a, b, in[11], S33, 1839030562); /* 35 */
HH ( b, c, d, a, in[14], S34, 4259657740); /* 36 */
HH ( a, b, c, d, in[ 1], S31, 2763975236); /* 37 */
HH ( d, a, b, c, in[ 4], S32, 1272893353); /* 38 */
HH ( c, d, a, b, in[ 7], S33, 4139469664); /* 39 */
HH ( b, c, d, a, in[10], S34, 3200236656); /* 40 */
HH ( a, b, c, d, in[13], S31, 681279174); /* 41 */
HH ( d, a, b, c, in[ 0], S32, 3936430074); /* 42 */
HH ( c, d, a, b, in[ 3], S33, 3572445317); /* 43 */
HH ( b, c, d, a, in[ 6], S34, 76029189); /* 44 */
HH ( a, b, c, d, in[ 9], S31, 3654602809); /* 45 */
HH ( d, a, b, c, in[12], S32, 3873151461); /* 46 */
HH ( c, d, a, b, in[15], S33, 530742520); /* 47 */
HH ( b, c, d, a, in[ 2], S34, 3299628645); /* 48 */
/* Round 4 */
#define S41 6
#define S42 10
#define S43 15
#define S44 21
II ( a, b, c, d, in[ 0], S41, 4096336452); /* 49 */
II ( d, a, b, c, in[ 7], S42, 1126891415); /* 50 */
II ( c, d, a, b, in[14], S43, 2878612391); /* 51 */
II ( b, c, d, a, in[ 5], S44, 4237533241); /* 52 */
II ( a, b, c, d, in[12], S41, 1700485571); /* 53 */
II ( d, a, b, c, in[ 3], S42, 2399980690); /* 54 */
II ( c, d, a, b, in[10], S43, 4293915773); /* 55 */
II ( b, c, d, a, in[ 1], S44, 2240044497); /* 56 */
II ( a, b, c, d, in[ 8], S41, 1873313359); /* 57 */
II ( d, a, b, c, in[15], S42, 4264355552); /* 58 */
II ( c, d, a, b, in[ 6], S43, 2734768916); /* 59 */
II ( b, c, d, a, in[13], S44, 1309151649); /* 60 */
II ( a, b, c, d, in[ 4], S41, 4149444226); /* 61 */
II ( d, a, b, c, in[11], S42, 3174756917); /* 62 */
II ( c, d, a, b, in[ 2], S43, 718787259); /* 63 */
II ( b, c, d, a, in[ 9], S44, 3951481745); /* 64 */
a += a0;
b += b0;
c += c0;
d += d0;
hash[0] = a;
hash[1] = b;
hash[2] = c;
hash[3] = d;
return;
}
__global__ void md5_cuda_calculate(void *memory, struct device_stats *stats, unsigned int *debug_memory) {
unsigned int id;
unsigned int *shared_memory;
uint hash[4];
int x;
id = (blockIdx.x * blockDim.x) + threadIdx.x;	// get our thread unique ID in this run
shared_memory = format_shared_memory(id, (unsigned int *)memory);
#ifdef DEBUG
// passes the computed hashes into debug memory
for(x=0; x<4; x++) {
debug_memory[(id * 4) + x] = (uint)shared_memory[x];
}
#endif
md5(shared_memory, hash);	// actually calculate the MD5 hash
if (hash[0] == target_hash[0] && hash[1] == target_hash[1] && hash[2] == target_hash[2] && hash[3] == target_hash[3]) {
// !! WE HAVE A MATCH !!
stats->hash_found = 1;
for(x=0; x<64; x++) {
// copy the matched word accross
stats->word[x] = *(char *)((char *)shared_memory + x);
}
}
}
extern "C" void md5_calculate(struct cuda_device *device) {
cudaEvent_t start, stop;
float time;
#ifdef GPU_BENCHMARK
cudaEventCreate(&start);
cudaEventCreate(&stop);
cudaEventRecord(start, 0);
cudaThreadSynchronize();
#endif
md5_cuda_calculate <<< device->max_blocks, device->max_threads, device->shared_memory >>> (device->device_global_memory, (struct device_stats *)device->device_stats_memory, (unsigned int *)device->device_debug_memory);
#ifdef GPU_BENCHMARK
cudaEventRecord(stop, 0);
cudaEventSynchronize(stop);
cudaEventElapsedTime(&time, start, stop);
cudaEventDestroy(start);
cudaEventDestroy(stop);
printf("CUDA kernel took %fms to calculate %d x %d (%d) hashes\n", time, device->max_blocks, device->max_threads, device->max_blocks * device->max_threads);
// print GPU stats here
#endif
}

EXL ★★★★★ ()
Ответ на: комментарий от EXL

#include <cuda.h>

Библиотека эта в С стандартом есть, или отдельно ставить надо?

intro ()
Ответ на: комментарий от intro

Библиотека эта в С стандартом есть, или отдельно ставить надо?

Видеокарта от NVidia имеется в каждом компьютере? Или её отдельно ставить надо?
И кстати, это не C. Как минимум потому, что ты его стандартным компилятором (gcc) не сможешь скомпилировать.

EXL ★★★★★ ()
Последнее исправление: EXL (всего исправлений: 1)
Ответ на: комментарий от EXL

Видеокарта от NVidia имеется в каждом компьютере? Или её отдельно ставить надо?

Блин, ну твой сарказм мне начинает нравится...)) Да, тупанул, признаю...))

И кстати, это не C. Как минимум потому, что ты его стандартным компилятором (gcc) не сможешь скомпилировать.

А вот за это спасибо. Компилить с помощью cuda-sdk надо будет?

intro ()
Ответ на: комментарий от intro

Компилить с помощью cuda-sdk надо будет?

Да. Там упрощенный диалект C. Я сильно не копался, видимо часть *.cu файлов компилируется nvcc, а остальные *.c-файлы программы с помощью стандартного системного компилятора. Потом все объектники собираются в приложение. Читай доку NVidia по этому CUDA. Для начала, со словарём, через усилие, скрипя зубами. Будет сложно, но потом любую документацию осилить сможешь.

EXL ★★★★★ ()
Последнее исправление: EXL (всего исправлений: 1)

А я думал, изучение английского сейчас в школах является обязательным.

Или у ТС он дальше по программе?

anonymous ()
Ответ на: комментарий от EXL

Спасибо за советы и ссылки. Хоть в конце нормально объяснили незнающему. :)

P.S.: насчёт английского, согласен - это просто громадный провал в знаниях и я стараюсь его сейчас восполнять. Но английского как такового у меня не было в школе. То препод некомпетентный, то препод - классный руководитель, и закрывал глаза на английский. Только после поступления начался у меня нормальный английский, но и тут не всё так просто - на факультет программирования я не попал на бюджет, а денег продолжить учёбу перейдя на контракт не было, поэтому пришлось оставить учёбу, и заняться изучением английского самостоятельно. Так что для меня не всё потеряно. Просто не надо относится к моему незнанию с критичным настроем. :)

intro ()
Ответ на: комментарий от anonymous

Написал ответ ниже. У меня он по программе ушёл давным-давно. Просто я его не учил до того, как занялся программированием. :(

intro ()
Ответ на: комментарий от intro

Просто не надо относится к моему незнанию с критичным настроем. :)

Не обращай внимание. Эти люди родились со знанием английского, набора инструкций x86 и С. Они все равно не поймут...

Suntechnic ★★★★★ ()

Чумазый пиздёныш с громкой отрыжкой и отчаянным воплём ввалился в комнату, где господа почивали после сытного завтрака. Плебей ошарашенно вылупился на удивлённых сэров, за пазухой у него явно было что-то запрятано. Ну, что у тебя там, зассыха? - снисходительно промолвил самый молодой из господ. А-а-фоть - заикаясь промямлила замарашка и трясущейся рукой вывалила на дорогой персидский ковёр кучу смрадного свежего говна. Лица элитариев побагровели от такого неслыханного безобразия. Камёрдинера сюда, живо! - воскликнул опять же самый молодой и самый вспыльчивый из них. Оказия сия окончилась, как можно безошибочно предугадать который раз не в пользу плебса, оно и к лучшему, не место черни среди высшего общества.

anonymous ()
Ответ на: комментарий от intro

nVidia QUADRO мне по-тестить не дадут. :)

Есть K600 же за 6.5к примерно.

По CUDA есть книжки на русском. Сам читал «CUDA в примерах». Для старта перед разбором офф. доков будет самое то.

Norgat ★★★★★ ()
Ответ на: комментарий от Suntechnic

Приличные люди сначала учили английский, и только потом программирование.

anonymous ()
Ответ на: комментарий от anonymous

Когда приличные люди учили программирование, никакого английского еще не было.

Suntechnic ★★★★★ ()
Ответ на: комментарий от Suntechnic

Что, приличные люди учили программирование до 800AD? Так те приличные люди более тысячи лет назад померли.

anonymous ()
Ответ на: комментарий от anonymous

Как шифрует? Какой алгоритм?

На форуме одном по языкам программирования есть такая запись:

Все 6 алгоритмов шифрования, на которых основана функция crypt() (а это DES, Extended DES, MD5, Blowfish, SHA256 и SHA512) являются необратимыми...

intro ()
Ответ на: комментарий от Norgat

Есть K600 же за 6.5к примерно.

Цены мне известны, только таких денег у меня нет, а на витрине квадры у друзей нет - это не самый распространенный продукт среди обычных пользователей, чтоб заказывать его со склада на витрину. :) На счёт книжек - поищу, за информацию спасибо.

intro ()
Ответ на: комментарий от anonymous

Я посмотрел бы на тебя как бы ты искал литературу (прикинь, интернетов тогда тоже считай что не было) на английском по программированию году этак 1992, не имея доступа к вузавским библиотекам.

Suntechnic ★★★★★ ()
Ответ на: комментарий от i-rinat

Чо сказать-то хотел этим? Мне тогда был доступен только spectrum и я писал сокобан на встроенном бейсике пользуясь книжками «Справочник команд BASIC компьютера Spectrum 128K» (это по бейсику) и «Описание работы Spectrum 16К» (это по ассемблеру и с описанием блоков ПЗУ).

Suntechnic ★★★★★ ()
Ответ на: комментарий от Suntechnic

И как я в 80х под ICL, а потом под DEC System 20 кодил, пользуясь исключительно английскими мануалами, так как других не было?

А в 92м году и интернеты были, и литература на английском во всех видах.

anonymous ()
Ответ на: комментарий от Suntechnic

Ну так ты дно, и к приличным людям никакого отношения не имеешь.

Приличные люди имели доступ к англоязычным источникам и в 80х, и в 70х, и даже в 60х.

anonymous ()
Ответ на: комментарий от intro

Ну, как минимум MD5 на GPGPU неплохо ложится, там мало расходящихся ветвлений. Другими не интересовался.

anonymous ()
Ответ на: комментарий от Suntechnic

Хочу сказать, что поход в библиотеку кажется сейчас чем-то диким. Там это прямым текстом сказано, разве нет?

i-rinat ★★★★★ ()
Ответ на: комментарий от i-rinat

О, да, в моей библитеки было полно книг по программированию на всех языках. Обе стояли как раз между Грозой и Му-му.

Suntechnic ★★★★★ ()
Ответ на: комментарий от anonymous

И как я в 80х под ICL, а потом под DEC System 20 кодил, пользуясь исключительно английскими мануалами, так как других не было?

Не знаю как ты кодил. В 80м я еще по русски читать не умел.

Suntechnic ★★★★★ ()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.