Loading code is extremely slow on some devices

Started by
1 comment, last by Bregma 11 years ago

Hi,

We've noticed that on some devices, our sound loading code takes an extremely long time compared to other devices. For example, with 25 files our sound loading times look like:

  • iPad ~5 seconds.

  • Galaxy S2: ~7 seconds.
  • Galaxy S3: ~38 seconds.

I've narrowed it down to sound loading via profiling but unfortunately I don't have frequent access to the device in question (the S3) and haven't been able to narrow it down further. This is compiled into a Marmalade app (to run on Android).

The sound loading code wasn't written by me, so I was wondering if anyone notices anything suspect or has any suggestions based on the following code:


//--------------------------------------------
bool CSoundEffect::LoadSound(const std::string &soundFilename)
{
// Search sound filename for last . - beginning of extension
std::string::size_type extensionStartPos = soundFilename.find_last_of(".");
ASSERT_MSG_TRAP(extensionStartPos != std::string::npos, "Unable to determine extension of file " << soundFilename, return false);


// Use this to extract extension and convert it to upper case
std::string extension = soundFilename.substr(extensionStartPos + 1);
StringUtils::StringToUpper(extension);


// Search for a loader for this extension
std::map<std::string, CSoundEffect::SoundLoaderFn>::iterator iLoader = s_SoundLoaderFunctions.find(extension);
ASSERT_MSG_TRAP(iLoader != s_SoundLoaderFunctions.end(), "No sound loader function registered for images with extension " << extension, return false);


// Open sound file stream
std::ifstream soundFileStream;
soundFileStream.open((CResourceManager::Get()->GetPathPrefix() + soundFilename).c_str(), std::ios::binary);
ASSERT_MSG_TRAP(soundFileStream.is_open(), "Sound " << CResourceManager::Get()->GetPathPrefix() + soundFilename << " not found", return false);


// Attempt to use loader function to complete image data structure
bool loadResult = iLoader->second(soundFileStream, m_SoundData);


// Close file
soundFileStream.close();


ASSERT_MSG_TRAP(loadResult, "Unable to load sound " << CResourceManager::Get()->GetPathPrefix() + soundFilename, return false);


return true;
}
//----------------------------------------
 

Loader Code:


#include "sound_loader_voc.h"


// Project includes
#include "logger.h"


// As VC6 is basically shite and what airplay targets, IW_ALIGNED isn't properly setup in s3eTypes
#if defined _MSC_VER && defined I3D_ARCH_X86
#undef IW_ALIGNED
#define IW_ALIGNED(X) __declspec(align(X))
#endif


