I threw together the attached program (compiles fine with gcc 2.95.2 on
Solaris 2.6 and egcs-2.91.66 on RedHat Linux 6.2) and ran it a few
times. Data is below. Usual disclaimers about hastily written code etc
:)

Machine = ghoul (generic intel, 384mb ram, dual p3-800, ide disk running
dma)

Sequential
Bytes Read      Time    Bytes / Sec
536870912       27.14   19783933.74
536870912       27.14   19783990.60
536870912       27.11   19801872.14
536870912       26.92   19942928.41
536870912       27.31   19657408.43
                        19794026.66 (avg)

Random          
Bytes Read      Time    Bytes / Sec
1073741824      519.57  2066589.21
1073741824      517.78  2073751.44
1073741824      516.92  2077193.23
1073741824      513.18  2092333.29
1073741824      510.68  2102579.88
                        2082489.41 (avg)

Machine = jedi (Sun E420, 3gb ram, dual 400s, test on single scsi disk)

Sequential              
Bytes Read      Time    Bytes / Sec
2097152000      65.19   32167675.28
2097152000      65.22   32154114.65
2097152000      65.16   32182561.99
2097152000      65.12   32206105.12
2097152000      64.67   32429463.26
                        32227984.06 (avg)

Random          
Bytes Read      Time    Bytes / Sec
4194304000      1522.22 2755394.79
4194304000      278.18  15077622.05
4194304000      91.43   45874730.07
4194304000      61.43   68273795.19
4194304000      54.55   76890231.51
                        41774354.72

If I interpret Tom's "divide" instruction correctly, is that a factor of
10 on the linux box?

