• No se han encontrado resultados

Otras operaciones

In document Biblioteca de números grandes en C++ (página 31-39)

Otras operaciones desarrolladas son:

Desplazamiento aritm´etico. Varios algoritmos de los presentados hacen uso del desplazamiento aritm´etico y es tambi´en una operaci´on muy utilizada por mejorar la velocidad de ejecuci´on de multiplicaciones y divisiones cuando se hacen con potencias de 2. Las rotaciones no est´an implementadas.

Operadores de bits y m´ascaras. En una implementaci´on completa de los operadores de enteros de C++ no pueden faltar las operaciones l´ogicas “o”, “y”, “o exclusivo” y “negaci´on” aplicadas sobre bits. Para facilitar la creaci´on de m´ascaras y, sobre todo, su uso eficiente se han a˜nadido tres operaciones para consultar/establecer el contenido de un bit indicando su posici´on y para establecer el contenido de un intervalo de bits (get bit,set bityset range).

Exponenciaci´on. El algoritmo r´apido de conversi´on a decimal requiere el c´alculo de potencias de 10. Como no pod´ıa ser de otra manera, he usado la exponenciaci´on binaria[23]. Tambi´en existe un m´etodo para exponenciaci´on en m´odulo.

Conversi´on de tipos. Es de tres tipos: de entero b´asico a n´umero grande y viceversa, y de n´umero grande de un tama˜no a n´umero grande de otro. Cuando se asigna un n´umero negativo a un n´umero grande se hace extensi´on de signo para facilitar una futura implementaci´on de n´umeros grandes con signo. Adem´as es posible designar el valor de un n´umero grande mediante una cadena de texto en las bases 2, 8, 10 y 16.

4.

Pruebas y resultados

He hecho pruebas para verificar la correcci´on de los resultados comparando con la biblioteca GMP. Aparte de eso he hecho comparaciones de velocidad de ejecuci´on con las bibliotecas GMP y TTMath.

La ejecuci´on se ha hecho en un procesador de la arquitecturax86-64, modelo AMD64 Phenom II X6 1050T de 6 n´ucleos, aunque todas las pruebas funcionaron en un solo hilo. Cada n´ucleo tiene 64 KB de cach´e de instrucciones y 64 KB de datos en el nivel 1 y 512 KB en el nivel 2. Hay una cach´e de 6 MB compartida por todos los n´ucleos. La velocidad de reloj era de 2200 MHz.

En las siguientes tablas se muestran los resultados. En la columna izquierda est´an los tama˜nos de los operandos usados; en las restantes aparece el n´umero de operaciones por segundo alcanzadas.

Sumas por segundo

Tama˜no gueb::big uint GMP TTMath 100 225432958 53330023 214277952 150 199155263 54792458 198740652 200 156554969 55824660 168572022 500 84318126 45609549 68353433 1000 33714132 34773819 29222259 2000 19397550 25188555 16791500 5000 8673320 12136349 7963292 10000 4506755 7750147 4392646 20000 2281162 3388096 2229634 50000 929014 1607045 916195 100000 466228 772899 362699 200000 229000 172851 171036 500000 85847 61485 57555 1000000 24858 9369 20409 2000000 10398 3833 8392

Desplazamientos aritm´eticos por segundo (31 bits a la izquierda)

Tama˜no gueb::big uint GMP TTMath 100 387631019 54596580 102139294 150 166483149 57057018 81172871 200 142551414 46757756 64764644 500 72155200 41345836 32232417 1000 33634088 30180593 5751876 2000 14245156 20887754 3270858 5000 6438706 9398081 1193211 10000 3360638 5183557 603582 20000 1721137 2771050 303377 50000 695328 1100436 137516 100000 325404 552342 71014 200000 167993 161930 35701 500000 66639 64067 13735 1000000 25171 8572 6537 2000000 13658 4211 2958

Multiplicaciones por segundo

Tama˜no gueb::big uint GMP TTMath 100 149375869 42877781 55063486 150 72441238 42933961 42855983 200 33191837 20742200 24826570 500 9660468 8871847 2192890 1000 2292563 2931087 638847 2000 524808 936794 184753 5000 93875 201664 27112 10000 30939 65988 8894 20000 10043 23241 2956 50000 2040 6425 745 100000 675 2409 246 200000 219 905 81 500000 51 271 24 1000000 17 111 8 2000000 5 53 2

