¿Cómo se escala el costo de la memoria EVM?

¿Cómo se escala el costo de la memoria EVM?

Me pregunto cómo se escala el costo de la memoria EVM (no el almacenamiento).

De hecho, después de haber leído otros controles de calidad, el costo de la memoria no es una función lineal, pero ¿debería aumentar rápidamente a medida que se asigna más?

Habiendo escrito el algoritmo de Floyd-Warshall en Solidity, que asigna una matriz (nxn) de uint, veo que el consumo de gas aumenta extremadamente rápido. Este es el caso incluso para n = 24, donde el uso de gas es de aproximadamente 19085414.

pragma solidity ^0.4.11;

contract APSPBenchmark is usingOraclize {

    event OraclizeEvent0(string desc);        
    event OraclizeEvent1(string desc, int[] apsp);

    int constant INF = -1;

    function APSPBenchmark() public payable {}    

    /*
    * Local all-pairs shortest path
    */
    function localAPSP(int[] w) public {

        int[] memory apsp  = allPairsShortestPath(w);

        OraclizeEvent0("local");
        //OraclizeEvent1("local", apsp); // Infinity encoded as -1
    }

    /*
    * All-pairs shortest path
    */
    function allPairsShortestPath(int[] w) private constant returns(int[]) {
        uint i; uint j; uint k;
        uint n = babylonian(w.length);
        int[] memory d = new int[](n * n);

        for (i = 0; i < n; ++i) {
            for (j = 0; j < n; ++j) {
                d[i * n +j] = (w[i * n + j] >= 100 ? INF : w[i * n + j]);
            }   
            d[i * n + i] = 0;
        }

        for (k = 0; k < n; ++k) {
            for (i = 0; i < n; ++i) {
                for (j = 0; j < n; ++j) {
                    if (d[i * n + k] == INF || d[k * n + j] == INF) continue;
                    else if (d[i * n + j] == INF) d[i * n + j] = d[i * n + k] + d[k * n + j];
                    else d[i * n + j] = min(d[i * n +j], d[i * n + k] + d[k * n + j]);
                }
            }
        }

        return d;
    }

    /*
    * Minimum of two values
    */
    function min(int a, int b) private constant returns(int)  {
        return a < b ? a : b;
    }    

    /*
    * Babylonian sqrt 
    */
    function babylonian(uint n) private constant returns(uint) {
        uint x = n;
        uint y = 1;
        while (x > y) {
            x = (x + y) / 2;
            y = n / x;
        }
        return x;
    }
}
Intente definir INFcon constante: int constant INF = -1;un problema es que el código anterior se está INFalmacenando. Cada vez que lo usa en el cuerpo, leerlo desde el almacenamiento cuesta 200 de gas. Declararlo constantevita esto.
¿Eso soluciona el enorme consumo de memoria? No puedo intentarlo ahora.
Según mi respuesta, la memoria no es tu problema. Cambiar INFa constante le ahorrará hasta 600 de gas en su circuito interno (3 lecturas de INF). Su bucle interno es n^3, por lo que para n=24 esto le ahorra hasta 600 * 24^3 = 8,3 millones de gasolina. Eso debería ayudar.
¡Gracias! Aún así, restando 8 millones de gas de 19085414 (~ 19 millones) es ~ 11 millones de gas. ¿No es esto demasiado?
¿O son todas las operaciones aritméticas y las búsquedas de memoria las que dan como resultado un consumo de gas tan alto?
Le sugiero que pruebe lo anterior primero y luego eche un vistazo a lo que queda. Este no es todo su contrato, así que no sé qué más está pasando. Hay algunas optimizaciones manuales que puede hacer en el ciclo interno, y puede probar el optimizador Solidity (es bastante bueno ahora), pero una cosa a la vez. Si no progresa, haga una nueva pregunta del tipo "cómo optimizo este código", publicando el contrato completo. La pregunta tal como la hiciste está respondida, así que ya sabes qué hacer ;-)
Probado ahora. Con su optimización, el costo es 10795214 (~ 10 millones) según la hipótesis. De hecho, el código siempre se puede optimizar, pero no veo ninguna codificación "muy mala" aquí. He incluido todo el contrato.
Y con la optimización, el costo es 10173951 (~ 10 millones) - gracias, acepté su respuesta.

Respuestas (2)

El costo de gas de la expansión de la memoria se define en el Apéndice H del Libro Amarillo , de la siguiente manera,

Fórmula de costo de memoria

G_memory es 3 y aes el número de palabras de 256 bits intasignadas.

Para a=24^2 esto llega a 3 * 576 + 648 = 2,376. Entonces parece que el costo de la memoria no es su principal problema.

Lo anterior responde a tu pregunta. Sin embargo, también estoy intrigado en cuanto a dónde va el gas, así que investigaré un poco más y publicaré en los comentarios si tengo alguna idea.
No sale la imagen que pones @benjaminion
@Avatar: la imagen parece estar bien en general; puede ser un problema local. De todos modos, es solo la ecuación 222 del Libro Amarillo .
Tenga en cuenta que el costo de la memoria no es el costo de "expansión". Ver aquí ethereum.stackexchange.com/a/62732/44907 para la distinción

La fórmula en esta respuesta en realidad no es el costo de expansión , es el costo de la memoria.

El costo de expansión es el costo de memoria adicional para las palabras de memoria previamente intactas. Consulte los documentos de Solidity y la cita a continuación del Apéndice H en el documento amarillo .

Tenga en cuenta también que Cmem es la función de costo de memoria (siendo la función de expansión la diferencia entre el costo antes y después). Es un polinomio, con el coeficiente de orden superior dividido y piso, y por lo tanto lineal hasta 724B de memoria utilizada, después de lo cual cuesta sustancialmente más.

(No hay suficiente representante para comentar)