art with code
void
mmul(const float *a, const float *b, float *r)
{
for (int i=0; i<16; i+=4)
for (int j=0; j<4; j++)
r[i+j] = b[i]*a[j] + b[i+1]*a[j+4] + b[i+2]*a[j+8] + b[i+3]*a[j+12];
}
memalign(16, 16*sizeof(float)) or posix_memalign or compiler pragmas to do that. If the matrices are not aligned to 16-byte boundaries, the SSE code will segfault. I'm using the SSE intrinsics, so you won't see any cool inline assembler tricks here.
void
mmul_sse_dot(const float * a, const float * b, float * r)
{
__m128 a_line, b_line, r_line;
float mc[16] __attribute__((aligned(16))); // 16-byte aligned temp array
for (int i=0; i<16; i+=4) {
b_line = _mm_load_ps(&b[i]); // b_line = vec4(column(b, i))
// remember that we are column-major
for (int j=0; j<4; j++) {
a_line = _mm_set_ps(a[j+12], a[j+8], a[j+4], a[j]);
// a_line = vec4(row(a, j))
// note that SSE is little-endian
r_line = _mm_mul_ps(a_line, b_line); // r_line = a_line * b_line
_mm_store_ps(mc, r_line); // copy r_line to memory
r[i+j] = mc[0]+mc[1]+mc[2]+mc[3]; // r[i][j] = sum(r_line)
// = dot(a_line, b_line)
}
}
}
R[I] = A[I][0] * B[0] +
A[I][1] * B[1] +
A[I][2] * B[2] +
A[I][3] * B[3]
void
mmul_sse(const float * a, const float * b, float * r)
{
__m128 a_line, b_line, r_line;
for (int i=0; i<16; i+=4) {
// unroll the first step of the loop to avoid having to initialize r_line to zero
a_line = _mm_load_ps(a); // a_line = vec4(column(a, 0))
b_line = _mm_set1_ps(b[i]); // b_line = vec4(b[i][0])
r_line = _mm_mul_ps(a_line, b_line); // r_line = a_line * b_line
for (int j=1; j<4; j++) {
a_line = _mm_load_ps(&a[j*4]); // a_line = vec4(column(a, j))
b_line = _mm_set1_ps(b[i+j]); // b_line = vec4(b[i][j])
// r_line += a_line * b_line
r_line = _mm_add_ps(_mm_mul_ps(a_line, b_line), r_line);
}
_mm_store_ps(&r[i], r_line); // r[i] = r_line
}
}
void mmul_vec4
(const float * a, const float * b, float * r)
{
for (int i=0; i<16; i+=4) {
vec4 rl = vec4(a) * vec4(b[i]);
for (int j=1; j<4; j++)
rl += vec4(&a[j*4]) * vec4(b[i+j]);
rl >> &r[i];
}
}
struct vec4
{
__m128 xmm;
vec4 (__m128 v) : xmm (v) {}
vec4 (float v) { xmm = _mm_set1_ps(v); }
vec4 (float x, float y, float z, float w)
{ xmm = _mm_set_ps(w,z,y,x); }
vec4 (const float *v) { xmm = _mm_load_ps(v); }
vec4 operator* (const vec4 &v) const
{ return vec4(_mm_mul_ps(xmm, v.xmm)); }
vec4 operator+ (const vec4 &v) const
{ return vec4(_mm_add_ps(xmm, v.xmm)); }
vec4 operator- (const vec4 &v) const
{ return vec4(_mm_sub_ps(xmm, v.xmm)); }
vec4 operator/ (const vec4 &v) const
{ return vec4(_mm_div_ps(xmm, v.xmm)); }
void operator*= (const vec4 &v)
{ xmm = _mm_mul_ps(xmm, v.xmm); }
void operator+= (const vec4 &v)
{ xmm = _mm_add_ps(xmm, v.xmm); }
void operator-= (const vec4 &v)
{ xmm = _mm_sub_ps(xmm, v.xmm); }
void operator/= (const vec4 &v)
{ xmm = _mm_div_ps(xmm, v.xmm); }
void operator>> (float *v)
{ _mm_store_ps(v, xmm); }
};
charCodeAt and the usual bit munging operators (&, |, <<, >>). First, you need the byte value at some point in the string. As charCodeAt operates on characters and not bytes, you need to tell the browser to get the data as an unparsed string by calling overrideMimeType("text/plain; charset=x-user-defined") on your XMLHttpRequest. Then mask away the high byte of each char code to get the low byte value. The following works at least on Firefox and WebKit, I don't know about IE and Opera.
var byteValue = str.charCodeAt(index) & 0xFF;
// read big-endian (network byte order) unsigned 32-bit int from data, at offset
readUInt32 = function(data, offset) {
return ((data.charCodeAt(offset) & 0xFF) << 24) +
((data.charCodeAt(offset+1) & 0xFF) << 16) +
((data.charCodeAt(offset+2) & 0xFF) << 8) +
(data.charCodeAt(offset+3) & 0xFF);
}
// read big-endian (network byte order) unsigned 16-bit int from data, at offset
readUInt16 = function(data, offset) {
return ((data.charCodeAt(offset) & 0xFF) << 8) +
(data.charCodeAt(offset+1) & 0xFF);
}
uint16(normalized_float * 65535):
readNormalizedUFixedPoint16 = function(data, offset) {
return readUInt16(data, offset) / 65535.0;
}
// read big-endian (network byte order) 32-bit float
readFloat32 = function(data, offset) {
var b1 = data.charCodeAt(offset) & 0xFF,
b2 = data.charCodeAt(offset+1) & 0xFF,
b3 = data.charCodeAt(offset+2) & 0xFF,
b4 = data.charCodeAt(offset+3) & 0xFF;
var sign = 1 - (2*(b1 >> 7)); // sign = bit 0
var exp = (((b1 << 1) & 0xff) | (b2 >> 7)) - 127; // exponent = bits 1..8
var sig = ((b2 & 0x7f) << 16) | (b3 << 8) | b4; // significand = bits 9..31
if (sig == 0 && exp == -127)
return 0.0;
return sign * (1 + sig * Math.pow(2, -23)) * Math.pow(2, exp);
}