Я предпочитаю Python над Scala. Но поскольку Spark изначально написан в Scala, я ожидал, что мой код будет работать быстрее в Scala, чем версия Python по понятным причинам.
С этим предположением я решил изучить и написать версию Scala некоторого очень распространенного кода предварительной обработки для некоторых 1 GB данных. Данные выбираются из конкурса SpringLeaf на Kaggle. Просто чтобы дать обзор данных (он содержит 1936 размеров и 145232 строки). Данные состоят из различных типов, например. int, float, string, boolean. Я использую 6 ядер из 8 для обработки Spark; поэтому я использовал minPartitions=6
, чтобы каждое ядро что-то обрабатывало.
Scala Код
val input = sc.textFile("train.csv", minPartitions=6)
val input2 = input.mapPartitionsWithIndex { (idx, iter) =>
if (idx == 0) iter.drop(1) else iter }
val delim1 = "\001"
def separateCols(line: String): Array[String] = {
val line2 = line.replaceAll("true", "1")
val line3 = line2.replaceAll("false", "0")
val vals: Array[String] = line3.split(",")
for((x,i) <- vals.view.zipWithIndex) {
vals(i) = "VAR_%04d".format(i) + delim1 + x
}
vals
}
val input3 = input2.flatMap(separateCols)
def toKeyVal(line: String): (String, String) = {
val vals = line.split(delim1)
(vals(0), vals(1))
}
val input4 = input3.map(toKeyVal)
def valsConcat(val1: String, val2: String): String = {
val1 + "," + val2
}
val input5 = input4.reduceByKey(valsConcat)
input5.saveAsTextFile("output")
Код Python
input = sc.textFile('train.csv', minPartitions=6)
DELIM_1 = '\001'
def drop_first_line(index, itr):
if index == 0:
return iter(list(itr)[1:])
else:
return itr
input2 = input.mapPartitionsWithIndex(drop_first_line)
def separate_cols(line):
line = line.replace('true', '1').replace('false', '0')
vals = line.split(',')
vals2 = ['VAR_%04d%s%s' %(e, DELIM_1, val.strip('\"'))
for e, val in enumerate(vals)]
return vals2
input3 = input2.flatMap(separate_cols)
def to_key_val(kv):
key, val = kv.split(DELIM_1)
return (key, val)
input4 = input3.map(to_key_val)
def vals_concat(v1, v2):
return v1 + ',' + v2
input5 = input4.reduceByKey(vals_concat)
input5.saveAsTextFile('output')
Scala Производительность Стадия 0 (38 минут), этап 1 (18 секунд)
Производительность Python Этап 0 (11 минут), этап 1 (7 сек)
Оба создают разные графики визуализации DAG (из-за которых оба изображения показывают разные функции этапа 0 для Scala (map
) и Python (reduceByKey
))
Но, по сути, оба кода пытаются преобразовать данные в (size_id, string of list of values) RDD и сохранять на диск. Выход будет использоваться для вычисления различных статистических данных для каждого измерения.
Показатель производительности, Scala код для этих реальных данных вроде этого, кажется, работает в 4 раза медленнее, чем версия Python. Хорошей новостью для меня является то, что она дала мне хорошую мотивацию остаться с Python. Плохая новость: я не совсем понял, почему?