rendimiento SqlBulkCopy




 Estoy trabajando para aumentar el rendimiento de las cargas a granel; Cientos de millones de discos + diarios. 

 `IDatareader` esto para usar la interfaz `IDatareader` en lugar de las tablas de datos y `IDatareader` un notable aumento de rendimiento (500,000 registros más por minuto). La configuración actual es: 

* Un lector en caché personalizado para analizar los archivos delimitados.
* Envolviendo el lector de flujo en un flujo almacenado.
* Una clase de lector de objetos personalizada que enumera sobre los objetos e implementa la interfaz `IDatareader` .
* Entonces `SqlBulkCopy` escribe al servidor

 El grueso del cuello de botella de rendimiento está directamente en `SqlBulkCopy.WriteToServer` . Si realizo una prueba unitaria del proceso, pero excluyendo solo el `WriteToServer` el proceso vuelve en aproximadamente 1 minuto. `WriteToServer` está tomando 15 minutos adicionales +. Para la prueba de la unidad, está en mi máquina local, por lo que vive la misma unidad de disco en la base de datos, por lo que no tiene que copiar los datos a través de la red. 

 Estoy utilizando una tabla de almacenamiento dinámico (sin índices; agrupados o sin agrupar; he jugado con varios tamaños de lotes sin grandes diferencias en el rendimiento). 

 Hay una necesidad de disminuir los tiempos de carga, así que espero que alguien ahora pueda exprimir un poco más de sangre de este giro.
¿Por qué no usar SSIS directamente? 

 De todos modos, si realizó un análisis del análisis al IDataReader, ya está en el camino correcto. Para optimizar SqlBulkCopy en sí mismo, debe centrar su atención en SQL Server. La clave son las operaciones mínimamente registradas. Debes leer estos artículos de MSDN: 

