ImagingMemoryBlock structure

This commit is contained in:
Alexander 2017-09-18 22:41:28 +03:00
parent 67e1e03c79
commit 44c2698f69
3 changed files with 42 additions and 27 deletions

View File

@ -124,6 +124,7 @@ class TestCoreMemory(PillowTestCase):
@unittest.skipIf(is_pypy, "images are not collected") @unittest.skipIf(is_pypy, "images are not collected")
def test_clear_cache_stats(self): def test_clear_cache_stats(self):
Image.core.reset_stats() Image.core.reset_stats()
Image.core.clear_cache()
Image.core.set_blocks_max(128) Image.core.set_blocks_max(128)
Image.core.set_block_size(4096) Image.core.set_block_size(4096)
Image.new('RGB', (256, 256)) Image.new('RGB', (256, 256))
@ -131,6 +132,7 @@ class TestCoreMemory(PillowTestCase):
Image.core.clear_cache() Image.core.clear_cache()
stats = Image.core.get_stats() stats = Image.core.get_stats()
print(stats)
self.assertGreaterEqual(stats['new_count'], 2) self.assertGreaterEqual(stats['new_count'], 2)
self.assertGreaterEqual(stats['allocated_blocks'], 64) self.assertGreaterEqual(stats['allocated_blocks'], 64)
self.assertGreaterEqual(stats['reused_blocks'], 64) self.assertGreaterEqual(stats['reused_blocks'], 64)

View File