On Thu, 2002-04-18 at 01:16, Tom Lane wrote:
> "Luis Alberto Amigo Navarro" <[EMAIL PROTECTED]> writes:
> > On my own few experience I think this could be solved decreasing
> > random_page_cost, if you would prefer to use indexes than seq scans, then
> > you can lower random_page_cost to a point in which postgres works as you
> > want. So the planner would prefer indexes when in standard conditions it
> > would prefer seq scans.
> 
> It's entirely possible that the default value of random_page_cost is too
> high, at least for many modern machines.  The experiments I did to get
> the 4.0 figure were done a couple years ago, on hardware that wasn't
> exactly new at the time.  I have not heard of anyone else trying to
> measure it though.
> 
> I don't think I have the source code I used anymore, but the principle
> is simple enough:
> 
> 1. Make a large file (several times the size of your machine's RAM, to
> ensure you swamp out kernel disk buffering effects).  Fill with random
> data. (NB: do not fill with zeroes, some filesystems optimize this away.)
> 
> 2. Time reading the file sequentially, 8K per read request.
> Repeat enough to get a statistically trustworthy number.
> 
> 3. Time reading randomly-chosen 8K pages from the file.  Repeat
> enough to get a trustworthy number (the total volume of pages read
> should be several times the size of your RAM).
> 
> 4. Divide.
> 
> The only tricky thing about this is making sure you are measuring disk
> access times and not being fooled by re-accessing pages the kernel still
> has cached from a previous access.  (The PG planner does try to account
> for caching effects, but that's a separate estimate; the value of
> random_page_cost isn't supposed to include caching effects.)  AFAIK the
> only good way to do that is to use a large test, which means it takes
> awhile to run; and you need enough spare disk space for a big test file.
> 
> It'd be interesting to get some numbers for this across a range of
> hardware, filesystems, etc ...
> 
>                       regards, tom lane
> 
> ---------------------------(end of broadcast)---------------------------
> TIP 1: subscribe and unsubscribe commands go to [EMAIL PROTECTED]
> 


#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <sys/stat.h>
#include <sys/time.h>

/**
 * Constants
 */

#define BLOCK_SIZE		(8192)

/**
 * Prototypes
 */

	// Creates the test file filled with random data
	void createTestFile(char *testFileName, long long fileSize);

	// Handles runtime errors by displaying the function, activity and error number
	void handleError(char *functionName, char *activity);

	// Standard entry point
	int main(int argc, char *args[]);

	// Prints correct usage and quits
	void printUsageAndQuit();

	// Tests performance of random reads of the given file
	void testRandom(char *testFileName, long long amountToRead);

	// Tests performance of sequential reads of the given file
	void testSeq(char *testFileName);

/**
 * Definitions
 */

/**
 * createTestFile()
 */
void createTestFile(char *testFileName, long long fileSize)
{
	FILE *testFile;
	long long reps, i, j, bufferReps;
	time_t timetmp;
	long long *buffer;
	size_t written;

	// Indicate op
	printf("Creating test file %s of %lld mb\n",testFileName,fileSize);

	// Adjust file size to bytes
	fileSize *= (1024*1024);

	// Allocate a buffer for writing out random long longs
	if (!(buffer = malloc(BLOCK_SIZE)))
		handleError("createTestFile()","malloc");

	// Open the file for writing
	if (!(testFile = fopen(testFileName, "wb")))
		handleError("createTestFile()","fopen");

	// Initialise the random number generator
	srandom(time(NULL));

	// Write data
	reps 		= fileSize / BLOCK_SIZE;
	bufferReps	= BLOCK_SIZE / sizeof(long long);
	for (i = 0; i < reps; i++)
	{
		// Fill buffer with random data
		for (j = 0; j < bufferReps; j++)
			buffer[j] = random();

		// Write
		written = fwrite(buffer, sizeof(long long), bufferReps, testFile);
		if (written != bufferReps)
			handleError("createTestFile()","fwrite");
	}

	// Flush and close
	if (fflush(testFile))
		handleError("createTestFile()","fflush");
	if (fclose(testFile))
		handleError("createTestFile()","fclose");

	// Free buffer
	free(buffer);
}

/**
 * handleError()
 */
void handleError(char *functionName, char *activity)
{
	fprintf(stderr, "Error in %s while attempting %s. Error %d (%s)\n", functionName, activity, errno, strerror(errno));
	exit(1);
}

/**
 * main()
 */
int main(int argc, char *argv[])
{
	// Print usage and quit if argument count is definitely incorrect
	if (argc < 3)
	{
		// Definitely wrong
		printUsageAndQuit();
	}
	else
	{
		// Dispatch
		if (!strcmp(argv[1], "create"))
		{
			if (argc != 4)
				printUsageAndQuit();

			// Create the test file of the specified size
			createTestFile(argv[2], atol(argv[3]));
		}
		else if (!strcmp(argv[1], "seqtest"))
		{
			if (argc != 3)
				printUsageAndQuit();

			// Test performance of sequential reads
			testSeq(argv[2]);
		}
		else if (!strcmp(argv[1], "rndtest"))
		{
			if (argc != 4)
				printUsageAndQuit();

			// Test performance of random reads
			testRandom(argv[2], atol(argv[3]));
		}
		else
		{
			// Unknown command
			printUsageAndQuit();
		}
	}

	return 0;
}

/**
 * printUsageAndQuit()
 */
void printUsageAndQuit()
{
	puts("USAGE: rndpgcst [create <file> <size_in_mb>] | [seqtest <file>] | [rndtest <file> <read_in_mb>]");

	exit(1);
}

/**
 * testSeq()
 */
void testSeq(char *testFileName)
{
	FILE *testFile;
	char *buffer;
	long long reps, totalRead, thisRead, timeTaken;
	struct timeval startTime, endTime;
	struct timezone timezoneDiscard;

	// Indicate op
	printf("Sequential read test of %s\n",testFileName);

	// Grab a buffer
	buffer = malloc(BLOCK_SIZE);

	// Open the file for reading
	if (!(testFile = fopen(testFileName, "rb")))
		handleError("testSeq()","fopen");

	// Start timer
	if (gettimeofday(&startTime, &timezoneDiscard) == -1)
		handleError("testSeq()", "gettimeofday start");

	// Read all data from file
	totalRead = 0;
	while ((thisRead = fread(buffer, 1, BLOCK_SIZE, testFile)) != 0)
		totalRead += thisRead;

	// End timer
	if (gettimeofday(&endTime, &timezoneDiscard) == -1)
		handleError("testSeq()", "gettimeofday start");

	// Close
	if (fclose(testFile))
		handleError("testSeq()","fclose");

	// Free the buffer
	free(buffer);

	// Display time taken
	timeTaken = (endTime.tv_sec - startTime.tv_sec) * 1000000;
	timeTaken += (endTime.tv_usec - startTime.tv_usec);
	printf("%lld bytes read in %f seconds\n", totalRead, (double) timeTaken / (double) 1000000);
}

/**
 * testRandom()
 */
void testRandom(char *testFileName, long long amountToRead)
{
	FILE *testFile;
	long long reps, i, fileSize, timeTaken, totalRead, readPos, thisRead, offsetMax;
	struct stat fileStats;
	char *buffer;
	struct timeval startTime, endTime;
	struct timezone timezoneDiscard;

	// Indicate op
	printf("Random read test of %s for %lld mb\n", testFileName, amountToRead);

	// Initialise the random number generator
	srandom(time(NULL));

	// Adjust amount to read
	amountToRead *= (1024*1024);

	// Determine file size
	if (stat(testFileName, &fileStats) == -1)
		handleError("testRandom()", "stat");
	fileSize = fileStats.st_size;

	// Grab a buffer
	buffer = malloc(BLOCK_SIZE);

	// Open the file for reading
	if (!(testFile = fopen(testFileName, "rb")))
		handleError("testRandom()","fopen");

	// Start timer
	if (gettimeofday(&startTime, &timezoneDiscard) == -1)
		handleError("testRandom()", "gettimeofday start");

	// Read data from file
	reps 		= amountToRead / BLOCK_SIZE;
	offsetMax	= fileSize / BLOCK_SIZE;
	for (i = 0; i < reps; i++)
	{
		// Determine read position
		readPos = (random() % offsetMax) * BLOCK_SIZE;

		// Seek and read
		if (fseek(testFile, readPos, SEEK_SET) == -1)
			handleError("testRandom()","fseek");
		if ((thisRead = fread(buffer, 1, BLOCK_SIZE, testFile)) != BLOCK_SIZE)
			handleError("testRandom()","fread");
	}

	// End timer
	if (gettimeofday(&endTime, &timezoneDiscard) == -1)
		handleError("testRandom()", "gettimeofday start");

	// Close
	if (fclose(testFile))
		handleError("testRandom()","fclose");

	// Free the buffer
	free(buffer);

	// Display time taken
	timeTaken = (endTime.tv_sec - startTime.tv_sec) * 1000000;
	timeTaken += (endTime.tv_usec - startTime.tv_usec);
	printf("%lld bytes read in %f seconds\n", amountToRead, (double) timeTaken / (double) 1000000);
}

---------------------------(end of broadcast)---------------------------
TIP 2: you can get off all lists at once with the unregister command
    (send "unregister YourEmailAddressHere" to [EMAIL PROTECTED])

Reply via email to