Files @ r13257:4c5b8120be59
Branch filter:

Location: cpp/openttd-patchpack/source/src/core/pool_func.hpp

rubidium
(svn r17776) -Codechange: [SDL] make "update the video card"-process asynchronious. Profiling with gprof etc. hasn't shown us that DrawSurfaceToScreen takes a significant amount of CPU; only using TIC/TOC it became apparant that it was a heavy CPU-cycle user or that it was waiting for something.
The benefit of making this function asynchronious ranges from 2%-25% (real time) during fast forward on dual core/hyperthreading-enabled CPUs; 8bpp improvements are, in my test cases, significantly smaller than 32bpp improvements.
On single core non-hyperthreading-enabled CPUs the extra locking/scheduling costs up to 1% extra realtime in fast forward. You can use -v sdl:no_threads to disable threading and undo this loss.
During normal non-fast-forwarded games the benefit/costs are negligable except when the gameloop takes more than about 90% of the time of a tick.
Note that allegro's performance does not improve with this system, likely due to their way of getting data to the video card. It is not implemented for the OS X/Windows video backends, unless (ofcourse) SDL is used there.
Funny is that the performance of the 32bpp(-anim) blitter is, at least in some test cases, significantly faster (more than 10%) than the 8bpp(-optimized) blitter when looking at real time in fast forward on a dual core CPU; it was slower.
The idea comes from a paper/report by Idar Borlaug and Knut Imar Hagen.
/* $Id$ */

/*
 * This file is part of OpenTTD.
 * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
 * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
 */

/** @file pool_func.hpp Some methods of Pool are placed here in order to reduce compilation time and binary size. */

#ifndef POOL_FUNC_HPP
#define POOL_FUNC_HPP

#include "alloc_func.hpp"
#include "mem_func.hpp"
#include "pool_type.hpp"

#define DEFINE_POOL_METHOD(type) \
	template <class Titem, typename Tindex, size_t Tgrowth_step, size_t Tmax_size> \
	type Pool<Titem, Tindex, Tgrowth_step, Tmax_size>

DEFINE_POOL_METHOD(inline)::Pool(const char *name) :
		name(name),
		size(0),
		first_free(0),
		first_unused(0),
		items(0),
		cleaning(false),
		data(NULL)
{ }

DEFINE_POOL_METHOD(inline void)::ResizeFor(size_t index)
{
	assert(index >= this->size);
	assert(index < Tmax_size);

	size_t new_size = min(Tmax_size, Align(index + 1, Tgrowth_step));

	this->data = ReallocT(this->data, new_size);
	MemSetT(this->data + this->size, 0, new_size - this->size);

	this->size = new_size;
}

DEFINE_POOL_METHOD(inline size_t)::FindFirstFree()
{
	size_t index = this->first_free;

	for (; index < this->first_unused; index++) {
		if (this->data[index] == NULL) return index;
	}

	if (index < this->size) {
		return index;
	}

	assert(index == this->size);
	assert(this->first_unused == this->size);

	if (index < Tmax_size) {
		this->ResizeFor(index);
		return index;
	}

	assert(this->items == Tmax_size);

	return NO_FREE_ITEM;
}

DEFINE_POOL_METHOD(inline void *)::AllocateItem(size_t size, size_t index)
{
	assert(this->data[index] == NULL);

	this->first_unused = max(this->first_unused, index + 1);
	this->items++;

	Titem *item = this->data[index] = (Titem *)CallocT<byte>(size);
	item->index = (uint)index;
	return item;
}

DEFINE_POOL_METHOD(void *)::GetNew(size_t size)
{
	size_t index = this->FindFirstFree();

	if (index == NO_FREE_ITEM) {
		error("%s: no more free items", this->name);
	}

	this->first_free = index + 1;
	return this->AllocateItem(size, index);
}

DEFINE_POOL_METHOD(void *)::GetNew(size_t size, size_t index)
{
	if (index >= Tmax_size) {
		usererror("failed loading savegame: %s index " PRINTF_SIZE " out of range (" PRINTF_SIZE ")", this->name, index, Tmax_size);
	}

	if (index >= this->size) this->ResizeFor(index);

	if (this->data[index] != NULL) {
		usererror("failed loading savegame: %s index " PRINTF_SIZE " already in use", this->name, index);
	}

	return this->AllocateItem(size, index);
}

DEFINE_POOL_METHOD(void)::FreeItem(size_t index)
{
	assert(index < this->size);
	assert(this->data[index] != NULL);
	free(this->data[index]);
	this->data[index] = NULL;
	this->first_free = min(this->first_free, index);
	this->items--;
	if (!this->cleaning) Titem::PostDestructor(index);
}

DEFINE_POOL_METHOD(void)::CleanPool()
{
	this->cleaning = true;
	for (size_t i = 0; i < this->first_unused; i++) {
		delete this->Get(i); // 'delete NULL;' is very valid
	}
	assert(this->items == 0);
	free(this->data);
	this->first_unused = this->first_free = this->size = 0;
	this->data = NULL;
	this->cleaning = false;
}

#undef DEFINE_POOL_METHOD

/**
 * Force instantiation of pool methods so we don't get linker errors.
 * Only methods accessed from methods defined in pool.hpp need to be
 * forcefully instantiated.
 */
#define INSTANTIATE_POOL_METHODS(name) \
	template void * name ## Pool::GetNew(size_t size); \
	template void * name ## Pool::GetNew(size_t size, size_t index); \
	template void name ## Pool::FreeItem(size_t index); \
	template void name ## Pool::CleanPool();

#endif /* POOL_FUNC_HPP */