Ho sentito parlare molto di map / reduce, specialmente nel contesto del sistema di elaborazione massicciamente parallelo di Google. Cos'è esattamente?
Ho sentito parlare molto di map / reduce, specialmente nel contesto del sistema di elaborazione massicciamente parallelo di Google. Cos'è esattamente?
Risposte:
Dall'abstract della pagina di pubblicazione della ricerca MapReduce di Google :
MapReduce è un modello di programmazione e un'implementazione associata per l'elaborazione e la generazione di set di dati di grandi dimensioni. Gli utenti specificano una funzione mappa che elabora una coppia chiave / valore per generare un set di coppie chiave / valore intermedie e una funzione di riduzione che unisce tutti i valori intermedi associati alla stessa chiave intermedia.
Il vantaggio di MapReduce è che l'elaborazione può essere eseguita in parallelo su più nodi di elaborazione (più server), quindi è un sistema che può scalare molto bene.
Poiché si basa sul modello di programmazione funzionale , i passaggi map
e reduce
non hanno effetti collaterali (lo stato ei risultati di ciascuna sottosezione di un map
processo non dipendono da un altro), quindi il set di dati mappato e ridotto può essere separato su più nodi di elaborazione.
Può il tuo linguaggio di programmazione di Joel farlo? il pezzo discute come la comprensione della programmazione funzionale fosse essenziale in Google per creare MapReduce, che alimenta il suo motore di ricerca. È un'ottima lettura se non hai familiarità con la programmazione funzionale e come consente il codice scalabile.
Vedi anche: Wikipedia: MapReduce
Domanda correlata: spiega semplicemente mapreduce
Spiega meglio di quello che posso. Aiuta?
Map è una funzione che applica un'altra funzione a tutti gli elementi di un elenco, per produrre un altro elenco con tutti i valori di ritorno su di esso. (Un altro modo per dire "applica f a x" è "chiama f, passandola x". Quindi a volte suona meglio dire "applica" invece di "chiama".)
Questo è il modo in cui la mappa è probabilmente scritta in C # (si chiama Select
ed è nella libreria standard):
public static IEnumerable<R> Select<T, R>(this IEnumerable<T> list, Func<T, R> func)
{
foreach (T item in list)
yield return func(item);
}
Dato che sei un tizio di Java, e Joel Spolsky ama raccontare BUGIE GRAVOSAMENTE INGIUSTE su quanto sia schifoso Java (in realtà, non sta mentendo, è schifoso, ma sto cercando di conquistarti), ecco il mio tentativo molto approssimativo di una versione Java (non ho un compilatore Java e ricordo vagamente la versione 1.1 di Java!):
// represents a function that takes one arg and returns a result
public interface IFunctor
{
object invoke(object arg);
}
public static object[] map(object[] list, IFunctor func)
{
object[] returnValues = new object[list.length];
for (int n = 0; n < list.length; n++)
returnValues[n] = func.invoke(list[n]);
return returnValues;
}
Sono sicuro che questo può essere migliorato in un milione di modi. Ma è l'idea di base.
Riduci è una funzione che trasforma tutti gli elementi di un elenco in un unico valore. Per fare ciò, è necessario assegnare un'altra funzione func
che trasforma due elementi in un unico valore. Funzionerebbe dando i primi due elementi a func
. Quindi il risultato di quello insieme al terzo elemento. Quindi il risultato di ciò con il quarto elemento e così via fino a quando tutti gli elementi non sono stati eliminati e ci rimane un valore.
In C # viene chiamato reduce Aggregate
ed è di nuovo nella libreria standard. Salterò direttamente a una versione Java:
// represents a function that takes two args and returns a result
public interface IBinaryFunctor
{
object invoke(object arg1, object arg2);
}
public static object reduce(object[] list, IBinaryFunctor func)
{
if (list.length == 0)
return null; // or throw something?
if (list.length == 1)
return list[0]; // just return the only item
object returnValue = func.invoke(list[0], list[1]);
for (int n = 1; n < list.length; n++)
returnValue = func.invoke(returnValue, list[n]);
return returnValue;
}
Queste versioni di Java richiedono l'aggiunta di generici, ma non so come farlo in Java. Ma dovresti essere in grado di passare loro classi interne anonime per fornire i funtori:
string[] names = getLotsOfNames();
string commaSeparatedNames = (string)reduce(names,
new IBinaryFunctor {
public object invoke(object arg1, object arg2)
{ return ((string)arg1) + ", " + ((string)arg2); }
}
Si spera che i generici si sbarazzino dei calchi. L'equivalente typesafe in C # è:
string commaSeparatedNames = names.Aggregate((a, b) => a + ", " + b);
Perché questo "cool"? Semplici modi per suddividere calcoli più grandi in pezzi più piccoli, in modo che possano essere rimessi insieme in modi diversi, sono sempre interessanti. Il modo in cui Google applica questa idea è la parallelizzazione, perché sia la mappa che la riduzione possono essere condivise su più computer.
Ma il requisito fondamentale NON è che la tua lingua possa trattare le funzioni come valori. Qualsiasi lingua OO può farlo. Il requisito effettivo per la parallelizzazione è che le piccole func
funzioni passate per mappare e ridurre non devono utilizzare o aggiornare alcuno stato. Devono restituire un valore che dipende solo dagli argomenti loro passati. Altrimenti, i risultati saranno completamente sbagliati quando proverai a eseguire il tutto in parallelo.
Dopo essermi sentito molto frustrato con waffley molto lunghi o post di blog vaghi molto brevi, alla fine ho scoperto questo documento molto buono e conciso .
Poi sono andato avanti e l'ho reso più conciso traducendolo in Scala, dove ho fornito il caso più semplice in cui un utente specifica semplicemente le parti map
e reduce
dell'applicazione. In Hadoop / Spark, in senso stretto, viene impiegato un modello di programmazione più complesso che richiede all'utente di specificare esplicitamente altre 4 funzioni descritte qui: http://en.wikipedia.org/wiki/MapReduce#Dataflow
import scalaz.syntax.id._
trait MapReduceModel {
type MultiSet[T] = Iterable[T]
// `map` must be a pure function
def mapPhase[K1, K2, V1, V2](map: ((K1, V1)) => MultiSet[(K2, V2)])
(data: MultiSet[(K1, V1)]): MultiSet[(K2, V2)] =
data.flatMap(map)
def shufflePhase[K2, V2](mappedData: MultiSet[(K2, V2)]): Map[K2, MultiSet[V2]] =
mappedData.groupBy(_._1).mapValues(_.map(_._2))
// `reduce` must be a monoid
def reducePhase[K2, V2, V3](reduce: ((K2, MultiSet[V2])) => MultiSet[(K2, V3)])
(shuffledData: Map[K2, MultiSet[V2]]): MultiSet[V3] =
shuffledData.flatMap(reduce).map(_._2)
def mapReduce[K1, K2, V1, V2, V3](data: MultiSet[(K1, V1)])
(map: ((K1, V1)) => MultiSet[(K2, V2)])
(reduce: ((K2, MultiSet[V2])) => MultiSet[(K2, V3)]): MultiSet[V3] =
mapPhase(map)(data) |> shufflePhase |> reducePhase(reduce)
}
// Kinda how MapReduce works in Hadoop and Spark except `.par` would ensure 1 element gets a process/thread on a cluster
// Furthermore, the splitting here won't enforce any kind of balance and is quite unnecessary anyway as one would expect
// it to already be splitted on HDFS - i.e. the filename would constitute K1
// The shuffle phase will also be parallelized, and use the same partition as the map phase.
abstract class ParMapReduce(mapParNum: Int, reduceParNum: Int) extends MapReduceModel {
def split[T](splitNum: Int)(data: MultiSet[T]): Set[MultiSet[T]]
override def mapPhase[K1, K2, V1, V2](map: ((K1, V1)) => MultiSet[(K2, V2)])
(data: MultiSet[(K1, V1)]): MultiSet[(K2, V2)] = {
val groupedByKey = data.groupBy(_._1).map(_._2)
groupedByKey.flatMap(split(mapParNum / groupedByKey.size + 1))
.par.flatMap(_.map(map)).flatten.toList
}
override def reducePhase[K2, V2, V3](reduce: ((K2, MultiSet[V2])) => MultiSet[(K2, V3)])
(shuffledData: Map[K2, MultiSet[V2]]): MultiSet[V3] =
shuffledData.map(g => split(reduceParNum / shuffledData.size + 1)(g._2).map((g._1, _)))
.par.flatMap(_.map(reduce))
.flatten.map(_._2).toList
}
Map è un metodo JS nativo che può essere applicato a un array. Crea un nuovo array come risultato di alcune funzioni mappate su ogni elemento dell'array originale. Quindi, se hai mappato una funzione (elemento) {return element * 2;}, restituirebbe un nuovo array con ogni elemento raddoppiato. La matrice originale rimarrebbe invariata.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map
Reduce è un metodo JS nativo che può essere applicato anche a un array. Applica una funzione a un array e ha un valore di output iniziale chiamato accumulatore. Esegue un ciclo attraverso ogni elemento dell'array, applica una funzione e li riduce a un singolo valore (che inizia come accumulatore). È utile perché puoi avere qualsiasi output tu voglia, devi solo iniziare con quel tipo di accumulatore. Quindi, se volessi ridurre qualcosa in un oggetto, inizierei con un accumulatore {}.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/Reduce?v=a
Riduci mappa:
Per eseguire qualcosa di grande, possiamo usare la potenza di calcolo di diversi computer nel nostro ufficio. La parte difficile è dividere l'attività tra diversi computer ed è eseguita dalla libreria MapReduce.
L'idea di base è dividere il lavoro in due parti: una mappa e una riduzione. Map fondamentalmente prende il problema, lo divide in sottoparti e invia le sottoparti a macchine diverse, in modo che tutti i pezzi vengano eseguiti contemporaneamente. Riduci prende i risultati dalle sottoparti e li combina di nuovo insieme per ottenere un'unica risposta.
L'input è un elenco di record.Il risultato del calcolo della mappa è un elenco di coppie chiave / valore. Riduci prende ogni insieme di valori che ha la stessa chiave e li combina in un unico valore. Non puoi dire se il lavoro è stato diviso in 100 pezzi o 2 pezzi; il risultato finale assomiglia più o meno al risultato di una singola mappa.
Si prega di guardare la mappa semplice e ridurre il programma:
La funzione mappa viene utilizzata per applicare alcune funzioni sul nostro elenco originale e quindi viene generato un nuovo elenco. La funzione map () in Python accetta una funzione e una lista come argomento. Viene restituito un nuovo elenco applicando la funzione a ciascun elemento dell'elenco.
li = [5, 7, 4, 9]
final_list = list(map(lambda x: x*x , li))
print(final_list) #[25, 49, 16, 81]
La funzione reduce () in Python accetta una funzione e una lista come argomento. La funzione viene chiamata con una funzione lambda e un elenco e viene restituito un nuovo risultato ridotto. Ciò esegue un'operazione ripetitiva sulle coppie dell'elenco.
#reduce func to find product/sum of list
x=(1,2,3,4)
from functools import reduce
reduce(lambda a,b:a*b ,x) #24