Examples

Message Switching Test

This example is meant to get a feel for the message switching speed of the CellSpeak Virtual Machine. The speed with which a message is exchanged between two cells depends on many factors: the speed of the machine, the number of cores, whether the cells are running on the same machine, in the same process, the size of the message and so forth.

The message switch inside the CellSpeak Virtual Machine will choose a different approach depending on these factors and will always try to deliver the message as fast as possible.

The program below will give you a good feel for the type of speeds and throughput that can be handled by the CellSpeak message switch. It implements two tests, the first test focuses on delivering many small messages - like the parameters in a function call - whereas the second test sends larger messages to test the bandwith of the message switch. On my computer small messages are sent and received at a rate of around 3 million messages per second. For the large messages, the bandwith reaches around 10 GBytes/second.





(1) We do not bother to define a type - there is only one such variable - a name spared.






















(2) This counts as a single expression, so we can use the short form of the function definition.







(3) We derive the type of the parameter simply by stating that it is the same as the type of the variable T.

08 Message Switching Test.celsrc
-.-
	A Roman Legion
	
	In the example below we create a Roman army of 10 cohorts of 500 legionaires each (5000 cells)
	The 500 legionaires then send each 200 messages to their Centurion who then sends back the 
	results to the Legatus (yje general of roman legion).This process is repeated a number of times, 
	and the total message throughput is calculated and displayed. 
	
	The same is done then with 20 messages who have a random size between 0 and 64 kBytes. 
	For these messages the bandwith is calculated and displayed.
	
	To run the program, instantiate the design 'Legion.Legatus'.
-.-
use Windows, Math, Strings, Editor, System

group Legion

-- The Legatus is the genaral of a legion
design Legatus is

	const int NrOfCohorts = 10			-- There are ten cohorts in a legion of each about 500 legionaires
	const int NrOfTestRounds = 10		-- We do the test several times
	cell Window							-- The output window
	int Rounds = 1						-- The nr of test rounds 	
	cell Centurions[NrOfCohorts]		-- The list of Centurion cells
	ansi TestCommand					-- a string for the test command
	
	-- A variable to collect the result of one test round
(1)	var Test is record
	
		float 	msec					-- duration of the test in msec
		int 	Messages				-- The nr of messages recvd
		int 	KBytes					-- The nr of KBytes received
		int		Replies 				-- The nr of replies received
		ClockClass 	Clock				-- A C++ interface to the clock
		word StartTime					-- The start time of the test
	
		function Reset is
			msec = Messages = KBytes = Replies = 0
			StartTime = Clock.Ticks()
		end
		
		function Stop => msec = Clock.Duration( StartTime )
		
		function Report(cell Window) is 
		
			-- Display the nr of messages that were sent/rcvd per second
			Window <- print("\n\t[msec] msec for [Messages/1000] KMsg => [Messages/msec] KMsg/sec")
			
			-- if we have the bytecount, print the result
			KBytes ? Window <- print("\n\t[KBytes/1000] MBytes were sent/rcvd in [msec] msec - throughput => [KBytes/msec] MBytes/sec")			
		end
	end
	
	-- A variable to collect the results of all test rounds
	var AllTests is record
	
		float 	msec
		int 	NrOfTests
		int 	Messages
		int 	KBytes
		
(2)		function Reset => msec = NrOfTests = Messages = KBytes = 0
		
		function Report(cell Window) is
		
			-- print the overall result for the test
			Window <- print("\n\n\tAfter [NrOfTests] tests: [msec/1000] seconds for [Messages/1000] KMsg => [Messages/msec] KMsg/sec")			
		
			-- if we have the bytecount, print the result
			KBytes ? Window <- print("\n\t[KBytes/1000] MBytes were sent/rcvd in [msec/1000] sec - throughput => [KBytes/msec] MBytes/sec")
		end
		
		-- A function to add the test results to the overall results
