Pull to refresh
14
0
Alexander Kochurov @A_Kochurov

User

Send message
Насчет последнего согласен — производительность PIG-скриптов зачастую более предсказуема, чем на Hive. Заранее знаешь, сколько примерно будут выполняться какие конструкции. Плюс благодаря более низкоуровневому подходу можно более гибко управлять обработкой данных.

У нас был такой случай — один клиент дал нам доступ к логам своей системы, мы на своей стороне реализовали обработку на PIG. Скрипт работал что-то около часа на 4 машинах.

Когда клиент узнал, сколько занимает у нас обработка его данных, у него отпала челюсть. Оказалось, что они сами обрабатывали логи своего сервера с помощью Hive. Обычный дневной процессинг у них занимал что-то около 6 часов на кластере из десятка машин. Подозреваю, что их программисты получили втык за перерасход вычислительных ресурсов :-)

Позже они показывали нам свои Hive-запросы. Оптимизировать там мне ничего не удалось — по сути, и оптимизировать-то было нечего, пара фильтров, группировки, сортировки, снова группировки. Честно говоря, я так и не понял, что же они делали не так, но факт остается фактом — тормозило оно безбожно.
Рановато скидываете его со счетов! Удобнейшая вещь.

Мы используем PIG для парсинга логов сторонних систем, с которыми мы интегрированы. Объем логов — от 10М до 500М записей в день.

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

pig незаменим, когда нужно много работать с текстом. Например, из URL выдернуть все параметры, отфильтровать их, по каждому параметру собрать статистику по встречаемости значений по отдельности, плюс посчитать ковариации разных значений для пар параметров. С трудом представляю, как это сделать в SQL, а на свинье пишется в пару строк.

Свой «птичий язык» свинтуса сначала не внушает доверия, но со временем привыкаешь. ИМХО сложные скрипты на нем выглядят более читабельно, чем на SQL засчет того, что вместо двух сложнейших запросов на PIG как правило пишут 10 коротких и простых выражений.

Увы, для аналитики pig latin мало пригоден по причине наличия этого своего нестандартного языка (аналитикам его учить сложно, императивные языки для них в новинку, а SQL привычен), своей низкой интерактивности и некоторой задумчивости. Даже на наборах данных в пару тысяч строк и простых скриптах (фильтр-группировка-фильтр-сортировка) может генерироваться по несколько MR-задач.
Это было бы вполне логично, если бы от негарантийного ремонта получал доход именно сам Samsung. Но ведь основной гешефт получит локальный сервисный центр, правильно?

Если в автопроме производитель может хорошо навариться на запчастях и расходниках (что он благополучно и делает!), то в случае конденсаторов за 150 рублей при цене телевизора в десяток тысяч вряд ли это будет очень прибыльным, учитывая вред репутации и то, что проблема возникнет далеко не у 100% покупателей.

ИМХО, есть две более логичных причины сделать таким образом:
— либо поставить конденсатор таким образом было проще с точки зрения экономии времени сборщика или автоматизации производства. В масштабах корпорации экономия в 10 секунд на телевизор и один сантиметр провода может получиться многомиллионная.
— либо производитель планирует, что большинство покупателей не будет тащить телек в СЦ и тратиться на постгарантийный ремонт, а вместо этого купит новый. Но опять таки, это скользкая дорожка — ведь есть большая вероятность, что, памятуя о прошлой поломке, клиент купит не Samsung.
Для интереса попробовал запустить ваш код еще на старенькой, но довольно мощной Tesla C2050 — и на ней результат получился довольно неутешительный, 4.27 секунд.

Такая большая разница довольно любопытна. На моих задачах разница между этими двумя картами (GTX 660 и C2050) получалась гораздо более скромная. Число потоковых процессоров у 660й больше в два раза (448 у теслы против 960 у 660й), пропусная способность памяти одинаковая. На вашей задаче я бы ожидал разницу раза в полтора-два в худшем случае.
Поигрался с вашим кодом — на моей машине версия для GPU отрабатывает всего за секунду, а версию для CPU было лень адаптировать, ибо WinAPI.
(GPU: NVidia GTX 660).

