{"id":1663,"date":"2024-02-10T13:34:20","date_gmt":"2024-02-10T12:34:20","guid":{"rendered":"https:\/\/intsight.com\/?p=1663"},"modified":"2024-02-10T13:34:20","modified_gmt":"2024-02-10T12:34:20","slug":"xoshiro256","status":"publish","type":"post","link":"https:\/\/intsight.com\/index.php\/2024\/02\/10\/xoshiro256\/","title":{"rendered":"Xoshiro256**"},"content":{"rendered":"<p><span style=\"font-variant:small-caps;font-size:107%\">Aunque parezca que<\/span> el t\u00edtulo de la entrada lo tecle\u00f3 mi gato, es en realidad el nombre de un algoritmo de generaci\u00f3n de n\u00fameros aleatorio, inventado por Sebastiano Vigna y David Blackman (enlaces al final).<\/p>\n<p>El nombre es una combinaci\u00f3n de las operaciones principales del algoritmo: <strong>xor<\/strong>, <strong>shift<\/strong>, <strong>rotate<\/strong>. En realidad, hay toda una familia de algoritmos similares, con nombres que se parecen mucho. El xoshiro256** es, simplemente, el que ha adoptado .NET Core desde la versi\u00f3n 6 (<a href=\"https:\/\/source.dot.net\/#System.Private.CoreLib\/src\/libraries\/System.Private.CoreLib\/src\/System\/Random.Xoshiro256StarStarImpl.cs\" rel=\"noopener\" target=\"_blank\">implementaci\u00f3n aqu\u00ed<\/a>).<\/p>\n<p>Xoshiro256** es muy r\u00e1pido, es robusto (aunque no te recomiendan que lo uses en aplicaciones de criptograf\u00eda), y utiliza muy poca memoria. Como se puede ver en la implementaci\u00f3n de .NET 8, s\u00f3lo necesita cuatro variables de tipo <code>ulong<\/code>. Es decir, 32 bytes por cada instancia del generador.<\/p>\n<h4>Xoshiro vectorial<\/h4>\n<p>Austra utiliza generadores de n\u00fameros aleatorios a diestra y siniestra. \u00bfEs f\u00e1cil escribir una versi\u00f3n de este algoritmo usando instrucciones AVX? La respuesta es: no, a no ser que uses directamente AVX512F. El problema es que este algoritmo realiza una rotaci\u00f3n. Es curioso que los lenguajes modernos de programaci\u00f3n no te den directamente esta operaci\u00f3n como parte de los operadores de bit. C# tiene un <code>&gt;&gt;<\/code> y un <code>&lt;&lt;<\/code>, por ejemplo, pero las rotaciones tienes que buscarlas en la clase <code>BitOperations<\/code>. De todos modos, el problema es que AVX\/AVX2 no tiene una operaci\u00f3n SIMD de rotaci\u00f3n. Podr\u00edamos simularla con <em>shifts<\/em> y m\u00e1scaras, pero perder\u00edamos parte de las ventajas de la vectorizaci\u00f3n. Hay implementaciones en GitHub de generadores aleatorios con AVX2, pero no dan una ganancia de velocidad destacable.<\/p>\n<p>Hay otro problema con el que hay que tener sumo cuidado. Pongamos como ejemplo el constructor de la clase <code>DVector<\/code> que genera un vector con valores aleatorios:<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-linenumbers=\"false\">\n\/\/\/ <summary>\n\/\/\/ Creates a vector filled with a uniform distribution generator.\n\/\/\/ <\/summary>\n\/\/\/ <param name=\"size\">Size of the vector.<\/param>\n\/\/\/ <param name=\"rnd\">A random number generator.<\/param>\n[MethodImpl(MethodImplOptions.AggressiveInlining)]\npublic DVector(int size, Random rnd);\n<\/pre>\n<p>Omito la implementaci\u00f3n para simplificar. Lo que quiero subrayar es que este constructor recibe una instancia expl\u00edcita de la clase <code>Random<\/code>. \u00bfPor qu\u00e9? Pues porque al cliente de la librer\u00eda puede necesitar resultados <em>repetibles<\/em>. Es decir, puede que para que un vector \u00abaleatorio\u00bb tenga siempre los mismos resultados, podr\u00eda ser que la instancia de <code>Random<\/code> se inicializase siempre con la misma semilla. Si no nos interesa usar una semilla, casi siempre utilizaremos <code>Random.Shared<\/code>, que es una propiedad est\u00e1tica de <code>Random<\/code>, que es \u00fanica para cada hilo y que se crea por demanda.<\/p>\n<p>Podr\u00edamos complicarnos la vida al escribir la versi\u00f3n vectorizada de este constructor, y averiguar c\u00f3mo recuperar la semilla del generador que nos pasa el cliente para crear un generador vectorial con esa misma semilla. Pero:<\/p>\n<ol>\n<li>No tengo claro que recuperar esa semilla sea tarea f\u00e1cil<\/li>\n<li>El generador aleatorio vectorial que he programado no tiene, de momento, la posibilidad de inicializarse con una semilla (y no me parece tampoco sencillo).<\/li>\n<\/ol>\n<p>Por lo tanto, las rutinas que se han acelerado con el generador vectorial comprueban si el generador que han recibido del cliente es <code>Random.Shared<\/code>. Eso lo interpretamos como que al cliente no le interesa la repetibilidad de los resultados. Naturalmente, hay que verificar antes si AVX512F est\u00e1 disponible en el ordenador.<\/p>\n<p>Cuando usamos el generador vectorial, los tiempos de ejecuci\u00f3n se reducen a una cuarta o quinta parte. No se reducen a la octava parte mec\u00e1nicamente porque en la mayor\u00eda de estas rutinas pesa m\u00e1s la asignaci\u00f3n de memoria y el posterior llenado de la misma. Pero bajar el tiempo de ejecuci\u00f3n a la cuarta parte me parece meritorio.<\/p>\n<h4>Enlaces<\/h4>\n<p>Estos son los enlaces a la clase de Austra.Library que genera ocho n\u00fameros de golpe por llamada, y a la p\u00e1gina de Sebastiano Vigna, el autor del algoritmo que hemos vectorizado. Mi implementaci\u00f3n, con toda seguridad, est\u00e1 abierta a mejoras. Por ejemplo, no me convence la forma en que tengo que convertir un valor <code>ulong<\/code> en otro de tipo <code>double<\/code>, porque no es vectorial. Debe existir algo m\u00e1s eficiente, pero de momento no lo he encontrado. De todas maneras, ah\u00ed tiene mi implementaci\u00f3n, por si le es \u00fatil en alguno de sus proyectos.<\/p>\n<ul style=\"list-style-type:square\">\n<li><a href=\"https:\/\/github.com\/IanMarteens\/Austra\/blob\/master\/Austra.Library\/Helpers\/Random512.cs\" rel=\"noopener\" target=\"_blank\">La clase Random512, en Austra<\/a><\/li>\n<li><a href=\"https:\/\/prng.di.unimi.it\/\" rel=\"noopener\" target=\"_blank\">La p\u00e1gina de Sebastiano Vigna<\/a><\/li>\n<\/ul>\n<p>Queda tambi\u00e9n pendiente la vectorizaci\u00f3n de n\u00fameros aleatorios provenientes de una distribuci\u00f3n normal. Austra utiliza el m\u00e9todo de Box-Muller, pero este m\u00e9todo exige el c\u00e1lculo de un logaritmo y de senos y cosenos. No es tarea imposible, pero tengo primero que proporcionar una rutina que genere vectorialmente senos y cosenos y, quiz\u00e1s antes, decidir si me interesa realmente el m\u00e9todo de Box-Muller, o si merece la pena ir directamente a una implementaci\u00f3n vectorial del <a href=\"https:\/\/en.wikipedia.org\/wiki\/Ziggurat_algorithm\" rel=\"noopener\" target=\"_blank\">algoritmo del zigurat<\/a>. Todo, en su debido momento.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Aunque parezca que el t\u00edtulo de la entrada lo tecle\u00f3 mi gato, es en realidad el nombre de un algoritmo de generaci\u00f3n de n\u00fameros aleatorio, inventado por Sebastiano Vigna y David Blackman (enlaces al final). El nombre es una combinaci\u00f3n de las operaciones principales del algoritmo: xor, shift, rotate. En realidad, hay toda una familia [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":1664,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"jetpack_post_was_ever_published":false,"_jetpack_newsletter_access":"","_jetpack_dont_email_post_to_subs":false,"_jetpack_newsletter_tier_id":0,"_jetpack_memberships_contains_paywalled_content":false,"_jetpack_memberships_contains_paid_content":false,"footnotes":""},"categories":[4],"tags":[65,15,73,78,30],"class_list":["post-1663","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-c","tag-net","tag-algorithms","tag-austra","tag-avx","tag-statistics"],"jetpack_featured_media_url":"https:\/\/i0.wp.com\/intsight.com\/wp-content\/uploads\/2024\/02\/godAndEinstein.png?fit=500%2C500&ssl=1","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/intsight.com\/index.php\/wp-json\/wp\/v2\/posts\/1663","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/intsight.com\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/intsight.com\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/intsight.com\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/intsight.com\/index.php\/wp-json\/wp\/v2\/comments?post=1663"}],"version-history":[{"count":15,"href":"https:\/\/intsight.com\/index.php\/wp-json\/wp\/v2\/posts\/1663\/revisions"}],"predecessor-version":[{"id":1679,"href":"https:\/\/intsight.com\/index.php\/wp-json\/wp\/v2\/posts\/1663\/revisions\/1679"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/intsight.com\/index.php\/wp-json\/wp\/v2\/media\/1664"}],"wp:attachment":[{"href":"https:\/\/intsight.com\/index.php\/wp-json\/wp\/v2\/media?parent=1663"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/intsight.com\/index.php\/wp-json\/wp\/v2\/categories?post=1663"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/intsight.com\/index.php\/wp-json\/wp\/v2\/tags?post=1663"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}