|
Archives
D Programming
digitalmars.Ddigitalmars.D.bugs digitalmars.D.dtl digitalmars.D.ide digitalmars.D.dwt digitalmars.D.announce digitalmars.D.learn digitalmars.D.debugger D.gnu D C/C++ Programming
c++c++.announce c++.atl c++.beta c++.chat c++.command-line c++.dos c++.dos.16-bits c++.dos.32-bits c++.idde c++.mfc c++.rtl c++.stl c++.stl.hp c++.stl.port c++.stl.sgi c++.stlsoft c++.windows c++.windows.16-bits c++.windows.32-bits c++.wxwindows digitalmars.empire digitalmars.DMDScript electronics |
digitalmars.D.learn - D memory consumption/runtime speed problem
Hello,
I've been writing a bit of compression code and I noticed some strange
behavior that's driving me a bit batty. I don't know if it's a bug with
D or something I did. All I know is I can't figure it out.
Below is the simplified version of the code as a single file. It takes
two parameters. The first is a file to "compress" and the second is the
number of times to run the benchmark. E.g. bugtest foo.txt 2
Now, if I set the second parameter to 1, it runs decently fast. 26
seconds on my work laptop for a half-sized enwiki8 from the Hutter
challenge. If I set it to 2, then it takes about 142 seconds. In both
cases a lot of memory is used and I'm not really sure why. Also, after
it prints out the results, it takes several seconds for the program to exit.
Am I doing something wrong? I've tried every trick that I could find by
reading the documentation. Btw: The last time I tried this was with the
latest version of D released at the beginning of the month.
__CODE__
import std.conv;
import std.stdio;
import std.stream;
import std.date;
import std.mmfile;
import std.array;
string filename = "enwik8_small";
private immutable uint ONE_BYTE_VAL = (1 << 6) - 1;
private immutable uint TWO_BYTE_VAL = (1 << 14) - 1;
private immutable uint THREE_BYTE_VAL = (1 << 22) - 1;
private immutable uint FOUR_BYTE_VAL = (1 << 30) - 1;
private immutable uint ONE_BYTE_MASK = (0 << 6);
private immutable uint TWO_BYTE_MASK = (1 << 6);
private immutable uint THREE_BYTE_MASK = (2 << 6);
private immutable uint FOUR_BYTE_MASK = (3 << 6);
ubyte[] encodeNumber(in uint count)
{
if (count <= ONE_BYTE_VAL)
{
return [cast(ubyte)(ONE_BYTE_MASK | count)];
}
else if (count <= TWO_BYTE_VAL)
{
return [cast(ubyte)(TWO_BYTE_MASK | (count >>> 8))]
~ [cast(ubyte)(count & 0x000000ff)];
}
else if (count <= THREE_BYTE_VAL)
{
return [cast(ubyte)(THREE_BYTE_MASK | (count >>> 16))]
~ [cast(ubyte)((count >>> 8) & 0x000000ff)]
~ [cast(ubyte)(count & 0x000000ff)];
}
else if (count <= FOUR_BYTE_VAL)
{
return [cast(ubyte)(FOUR_BYTE_MASK | (count >>> 24))]
~ [cast(ubyte)((count >>> 16) & 0x000000ff)]
~ [cast(ubyte)((count >>> 8) & 0x000000ff)]
~ [cast(ubyte)(count & 0x000000ff)];
}
else
{
throw new Exception("Invalid count provided!");
}
}
void encode(in ubyte[] buff, out ubyte[] output)
{
ubyte currByte = buff[0];
uint count = 0;
auto appOutput = appender(&output);
foreach (byteVal; buff)
{
if (byteVal != currByte && count > 0)
{
appOutput.put(encodeNumber(count));
appOutput.put(currByte);
currByte = byteVal;
count = 0;
}
count++;
}
appOutput.put(encodeNumber(count));
appOutput.put(currByte);
}
void benchCode()
{
MmFile buff = new MmFile(filename);
ubyte[] encodedBytes;
encode(cast(ubyte[])buff[], encodedBytes);
}
void main(string[] args)
{
filename = args[1];
writeln("Benchmark time: ", benchmark!(benchCode)(to!(uint)(args[2])));
}
Jan 13 2010
Hello sybrandy,Hello, I've been writing a bit of compression code and I noticed some strange behavior that's driving me a bit batty. I don't know if it's a bug with D or something I did. All I know is I can't figure it out. Below is the simplified version of the code as a single file. It takes two parameters. The first is a file to "compress" and the second is the number of times to run the benchmark. E.g. bugtest foo.txt 2 Now, if I set the second parameter to 1, it runs decently fast. 26 seconds on my work laptop for a half-sized enwiki8 from the Hutter challenge. If I set it to 2, then it takes about 142 seconds. In both cases a lot of memory is used and I'm not really sure why. Also, after it prints out the results, it takes several seconds for the program to exit. Am I doing something wrong? I've tried every trick that I could find by reading the documentation. Btw: The last time I tried this was with the latest version of D released at the beginning of the month. Jan 13 2010
sybrandy:Now, if I set the second parameter to 1, it runs decently fast. 26 seconds on my work laptop for a half-sized enwiki8 from the Hutter challenge. If I set it to 2, then it takes about 142 seconds. In both cases a lot of memory is used and I'm not really sure why. Also, after it prints out the results, it takes several seconds for the program to exit. Jan 13 2010
sybrandy wrote:Hello, I've been writing a bit of compression code and I noticed some strange behavior that's driving me a bit batty. I don't know if it's a bug with D or something I did. All I know is I can't figure it out. ... Am I doing something wrong? I've tried every trick that I could find by reading the documentation. Btw: The last time I tried this was with the latest version of D released at the beginning of the month. Jan 13 2010
On Thu, 14 Jan 2010 01:00:31 -0500, Daniel Keep <daniel.keep.lists gmail.com> wrote:sybrandy wrote:Hello, I've been writing a bit of compression code and I noticed some strange behavior that's driving me a bit batty. I don't know if it's a bug with D or something I did. All I know is I can't figure it out. ... Am I doing something wrong? I've tried every trick that I could find by reading the documentation. Btw: The last time I tried this was with the latest version of D released at the beginning of the month. Jan 14 2010
<snip>
Using a small buffer as suggested by Daniel Keep and Steven
Schveighoffer significantly improved performance. Now down to about 5
seconds. I ended up using the static array buffer since the
encodeNumber function will be in its own file in my resulting program,
so I can keep it private. Doing something similar to the output buffer
had a similar effect and it's now processing everything in less than 2
seconds!
I didn't realize that all those little arrays were created. Perhaps
this is something that should be detailed in the arrays documentation
or, perhaps even better, an optimization guide? I honestly thought the
GC would help keep memory in check as I didn't want to assume a
worst-case scenario, which with my RLE implementation is 2 * input
buffer size, but I guess I have to.
Well, perhaps my original code will be of use if Walter and the gang
decide to try to revamp the GC and want some "bad" code to test it with.
Thanks all!
Btw: below is the updated code
import std.conv;
import std.stdio;
import std.stream;
import std.date;
import std.mmfile;
import std.array;
string filename = "enwik8_small";
private immutable uint ONE_BYTE_VAL = (1 << 6) - 1;
private immutable uint TWO_BYTE_VAL = (1 << 14) - 1;
private immutable uint THREE_BYTE_VAL = (1 << 22) - 1;
private immutable uint FOUR_BYTE_VAL = (1 << 30) - 1;
private immutable uint ONE_BYTE_MASK = (0 << 6);
private immutable uint TWO_BYTE_MASK = (1 << 6);
private immutable uint THREE_BYTE_MASK = (2 << 6);
private immutable uint FOUR_BYTE_MASK = (3 << 6);
private static ubyte[4] encodeBuff;
ubyte[] encodeNumber(in uint count)
{
if (count <= ONE_BYTE_VAL)
{
encodeBuff[0] = cast(ubyte)(ONE_BYTE_MASK | count);
return encodeBuff[0..1];
}
else if (count <= TWO_BYTE_VAL)
{
encodeBuff[0] = cast(ubyte)(TWO_BYTE_MASK | (count >>> 8));
encodeBuff[1] = cast(ubyte)(count & 0x000000ff);
return encodeBuff[0..2];
}
else if (count <= THREE_BYTE_VAL)
{
encodeBuff[0] = cast(ubyte)(THREE_BYTE_MASK | (count >>> 16));
encodeBuff[1] = cast(ubyte)((count >>> 8) & 0x000000ff);
encodeBuff[2] = cast(ubyte)(count & 0x000000ff);
return encodeBuff[0..3];
}
else if (count <= FOUR_BYTE_VAL)
{
encodeBuff[0] = cast(ubyte)(FOUR_BYTE_MASK | (count >>> 24));
encodeBuff[1] = cast(ubyte)((count >>> 16) & 0x000000ff);
encodeBuff[2] = cast(ubyte)((count >>> 8) & 0x000000ff);
encodeBuff[3] = cast(ubyte)(count & 0x000000ff);
return encodeBuff[0..4];
}
else
{
throw new Exception("Invalid count provided!");
}
}
void encode(in ubyte[] buff, ref ubyte[] output)
{
ubyte currByte = buff[0];
uint count = 0;
uint outIdx = 0;
ubyte[] temp;
foreach (byteVal; buff)
{
if (byteVal != currByte && count > 0)
{
temp = encodeNumber(count);
foreach (t; temp)
{
output[outIdx++] = t;
}
output[outIdx++] = currByte;
currByte = byteVal;
count = 0;
}
count++;
}
temp = encodeNumber(count);
foreach (t; temp)
{
output[outIdx++] = t;
}
output[outIdx++] = currByte;
}
void benchCode()
{
MmFile buff = new MmFile(filename);
ubyte[] encodedBytes;
encodedBytes.length = cast(size_t)buff.length * 2;
encode(cast(ubyte[])buff[], encodedBytes);
}
void main(string[] args)
{
filename = args[1];
writeln("Benchmark time: ", benchmark!(benchCode)(to!(uint)(args[2])));
}
Jan 14 2010
sybrandy:private immutable uint ONE_BYTE_VAL = (1 << 6) - 1; private immutable uint TWO_BYTE_VAL = (1 << 14) - 1; Jan 14 2010
|