Но если убрать лишнее ветвление тут:

for(k=offset; k<=(i+1)*PART_SIZE; k+=part1[j]*2)
            if(part[(k-(i*PART_SIZE)-1)/2]) part[(k-(i*PART_SIZE)-1)/2] = false;


вместо этого просто написав

for(k=offset; k<=(i+1)*PART_SIZE; k+=part1[j]*2)
            part[(k-(i*PART_SIZE)-1)/2] = false;


то будет работать на 15-20% быстрее, т.е. примерно за 0.81-0.86 секунды.
Как я понимаю, проблемы бы не возникло, если бы ключи внутри мапы ThreadLocalVariable -> ThreadLocalValue были бы тоже weak?

Ключи там и так на WeakReference-ах сделаны, но это не помогает т.к. в примере каждый объект ThreadLocal остается видим напрямую (не-weak) из своего значения.

Вы возможно хотели сказать, «если бы значения внутри мапы… были тоже weak»?
В этом случае конечно утечки бы не возникало. Но и пользоваться такой мапой было бы практически невозможно — она теряла бы значения в произвольные моменты времени при сборке мусора, если нет других ссылок на значения.
Они не на машинах ездят ;) Основной транспорт — мотоцикл. Да и в каждой машине ездит существенно больше людей чем у нас.

Но даже с учетом мотоциклов число транспортных средств у них невелико — судя по этой статье, что-то около 1.1 миллиона (впрочем, тут речь о зарегистрированных — реальное число может отличаться как в ту, так и в другую сторону), т.е. где-то чуть больше 4х на 1000 населения. Т.е. во всей стране выходит где-то в 4-6 раз меньше моторизованных транспортных средств, чем в одной только Москве.

Если смертность считать не на 100 тысяч душ населения, а на одно транспортное средство — то получатся цифры астрономические.

Большего ада бы не было, там и так пробки сплошные. Места нет для большего количества машин.

Я в Катманду не был, так что насчет пробок Вам виднее :-)
Впрочем возможно пробки именно из-за стиля езды и возникают — и при правильной организации были бы у них пустые дороги, и низкая смертность.
Ну а по поводу отсутствия места под автомобили — ну так у нас такие разговоры идут уже лет 15-20, и каждый год число автомобилей только растет.
То, что у нас высокая аварийность и смертность на дорогах еще не значит, что не может быть хуже.
Вы сравните число машин на 1000 жителей у нас и в Непале — разница больше, чем на порядок.

А теперь представьте себе, во что превратился бы этот ад на дорогах, будь в Непале столько машин, как и у нас — подозреваю, что Египет и Эритрея потеряли бы пальму первенства по числу смертей в ДТП на 100 тысяч жителей.
В плане количества букв, приходящихся на один звук (как eau), или которые не читаются или читаются непривычно для русского слуха (например «en» как «ан» в слове ensemble) мне кажется французский сильно превосходит английский и многие другие европейские языки.

Человеку который французский не знает навскидку предсказать как читается слово будет довольно сложно. Ну а речь в этой ветке как раз шла о том, нужна ли русская транскрипция для иностранных слов. ИМХО для французского нужна :-)
А во французском вообще полный швах: даже банальное знакомое всем «мерси боку» (с детства помню «судьбе шепнем мерси боку» из «Трех мушкетеров») пишется как merci beaucoup. Легко ли было бы угадать, как это произносится?
Не следует ли из этого, что там одни раз определяется откуда вызывать метод для класса и после warmup все вызовы будут занимать одинаковое время в пределах погрешности? Что, кстати, и подтверждается результатами.


Ну это же совсем про другое.

constant pool существует в байткоде. Поскольку компилятор не знает на этапе компиляции, в какой слот таблицы виртуальных методов попадет тот или иной метод, он создает запись в constant pool-е, которая содержит имя класса или интерфейса, который нужно вызвать, и сигнатуру метода.