@ -75,6 +75,11 @@ typedef struct ImagingPaletteInstance* ImagingPalette;
#define IMAGING_MODE_LENGTH 6+1 /* Band names ("1", "L", "P", "RGB", "RGBA", "CMYK", "YCbCr", "BGR;xy") */ #define IMAGING_MODE_LENGTH 6+1 /* Band names ("1", "L", "P", "RGB", "RGBA", "CMYK", "YCbCr", "BGR;xy") */
typedef struct {
char *ptr;
int size;
} ImagingMemoryBlock;
struct ImagingMemoryInstance { struct ImagingMemoryInstance {
/* Format */ /* Format */
@ -95,7 +100,7 @@ struct ImagingMemoryInstance {
/* Internals */ /* Internals */
char **image; /* Actual raster data. */ char **image; /* Actual raster data. */
char *block; /* Set if data is allocated in a single block. */ char *block; /* Set if data is allocated in a single block. */
char **blocks; /* Memory blocks for pixel storage */ ImagingMemoryBlock *blocks; /* Memory blocks for pixel storage */
int pixelsize; /* Size of a pixel, in bytes (1, 2 or 4) */ int pixelsize; /* Size of a pixel, in bytes (1, 2 or 4) */
int linesize; /* Size of a line, in bytes (xsize * pixelsize) */ int linesize; /* Size of a line, in bytes (xsize * pixelsize) */
@ -158,7 +163,7 @@ typedef struct ImagingMemoryArena {
int block_size; int block_size;
int blocks_max; int blocks_max;
int blocks_cached; int blocks_cached;
void **blocks; ImagingMemoryBlock *blocks;
int stats_new_count; int stats_new_count;
int stats_allocated_blocks; int stats_allocated_blocks;
int stats_reused_blocks; int stats_reused_blocks;

View File

@ -284,14 +284,14 @@ ImagingMemorySetBlocksMax(ImagingMemoryArena arena, int blocks_max)
free(arena->blocks); free(arena->blocks);
arena->blocks = NULL; arena->blocks = NULL;
} else if (arena->blocks != NULL) { } else if (arena->blocks != NULL) {
p = realloc(arena->blocks, sizeof(void*) * blocks_max); p = realloc(arena->blocks, sizeof(*arena->blocks) * blocks_max);
if ( ! p) { if ( ! p) {
// Leave previous blocks_max value // Leave previous blocks_max value
return 0; return 0;
} }
arena->blocks = p; arena->blocks = p;
} else { } else {
arena->blocks = calloc(sizeof(void*), blocks_max); arena->blocks = calloc(sizeof(*arena->blocks), blocks_max);
if ( ! arena->blocks) { if ( ! arena->blocks) {
return 0; return 0;
} }
@ -306,49 +306,57 @@ ImagingMemoryClearCache(ImagingMemoryArena arena, int new_size)
{ {
while (arena->blocks_cached > new_size) { while (arena->blocks_cached > new_size) {
arena->blocks_cached -= 1; arena->blocks_cached -= 1;
free(arena->blocks[arena->blocks_cached]); free(arena->blocks[arena->blocks_cached].ptr);
arena->stats_freed_blocks += 1; arena->stats_freed_blocks += 1;
} }
} }
void * ImagingMemoryBlock
memory_get_block(ImagingMemoryArena arena, int requested_size, int dirty) memory_get_block(ImagingMemoryArena arena, int requested_size, int dirty)
{ {
void *block; ImagingMemoryBlock block = {NULL, 0};
if (arena->blocks_cached > 0) { if (arena->blocks_cached > 0) {
// Get block from cache
arena->blocks_cached -= 1; arena->blocks_cached -= 1;
block = realloc(arena->blocks[arena->blocks_cached], requested_size); block = arena->blocks[arena->blocks_cached];
if ( ! block) { // Reallocate if needed
free(arena->blocks[arena->blocks_cached]); if (block.size != requested_size){
arena->stats_freed_blocks += 1; block.ptr = realloc(block.ptr, requested_size);
return NULL;
} }
arena->stats_reused_blocks += 1; if ( ! block.ptr) {
if (block != arena->blocks[arena->blocks_cached]) { // Can't allocate, free prevous pointer (it is still valid)
arena->stats_reallocated_blocks += 1; free(arena->blocks[arena->blocks_cached].ptr);
arena->stats_freed_blocks += 1;
return block;
} }
if ( ! dirty) { if ( ! dirty) {
memset(block, 0, requested_size); memset(block.ptr, 0, requested_size);
}
arena->stats_reused_blocks += 1;
if (block.ptr != arena->blocks[arena->blocks_cached].ptr) {
arena->stats_reallocated_blocks += 1;
} }
} else { } else {
if (dirty) { if (dirty) {
block = malloc(requested_size); block.ptr = malloc(requested_size);
} else { } else {
block = calloc(1, requested_size); block.ptr = calloc(1, requested_size);
} }
arena->stats_allocated_blocks += 1; arena->stats_allocated_blocks += 1;
} }
block.size = requested_size;
return block; return block;
} }
void void
memory_return_block(ImagingMemoryArena arena, void *block) memory_return_block(ImagingMemoryArena arena, ImagingMemoryBlock block)
{ {
if (arena->blocks_cached < arena->blocks_max) { if (arena->blocks_cached < arena->blocks_max) {
arena->blocks[arena->blocks_cached] = block; arena->blocks[arena->blocks_cached] = block;
arena->blocks_cached += 1; arena->blocks_cached += 1;
} else { } else {
free(block); free(block.ptr);
arena->stats_freed_blocks += 1; arena->stats_freed_blocks += 1;
} }
} }
@ -360,7 +368,7 @@ ImagingDestroyArray(Imaging im)
int y = 0; int y = 0;
if (im->blocks) { if (im->blocks) {
while (im->blocks[y]) { while (im->blocks[y].ptr) {
memory_return_block(&ImagingDefaultArena, im->blocks[y]); memory_return_block(&ImagingDefaultArena, im->blocks[y]);
y += 1; y += 1;
} }
@ -373,7 +381,7 @@ ImagingAllocateArray(Imaging im, int dirty)
{ {
int y, line_in_block, current_block; int y, line_in_block, current_block;
ImagingMemoryArena arena = &ImagingDefaultArena; ImagingMemoryArena arena = &ImagingDefaultArena;
char* p = NULL; ImagingMemoryBlock block = {NULL, 0};
int linesize, lines_per_block, blocks_count; int linesize, lines_per_block, blocks_count;
/* 0-width or 0-height image. No need to do anything */ /* 0-width or 0-height image. No need to do anything */
@ -391,7 +399,7 @@ ImagingAllocateArray(Imaging im, int dirty)
im->destroy = ImagingDestroyArray; im->destroy = ImagingDestroyArray;
/* One extra ponter is always NULL */ /* One extra ponter is always NULL */
im->blocks = (char **)calloc(sizeof(char *), blocks_count + 1); im->blocks = calloc(sizeof(*im->blocks), blocks_count + 1);
if ( ! im->blocks) { if ( ! im->blocks) {
return (Imaging) ImagingError_MemoryError(); return (Imaging) ImagingError_MemoryError();
} }
@ -406,14 +414,14 @@ ImagingAllocateArray(Imaging im, int dirty)
if (lines_remained > im->ysize - y) { if (lines_remained > im->ysize - y) {
lines_remained = im->ysize - y; lines_remained = im->ysize - y;
} }
p = memory_get_block(arena, lines_remained * linesize, dirty); block = memory_get_block(arena, lines_remained * linesize, dirty);
if ( ! p) { if ( ! block.ptr) {
return (Imaging) ImagingError_MemoryError(); return (Imaging) ImagingError_MemoryError();
} }
im->blocks[current_block] = p; im->blocks[current_block] = block;
} }
im->image[y] = p + linesize * line_in_block; im->image[y] = block.ptr + linesize * line_in_block;
line_in_block += 1; line_in_block += 1;
if (line_in_block >= lines_per_block) { if (line_in_block >= lines_per_block) {