Divisiones por segundo (el divisor tiene la mitad de bits)

Tama˜no gueb::big uint GMP TTMath 100: 2373963 12651306 223478978 150: 2148652 12641366 22649528 200: 1903429 9721190 2787253 500: 507155 4699269 1112222 1000: 187525 2624545 349989 2000: 63687 1362301 142015 5000: 13646 370517 34393 10000: 4015 115700 8098 20000: 1120 36117 2673 50000: 187 7941 406 100000: 48 2646 101 200000: 10 932 22 500000: 1 269 2 1000000: <1 108 <1 2000000: <1 46 <1

Se puede ver como en las operaciones sencillas como la suma y el desplazamiento aritm´etico es factible alcanzar la rapidez de una biblioteca de alto nivel como GMP. En tama˜nos peque˜nos el uso de tama˜no fijo permite al compilador convertir datos variables en constantes y, con ello, desenrollar bucles, intercalar el c´odigo de los procedimientos en vez de llamarlos y mantener los datos de trabajo en registros sin necesidad de acceder a la memoria. Todo esto repercute en un mayor rendimiento. Tambi´en se ve como a partir del tama˜no1000 esta ventaja desaparece, yGMP es superior. Probablemente se deba a que la biblioteca GMP dispone de c´odigo con bucles desenrollados mientras que el compilador no realiza esta tarea autom´aticamente con tama˜nos grandes. Finalmente, cuando el tama˜no pasa a ser mayor de 100000 bits el rendimiento de GMP vuelve a decaer, lo que puede ser achacable al desbordamiento de la memoria cach´e de nivel 1. El c´odigo de prueba que usan TTMath ygueb::big uint es tal que as´ı:

for ( u i n t 6 4 _ t i = 0; i < i t e r a c i o n e s ; i ++) c = a + b ;

Mientras que paraGMP es as´ı:

for ( u i n t 6 4 _ t i = 0; i < i t e r a c i o n e s ; i ++) m p z _ a d d ( c , a , b );

La variable de destino en el primer caso es siempre la misma, y su memoria asignada, por tanto, tambi´en. ParaGMP la variable de destino se crea en memoria din´amica y la memoria de esta variable podr´ıa ser distinta en dos llamadas consecutivas, sobre todo si se libera la memoria del contenido anterior antes de asignar el nuevo. Esto provocar´ıa que GMP estuviera usando la memoria correspondiente a 4 ´o m´as variables, mientras que las versiones de memoria est´atica usan 3. El l´ımite para contener 3 variables en 64 KB de cach´e ser´ıan 65536/38174762 bits, pero para 4 ser´ıan 65536/48 = 131072. De esta forma GMP comenzar´ıa a tener fallos de cach´e al llegar a ese l´ımite, y con variables de 200000 bits tendr´ıa muchos mas fallos que las otras dos bibliotecas.

En el caso de las multiplicaciones sucede lo mismo para tama˜nos peque˜nos, sin em- bargo al aumentar la disparidad es bastante grande. La biblioteca GMP usa m´etodos distintos para multiplicar seg´un las dimensiones de los operandos, como los algoritmos de Karatsuba[20], Toom-Cook[24] o Sch¨onhage-Strassen [8], mientras que TTMath y gueb::big uint solo usan Karatsuba.

Por ´ultimo, la divisi´on de gueb::big uint est´a a mucha distancia de tener un rendi- miento aceptable.

5.

Conclusiones y l´ıneas futuras

¿En qu´e medida se han logrado los objetivos?

- Facilidad de uso. Gracias a la conversi´on autom´atica desde y hacia tipos b´asicos y la sobrecarga de operadores la interfaz es casi id´entica a la de los tipos b´asicos. Al estar compuesta la biblioteca ´unicamente de ficheros de cabeceras se puede integrar en otros proyectos con sencillez, tan solo copiando los ficheros.

- Rapidez. Conocer de antemano el tama˜no de los n´umeros permite al compilador lograr mayor velocidad con n´umeros de hasta algo menos de 1000 bits, a partir de lo cual el tama˜no din´amico comienza a ganar ventaja.

En general, las operaciones son considerablemente r´apidas, pero la implementaci´on de la divisi´on es varios ´ordenes de magnitud m´as lenta que la implementaci´on que uso de referencia (la biblioteca GMP), y requiere m´as trabajo para mejorar. - Se puede implementar un tipo de datos de n´umeros en coma fija o coma flotante

