Manejo del entero con valores> 2 ^ 32 en Sparc 32 bits
assembly long-integer (2)
Mucho depende de qué versión de sparc y qué ABI estés usando. Si está utilizando sparc v8 o anterior, tiene modo de 32 bits con solo registros de 32 bits. En cuyo caso, cuando intenta cargar 5000000000 en un registro de 32 bits, falla y carga 5000000000 mod 2 32 en su lugar (que es 705032704). Esto es lo que parece estar sucediendo.
Si, por otro lado, tiene un procesador Sparc de 64 bits ejecutándose en modo de 32 bits (lo que generalmente se llama v8plus), entonces puede usar registros de 64 bits, así que esto funcionaría.
Codifiqué un pequeño programa que mide el tiempo invertido en un bucle (a través de un fragmento de código de ensamblaje Sparc en línea).
Todo está bien hasta que establezca el número de iteraciones por encima de aproximadamente 4.0 + 9 (por encima de 2 ^ 32).
Aquí está el fragmento de código:
#include <stdio.h>
#include <sys/time.h>
#include <unistd.h>
#include <math.h>
#include <stdint.h>
int main (int argc, char *argv[])
{
// For indices
int i;
// Set the number of executions
int nRunning = atoi(argv[1]);
// Set the sums
double avgSum = 0.0;
double stdSum = 0.0;
// Average of execution time
double averageRuntime = 0.0;
// Standard deviation of execution time
double deviationRuntime = 0.0;
// Init sum
unsigned long long int sum = 0;
// Number of iterations
unsigned long long int nLoop = 4000000000ULL;
//uint64_t nLoop = 4000000000;
// DEBUG
printf("sizeof(unsigned long long int) = %zu/n",sizeof(unsigned long long int));
printf("sizeof(unsigned long int) = %zu/n",sizeof(unsigned long int));
// Time intervals
struct timeval tv1, tv2;
double diff;
// Loop for multiple executions
for (i=0; i<nRunning; i++)
{
// Start time
gettimeofday (&tv1, NULL);
// Loop with Sparc assembly into C source
asm volatile ("clr %%g1/n/t"
"clr %%g2/n/t"
"mov %1, %%g1/n" // %1 = input parameter
"loop:/n/t"
"add %%g2, 1, %%g2/n/t"
"subcc %%g1, 1, %%g1/n/t"
"bne loop/n/t"
"nop/n/t"
"mov %%g2, %0/n" // %0 = output parameter
: "=r" (sum) // output
: "r" (nLoop) // input
: "g1", "g2"); // clobbers
// End time
gettimeofday (&tv2, NULL);
// Compute runtime for loop
diff = (tv2.tv_sec - tv1.tv_sec) * 1000000ULL + (tv2.tv_usec - tv1.tv_usec);
// Summing diff time
avgSum += diff;
stdSum += (diff*diff);
// DEBUG
printf("diff = %e/n", diff);
printf("avgSum = %e/n", avgSum);
}
// Compute final averageRuntime
averageRuntime = avgSum/nRunning;
// Compute standard deviation
deviationRuntime = sqrt(stdSum/nRunning-averageRuntime*averageRuntime);
// Print results
printf("(Average Elapsed time, Standard deviation) = %e usec %e usec/n", averageRuntime, deviationRuntime);
// Print sum from assembly loop
printf("Sum = %llu/n", sum);
Por ejemplo, con nLoop
<2 ^ 32, obtengo los valores correctos para diff
, avgSum
y stdSum
. De hecho, el printf
, con nLoop = 4.0e+9
, da:
sizeof(unsigned long long int) = 8
sizeof(unsigned long int) = 4
diff = 9.617167e+06
avgSum = 9.617167e+06
diff = 9.499878e+06
avgSum = 1.911704e+07
(Average Elapsed time, Standard deviation) = 9.558522e+06 usec 5.864450e+04 usec
Sum = 4000000000
El código se compila en Debian Sparc 32 bits Etch
con gcc 4.1.2
.
Desafortunadamente, si tomo por ejemplo nLoop = 5.0e+9
, obtengo valores pequeños e incorrectos para los tiempos medidos; aquí está la salida de printf en este caso:
sizeof(unsigned long long int) = 8
sizeof(unsigned long int) = 4
diff = 5.800000e+01
avgSum = 5.800000e+01
diff = 4.000000e+00
avgSum = 6.200000e+01
(Average Elapsed time, Standard deviation) = 3.100000e+01 usec 2.700000e+01 usec
Sum = 5000000000
No sé de dónde podría venir el problema. He realizado otras pruebas usando uint64_t
pero sin éxito.
Quizás el problema es que manejo large integers (> 2^32)
con 32 bits OS o puede ser el código ensamblado en línea que no admite 8 bytes enteros.
Si alguien pudiera darme algunas pistas para corregir este error,
Saludos
ACTUALIZACIÓN 1 :
Siguiendo el consejo de @Andrew Henle
, tomé el mismo código, pero en lugar del fragmento de ensamblaje Sparc en línea, acabo de poner un simple bucle.
Aquí está el programa con el bucle simple que tiene nLoop = 5.0e+9
(vea la línea " unsigned long long int nLoop = 5000000000ULL;
", entonces por encima del limit 2^32-1
:
#include <stdio.h>
#include <stdlib.h>
#include <sys/time.h>
#include <unistd.h>
#include <math.h>
#include <stdint.h>
int main (int argc, char *argv[])
{
// For indices of nRunning
int i;
// For indices of nRunning
unsigned long long int j;
// Set the number of executions
int nRunning = atoi(argv[1]);
// Set the sums
unsigned long long int avgSum = 0;
unsigned long long int stdSum = 0;
// Average of execution time
double averageRuntime = 0.0;
// Standard deviation of execution time
double deviationRuntime = 0.0;
// Init sum
unsigned long long int sum;
// Number of iterations
unsigned long long int nLoop = 5000000000ULL;
// DEBUG
printf("sizeof(unsigned long long int) = %zu/n",sizeof(unsigned long long int));
printf("sizeof(unsigned long int) = %zu/n",sizeof(unsigned long int));
// Time intervals
struct timeval tv1, tv2;
unsigned long long int diff;
// Loop for multiple executions
for (i=0; i<nRunning; i++)
{
// Reset sum
sum = 0;
// Start time
gettimeofday (&tv1, NULL);
// Loop with Sparc assembly into C source
/* asm volatile ("clr %%g1/n/t"
"clr %%g2/n/t"
"mov %1, %%g1/n" // %1 = input parameter
"loop:/n/t"
"add %%g2, 1, %%g2/n/t"
"subcc %%g1, 1, %%g1/n/t"
"bne loop/n/t"
"nop/n/t"
"mov %%g2, %0/n" // %0 = output parameter
: "=r" (sum) // output
: "r" (nLoop) // input
: "g1", "g2"); // clobbers
*/
// Classic loop
for (j=0; j<nLoop; j++)
sum ++;
// End time
gettimeofday (&tv2, NULL);
// Compute runtime for loop
diff = (unsigned long long int) ((tv2.tv_sec - tv1.tv_sec) * 1000000 + (tv2.tv_usec - tv1.tv_usec));
// Summing diff time
avgSum += diff;
stdSum += (diff*diff);
// DEBUG
printf("diff = %llu/n", diff);
printf("avgSum = %llu/n", avgSum);
printf("stdSum = %llu/n", stdSum);
// Print sum from assembly loop
printf("Sum = %llu/n", sum);
}
// Compute final averageRuntime
averageRuntime = avgSum/nRunning;
// Compute standard deviation
deviationRuntime = sqrt(stdSum/nRunning-averageRuntime*averageRuntime);
// Print results
printf("(Average Elapsed time, Standard deviation) = %e usec %e usec/n", averageRuntime, deviationRuntime);
return 0;
}
Este fragmento de código funciona correctamente, es decir, la sum
variable se imprime como (ver " printf("Sum = %llu/n", sum)
";):
Sum = 5000000000
Entonces, el problema proviene de la versión con el bloque de ensamblaje Sparc.
Sospecho, en este código de ensamblaje, la línea "mov %1, %%g1/n" // %1 = input parameter
para almacenar nLoop
en el %g1 register
(creo que %g1
es un registro de 32 bits, por lo que puede no almacena valores por encima de 2^32-1
).
Sin embargo, el parámetro de salida ( sum
variable) en la línea:
"mov %%g2, %0/n" // %0 = output parameter
está por encima del límite ya que es igual a 5000000000.
Adjunto el vimdiff entre la versión con el ciclo de ensamblaje y sin él:
A la izquierda, programa con ensamblaje, a la derecha, sin ensamblaje (solo un simple lazo en su lugar)
Le recuerdo que mi problema es que, para nLoop> 2 ^ 32-1 y con asamblea, obtengo un parámetro de sum
válido al final de la ejecución, pero no es válido (demasiado corto) el average
y standard deviation
tiempos de standard deviation
(gastados en el ciclo); aquí hay un ejemplo de salida con nLoop = 5000000000ULL
:
sizeof(unsigned long long int) = 8
sizeof(unsigned long int) = 4
diff = 17
avgSum = 17
stdSum = 289
Sum = 5000000000
diff = 4
avgSum = 21
stdSum = 305
Sum = 5000000000
(Average Elapsed time, Standard deviation) = 1.000000e+01 usec 7.211103e+00 usec
Con tomar nLoop = 4.0e+9
, es decir, nLoop = 4000000000ULL
, no hay problema, los valores de tiempo son válidos.
ACTUALIZACIÓN 2:
Estoy buscando más profundamente al generar código de ensamblaje. La versión con nLoop = 4000000000 (4.0e+9)
está debajo:
.file "loop-WITH-asm-inline-4-Billions.c"
.section ".rodata"
.align 8
.LLC1:
.asciz "sizeof(unsigned long long int) = %zu/n"
.align 8
.LLC2:
.asciz "sizeof(unsigned long int) = %zu/n"
.align 8
.LLC3:
.asciz "diff = %llu/n"
.align 8
.LLC4:
.asciz "avgSum = %llu/n"
.align 8
.LLC5:
.asciz "stdSum = %llu/n"
.align 8
.LLC6:
.asciz "Sum = %llu/n"
.global __udivdi3
.global __cmpdi2
.global __floatdidf
.align 8
.LLC7:
.asciz "(Average Elapsed time, Standard deviation) = %e usec %e usec/n"
.align 8
.LLC0:
.long 0
.long 0
.section ".text"
.align 4
.global main
.type main, #function
.proc 04
main:
save %sp, -248, %sp
st %i0, [%fp+68]
st %i1, [%fp+72]
ld [%fp+72], %g1
add %g1, 4, %g1
ld [%g1], %g1
mov %g1, %o0
call atoi, 0
nop
mov %o0, %g1
st %g1, [%fp-68]
st %g0, [%fp-64]
st %g0, [%fp-60]
st %g0, [%fp-56]
st %g0, [%fp-52]
sethi %hi(.LLC0), %g1
or %g1, %lo(.LLC0), %g1
ldd [%g1], %f8
std %f8, [%fp-48]
sethi %hi(.LLC0), %g1
or %g1, %lo(.LLC0), %g1
ldd [%g1], %f8
std %f8, [%fp-40]
mov 0, %g2
sethi %hi(4000000000), %g3
std %g2, [%fp-24]
sethi %hi(.LLC1), %g1
or %g1, %lo(.LLC1), %o0
mov 8, %o1
call printf, 0
nop
sethi %hi(.LLC2), %g1
or %g1, %lo(.LLC2), %o0
mov 4, %o1
call printf, 0
nop
st %g0, [%fp-84]
b .LL2
nop
.LL3:
st %g0, [%fp-32]
st %g0, [%fp-28]
add %fp, -92, %g1
mov %g1, %o0
mov 0, %o1
call gettimeofday, 0
nop
ldd [%fp-24], %o4
clr %g1
clr %g2
mov %o4, %g1
loop:
add %g2, 1, %g2
subcc %g1, 1, %g1
bne loop
nop
mov %g2, %o4
std %o4, [%fp-32]
add %fp, -100, %g1
mov %g1, %o0
mov 0, %o1
call gettimeofday, 0
nop
ld [%fp-100], %g2
ld [%fp-92], %g1
sub %g2, %g1, %g2
sethi %hi(999424), %g1
or %g1, 576, %g1
smul %g2, %g1, %g3
ld [%fp-96], %g2
ld [%fp-88], %g1
sub %g2, %g1, %g1
add %g3, %g1, %g1
st %g1, [%fp-12]
sra %g1, 31, %g1
st %g1, [%fp-16]
ldd [%fp-64], %o4
ldd [%fp-16], %g2
addcc %o5, %g3, %g3
addx %o4, %g2, %g2
std %g2, [%fp-64]
ld [%fp-16], %g2
ld [%fp-12], %g1
smul %g2, %g1, %g4
ld [%fp-16], %g2
ld [%fp-12], %g1
smul %g2, %g1, %g1
add %g4, %g1, %g4
ld [%fp-12], %g2
ld [%fp-12], %g1
umul %g2, %g1, %g3
rd %y, %g2
add %g4, %g2, %g4
mov %g4, %g2
ldd [%fp-56], %o4
addcc %o5, %g3, %g3
addx %o4, %g2, %g2
std %g2, [%fp-56]
sethi %hi(.LLC3), %g1
or %g1, %lo(.LLC3), %o0
ld [%fp-16], %o1
ld [%fp-12], %o2
call printf, 0
nop
sethi %hi(.LLC4), %g1
or %g1, %lo(.LLC4), %o0
ld [%fp-64], %o1
ld [%fp-60], %o2
call printf, 0
nop
sethi %hi(.LLC5), %g1
or %g1, %lo(.LLC5), %o0
ld [%fp-56], %o1
ld [%fp-52], %o2
call printf, 0
nop
sethi %hi(.LLC6), %g1
or %g1, %lo(.LLC6), %o0
ld [%fp-32], %o1
ld [%fp-28], %o2
call printf, 0
nop
ld [%fp-84], %g1
add %g1, 1, %g1
st %g1, [%fp-84]
.LL2:
ld [%fp-84], %g2
ld [%fp-68], %g1
cmp %g2, %g1
bl .LL3
nop
ld [%fp-68], %g1
sra %g1, 31, %g1
ld [%fp-68], %g3
mov %g1, %g2
ldd [%fp-64], %o0
mov %g2, %o2
mov %g3, %o3
call __udivdi3, 0
nop
mov %o0, %g2
mov %o1, %g3
std %g2, [%fp-136]
ldd [%fp-136], %o0
mov 0, %o2
mov 0, %o3
call __cmpdi2, 0
nop
mov %o0, %g1
cmp %g1, 1
bl .LL6
nop
ldd [%fp-136], %o0
call __floatdidf, 0
nop
std %f0, [%fp-144]
b .LL5
nop
.LL6:
ldd [%fp-136], %o4
and %o4, 0, %g2
and %o5, 1, %g3
ld [%fp-136], %o5
sll %o5, 31, %g1
ld [%fp-132], %g4
srl %g4, 1, %o5
or %o5, %g1, %o5
ld [%fp-136], %g1
srl %g1, 1, %o4
or %g2, %o4, %g2
or %g3, %o5, %g3
mov %g2, %o0
mov %g3, %o1
call __floatdidf, 0
nop
std %f0, [%fp-144]
ldd [%fp-144], %f8
ldd [%fp-144], %f10
faddd %f8, %f10, %f8
std %f8, [%fp-144]
.LL5:
ldd [%fp-144], %f8
std %f8, [%fp-48]
ld [%fp-68], %g1
sra %g1, 31, %g1
ld [%fp-68], %g3
mov %g1, %g2
ldd [%fp-56], %o0
mov %g2, %o2
mov %g3, %o3
call __udivdi3, 0
nop
mov %o0, %g2
mov %o1, %g3
std %g2, [%fp-128]
ldd [%fp-128], %o0
mov 0, %o2
mov 0, %o3
call __cmpdi2, 0
nop
mov %o0, %g1
cmp %g1, 1
bl .LL8
nop
ldd [%fp-128], %o0
call __floatdidf, 0
nop
std %f0, [%fp-120]
b .LL7
nop
.LL8:
ldd [%fp-128], %o4
and %o4, 0, %g2
and %o5, 1, %g3
ld [%fp-128], %o5
sll %o5, 31, %g1
ld [%fp-124], %g4
srl %g4, 1, %o5
or %o5, %g1, %o5
ld [%fp-128], %g1
srl %g1, 1, %o4
or %g2, %o4, %g2
or %g3, %o5, %g3
mov %g2, %o0
mov %g3, %o1
call __floatdidf, 0
nop
std %f0, [%fp-120]
ldd [%fp-120], %f8
ldd [%fp-120], %f10
faddd %f8, %f10, %f8
std %f8, [%fp-120]
.LL7:
ldd [%fp-48], %f8
ldd [%fp-48], %f10
fmuld %f8, %f10, %f8
ldd [%fp-120], %f10
fsubd %f10, %f8, %f8
std %f8, [%fp-112]
ldd [%fp-112], %f8
fsqrtd %f8, %f8
std %f8, [%fp-152]
ldd [%fp-152], %f10
ldd [%fp-152], %f8
fcmpd %f10, %f8
nop
fbe .LL9
nop
ldd [%fp-112], %o0
call sqrt, 0
nop
std %f0, [%fp-152]
.LL9:
ldd [%fp-152], %f8
std %f8, [%fp-40]
sethi %hi(.LLC7), %g1
or %g1, %lo(.LLC7), %o0
ld [%fp-48], %o1
ld [%fp-44], %o2
ld [%fp-40], %o3
ld [%fp-36], %o4
call printf, 0
nop
mov 0, %g1
mov %g1, %i0
restore
jmp %o7+8
nop
.size main, .-main
.ident "GCC: (GNU) 4.1.2 20061115 (prerelease) (Debian 4.1.1-21)"
.section ".note.GNU-stack"
Cuando genero la versión de código de ensamblado con nLoop = 5000000000 (5.0e+9)
, las diferencias se ilustran en la siguiente figura (con vimdiff
):
el bloque de la versión "4 Billions":
mov 0, %g2
sethi %hi(4000000000), %g3
es reemplazado en la versión "5 Billions" por:
mov 1, %g2
sethi %hi(705032192), %g3
or %g3, 512, %g3
Puedo ver que 5.0+e9
no se puede codificar en 32 bits, ya que la instrucción
sethi %hi(705032192), %g3
Paradójicamente, cuando compilo el código de ensamblado de la versión "5 Billions", la sum
parámetro de salida se calcula bien, es decir, es igual a 5 Billions
, y no puedo explicarlo.
Cualquier ayuda o comentario es bienvenido, gracias.
Parece que está haciendo operaciones de 32 bits en la mitad de un valor de 64 bits
A partir del código generado, aquí es donde nLoop
es una carga doble en %o4
y %o5
(ya que es un valor long long
64 bits):
ldd [%fp-24], %o4
clr %g1
clr %g2
Y luego solo trabajas con %o4
:
mov %o4, %g1 ; <---- what about %o5????
loop:
add %g2, 1, %g2
subcc %g1, 1, %g1
bne loop
nop
mov %g2, %o4
Para que esto funcione, vuelva a escribir su código de ensamblado para tratar %o4
+ %o5
juntos como un valor de 64 bits.