Regarding the answer to the link you provided, it lists the attributes: nslimit, nslimit1, mclimit for dot layout engine, and maxiter for neato or fdp layout engines; and attribute splines.
By default your code uses dot layout engine as described in documentation. If the choice of engine is not important to you, then for example neato gives almost 10 times acceleration (according to my observations), to use it (or to use the other engines such as circo or twopi, that are listed in the link that you pointed out in your question) you can see an example in the documentation:
import graphviz
g = graphviz.Graph(engine='neato')
# You can also change engine attribute on existing instance:
g.engine = 'circo'
To use the attributes nslimit, nslimit1 and etc, in your code, you can look in the GraphViz documentation to which object (Graph, Node or Edge) the attribute belongs (for example about nslimit, cite: Valid on: Graphs) and write it in your code according to the python graphviz library's documentation:
import graphviz
ps = graphviz.Digraph('pet-shop',
graph_attr={'nslimit': '0',
'nslimit1': '2'},
node_attr={'shape': 'plaintext'},
edge_attr={'color': 'black'})
For example, I will replace the line dot = Digraph(format="png") with:
dot = Digraph(format="png",
engine='neato', # Layout engine.
graph_attr=dict(splines='true',
sep='5',
overlap='scale'),
node_attr=dict(shape='circle',
margin='0',
fontsize='10',
width='0.2',
height='0.2',
fixedsize='true'),
edge_attr=dict(arrowsize='0.4'))
and get result (34 seconds, 55 MB, posted a thumbnail):

I recreated MWE from your example for testing:
from graphviz import Digraph
import random
nodes = 100 # 50 can be used for test purposes, to reduce time.
outgoing_edges = 40 # Because 40 edges * 100 nodes = 4000 edges
class G:
def __init__(self):
self.graph = {}
# Fill graph with randomly generated data:
for node in range(nodes):
self.graph[node] = random.sample(range(nodes), outgoing_edges)
def letter(self, key):
return str(key)
def draw_graph(self):
dot = Digraph(format="png")
edges = set()
for k, v in self.graph.items():
dot.node(self.letter(k))
for neighbor in v:
edge = self.letter(k) + "_" + self.letter(neighbor)
if edge not in edges:
edges.add(edge)
# dot.edges(list(edges))
for edge in edges:
dot.edge(*edge.split("_"))
dot.render('og.gv', view=True)
g = G()
g.draw_graph()
You can measure the speed of dot.render() with timeit:
import timeit
start_time = timeit.default_timer()
func1()
print(timeit.default_timer() - start_time)