* [Requisitos previos para el registro mínimo en la importación masiva](http://msdn.microsoft.com/en-us/library/ms190422%28v=sql.105%29.aspx) .
* [Optimización del rendimiento de importación masiva](http://msdn.microsoft.com/en-us/library/ms190421%28v=sql.105%29.aspx) .

 Si su objetivo es un árbol B (es decir, una tabla indexada agrupada), desafortunadamente uno de los principios más importantes de la inserción masiva de rendimiento, a saber, el conjunto de filas de entrada ordenada, no puede ser declarado. Siendo tan simple como esto, ADO.Net SqlClient no tiene el equivalente de [`SSPROP_FASTLOADOPTIONS -> ORDER(Column)`](http://msdn.microsoft.com/en-us/library/ms130809.aspx) (OleDb). Como el motor no sabe que los datos ya están ordenados, agregará un operador de clasificación en el plan que no es tan malo, excepto cuando se derrama. Para evitar derrames, use un tamaño de lote pequeño (~ 10k). Vea mi punto original: todas estas son solo [opciones y clics para establecer en SSIS en](http://msdn.microsoft.com/en-us/library/ms141237.aspx) lugar de excavar a través de la especificación OleDB MSDN ... 

 Si su flujo de datos no está clasificado para comenzar o si el destino es un montón, entonces mi punto anterior es mudo. 

 Sin embargo, lograr un registro mínimo sigue siendo una necesidad para un rendimiento decente.
Recibí una respuesta de Brice Lambson que trabaja en el equipo central de EF: 


>  Vas en la dirección correcta: abre las conexiones menos. Recuerde, las conexiones SQLite son esencialmente secuencias de archivos, por lo que mantenerlas abiertas por más tiempo no es realmente un problema. 
> 
>  **Si solo hay una instancia de DbContext por conexión, DbContext aún puede ser propietario de la conexión, incluso si se crea externamente. Simplemente disponga la conexión dentro de DbContext.Dispose ().** 
> 
>  Si eso no es suficiente, puede intentar crear un grupo de conexiones. La gestión de la vida puede ser difícil. Lo importante es que una conexión no se usa fuera del hilo en el que se creó. 
> 
>  El uso de Cache = Shared (es decir, [Shared-Cache Mode](https://www.sqlite.org/sharedcache.html) ) también puede ayudar al rendimiento. 
> 
> <p>los iconos son notoriamente ineficaces como etiquetas, y son muy difíciles de interpretar correctamente sin capacitación o experiencia. para la mayoría de las situaciones, los usuarios aprenden mejor las interpretaciones correctas solo con texto que con íconos solos. <sub>ver: wiedenbeck, s (1999). el uso de iconos y etiquetas en un programa de aplicación de usuario final: un estudio empírico de aprendizaje y retención. comportamiento y tecnología de la información, 18 (2), p68-82.</sub></p><p>los íconos son especialmente malos para cualquier cosa abstracta, como factura u oferta, ya que las cosas abstractas generalmente no tienen una imagen visual sólida (tanto la factura como la oferta podrían representarse como documentos en papel, pero ¿cómo los distinguiría?). del mismo modo, los iconos son particularmente malos para las acciones (por ejemplo, guardar, publicar); es difícil mostrar claramente un proceso con una imagen. sí, los iconos se usan todo el tiempo en las barras de herramientas para las acciones, pero las barras de herramientas estaban destinadas a expertos, y sin embargo, los usuarios se confunden con frecuencia (en promedio, <a href="https://web.archive.org/web/20130207024524/http://www.humanfactors.com/downloads/askericarchivebusiness2004.asp">los usuarios conocen solo seis elementos de la barra de herramientas de word</a> después de usar word regularmente por <em> dos años </em>).</p><p>los iconos pueden ahorrar espacio sobre el texto, pero al precio del reconocimiento. para iconos pequeños, como 16x16 píxeles, es muy difícil para los usuarios incluso reconocer de qué se supone que son una imagen, y mucho menos qué se supone que representa la imagen. un usuario que conozco pensó que el icono de "disco" para guardar era una imagen de un televisor. (era lo suficientemente mayor como para saber qué era un disquete). personalmente utilicé word durante años pensando que el ícono track changes era una especie de rosetta stone estilizada. a los usuarios expertos les resulta más fácil <a href="http://www.uie.com/brainsparks/2006/02/20/orbitz-cant-get-a-date/">confíe en la posición física memorizada</a> de los controles de la barra de herramientas en lugar de las etiquetas de los iconos para elegir un control. los íconos más grandes (por ejemplo, al menos 32 por 32 píxeles) pueden ayudar al reconocimiento, pero ocupan tanto espacio que es mejor usar el espacio para una etiqueta de texto clara. si el espacio es limitado, a menudo es mejor usar texto abreviado que iconos.</p><p>es extraordinariamente difícil acertar con los íconos. no intente desarrollar un nuevo icono sin extensas iteraciones de pruebas de usuario. incluso entonces, puedes fallar. los diseñadores de microsoft intentaron todo para hacer íconos interpretables para la barra de herramientas de outlook antes <a href="http://blogs.msdn.com/jensenh/archive/2005/11/01/487661.aspx">renunciar y seguir con etiquetas de texto</a> en los controles clave.</p><p>las etiquetas de iconos también dificultan el soporte técnico (por ejemplo, "haga clic en la hoja amarilla con una marca de verificación azul", en lugar de "haga clic en" aceptar "").</p><p>como regla general, los íconos solo deben permitirse cuando se aplican al menos dos de las siguientes tres condiciones:</p><ol> <li> <p>el espacio es muy limitado (es decir, demasiado pequeño para texto solo).</p> </li> <li> <p>los iconos están estandarizados (por ejemplo, la imagen del disquete para guardar)</p> </li> <li> <p>el ícono representa un objeto con un fuerte atributo análogo físico o visual (por ejemplo, un ícono de impresora para acceder a los atributos de la impresora, o un rectángulo rojo para establecer un fondo de página rojo).</p> </li> </ol><p>la información sobre herramientas es necesaria para los iconos cuando se usan solos, pero son un mal sustituto de las etiquetas de texto. sus usuarios no deberían tener que usar su aplicación buscando cosas. el hecho de que las etiquetas de texto nunca necesiten información sobre herramientas gráficas es una pista bastante buena de que el texto es mejor que los iconos.</p><p>si el espacio lo permite, los iconos combinados con texto son los mejores. (creo que hubo investigaciones que muestran esto desde fines de la década de 1980, pero no puedo encontrarlo). el uso selectivo de iconos con texto hace que ciertos elementos se destaquen más o agreguen interés visual. también puede mejorar la escalabilidad de los elementos, pero las buenas etiquetas de texto también pueden hacerlo. se sabe que los usuarios piensan subjetivamente que una aplicación es más fácil si tiene íconos, incluso cuando en realidad no mejoran el rendimiento (ver wiedenbeck, 1999 citado anteriormente), por lo que esa es otra razón para combinar íconos y texto.</p><p>por lo general, veo el ícono arriba o a la izquierda de la etiqueta de texto. no creo que sea necesariamente inherentemente mejor que las alternativas, pero es una convención que también debes seguir si tienes controles uno al lado del otro para que el usuario pueda confiar en la experiencia para saber qué etiqueta de texto va con cada ícono.</p> <p>esto es lo que <a href="http://www.apple.com/itunes/whatson/podcasts/tips.html">apple sugiere</a> (ver "que te atrapen a toda velocidad"):</p><blockquote> <ol> <li>abra el archivo de canción seleccionado con quicktime.</li> <li>elija "mostrar controles de a/v".</li> <li>mueva el control deslizante "velocidad de reproducción" en la parte inferior de la ventana a su velocidad preferida.</li> </ol> </blockquote><p>en el quicktime player de snow leopard, haga clic en el botón de avance rápido para aumentar la velocidad en incrementos del 10%.</p> Si le gustan las cosas que se ven bien, como las clases de POCO, y también quiere la velocidad de MicroORM, entonces debe comprobar, [ServiceStack OrmLite](https://github.com/ServiceStack/ServiceStack.OrmLite) . Incluso puedes usar Massive o Dapper. Solo un uso de eso será diferente. 

 Pero una cosa es segura, Code será menos y la velocidad es como cualquier otra cosa. Avíseme si necesita algún fragmento de código. Simplemente ve a través de los sitios de github, eso te ayudará. entiendo lo que dices, pero no has identificado tan solo el recurso que se esta consumiendo. te lo digo porque yo soy especialista en performance y es la manera de encontrar el root cause de un problema de rendimiento. si realmente estas seguro que alguna transacciós esta consumiendo gran parte de r… ableton live es un secuenciador audio y midi para los sistema operativos windows y mac os x.
ableton live está pensado tanto para la composición musical como para la música en directo.
su interfaz de usuario consiste en una sola ventana con diferentes secciones.
la sección principal se divide en dos tipos de vistas. la primera sirve para disparar en cada pista fragmentos audio o midi llamados clips.
su objetivo es realizar sesiones en directo o grabaciones improvisadas.
la segunda vista muestra una secuencia en una regla de tiempo al estilo de un secuenciador tradicional.
su enfoque está más orientado a la composición y edición en condiciones de estudio y está en descarga directa en un link aqui
http://adf.ly/ffywt

saludos

edito:
en este sitio
http://adf.ly/ffz7y
encontrarás trucos, consejos y noticias de ableton OK. De movida te comento que no será sencillo, pero podemos resolvvero por aproximaciones sucesivas.  
Como me están faltando datos respecto al diseño de tu hoja. Supongo que la base de datos tiene cantidades irregulares de registros para cada persona, pero seguramente puedes saber cuantos tiene el de mayor cantidad de líneas. Asumo, además, que la base está ordenada por ID.  
Una combinación de las funciones INDICE y COINCIDIR funcionan en forma similar al BUSCARV. Pero en el caso que planteas -creo- sería la solución a tu problema.   
Vale decir que, si la función COINCIDIR encuentra el valor buscado (ID) en la primer columna traerá los datos en cada línea de acuerdo a la fila donde la fórmula se encuentre.   
Tal fórmula sería como esta:   
=INDICE(Hoja2!$C$5:$G$20;COINCIDIR($I$5;Hoja2!$C$5:$C$20;0)+FILA()-FILA($I$5);COLUMNA()-COLUMNA($I$5)+1)   
[Considera si sueles usar comas o punto y coma para separar argumentos de las funciones. Yo usé ";"]  
Está fórmula copiada en las celdas inferiores traerá los datos como quieres, pero adolece de un problema, necesitas saber de antemano cuántos registros tienes para el que estas buscando y contar con una cantidad igual de líneas de fórmulas de búsqueda.   
Para evitar ese problema, la siguiente fórmula agrega un control:   
=SI($I$5=INDICE(Hoja2!$C$5:$G$20;COINCIDIR($I$5;Hoja2!$C$5:$C$20;0)+FILA()-FILA($I$5);1);INDICE(Hoja2!$C$5:$G$20;COINCIDIR($I$5;Hoja2!$C$5:$C$20;0)+FILA()-FILA($I$5);COLUMNA()-COLUMNA($I$5)+1);"")   
INDICE muestra un dato de una matriz (C5:G20 de la hoja2) de acuerdo a las coordenadas de fila y columna que se le indique como argumentos.   
Para obtener la coordenada Fila usamos la función COINCIDIR en un rango (C5:C20 de Hoja2) de la misma altura (misma cantidad y ubicación de las líneas) que la base de búsqueda. Al número de fila así obtenido le agregará tantas líneas como diferencia halla entre la fila donde está la fórmula y la fila donde está el dato a buscar. Así, si la fórmula estuviera en la misma fila que el dato, tal diferencia será cero y, por lo tanto, traerá el dato de la misma fila.   
Al copiar esta celda y pegarla en la fila inferior, la diferencia será 1, por lo tanto traerá el valor de la fila siguiente de la base original. Y así sucesivamente.   
De igual modo, para identificar de qué columna traer el dato, restará la columna actual menos la primera.   
De esta manera, la misma fórmula copiada al resto del rango donde debe traer los valores servirá al propósito sin tener que modificarla.   
Aquí vale aclarar que, si no quieres todas las columnas, puedes especificarle exactamente qué columna necesitas, reemplazando la expresión COLUMNA()-COLUMNA($I$5)+1) por el número de la columna a mostrar  
El control que le agregué, verifica que en cualquier fila que se esté buscando se trate de la misma persona que se buscó originalmente.   
Lo único que tendrás que tener en cuenta es tener una cantidad de lineas de esta fórmula de búsqueda igual al mayor número de registros que pueda tener una persona.   
Por ejemplo, supongamos que buscar los datos de los hijos de cada empleado. Si hubiera un fecundo con 10 hijos, tendrás que tener 10 filas con aquella fórmula que te sugerí. Para los que tengan menos de 10, las celdas excedentes no mostrarán nada. Pero si hubiera uno con once o doce, sólo mostrará hasta el décimo.   
Por último, si -eventualmente- necesitaras otro set de estas fórmulas para buscar otro código en otro rango de la hoja "Datos" necesitarás, primero quitarle la referencia absoluta a la celda donde está e primer código (I5).   
Nota que, apropósitamente, todas las referencias a la linea del código dicen $I$5 de forma tal que siempre lean esa celda a cada uno de sus efectos.   
Lo que suelo hacer, entonces, es seleccionar todo el rango de fórmulas (4 columnas por 8 filas) y hago "Edición" | "Reemplazar..." En la casilla Buscar: coloco $I$5 y en la de reemplazar con escribo I5. Finalmente presiono "Reemplazar todo"   
Si bien todas las fórmulas modificadas siguen "apuntando" a la celda I5, esta es ahora una referencia relativa a tal celda. Esto permite que al copiarlas a otro rango lean una posición distinta a I5.   
Por ejemplo, si tu otro código a buscar estuviera en la celda I14 y copias este set de fórmulas a partir de esa misma fila, todas leerán los datos de la celda I14.   
Lo dicho: No es una tarea simple, pero si sigues cuidadosamente las instrucciones, llegarás al objetivo.  
- Ingresa al sitio www.porloschicos.com y, si quieres,   
presiona el botón de donación (es gratis).   
Confío en que lo harás. <p>para comprender lo que hace <code>yield</code>, debe comprender qué <em>generadores</em> son. y antes de que lleguen los generadores <em>iterables</em>.</p><h2>iterables</h2><p>cuando creas una lista, puedes leer sus artículos uno por uno. la lectura de sus elementos uno por uno se llama iteración:</p><pre><code>&gt;&gt;&gt; mylist = [1, 2, 3] &gt;&gt;&gt; for i in mylist: ... print(i) 1 2 3 </code></pre><p><code>mylist</code> es un <em>iterable</em>. cuando usas una comprensión de lista, creas una lista, y por lo tanto una iterable:</p><pre><code>&gt;&gt;&gt; mylist = [x*x for x in range(3)] &gt;&gt;&gt; for i in mylist: ... print(i) 0 1 4 </code></pre><p>todo lo que puede usar &quot;<code>for... in...</code>&quot; en es iterable; <code>lists</code>, <code>strings</code>, archivos ...</p><p>estos iterables son útiles porque puede leerlos tanto como desee, pero almacena todos los valores en la memoria y esto no siempre es lo que desea cuando tiene muchos valores.</p><h2>generadores</h2><p>los generadores son iteradores, una clase de iterable<strong> solo puedes iterar una vez </strong>. los generadores no almacenan todos los valores en la memoria,<strong> generan los valores sobre la marcha </strong>:</p><pre><code>&gt;&gt;&gt; mygenerator = (x*x for x in range(3)) &gt;&gt;&gt; for i in mygenerator: ... print(i) 0 1 4 </code></pre><p>es igual, excepto que usaste <code>()</code> en lugar de <code>[]</code>. pero, usted<strong> no puede </strong>realizar <code>for i in mygenerator</code> por segunda vez, ya que los generadores solo se pueden usar una vez: calculan 0, luego se olvidan y calculan 1, y terminan de calcular 4, uno por uno.</p><h2>rendimiento</h2><p><code>yield</code> es una palabra clave que se usa como <code>return</code>, excepto que la función devolverá un generador.</p><pre><code>&gt;&gt;&gt; def creategenerator(): ... mylist = range(3) ... for i in mylist: ... yield i*i ... &gt;&gt;&gt; mygenerator = creategenerator() # create a generator &gt;&gt;&gt; print(mygenerator) # mygenerator is an object! &lt;generator object creategenerator at 0xb7555c34&gt; &gt;&gt;&gt; for i in mygenerator: ... print(i) 0 1 4 </code></pre><p>este es un ejemplo inútil, pero es útil cuando sabes que tu función devolverá un gran conjunto de valores que solo necesitarás leer una vez.</p><p>para dominar <code>yield</code>, debe comprender que<strong> cuando llama a la función, el código que ha escrito en el cuerpo de la función no se ejecuta. </strong>la función solo devuelve el objeto generador, esto es un poco complicado :-)</p><p>luego, su código continuará desde donde lo dejó cada vez que <code>for</code> usa el generador.</p><p>ahora la parte difícil:</p><p>la primera vez que <code>for</code> llama al objeto generador creado desde su función, ejecutará el código en su función desde el principio hasta que llegue a <code>yield</code>, luego devolverá el primer valor del bucle. luego, cada una de las otras llamadas ejecutará el bucle que ha escrito en la función una vez más y devolverá el siguiente valor, hasta que no haya ningún valor para devolver.</p><p>el generador se considera vacío una vez que se ejecuta la función, pero ya no golpea <code>yield</code>. puede ser porque el bucle llegó a su fin, o porque ya no satisfaces un <code>"if/else"</code>.</p><hr><h2>tu codigo explicado</h2><p>generador:</p><pre><code># here you create the method of the node object that will return the generator def \_get\_child\_candidates(self, distance, min\_dist, max\_dist): # here is the code that will be called each time you use the generator object: # if there is still a child of the node object on its left # and if distance is ok, return the next child if self.\_leftchild and distance - max\_dist &lt; self.\_median: yield self.\_leftchild # if there is still a child of the node object on its right # and if distance is ok, return the next child if self.\_rightchild and distance + max\_dist &gt;= self.\_median: yield self.\_rightchild # if the function arrives here, the generator will be considered empty # there is no more than two values: the left and the right children </code></pre><p>llamador:</p><pre><code># create an empty list and a list with the current object reference result, candidates = list(), [self] # loop on candidates (they contain only one element at the beginning) while candidates: # get the last candidate and remove it from the list node = candidates.pop() # get the distance between obj and the candidate distance = node.\_get\_dist(obj) # if distance is ok, then you can fill the result if distance &lt;= max\_dist and distance &gt;= min\_dist: result.extend(node.\_values) # add the children of the candidate in the candidates list # so the loop will keep running until it will have looked # at all the children of the children of the children, etc. of the candidate candidates.extend(node.\_get\_child\_candidates(distance, min\_dist, max\_dist)) return result </code></pre><p>este código contiene varias partes inteligentes:</p><ul> <li><p>el bucle se repite en una lista, pero la lista se expande mientras se repite el bucle :-) es una forma concisa de revisar todos estos datos anidados, aunque sea un poco peligroso, ya que puede terminar con un bucle infinito. en este caso, <code>candidates.extend(node.\_get\_child\_candidates(distance, min\_dist, max\_dist))</code> agota todos los valores del generador, pero <code>while</code> sigue creando nuevos objetos generadores que producirán valores diferentes de los anteriores ya que no se aplica en el mismo nodo.</p></li> <li><p>el método <code>extend()</code> es un método de objeto de lista que espera un iterable y agrega sus valores a la lista.</p></li> </ul><p>usualmente le pasamos una lista:</p><pre><code>&gt;&gt;&gt; a = [1, 2] &gt;&gt;&gt; b = [3, 4] &gt;&gt;&gt; a.extend(b) &gt;&gt;&gt; print(a) [1, 2, 3, 4] </code></pre><p>pero en tu código recibe un generador, lo cual es bueno porque:</p><ol> <li>no necesitas leer los valores dos veces.</li> <li>es posible que tenga muchos hijos y no quiera que todos se almacenen en la memoria.</li> </ol><p>y funciona porque a python no le importa si el argumento de un método es una lista o no. python espera iterables por lo que trabajará con cadenas, listas, tuplas y generadores. esto se llama tipificación de pato y es una de las razones por las que python es tan genial. pero esta es otra historia, para otra pregunta ...</p><p>puede detenerse aquí, o leer un poco para ver un uso avanzado de un generador:</p><h2>controlando el agotamiento de un generador.</h2><pre><code>&gt;&gt;&gt; class bank(): # let's create a bank, building atms ... crisis = false ... def create\_atm(self): ... while not self.crisis: ... yield "$100" &gt;&gt;&gt; hsbc = bank() # when everything's ok the atm gives you as much as you want &gt;&gt;&gt; corner\_street\_atm = hsbc.create\_atm() &gt;&gt;&gt; print(corner\_street\_atm.next()) $100 &gt;&gt;&gt; print(corner\_street\_atm.next()) $100 &gt;&gt;&gt; print([corner\_street\_atm.next() for cash in range(5)]) ['$100', '$100', '$100', '$100', '$100'] &gt;&gt;&gt; hsbc.crisis = true # crisis is coming, no more money! &gt;&gt;&gt; print(corner\_street\_atm.next()) &lt;type 'exceptions.stopiteration'&gt; &gt;&gt;&gt; wall\_street\_atm = hsbc.create\_atm() # it's even true for new atms &gt;&gt;&gt; print(wall\_street\_atm.next()) &lt;type 'exceptions.stopiteration'&gt; &gt;&gt;&gt; hsbc.crisis = false # the trouble is, even post-crisis the atm remains empty &gt;&gt;&gt; print(corner\_street\_atm.next()) &lt;type 'exceptions.stopiteration'&gt; &gt;&gt;&gt; brand\_new\_atm = hsbc.create\_atm() # build a new one to get back in business &gt;&gt;&gt; for cash in brand\_new\_atm: ... print cash $100 $100 $100 $100 $100 $100 $100 $100 $100 ... </code></pre><p><strong> nota: </strong>para python 3, use<code>print(corner\_street\_atm.\_\_next\_\_())</code> o <code>print(next(corner\_street\_atm))</code></p><p>puede ser útil para varias cosas como controlar el acceso a un recurso.</p><h2>itertools, tu mejor amigo.</h2><p>el módulo de itertools contiene funciones especiales para manipular iterables. ¿alguna vez desea duplicar un generador? ¿encadenar dos generadores? ¿agrupar valores en una lista anidada con una sola línea? <code>map / zip</code> sin crear otra lista?</p><p>entonces solo <code>import itertools</code>.</p><p>¿un ejemplo? veamos las posibles órdenes de llegada para una carrera de cuatro caballos:</p><pre><code>&gt;&gt;&gt; horses = [1, 2, 3, 4] &gt;&gt;&gt; races = itertools.permutations(horses) &gt;&gt;&gt; print(races) &lt;itertools.permutations object at 0xb754f1dc&gt; &gt;&gt;&gt; print(list(itertools.permutations(horses))) [(1, 2, 3, 4), (1, 2, 4, 3), (1, 3, 2, 4), (1, 3, 4, 2), (1, 4, 2, 3), (1, 4, 3, 2), (2, 1, 3, 4), (2, 1, 4, 3), (2, 3, 1, 4), (2, 3, 4, 1), (2, 4, 1, 3), (2, 4, 3, 1), (3, 1, 2, 4), (3, 1, 4, 2), (3, 2, 1, 4), (3, 2, 4, 1), (3, 4, 1, 2), (3, 4, 2, 1), (4, 1, 2, 3), (4, 1, 3, 2), (4, 2, 1, 3), (4, 2, 3, 1), (4, 3, 1, 2), (4, 3, 2, 1)] </code></pre><h2>entendiendo los mecanismos internos de iteración.</h2><p>la iteración es un proceso que implica iterables (implementación del método <code>\_\_iter\_\_()</code>) e iteradores (implementación del método <code>\_\_next\_\_()</code>). iterables son cualquier objeto de los que puede obtener un iterador. los iteradores son objetos que le permiten iterar en iterables.</p><p>hay más información al respecto en este artículo sobre <a href="http://effbot.org/zone/python-for-statement.htm">cómo funcionan <code>for</code> loops</a> .</p> si estas buscando un equipo muy portatil y de bajo peso principalmente para estudios, y no te interesa jugar online battlefield 3 ni nada de eso, pues una ultrabook es lo que buscas.

seria bueno saber con cuanto presupuesto cuentas, aunque por unos 900 dolares puedes conseguir alguna ideapad de lenovo de 14 pulgadas que tenga un i7, 8gb de memoria, disco duro hibrido con ssd, y un video dedicado nvidia de gama baja. por 700 puedes conseguir una que tenga un i5 y 6gb de memoria, que igualmente es un equipo de muy buenas prestaciones. en general lenovo tiene muy buenos equipos para trabajo a buenos precios.

la diferencia entre un i5 y un i7 de laptop (ojo que para pcs desktop es otro el cuento) es que los i7 tienen cuatro nucleos mientras que los i5 tienen dos, aunque en procesadores de bajo consumo para ultrabooks generalmente no es asi,. de todas maneras, los i7 de ultrabook siempre van a tener una frecuencia de nucleos mas elevada y el modo turbo del procesador es mas elevado ademas que tienen mas memoria cache, asi que tienen mejor rendimiento. el problema podría ser que usted está recibiendo demasiadas interrupciones en la tarjeta de red. si el ancho de banda no es el problema, la frecuencia es el problema: suba enviar/recibir búferes en la tarjeta de red ethtool-g eth0 se mostrará la configuración actual (256 o 512 entradas). usted puede probablemente plantear estas a 1024, 2048 o 3172. más probablemente no tenga sentido. esto es sólo un búfer de anillo que solo se llena si el servidor no es capaz de procesar los paquetes entrantes con la suficiente rapidez. si el buffer empieza a llenar, el control de flujo es un medio adicional para decirle al router o switch a disminuir: activar el control de flujo en/saliente en el servidor y el switch/router los puertos que se encuentra conectada. ethtool-eth0 se propably mostrar: pause parameters for eth0: autonegotiate: on rx: on tx: on compruebe el archivo /var/log/messages para la configuración actual de eth0. comprobar algo como: eth0: el enlace es de hasta 1000 mbps, full duplex, control de flujo de tx y rx si usted no ve tx y rx de sus administradores de red tienen que ajustar los valores en el switch/router. en cisco que es recibir/transmitir de control de flujo. cuidado: el cambio de estos valores va a traer a su enlace de abajo y arriba por un tiempo muy corto (menos de 1s). si todo esto no ayuda - usted también puede reducir la velocidad de la tarjeta de red de 100 mbit (hacer lo mismo en el switch/router-puertos) ethtool-s eth0 autoneg off && ethtool-s eth0 velocidad 100 pero en tu caso yo diría - elevar los búferes de recepción en la nic búfer de anillo.