(3)			function Add( var T like Test ) is
		
			-- Add the total of one round to the overall total
			msec += T.msec

			-- Also add the nr of messages of one round to the total for all rounds
			Messages += T.Messages
			
			-- Also add the nr of byte sent
			T.KBytes ? KBytes += T.KBytes	
			
			-- Counter goes one up
			NrOfTests += 1		
		end		
	end
		
	-- Create the centurii of the legion
	constructor is
				
		-- Get a clock
		Test.Clock.Create()
		
		-- We create a cell for output
		Window = create MenuWindow("Message switching test")
		
		-- set the stdout to the same window (mainly used for debugging)
		system <- stdout.Set( Window )
		
		-- Reset the test record (to count the nr of replies)
		Test.Reset()
		
		-- Create an array of centurii
		for each Centurion in Centurions do			
		
			-- Create a new centurion
			Centurion = create CenturionDesign(self)
			
			-- The centurion will reply when he has created his cohort
			Centurion <- AreYouReady
		end		
	end

	-- A Centurion sends the message 'Paratus' to the legatus to signal he is ready for the test (the cohorts have been created)
	on Paratus do
		
		-- Count how many Centurions have responded
		Test.Replies += 1
		
		-- If all are ready..
		if Test.Replies is NrOfCohorts then
			
			-- Give some explanation
			Window <- print("
	Small Message test
	------------------
	There are 10 Cohorts of 500 Legionaries each.
	Each Legionary will send 200 messages to its Centurion and the Centurion then send the results
	for his Cohort back to the Legatus of the Legion. This means that for each test 1 million messages
	are sent and received (not counting the message traffic between the Centurii and the Legatus).
	The test is repeated [NrOfTestRounds] times.\n")
			
			-- Reset the overall test results
			AllTests.Reset()
			
			-- Reset the test results
			Test.Reset()
				
			-- The first test is the small message test
			TestCommand = "SmallMessageTest"

			-- start the small message test by sending a command to each Centurion
			for each Centurion in Centurions => Centurion <- (TestCommand)	
		end
	end 
	
	-- Centurions send their result to the Legatus
	on Results( int Messages, int Bytes ) do
							
		-- Count the nr of test that were finalised
		Test.Replies += 1
		
		-- Add the nr of messages to the total nr
		Test.Messages += Messages
		
		-- Only the second test counts the nr of bytes
		Bytes ? Test.KBytes += Bytes / 1000

		-- if every cohort has finalised the test..
		if Test.Replies is NrOfCohorts then
		
			-- Stop the test 
			Test.Stop()
			
			-- Add the test results to the overall test results
			AllTests.Add( Test )
			
			-- print the test reports
			Test.Report( Window )
	
			-- Get the status of the message switch (for debugging purpose only)
			//system <- MessageSwitch.GetStatus
			
			-- if we have done all rounds..
			if AllTests.NrOfTests is NrOfTestRounds then

				-- report the total test results
				AllTests.Report(Window)
				
				-- If we have not yet done the big message test we launch it now
				if TestCommand is "SmallMessageTest" then
							
					-- Give some explanation
					Window <- print("
				
	Big Message test
	----------------
	Each Legionary will send 20 messages with random length between 0 and 64KBytes to its Centurion. 
	Each Cohort will therefore send approximately 500 * 20 * 32 KBytes = 320 MBytes, so all cohorts together
	will send around 3.2 GBytes in 100,000 messages.
	The test is also repeated [NrOfTestRounds] times.\n")
				
					-- Set the new command
					TestCommand = "BigMessageTest"
				
					-- Reset
					Test.Reset()
					
					-- Reset also the overall results
					AllTests.Reset()
					
					-- Start the big message test
					for each Centurion in Centurions => Centurion <- (TestCommand)			
				end		
			else		
				-- reset the test results
				Test.Reset()
				
				-- relaunch the test
				for each Centurion in Centurions => Centurion <- (TestCommand)
			end			
		end
	end
	
end -- Legatus

-.-
	A Centurion commands a group of 80 legionaries, a Centuria. Six Centuria make up a cohort and 
	the most senior Centurion held command over the cohort. In our example here we just have one
	Centurion for a cohort of 500 legionaries
-.-
design CenturionDesign(cell Legatus) is
	
	keep Legatus						-- keep the id of the chief commander	
	const int NrOfLegionaries = 500		-- The nr of legionaires in a cohort
	const int NrOfSmallMessages = 200	-- The nr of messages we will request each legionaire to send
	const int NrOfBigMessages = 20		-- The nr of messages we will request each legionaire to send
	int NrOfReplies = 0					-- The nr of legionaires done sending
	int Messages = 0					-- The count of messages received
	int Bytes =0						-- Nr of bytes received

	-- A Centurion commands a cohort 
	cell Cohort = create CohortDesign( NrOfLegionaries )

	-- A request from the Legatues
	on AreYouReady do
		Legatus <- Paratus
	end
	
	-- The signal from the Legatus to start the test
	on SmallMessageTest do
		
		-- Reset the counters
		NrOfReplies = Messages = Bytes = 0
	
		-- Request all legionaries to send messages
		Cohort <- SendSmallMessages( NrOfSmallMessages )
	end
	
	-- The signal from the Legatus to start the test
	on BigMessageTest do
	
		-- Reset the counters
		NrOfReplies = Messages = Bytes = 0	
		
		-- Request all legionaries to send messages
		Cohort <- SendBigMessages( NrOfBigMessages )
	end
	
	-- This is the small test message that each legionary sends n-times
	on TestMessage(int a, float b, xyz vector) do
		Messages += 1
	end
	
	-- This is the big test message that each legionary sends n-times
	on TestMessage(byte[] M) do	
		Messages += 1	
		Bytes += nel M	
	end
	
	-- A legionaire confirms that he has sent all test messages with this message
	on AllMessagesSent do
			
		-- keep track of the nr of send reports
		NrOfReplies += 1
		
		-- Check if all legionaires are done sending
		NrOfReplies is NrOfLegionaries ? Legatus <- Results( Messages, Bytes ) 		
	end
	
end -- Centurion

-- A cohort is a small group of legionaries 
design CohortDesign( int NrOfLegionaries ) is 

	-- just create all the legionaries
	constructor is
		for i = 1 to NrOfLegionaries => create LegionaryDesign
	end
	
	-- default action is to pass all messages to the legionaries
	on ? =>	all <- (same)

end -- Cohort

-- A legionary - just does what he is asked to do
design LegionaryDesign is

	-- The small test message that we send is an int, a float and a vector (20 bytes)
	-- together with the name (12 bytes) and the signature (4 bytes ) this makes 36 bytes.
	-- A message also has a header of 24 bytes, so the total size is 60 bytes.

	on SendSmallMessages(int NrOfMessages) do	
			
		-- send the nr of test messages requested
		for i = 1 to NrOfMessages => sender <- TestMessage( 10, 20.5, [1,2,3] )

		-- signal the end of transmission
		sender <- AllMessagesSent
	end
	
	-- To be able to send a large nr of big messages without running out of memory, we send these 
	-- messages one by one iso all at once as we did for the small messages: 10 cohorts of 
	-- 500 Legionaries sending 20 messages of +- 32 KBytes = 3.200.000.000 bytes !
	-- We do this by using the delivery notification (see below)
	
	-- we keep the count and destination
	int NrOfMessages
	cell Destination
	
	-- A function to send a big message 
	function CheckAndSend is 
		-- a test message of a given size between 0 and 65k
		byte BigMessage[ randi() and 0x0000FFFF ]
		
		-- more to send ? 
		(NrOfMessages -~ 1) ? Destination <+- TestMessage( BigMessage ) : Destination <- AllMessagesSent
	end
	
	-- A message to start sending the big messages
	on SendBigMessages(int NrOfMessages) do
	
		-- save the nr of messages
		keep NrOfMessages
		
		-- we will again send the messages to the sender - save the sender
		Destination = sender
		
		-- Send the first message
		CheckAndSend()
	end
	
	-- After each delivery notification ...
	on TestMessage.DN do
		
		-- ..we send the next message as long as required
		CheckAndSend()
	end
end -- Legionary