//--------------------------------------------
// Anonymous namespace
//--------------------------------------------
namespace
{
// Block types
enum VocBlockTypes
{
eBlockTerminator,
eBlockSoundData,
eBlockSoundContinue,
eBlockSilence,
eBlockMarker,
eBlockASCII,
eBlockRepeat,
eBlockEndRepeat,
eBlockExtendedSoundAttribute,
eBlockExtendedSoundData,
};


// Possible formats of eBlockExtendedSoundData
enum ExtendedSoundDataFormat
{
eExtendedSoundFormatPCM8 = 0x0000,
    eExtendedSoundFormatADPCM4 = 0x0001,
eExtendedSoundFormatADPCM3 = 0x0002,
eExtendedSoundFormatADPCM2 = 0x0003,
eExtendedSoundFormatPCM16 = 0x0004,
eExtendedSoundFormatCCITTALaw = 0x0006,
eExtendedSoundFormatCCITTULaw = 0x0007,
eExtendedSoundFormat16ADPCM4 = 0x02000,
};


// Header structure for a VOC file
IW_ALIGNED(1) struct VocHeader
{
char m_String[19];
u8 m_Pad;
u16 m_Offset;
u16 m_Version[2];
};


// Header structure for all data blocks
IW_ALIGNED(1) struct VocDataBlockHeader
{
u8 m_Type;
u8 m_Size[3];


u32 GetSize() const{ return m_Size[0] + (m_Size[1] << 8) + (m_Size[2] << 16); }
};


// Sub-header structure for an extended sound data block
IW_ALIGNED(1) struct ExtendedSoundDataHeader
{
u32 m_SamplesPerSec;
    u8 m_BitsPerSample;
    u8 m_NumChannels;
u16 m_Format;
    u8 m_Padding[4];
};


bool ReadHeaderGetOffset(std::istream &inputFile, u16 &offset)
{
VocHeader header;
inputFile.read(reinterpret_cast<char*>(&header), sizeof(VocHeader));


// Validate header
if(memcmp(header.m_String, "Creative Voice File", 19) != 0)
{
return false;
}


// Transfer offset
offset = header.m_Offset;


return true;
}


bool ReadDataBlockExtendedSoundData(std::istream &inputFile, CSoundEffect::SoundData &soundData)
{ 
// Read main block header
VocDataBlockHeader header;
inputFile.read(reinterpret_cast<char*>(&header), sizeof(VocDataBlockHeader));


// Check that we're reading the right sort of data block
ASSERT_MSG_TRAP(header.m_Type == eBlockExtendedSoundData, "Only extended sound data blocks are supported", return false);


// Read the extended header
ExtendedSoundDataHeader extendedHeader;
inputFile.read(reinterpret_cast<char*>(&extendedHeader), sizeof(ExtendedSoundDataHeader));


// Check various aspects of the sound format
ASSERT_MSG_TRAP(extendedHeader.m_BitsPerSample == 16, "s3eSound only supports 16 bit samples", return false);
ASSERT_MSG_TRAP(extendedHeader.m_NumChannels == 1, "Only mono sound is currently supported", return false);
ASSERT_MSG_TRAP(extendedHeader.m_Format == eExtendedSoundFormatPCM16, "Only 16 bit PCM is currently supported", return false);


// Transfer sample rate into sound data structure
soundData.m_SamplesPerSec = extendedHeader.m_SamplesPerSec;


// Get size of chunk, subtract header size and convert to samples
u32 numBytes = header.GetSize() - 12;
u32 numSamples = numBytes >> 1;


// Allocate memory for samples and read from file
soundData.m_Samples.resize(numSamples);
inputFile.read(reinterpret_cast<char*>(&soundData.m_Samples.front()), numBytes);


return true;
};
};


//--------------------------------------------
// SoundLoaderVoc
//--------------------------------------------
bool SoundLoaderVoc::Loader(std::istream &inputFile, CSoundEffect::SoundData &soundData)
{
// Read header, determine data offset and seek there
u16 dataOffset;
if(!ReadHeaderGetOffset(inputFile, dataOffset))
{
ASSERT_MSG(false, "Not a valid VOC file");
return false;
}


// Seek to start of data offset
inputFile.seekg(dataOffset, std::ifstream::beg);


ReadDataBlockExtendedSoundData(inputFile, soundData);
return true;
}
 

Thanks for reading!

Advertisement

There's nothing too terrible about the code, so it may just be device limitations. About the only thing you could do to improve the code further would be to read the entire file in one go, and step over the data blocks using pointers (rather than allocating and then reading in 3 blocks). possibly pointless though, because as I said before, it doesn't look too bad. One advantage of reading it in a single block however, is that you may be able to find a speed improvement using unbuffered fileIO (i.e. open/read, rather than fopen/fread). It might be worth trying memory mapped files to see if they help (mmap). Beyond that, there is only one further optimisation that makes sense... compress the data (or use a lower bitrate). By shrinking the size of the files on disk, you'll reduce the fileIO overhead, and it should go much faster. If the worst comes to the worst, you could try packaging up the sound files into a single data blob, and read all of them at once. That *might* help, maybe....

Before doing any of this though, take a look to see where the app data is actually being stored. You might find that the S3 is storing the data on microSD card rather than the internal storage, which I'd assume would make things a bit slower. Purchasing a super fast microSD card might help, so might deleting any unused data/apps from the device.

I experienced similar problems with the memory allocator in the Samsung kernels. They had patched out the regular Linux allocator and implemented their own that prevented large-chunk allocation, so when media needed them dynamically they weren't available and various coalesce strategies were required in the driver userland. That was a couple of years ago, and they may have fixed that problem (but then again, maybe not). The symptom was not only long load times, but frequent pauses and sometimes media playback freezes.

Stephen M. Webb
Professional Free Software Developer

This topic is closed to new replies.

Advertisement