При загрузке класса JVM разрешает зависимости класса и создает таблицы виртуальных методов в памяти. А уже дальше случается всякая магия вроде инлайнинга и тому подобного.

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

Почему у iterations такое маленькое значение в аннотациях?


Не такое уж маленькое:

@Warmup(iterations = 5, time = 1, timeUnit = SECONDS)


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

Полагаю, что этого вполне достаточно.

Что изменится, если разные ключики понадобавлять, типа -server?


Хороший вопрос. Автор пускал я так понимаю с настройками по-умолчанию (судя по этому).

На досуге попробую поиграться с другими комбинациями.
Не следует ли из этого, что там одни раз определяется откуда вызывать метод для класса и после warmup все вызовы будут занимать одинаковое время в пределах погрешности? Что, кстати, и подтверждается результатами.


Ну это же совсем про другое.

constant pool существует в байткоде. Поскольку компилятор не знает на этапе компиляции, в какой слот таблицы виртуальных методов попадет тот или иной метод, он создает запись в constant pool-е, которая содержит имя класса или интерфейса, который нужно вызвать, и сигнатуру метода.

При загрузке класса JVM разрешает зависимости класса и создает таблицы виртуальных методов в памяти. А уже дальше случается всякая магия вроде инлайнинга и тому подобного.

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

Почему у iterations такое маленькое значение в аннотациях?


Ну не такое уж маленькое:

@Warmup(iterations = 5, time = 1, timeUnit = SECONDS)


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

Полагаю, что этого вполне достаточно.

Что изменится, если разные ключики понадобавлять, типа -server?

Хороший вопрос. Автор пускал я так понимаю с настройками по-умолчанию (судя по этому).

На досуге попробую поиграться с другими комбинациями.
Есть кстати весьма полезных алгоритм суммирования Кохэна (вики), который сильно уменьшает погрешность суммирования.

В некоторых случаях он позволяет получить результат с использованием float-а более точный, без этого алгоритма с использованием double. Хотя это конечно не повод повсеместно переходить с double на float, или использовать числа с плавающей точкой для денег :-) Не стоит к тому же пихать такое суммирование всюду, достаточно только в тех местах, где действительно повышенные требования к точности.

Пример:

float[] floats = new float[100];
Arrays.fill(floats, 0.01f);

double[] doubles = new double[100];
Arrays.fill(doubles, 0.01);

float fsum = 0;
for (float x: floats) {
  fsum += x;
}
System.out.println(fsum);
// -> выведет 0.99999934, позорная ошибка округления

double dsum = 0;
for (double x: doubles) {
  dsum += x;
}
System.out.println(dsum);
// -> выведет 1.0000000000000007 - уже неплохо

System.out.println(kahanSum(floats));
// -> выведет ровно 1.0 - ура!


Само суммирование (из вики):
static float kahanSum(float... input) {
  float sum = 0;
  float c = 0;          
  for (float v : input) {
    float y = v - c; 
    float t = sum + y;
    c = (t - sum) - y;
    sum = t;
  }
  return sum;
}
Но я таки бы сделал упор, что это ошибка утечки памяти с внутренними классами, а как одно из проявлений — работа с ThreadLocal.

Внутренние классы — это случай, в котором проще всего словить такую утечку, и который потом очень сложно обнаружить.

Однако в запутанных иерархиях классов легко и непринужденно можно выстрелить в ногу и без внутренних классов. К слову там, где я это впервые нашел, не было никаких внутренних классов :-)

Пожалуй, у описанной мною проблемы ноги растут из использования словарей со слабыми ссылками на ключи, на которых построены ThreadLocal-ы.
И при встрече в коде с ThreadLocal или WeakHashMap у программиста должен в голове раздаваться тревожный звоночек: «а что туда кладется? хорошо ли я подумал, прежде чем так сделать? не лучше ли предварительно переупаковать мои данные в какую-то простую структуру без циклических ссылок?»

P.S. За ссылки спасибо.

Information

Rating
Does not participate
Location
Германия
Date of birth
Registered
Activity