con bastante facilidad bas´andose en esta biblioteca. La estructura compacta en memoria posibilita, por ejemplo, implementar n´umeros en coma flotante que sigan el est´andar IEEE-754.

La biblioteca se puede mejorar o ampliar por las siguientes v´ıas:

Divisi´on por “divide y vencer´as”. La divisi´on larga en bloques se basa en el uso de una instrucci´on de divisi´on entera mediante hardware, pues es de suponer que ´esta sea bastante r´apida. ¿Qu´e ocurre si no hay tal instrucci´on? ¿Es m´as r´apido el m´etodo cl´asico o una emulaci´on de la divisi´on entera usando registros como ope- randos puede ser de ayuda en la divisi´on larga en bloques? Llevando este concepto al extremo, ¿podemos basar la divisi´on de n´umeros deN bits en la suposici´on de que ya disponemos de una divisi´on de n´umeros de N2 bits y usarla con la divisi´on larga mejorada? ¿Podemos implementar la divisi´on de n´umeros de N2 bits median- te una divisi´on de n´umeros de N4 bits? De aqu´ı surge otro m´etodo de divisi´on de n´umeros grandes, aplicando el famoso paradigma de dise˜no “divide y vencer´as”. Cuadrado m´as eficiente. Si represent´asemos los productos parciales de las cifras de

una multiplicaci´on en una tabla comprobar´ıamos que cuando los dos factores de la multiplicaci´on son iguales la tabla tiene el aspecto de una matriz sim´etrica. Como casi la mitad de estos c´alculos est´an repetidos pueden ahorrarse y aumentar la velocidad del c´alculo del cuadrado de un n´umero, lo que beneficiar´a a la operaci´on de exponenciaci´on que se basa en el mismo.

Multiplicaciones de Toom-Cook y Sch¨onhage-Strassen. La multiplicaci´on de Toom- Cook[24] es una generalizaci´on del algoritmo de Karatsuba que funciona partiendo los factores en fragmentos de igual tama˜no, tipicamente 3 ´o m´as, que son opera- dos recursivamente de forma que se evitan varias multiplicaciones originalmente necesarias. Debido a su sobrecoste en otras operaciones, no resulta pr´actico con

n´umeros relativamente peque˜nos, para los que la multiplicaci´on cl´asica o la de Ka- ratsuba son m´as r´apidas. Cuando se llega a un cierto l´ımite, aumentar el n´umero de fragmentos resulta contraproducente y comienza a ser viable otro algoritmo: Sch¨onhage-Strassen[8].

El algoritmo de Sch¨onhage-Strassen concibe la multiplicaci´on cl´asica de enteros como un producto de convoluci´on[25] y, como tal, se puede calcular transformando el dominio del problema mediante una transformada de Fourier[26], que posibilita calcular la convoluci´on con menos operaciones. Posteriormente hay que hacer la transformaci´on inversa.

Enteros con signo. Las operaciones de resta y opuesto de un n´umero operan en complemento a 2, lo que facilita el paso a manejar enteros con signo. Las operacio- nes de multiplicaci´on y divisi´on requerir´ıan una implementaci´on distinta, aunque si tenemos en cuenta su, comparativamente, lenta velocidad de ejecuci´on, un par de cambios de signo antes de operar no tendr´ıa un impacto demasiado apreciable en el rendimiento total, haciendo evitable reimplementar estas operaciones. Tama˜no variable. Para tama˜nos relativamente peque˜nos (128-1024 bits...) el cono-

cimientoa priori del tama˜no del n´umero resulta muy ventajoso para el compilador, pues puede propagar los valores conocidos de estos datos y preparar versiones m´as r´apidas del c´odigo para esos tama˜nos, al ahorrar c´alculos durante la ejecuci´on. Cuando el tama˜no crece m´as se hacen patentes otros problemas como el desper- dicio de memoria y de tiempo de procesador en n´umeros de mucho tama˜no que pueden contener temporalmente valores peque˜nos. La parte alta de estos n´umeros contiene ceros que hay que almacenar y actualizar en cada operaci´on aritm´etica. Aparte de las ventajas y desventajas explicadas antes, los procesadores operan con palabras de tama˜no fijo por una raz´on: simplicidad. Pero una vez que necesitamos superar esos tama˜nos, y no teniendo instrucciones nativas para operar con tama˜nos mayores y fijos, ¿seguimos necesitando que sean fijos?

Referencias

[1] Arbitrary-precision arithmetic: Applications. url: https://en.wikipedia.org/

wiki/Arbitrary-precision_arithmetic#Applications(visitado 27-08-2017).

[2] Jonathan M. Borwein David H. Bailey.High-Precision Arithmetic in Mathematical Physics. url: http : / / www . mdpi . com / 2227 - 7390 / 3 / 2 / 337 / pdf (visitado 27-08-2017).

[3] Not invented here. url:https://en.wikipedia.org/wiki/Not_invented_here (visitado 12-06-2018).

[4] List of arbitrary-precision arithmetic software. url: https : / / en . wikipedia .

org / wiki / List _ of _ arbitrary - precision _ arithmetic _ software (visitado

20-08-2017).

[5] Boost (C++ libraries). url: https://en.wikipedia.org/wiki/Boost_(C%2B%

2B_libraries)(visitado 20-08-2017).

[6] Christian Kaiser Tomasz Sowa. Frequency asked questions about TTMath. url:

http://www.ttmath.org/faq(visitado 20-08-2017).

[7] GNU Multiple Precision Arithmetic Library. url: https://en.wikipedia.org/

wiki/GNU_Multiple_Precision_Arithmetic_Library(visitado 20-08-2017).

[8] Sch¨onhage–Strassen algorithm.url:https://en.wikipedia.org/wiki/Sch%C3%

B6nhage%E2%80%93Strassen_algorithm(visitado 20-08-2017).

[9] Divide and Conquer Division. url:https://gmplib.org/manual/Divide-and-

Conquer-Division.html(visitado 20-08-2017).

[10] Class Library for Numbers. url: https : / / en . wikipedia . org / wiki / Class _

Library_for_Numbers(visitado 20-08-2017).

[11] David H. Bailey y col.High-Precision Software Directory.url:http://crd.lbl.

gov/~dhbailey/mpdist/arprec-2.2.19.tar.gz(visitado 13-08-2017).

[12] Michael C. Ring.url:https://github.com/LuaDist/mapm/raw/master/m_apm. h(visitado 20-08-2017).

[13] MPIR (mathematics software). url: https://en.wikipedia.org/wiki/MPIR_

(mathematics_software)(visitado 20-08-2017).

[14] Libgcrypt.url:https://en.wikipedia.org/wiki/Libgcrypt(visitado 12-05-2018). [15] Status of Supported Architectures from Maintainers’ Point of View.url:https:

//gcc.gnu.org/backends.html(visitado 24-08-2017).

[16] Middle-endian. url: https://en.wikipedia.org/wiki/Endianness#Middle-

endian(visitado 25-08-2017).

[17] Stream buffers. url: https://en.wikipedia.org/wiki/Cache_prefetching#

Stream_buffers(visitado 26-08-2017).

[19] c++11 - Is signed integer overflow still undefined behaviour in C++? url:https: //stackoverflow.com/questions/16188263/is- signed- integer- overflow-

still-undefined-behavior-in-c(visitado 04-07-2018).

[20] Karatsuba algorithm. url: https : / / en . wikipedia . org / wiki / Karatsuba _

algorithm(visitado 29-05-2018).

[21] Divisi´on larga. url: https://es.wikipedia.org/wiki/Divisi%C3%B3n_larga (visitado 29-05-2018).

[22] Integer division with remainder. url: https : / / en . wikipedia . org / wiki /

Division _ algorithm # Integer _ division _ (unsigned ) _with _ remainder (vi-

sitado 29-05-2018).

[23] Exponenciaci´on binaria.url:https://es.wikipedia.org/wiki/Exponenciaci%

C3%B3n_binaria(visitado 03-07-2018).

[24] Toom-Cook multiplication.url:https://en.wikipedia.org/wiki/Toom%E2%80%

93Cook_multiplication (visitado 06-07-2018).

[25] Convoluci´on.url:https://es.wikipedia.org/wiki/Convoluci%C3%B3n (visi- tado 06-07-2018).

[26] Transformada r´apida de Fourier. url: https : / / es . wikipedia . org / wiki /

In document Biblioteca de números grandes en C++ (página 31-39)

Documento similar